PANELITH DOCS Open the interactive docs ↗

Start · Panelith 0.1.0 · plain HTML edition; the interactive edition has search and live examples

A first app

On this page 6 sections
  1. A kind
  2. A kind with behaviour
  3. A command
  4. A list
  5. The app
  6. What to read next

A complete application in a page: a brand, two pane kinds, one command with a hotkey, one list. Every piece below is live in these docs, so each Show opens the real thing.

A kind#

A kind is a type of pane. Subclass Pane, name it, pick a widget, return the first state:

class About(Pane):
    kind = "about"
    label = "About"
    glyph = "info"
    widget = "markdown"
    tip = "A markdown pane whose text came from Python"

    def initial_state(self, ctx):
        return widgets.markdown("# Hello\n\nThis text came from **Python**.\n\n- Split the canvas with `alt+shift+\\`\n- Open the command bar with `alt+shift+.`")

widget = "markdown" means the shell renders state["markdown"]. tip is the launcher tooltip; glyph and label are what the tab shows. Nothing else is required.

A kind with behaviour#

State is a dict the widget renders; a handler is a method decorated with the event name it answers. The server keeps every pane's state, so a handler reads the current value from ctx.pane.state and returns an action that replaces it:

class Counter(Pane):
    kind = "counter"
    persist = ["items"]  # a saved layout keeps the count
    label = "Counter"
    glyph = "bolt"
    widget = "keyvalue"
    tip = "A keyvalue pane with a bump event"

    def initial_state(self, ctx):
        return {"items": [{"key": "Count", "value": 0}]}

    @event("bump")
    def bump(self, ctx, payload):
        n = ctx.pane.state["items"][0]["value"] + payload.get("by", 1)
        return a.set_state(ctx.cid, {"items": [{"key": "Count", "value": n}]})

Open it with Show, then press alt+shift+b while it is focused.

A command#

Commands live in the bar (alt+shift+.) and can have a hotkey. This one is scoped to the counter kind, so it only appears, and its hotkey only fires, while a counter pane is focused. It does not touch the pane's state; it sends the pane an event and lets the pane's own handler do the work:

class Bump(Command):
    id = "counter.bump"
    title = "Counter: Add One"
    keywords = "counter add increment bump"
    scope = "kind:counter"  # offered only while a counter pane is focused
    hotkey = "b"  # alt+shift+b

    def run(self, ctx):
        return a.send_event(ctx.cid, "bump", by=1)
With a Counter pane focused the bar offers Bump; from any other pane it is left out. Enter runs it.

A list#

A ListSource provides rows. The drawer shows them with a search box and facets; a sigil puts the same rows in the command bar; row_action names the command a chosen row runs with {"row": row}:

class Fruits(ListSource):
    id = "fruits"
    title = "Fruits"
    sigil = "."
    columns = ["id", "name", "color"]
    search = ["name"]
    facets = ["color"]

    def rows(self, ctx):
        return [{"id": 1, "name": "Apple", "color": "red"}, {"id": 2, "name": "Lime", "color": "green"}]
The list button toggles a drawer. These docs' own list is the Contents, with a section facet.

The app#

The app lists what it ships and sets the shell defaults it changes:

class Hello(App):
    id = "hello"
    title = "Hello Panelith"
    brand = Brand.text("HELLO, PANELITH", colors=["#4B8BBE", "#FFD43B"])
    kinds = [About, Counter]
    commands = [Bump]
    lists = [Fruits]
    topbar = TopBar(buttons=["list:fruits", "hotkeys"])
Open ↗ runs this very app on a page of its own, in a new tab. · Open ↗

Run it with panelith run hello:Hello --open. The canvas opens on a launcher: a tile per kind, plus tiles for the command bar and the Fruits list, which the shell adds. Open Counter, press alt+shift+b, and the number changes: the hotkey ran the command, the command sent an event to the focused pane, the handler returned a new state, the shell re-rendered the widget. Open the drawer from the top bar and the fruit rows appear with a colour facet. Nothing in that sequence was JavaScript.

Tip
Kinds do not need an "open" command. Every kind with a launcher tile gets a <kind>.new command generated for it, so the tile and the command-bar entry are one thing. The Open ↗ button above runs this app on a page of its own, so you can try the sequence without installing anything. On a phone the launcher tiles, the command bar and the drawer are all there; the hotkeys need a keyboard.

Panes, state and events is the authoring model in full. Commands, hotkeys and the launcher and Lists and the drawer cover the rest of the shell. Widgets lists what a pane can show.