Reference · Panelith 0.1.0 · plain HTML edition; the interactive edition has search and live examples
Schema
On this page 4 sections
- The shell (the manifest)
- AppInfo
- Brand
- Theme
- Canvas
- PaneHeader
- Glyph
- KindSpec
- HotkeySpec
- CommandSpec
- ListColumn
- ListStatus
- ListSpec
- TopBar
- Manifest
- Actions
- OpenPane
- SetPaneState
- PatchPaneState
- Subtab
- SetSubtabs
- ClosePane
- FocusPane
- RenameTab
- SetTitle
- Notify
- RefreshList
- OpenCommandBar
- RunBuiltin
- SpotStep
- Spotlight
- SetRoute
- OpenUrl
- SendEvent
- ActionList
- LayoutRecord
- EmptyCall
- Requests
- TabRef
- Viewport
- ClientContext
- RegisterPane
- PaneEvent
- CommandCall
- RouteCall
- ListRows
- UIAction
The pydantic models in panelith.schema: the contract between the Python side and the shell. The manifest describes the shell; actions are what handlers return; requests are what the shell sends.
The shell (the manifest)#
AppInfo#
| Field |
Type |
Default |
Description |
id |
str |
required |
the app id: [a-z][a-z0-9_-]*; names the session cookie and the local-storage namespace |
title |
str |
required |
the window title |
version |
str |
'0.1' |
the app's version string |
Brand#
The top-left mark. text renders the title in a monoline wordmark style,
svg inlines markup verbatim, image points at a URL (served by the app or
external).
| Field |
Type |
Default |
Description |
text |
str | None |
None |
the wordmark text for a text brand; each word may take a colour |
colors |
list[str] |
[] |
per-word colors for text brands |
svg |
str | None |
None |
inline SVG markup for an svg brand |
src |
str | None |
None |
the image URL for an image brand |
alt |
str |
'' |
alt text for svg and image brands |
height |
int |
28 |
the mark's height in pixels |
short |
str | None |
None |
compact form shown on phones in place of the mark; a text brand defaults to its initials |
Theme#
CSS custom-property overrides. Keys are token names without the --pl-
prefix (accent, pane-bg, …); families maps a family id to its color,
emitted as --pl-family-<id>.
| Field |
Type |
Default |
Description |
scheme |
str |
'dark' |
the colour scheme the shell starts in; auto follows the OS; the visitor's own toggle (the app.theme built-in) is remembered per app — Options: dark · light · auto |
tokens |
dict[str, str] |
{} |
--pl-* overrides by name without the prefix (accent, pane-bg, …) — Options: 45 optionsaccent assessment author-purple backdrop bad bg cmdbar-top cmdbar-trigger-h cmdbar-width code-bar code-bg code-class code-comment code-decorator code-function code-keyword code-number code-operator code-punct code-string collapsed-px collected danger divider divider-hover drop family-assessment family-author family-independent family-problem focus focus-outline mono ok pane-bg pane-border pane-gap sans shadow-rgb text text-dim topbar-h warn wash-rgb white
as keys; values and defaults in Tokens |
families |
dict[str, str] |
{'independent': '#4f8cff', 'problem': '#e8a33d', 'author'... |
family id → colour, emitted as --pl-family-<id> |
Canvas#
| Field |
Type |
Default |
Description |
max_columns |
int |
2 |
how many columns the canvas may split into (1–4) |
max_rows |
int |
3 |
how many rows per column (1–6) |
initial |
str |
'launcher' |
what the first pane shows: the launcher tiles, nothing, or a kind — Options: launcher · empty · kind |
initial_kind |
str | None |
None |
the kind opened at start when initial='kind' |
bottom |
bool |
False |
allow one full-width pane across the bottom of every column (the pane.splitBottom built-in); it holds one pane, resizes against the grid, and is off by default |
| Field |
Type |
Default |
Description |
visible |
bool |
True |
False renders the body only: no tab strip, no icons |
glyph |
bool |
True |
show the kind's glyph on the tab |
rename |
bool |
True |
double-click renames the tab |
close |
bool |
True |
a × on the tab |
chain |
bool |
True |
the tether icon when the pane has tethered partners |
cycler |
bool |
True |
the ‹ › buttons of a kind with a cycler |
maximize |
bool |
True |
the maximize / restore icon |
collapse |
bool |
True |
the collapse icon |
Glyph#
A custom tab/launcher glyph: SVG path d strings drawn in a 24×24 box with
the family stroke (the built-in glyphs are referenced by name instead).
| Field |
Type |
Default |
Description |
paths |
list[str] |
required |
SVG path d strings in a 24×24 box |
fill |
bool |
False |
fill the paths with the family colour instead of stroking them |
KindSpec#
| Field |
Type |
Default |
Description |
id |
str |
required |
the kind id |
label |
str |
required |
the tab name and launcher caption |
glyph |
str | Glyph |
'ring' |
a built-in glyph name or a custom Glyph — Options: 49 optionsring square table list grid form file gear bolt star home info bell calendar database globe image play book user chat flask dollar key cube math code prompt terminal output graph chart plot solve steps puzzle puzzle-left puzzle-right text document pen author checklist report timer clipboard folder search commands
or a Glyph; pictured in Glyphs |
family |
str |
'independent' |
the colour family of the glyph, tile and tethers — Options: independent · problem · author · assessment or any key of Theme.families |
widget |
str |
'markdown' |
the widget that renders the body — Options: markdown · html · table · keyvalue · output · form · text · blocks or a plugin's id |
widget_options |
dict[str, Any] |
{} |
passed to the widget unchanged |
persist |
list[str] |
[] |
state keys a saved layout carries for this kind; the rest is rebuilt by restore_state |
subtabs |
bool |
False |
a sub-tab strip inside the pane |
closable |
bool |
True |
a × on the tab and a close command |
launcher |
bool |
True |
a launcher tile and a generated <kind>.new opener |
launcher_group |
str | None |
None |
a heading the launcher groups this kind's tile under; None keeps it in the first, unnamed group |
tip |
str |
'' |
the launcher tile's tooltip |
cycler |
str | None |
None |
a list id: ‹ › buttons that send a cycle event |
header |
PaneHeader | None |
None |
this kind's header flags; None inherits the app's |
single |
bool |
False |
at most one pane of this kind is open at a time |
HotkeySpec#
A chord under the app's modifier namespace. code is a KeyboardEvent.code
(KeyK, Quote, Enter); label is what the help overlay prints.
| Field |
Type |
Default |
Description |
code |
str |
required |
the KeyboardEvent.code the shell matches |
ctrl |
bool |
False |
the chord also needs control |
label |
str |
required |
what the help overlay prints |
CommandSpec#
| Field |
Type |
Default |
Description |
id |
str |
required |
the command id |
title |
str |
required |
the entry in the bar |
keywords |
str |
'' |
extra text the bar's search matches |
hotkey |
HotkeySpec | None |
None |
the parsed chord, if any |
scope |
str |
'global' |
'global' or 'kind:' (shown only when such a pane is focused) — Options: global · kind:<kind id> |
server |
bool |
True |
False: the shell runs it itself (the built-in pane commands) |
launcher |
bool |
False |
the launcher shows a tile for this command |
launcher_label |
str | None |
None |
the tile's caption |
launcher_glyph |
str | Glyph | None |
None |
the tile's icon — Options: 49 optionsring square table list grid form file gear bolt star home info bell calendar database globe image play book user chat flask dollar key cube math code prompt terminal output graph chart plot solve steps puzzle puzzle-left puzzle-right text document pen author checklist report timer clipboard folder search commands
or a Glyph; pictured in Glyphs |
launcher_color |
str | None |
None |
the tile's colour |
launcher_tip |
str | None |
None |
the tile's tooltip |
launcher_group |
str | None |
None |
a heading the launcher groups the tile under |
ListColumn#
| Field |
Type |
Default |
Description |
key |
str |
required |
the row key |
label |
str | None |
None |
the header text; the key when omitted |
width |
int | None |
None |
a fixed width in pixels |
grow |
bool |
False |
take the spare width |
mono |
bool |
False |
monospace, for ids and figures |
ListStatus#
| Field |
Type |
Default |
Description |
key |
str |
required |
the row key the marks and tones read |
marks |
dict[str, str] |
{} |
value -> glyph, e.g. {'solved': '✓'} |
tones |
dict[str, str] |
{} |
value -> 'ok' | 'half' | 'dim' |
ListSpec#
| Field |
Type |
Default |
Description |
id |
str |
required |
the list id |
title |
str |
required |
the drawer heading and the bar's section name |
sigil |
str | None |
None |
one character that lists this source in the command bar — Options: any single character other than the command sigil |
drawer |
bool |
True |
whether the list has a drawer |
columns |
list[ListColumn] |
required |
the columns a row shows |
search |
list[str] |
[] |
row keys the search box matches |
facets |
list[str] |
[] |
row keys that get a filter button each |
status |
ListStatus | None |
None |
a mark and tone per row from one key |
row_action |
str | None |
None |
command id run with {row} when a row is chosen |
empty_text |
str |
'Nothing here yet' |
what an empty list says |
live |
bool |
False |
the server searches: every keystroke fetches /api/lists/?q=… and the shell keeps the order it gets back |
snippet |
str | None |
None |
a row key shown dim under the row's columns (a search excerpt) |
tree |
str | None |
None |
a row key holding an indent level (0, 1, 2): level 0 rows read as headings, deeper rows sit indented beneath them |
chips |
list[str] |
[] |
facets (from facets) shown as a row of toggle chips above the rows instead of a filter button |
TopBar#
| Field |
Type |
Default |
Description |
buttons |
list[str] |
['hotkeys'] |
right-side buttons in order: 'hotkeys', 'commands', 'layouts' (save and restore the canvas), or 'list:' — Options: hotkeys · commands · layouts · list:<list id> |
placeholder |
str |
'Search commands…' |
the text in the idle bar |
trigger_sigil |
str | None |
None |
the sigil the top-bar trigger opens the bar with (default: the command sigil); a docs app opens straight into its search list |
Manifest#
| Field |
Type |
Default |
Description |
app |
AppInfo |
required |
id, title, version |
brand |
Brand |
required |
the top-left mark |
theme |
Theme |
required |
scheme, tokens, families |
canvas |
Canvas |
required |
grid limits and the start mode |
pane_header |
PaneHeader |
required |
the default header flags |
topbar |
TopBar |
required |
buttons, placeholder, trigger sigil |
command_sigil |
str |
'>' |
the character that lists commands in the bar |
hotkey_modifiers |
list[str] |
['alt', 'shift'] |
the chord every hotkey lives under — Options: alt · shift · ctrl · meta |
kinds |
list[KindSpec] |
required |
every kind |
commands |
list[CommandSpec] |
required |
the built-ins first, then the app's commands and generated openers |
lists |
list[ListSpec] |
required |
every list source |
features |
dict[str, bool] |
{'sse': True} |
sse (push is on), routes (the app handles the URL hash), on_empty (the app wants to hear when the canvas is emptied) and layouts_server (saved layouts go to the app's LayoutStore instead of the browser) |
plugins |
list[str] |
[] |
URLs of ES modules the shell imports before first render (custom widgets) |
Actions#
OpenPane#
| Field |
Type |
Default |
Description |
kind |
str |
required |
the kind to open |
name |
str | None |
None |
the tab name; the kind's label when omitted |
state |
dict[str, Any] | None |
None |
the pane's first state; skips initial_state |
parent |
str | None |
None |
cid of the tab this one is tethered to |
placement |
str |
'focused' |
where a child goes relative to its parent; 'tab' = the parent's own pane — Options: focused · below · beside · new · tab |
focus |
bool |
True |
focus the new tab |
cid |
str | None |
None |
reuse: surface this cid instead of opening a new tab if it exists |
size |
float | None |
None |
fraction of the parent's row/column a new below/beside pane takes |
SetPaneState#
| Field |
Type |
Default |
Description |
cid |
str |
required |
the pane |
state |
dict[str, Any] |
required |
the whole new state |
PatchPaneState#
| Field |
Type |
Default |
Description |
cid |
str |
required |
the pane |
patch |
dict[str, Any] |
required |
fields merged into the state |
Subtab#
| Field |
Type |
Default |
Description |
id |
str |
required |
the sub-tab id |
name |
str |
required |
the sub-tab name |
SetSubtabs#
| Field |
Type |
Default |
Description |
cid |
str |
required |
the pane |
subtabs |
list[Subtab] |
required |
the strip, in order |
active |
str | None |
None |
the selected id |
ClosePane#
| Field |
Type |
Default |
Description |
cid |
str |
required |
the tab to close |
FocusPane#
| Field |
Type |
Default |
Description |
cid |
str |
required |
the tab to focus |
| Field |
Type |
Default |
Description |
cid |
str |
required |
the tab |
name |
str |
required |
its new name |
SetTitle#
| Field |
Type |
Default |
Description |
text |
str | None |
required |
the top bar's centre text; None restores the placeholder |
Notify#
| Field |
Type |
Default |
Description |
text |
str |
required |
the toast text |
tone |
str |
'info' |
its colour — Options: info · ok · warn · error |
ms |
int |
2600 |
how long it stays |
RefreshList#
| Field |
Type |
Default |
Description |
list_id |
str |
required |
the list to fetch again |
OpenCommandBar#
| Field |
Type |
Default |
Description |
sigil |
str |
'>' |
the sigil to open with |
query |
str |
'' |
text typed after the sigil |
RunBuiltin#
| Field |
Type |
Default |
Description |
id |
str |
required |
a built-in command id, or list:<id> |
SpotStep#
One stop of a spotlight: the shell element to ring (a data-pl hook such as
topbar.brand, cmdbar, drawer, launcher, help, pane:<cid>,
tab:<cid>, header:<cid>, body:<cid>, subtabs:<cid>, divider:<col>,
divider:<col>:<row>, cmdbar.row:<command id>, launcher.tile:<command id>,
topbar.list:<id>, topbar.hotkeys, topbar.commands, toast), a caption,
an optional auto-advance after ms, and a family name for the ring colour.
| Field |
Type |
Default |
Description |
target |
str |
required |
the data-pl hook of the element to ring |
text |
str | None |
None |
caption shown beside the ring |
ms |
int | None |
None |
advance (or dismiss) after this many milliseconds; None waits for a click |
tone |
str | None |
None |
a theme family name for the ring colour, or preview (the yellow warn colour); None uses the accent |
patch |
dict[str, Any] | None |
None |
a preview: a partial manifest (brand, theme, command_sigil, topbar, pane_header) the shell applies while this step shows and reverts when the spotlight ends |
Spotlight#
Ring a shell element with a caption, or walk the user through several
(a tour). An empty steps list clears any spotlight showing.
| Field |
Type |
Default |
Description |
steps |
list[SpotStep] |
[] |
the stops, in order; empty clears the spotlight |
dismiss |
str |
'click' |
click: any click outside the caption dismisses; auto: only the caption buttons, Escape or ms do |
SetRoute#
Put path in the address bar's hash (#/guide/panes) without reloading or
adding history, so the visitor can share or bookmark where they are. The shell
posts the hash back to App.on_route on load and whenever it changes by hand.
| Field |
Type |
Default |
Description |
path |
str |
required |
the hash to write, without the # |
push |
bool |
False |
add a history entry, so the browser's Back returns to the previous hash (the default replaces the current entry) |
OpenUrl#
Open a URL: a new tab by default (the browser allows it right after the
user's click that produced this action), or navigate the current one.
| Field |
Type |
Default |
Description |
url |
str |
required |
the URL |
new_tab |
bool |
True |
open a new tab rather than navigating |
SendEvent#
Have the shell post an event back to a pane (lets a command delegate to the
pane's own handler, e.g. code.run -> the editor's run event).
| Field |
Type |
Default |
Description |
cid |
str |
required |
the pane |
name |
str |
required |
the event name |
payload |
dict[str, Any] |
{} |
the event payload |
ActionList#
| Field |
Type |
Default |
Description |
actions |
list[Union[OpenPane, SetPaneState, PatchPaneState, SetSubtabs, ClosePane, FocusPane, RenameTab, SetTitle, Notify, RefreshList, OpenCommandBar, RunBuiltin, SendEvent, Spotlight, OpenUrl, SetRoute]] |
[] |
the actions, applied in order |
LayoutRecord#
A saved canvas layout as the shell stores it: the split geometry and, per
tab, the kind and the keys it persists. The shell builds and reads data;
a LayoutStore only keeps records by profile.
| Field |
Type |
Default |
Description |
id |
str |
required |
stable id (the shell derives it from the name) |
name |
str |
required |
the name the visitor gave it |
saved_at |
float |
required |
epoch seconds |
summary |
str |
'' |
one line: pane count and what is open |
data |
dict[str, Any] |
{} |
the layout: columns, rows, tabs with their seeds |
EmptyCall#
The shell's report that the canvas has no tabs left (App.on_empty).
| Field |
Type |
Default |
Description |
context |
ClientContext |
ClientContext(...) |
the client context |
Requests#
TabRef#
| Field |
Type |
Default |
Description |
cid |
str |
required |
the tab's content id |
kind |
str |
required |
its kind |
name |
str | None |
None |
its name |
parent |
str | None |
None |
the cid it is tethered to |
Viewport#
| Field |
Type |
Default |
Description |
width |
int |
0 |
window width in CSS pixels |
height |
int |
0 |
window height in CSS pixels |
ClientContext#
What the shell knows that a handler may want: the focused tab, every
open tab (cid, kind, parent) in reading order, and the window size.
| Field |
Type |
Default |
Description |
focused |
TabRef | None |
None |
the focused tab |
tabs |
list[TabRef] |
[] |
every open tab in reading order |
columns |
int |
1 |
how many columns the canvas has |
viewport |
Viewport | None |
None |
the window size |
RegisterPane#
| Field |
Type |
Default |
Description |
cid |
str |
required |
the new tab's cid |
kind |
str |
required |
its kind |
parent |
str | None |
None |
the cid it is tethered to |
state |
dict[str, Any] | None |
None |
state carried by the open_pane action, or the browser's persisted copy on a restore |
restore |
bool |
False |
the browser is re-registering a pane it restored from local storage |
seed |
dict[str, Any] | None |
None |
a saved layout is being restored: the keys the kind persists (Pane.persist); the state comes from restore_state and the kind's children are not re-opened (the layout holds them) |
context |
ClientContext |
ClientContext(...) |
the client context |
PaneEvent#
| Field |
Type |
Default |
Description |
name |
str |
required |
the event name |
payload |
dict[str, Any] |
{} |
the payload |
context |
ClientContext |
ClientContext(...) |
the client context |
CommandCall#
| Field |
Type |
Default |
Description |
args |
dict[str, Any] |
{} |
arguments for ctx.args (a list row under row) |
context |
ClientContext |
ClientContext(...) |
the client context |
RouteCall#
| Field |
Type |
Default |
Description |
path |
str |
required |
the URL hash without # |
context |
ClientContext |
ClientContext(...) |
the client context |
ListRows#
| Field |
Type |
Default |
Description |
rows |
list[dict[str, Any]] |
required |
the rows |
total |
int | None |
None |
how many rows in all |
UIAction#
UIAction is the discriminated union of every action model (on type); ActionList is {"actions": [UIAction, ...]}, the shape of every reply and every pushed event.