Skip to main content

A modern terminal UI framework powered by hypermedia served over HTTP.

Project description

celx

celx

A modern terminal UI framework powered by hypermedia served over HTTP.

pip install sh40-celx

See /server for an example server & app.

Quickstart

celx is a TUI application framework inspired by htmx. It emphasizes the usage of hypermedia as the engine of application state (HATEOAS) as an alternative to reactive client-side frameworks. celx apps are written as XML fragments, and communicated through the Hypertext Transfer Protocol (HTTP).

Let's start with a basic application index (running on /):

<celx version="0">
  <page>
    <tower eid="root">
      <row eid="header">
        <text>Hello World</text>
      </row>
      <tower eid="body">
        <text>This is the app's body</text>
        <button>Insert content</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 modify header to only take 1 cell of height:

<row eid="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: 1

You can insert <style> tags into the page and celx tags as well. We don't add a scoping header in such cases, as those objects aren't selectable. page styles are local to the current page object, celx (app) styles are global to all pages.

We use Celadon under 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>
  Insert content
  <style> width: null </style>
</button>

Alternatively, you can use a pre-defined group to do the same:

<button group="width-fill">
  Insert content
</button>

Adding interactivity

You 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:

<button group="width-fill" on-submit="GET /content; swap in #body">
  Insert content
</button>

And let's add the corresponding endpoint to the server (running on /content):

<text>This is some cool content</text>

After this, our button will:

  • Send an HTTP GET request to /content, keeping its result
  • Parse the result as a widget, and swap #body's children with it

The syntax used here is quite simple:

<command> <arg1> <arg2> ...

...where command is one of:

  • GET
  • POST
  • insert
  • swap
  • append

POST optionally 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, swap and append take a location as their first argument, similar to hx-swap. Its value must be one of:

  • in: Add result into the targets children
  • before: Add result before the target (by essentially executing the command on the target's parent, offset by the target's offset)
  • after: Add result after the target (the same way)
  • None: (only for swap) Replaces the target widget completely, deleting it from its parent and putting result in its place.

While swap replaces the target's (or its parent's) children completely (deleting previous content), insert and append add onto the current list

So in effect, our text and button will disappear and get replaced by whatever our server returns.

rule

Features

Hypermedia as the Engine of Application State

Since applications are served over HTTP, you don't have to write any client side code. So why is that a good thing?

  • No client-side state duplication (your client doesn't even have to be aware of 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 do anything with 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 server

    Don'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 code

    Your 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 a pip install.

  • Running a celx & html of the same backend on the same server

    Since 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 engine

As 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 rather Celadon'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
    # guidelines
    content_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: null

rule

Documentation

Once 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 using python3 -m pydoc <name>.

We will also create some example server applications to get you started with.

rule

See also

  • Hypermedia 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 runtime
  • Zenith: 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!)

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

sh40_celx-0.6.0.tar.gz (12.7 kB view details)

Uploaded Source

Built Distribution

sh40_celx-0.6.0-py3-none-any.whl (12.8 kB view details)

Uploaded Python 3

File details

Details for the file sh40_celx-0.6.0.tar.gz.

File metadata

  • Download URL: sh40_celx-0.6.0.tar.gz
  • Upload date:
  • Size: 12.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.7.1 importlib_metadata/4.12.0 pkginfo/1.8.2 requests/2.26.0 requests-toolbelt/0.9.1 tqdm/4.65.0 CPython/3.9.18

File hashes

Hashes for sh40_celx-0.6.0.tar.gz
Algorithm Hash digest
SHA256 c27e8215f8a5cf2192fb0d7e9bc3ab59f22dbcaf32e58e7cc9b35b85e1cd365c
MD5 cca2497c177df97bb7189818a41011e7
BLAKE2b-256 fbe5ae84a0548df2e533b97c2b330338270529d9b72f05cc5c905107946c3ea1

See more details on using hashes here.

File details

Details for the file sh40_celx-0.6.0-py3-none-any.whl.

File metadata

  • Download URL: sh40_celx-0.6.0-py3-none-any.whl
  • Upload date:
  • Size: 12.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.7.1 importlib_metadata/4.12.0 pkginfo/1.8.2 requests/2.26.0 requests-toolbelt/0.9.1 tqdm/4.65.0 CPython/3.9.18

File hashes

Hashes for sh40_celx-0.6.0-py3-none-any.whl
Algorithm Hash digest
SHA256 321ae8963db572a2df42fda5b1f1314742245dd5be4baf091ea80a3303e1d555
MD5 90efb07a20643981c4038e69943e1b1f
BLAKE2b-256 8b8e16d3f331eff537848d84512fb705338f0d9f484104d2c991bfd1e2109af5

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page