Start · Panelith 0.1.0 · plain HTML edition; the interactive edition has search and live examples
A first app
On this page 6 sections
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)
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 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"])
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>.newcommand 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.
What to read next#
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.