Package: sh40-celx
celxA modern terminal UI framework powered by hypermedia served over HTTP.pip install sh40-celxSee/serverfor an example server & app.Quickstartcelxis a TUI application framework inspired byhtmx. It emphasizes the usage of hypermedia
as the engine of application state (HATEOAS) as an alternative to reactive client-side frameworks.celxapps are
written as XML fragments, and communicated through the Hypertext Transfer Protocol (HTTP).Let's start with a basic application index (running on/):<celxversion="0"><page><towereid="root"><roweid="header"><text>HelloWorld</text></row><towereid="body"><text>Thisistheapp'sbody</text><button>Insertcontent</button></tower></tower></page></tower>At the moment, both the header and body will take equal amounts of space on the page. You probably don't want this,
so let's modifyheaderto only take 1 cell of height:<roweid="header"><style>height:1</style></row>You can insert a<style>tag everywhere, and it is always scoped to its parent widget. In effect, this means the above
style gets converted to:Row#header:height:1You can insert<style>tags into thepageandcelxtags as well. We don't add a scoping header in such cases,
as those objects aren't selectable.pagestyles are local to the current page object,celx(app) styles are global
to all pages.We useCeladonunder the hood, so we inherit its selector & styling syntax. You can
set any (already defined) attributes of any selected widget. Let's make the app's button span the whole width using a local
style:<button>Insertcontent<style>width:null</style></button>Alternatively, you can use a pre-defined group to do the same:<buttongroup="width-fill">Insertcontent</button>Adding interactivityYou can press our button already, but you might notice it doesn't do anything. Let's fix that.First, add an on-submit event to the button:<buttongroup="width-fill"on-submit="GET /content; swap in #body">Insertcontent</button>And let's add the corresponding endpoint to the server (running on/content):<text>Thisissomecoolcontent</text>After this, our button will:Send an HTTPGETrequest to/content, keeping its resultParse the result as a widget, and swap#body's children with itThe syntax used here is quite simple:<command> <arg1> <arg2> ......where command is one of:GETPOSTinsertswapappendPOSToptionally takes a selector for its first arg (POST #parent /content), which controls the
widget whos serialized result will be sent in the request. It defaults to the parent of the widget
executing the request (our button, in this case).insert,swapandappendtake a location as their first argument, similar tohx-swap. Its
value must be one of:in: Add result into the targets childrenbefore: Add resultbeforethe target (by essentially executing the command on the target's parent, offset
by the target's offset)after: Add resultafterthe target (the same way)None: (only forswap) Replaces the target widget completely, deleting it from its parent and putting
result in its place.Whileswapreplaces the target's (or its parent's) children completely (deleting previous content),insertandappendadd onto the current listSo in effect, our text and button will disappear and get replaced by whatever our server returns.FeaturesHypermedia as the Engine of Application StateSince applications are served over HTTP, you don't have to writeanyclient side code. So why is that
a good thing?No client-side state duplication (your client doesn't even have to beawareof state)You cannot (and should never) trust client side code. If your application state mutates on the
client side, you must be able to validate it on the server, as the client could doanythingwith that state. This essentially means you have to have duplicated state, and validation on both
sides.Since all of your state is on the server, you avoid most of these issues.You're free to choose your own serverDon't like Python? You can use any HTTP server, in ANY language. Python is more than fast enough
for our runtime, and this way all custom logic & slow operations happen on the server side in the
language of your choosing.Instant usability, no need to install potentially dangerous application codeYour users only need the celx runtime to run your application. From that point on, trying out a new
app takes as much as writing in the URL its served at, and pressing enter. No further installation,
no 'clone my repo, download my build tool and execute these commands', not even apip install.Running a celx & html of the same backend on the same serverSince every bit of state is handled on the backend, you can simply send out different formats to represent
the same interfaces based on who is listening. In the (near) future celx will send a specific header
to tell the server to send celx' XML instead of HTML.A sophisticated styling engineAs shown above, each<style>tag is scoped to its parent widget. Think of this as a less error-prone
version of CSS' inline styles, or a more readable Tailwind. This approach doesn't fully replace the need
for Tailwind-like helper groups, so we have a few of those as well.Our (or ratherCeladon's) styling system is also pretty neat in
other ways. It supports nested styles, hierarchal queries for both direct and indirect parents, states
(CSS' pseudoclasses), as well as your basic CSS stuff like types, ids and classes (named 'groups' in our case).Here is an example from Celadon's README. Most of this should feel fairly familiar if you're used to
working with CSS:Button:fill_style:'@ui.primary'# Automatically use either white or black, based on the W3C contrast# guidelinescontent_style:''# On hover, become a bit brighter/hover:# Equivalent to 'Button/hover'fill_style:'@ui.primary+1'# Become a bit taller if in the 'big' group.big:height:3# If part of a Row in the 'button-row' group, fill available width.# '&' stands for the selector in the previous nesting layer, `Button`# in this case.Row.button-row > &:width:nullDocumentationOnce the shade40 suite gets to a settled state (near 1.0), documentation will be
hosted both online and as a celx application. Until then some of the widget references by usingpython3 -m pydoc <name>.We will also create some example server applications to get you started with.See alsoHypermedia Systems: The primary inspiration for the library. A great book, available
as hard-copy, ebook or even for free.Celadon: The core of the framework, providing the widget & styling
systems and the application runtimeZenith: The inline-markup language used by the framework, which can
be used either directly in widgets<text>[bold]Title</text>or in style definitions<style> content_style: bold </style>.Slate: The engine powering every interaction we make to the terminal and
its APIs, providing us with intelligent per-changed-character drawing and a way to color text (quite a useful
feature!)