Reference · Panelith 0.1.0 · plain HTML edition; the interactive edition has search and live examples
App
On this page 6 sections
App is the base class an application subclasses. Its class attributes are the shell defaults; override what differs and list the kinds, commands and lists the app ships. One instance serves every visitor (Sessions).
Attributes#
| Attribute | Type | Default | What it changes |
|---|---|---|---|
id |
str |
'app' |
the app id: the session cookie (panelith_<id>) and the local-storage namespace |
title |
str |
'App' |
the window title and the default text brand |
version |
str |
'0.1' |
shown in the manifest |
description |
str |
'' |
|
brand |
Brand | None |
None |
the top-left mark: Brand.text(...), Brand.svg(...), Brand.svg_file(...), Brand.image(...); defaults to the title as text |
theme |
Theme |
Theme(...) |
Theme(scheme=, tokens=, families=) |
canvas |
Canvas |
Canvas(...) |
Canvas(max_columns=, max_rows=, initial=, initial_kind=) |
pane_header |
PaneHeader |
PaneHeader(...) |
PaneHeader(...): the header controls of every kind that does not set its own |
topbar |
TopBar |
TopBar(...) |
TopBar(buttons=, placeholder=, trigger_sigil=) |
command_sigil |
str |
'>' |
the character the bar treats as 'commands' — Options: any single character the bar does not already use (? and . are typical list sigils) |
hotkey_modifiers |
list[str] |
['alt', 'shift'] |
the chord every hotkey lives under: any of alt, shift, ctrl, meta |
builtin_commands |
list[dict[str, Any]] |
[21 entries] |
the shell's own commands to offer (a filtered copy of BUILTIN_PANE_COMMANDS) — Options: 21 optionspane.splitVertical pane.splitHorizontal pane.splitBottom pane.close pane.collapse pane.maximize pane.focusLeft pane.focusRight pane.focusUp pane.focusDown pane.moveContentPrev pane.moveContentNext pane.cyclePrev pane.cycleNext pane.subtabPrev pane.subtabNext pane.rename app.commandBar app.help app.theme app.layouts |
builtin_hotkeys |
dict[str, str | None] |
{} |
{id: key} re-maps a built-in's key; None disables it — Options: 21 optionspane.splitVertical pane.splitHorizontal pane.splitBottom pane.close pane.collapse pane.maximize pane.focusLeft pane.focusRight pane.focusUp pane.focusDown pane.moveContentPrev pane.moveContentNext pane.cyclePrev pane.cycleNext pane.subtabPrev pane.subtabNext pane.rename app.commandBar app.help app.theme app.layouts |
kinds |
list[type[Pane]] |
[] |
the Pane subclasses the app ships (plus any defined in the class body) |
commands |
list[type[Command]] |
[] |
the Command subclasses |
lists |
list[type[ListSource]] |
[] |
the ListSource subclasses |
plugins |
list[str | pathlib.Path] |
[] |
ES modules with custom widgets, paths relative to the file defining the app; served at /plugins/<name> |
session_ttl |
float |
7200 |
seconds an idle, disconnected visitor's session is kept |
max_sessions |
int |
1000 |
how many sessions are kept at most; the idlest go first |
launcher_kinds |
list[str] | None |
None |
|
layouts |
LayoutStore | None |
None |
None: saved layouts stay in the browser; a LayoutStore keeps them on the server per profile(ctx) (Saved layouts) |
max_layouts |
int |
50 |
Hooks and methods#
on_start#
on_start(self) -> None
Called once when the server starts (start background threads here).
on_route#
on_route(self, ctx: Context, path: str) -> Any
Override to open what path (the URL hash without #, e.g.
/guide/panes#events) names. Returning actions that include set_route
is fine. Only when overridden does the shell send routes at all.
profile#
profile(self, ctx: Context) -> str | None
Who the visitor is, for a server-side LayoutStore (and anything else an
app keeps per person). The default returns ctx.user, which is None until an
app sets it, so every visitor shares one profile; an app with accounts
returns the username it resolves from the session (self.session().data)
or a cookie.
| Argument | Meaning |
|---|---|
ctx |
a context for the current session (no pane). |
extend_server#
extend_server(self, api: Any) -> None
Hook: add routes to the FastAPI app that serves this App, before the shell's catch-all is registered. The docs app uses it for its crawlable HTML pages and sitemap; a plain app needs nothing here.
| Argument | Meaning |
|---|---|
api |
the FastAPI application for this App (one per mount). |
push#
push(self, *actions: Any) -> None
Send actions to the current visitor's open tabs (needs the running
server). Outside a request there is no current visitor, so this reaches
every session, like broadcast.
broadcast#
broadcast(self, *actions: Any) -> None
Send actions to every connected visitor. State actions are mirrored into each session separately, so a cid only has an effect where it exists.
after_reply#
after_reply(self, fn: Callable[[], Any]) -> None
Run fn once the current request's reply has been applied (start a
worker thread here so its pushes cannot be overwritten by the reply's own
state actions). Outside a request it runs at once.
bind#
bind(self, session: Session) -> Iterator[Session]
Serve the enclosed code as session (what the server wraps each request in).
Callbacks queued with after_reply run when the block exits, i.e. after
the handler's actions have been mirrored into the session.
manifest#
manifest(self) -> s.Manifest
run#
run(self, host: str = '127.0.0.1', port: int = 8000, open_browser: bool = False, log_level: str = 'info', forwarded_allow_ips: str | None = None) -> None
Serve the app with uvicorn. Behind a reverse proxy pass
forwarded_allow_ips (the proxy's address, or "*") so X-Forwarded-Proto
is trusted and the session cookie is marked Secure on https.
kind#
kind(self, kind_id: str) -> Pane | None
command#
command(self, command_id: str) -> Command | None
list_source#
list_source(self, list_id: str) -> ListSource | None
context#
context(self, client: s.ClientContext, pane: PaneRef | None = None, args: dict[str, Any] | None = None) -> Context
session#
A property. The visitor whose request is being served (a shared default otherwise).
panes#
A property. The current session's pane table: cid -> live record with its state.
sessions#
A property.
LayoutStore#
Where saved layouts live when an app keeps them on the server instead of in
the browser (App.layouts = MyStore()). Records are schema.LayoutRecord
dicts, kept per profile: App.profile(ctx) names the visitor (a username once
accounts exist); None is the shared default. Subclass and implement the three
methods over any storage; MemoryLayoutStore is the in-process reference.
Set App.layouts to an instance to keep saved layouts on the server (Saved layouts); the shell then uses /api/layouts.
Methods to implement#
list#
list(self, profile: str | None) -> list[dict[str, Any]]
Every saved layout of profile, newest first.
save#
save(self, profile: str | None, record: dict[str, Any]) -> None
Store record (replacing one with the same id).
delete#
delete(self, profile: str | None, layout_id: str) -> None
Remove the layout layout_id; a missing id is not an error.
MemoryLayoutStore#
Layouts kept in this process, per profile: the reference implementation and what tests use. They vanish with the process.
Brand#
Factories for the top-left mark; each returns a schema.Brand.
text#
text(text: str, colors: Iterable[str] = (), height: int = 28, short: str | None = None) -> s.Brand
short is the phone form (default: the initials, in the same colors).
svg#
svg(markup: str, height: int = 28, alt: str = '', short: str | None = None) -> s.Brand
short, if given, replaces the mark on phones (a monogram as text).
svg_file#
svg_file(path: str | os.PathLike[str], height: int = 28, alt: str = '', short: str | None = None) -> s.Brand
image#
image(src: str, height: int = 28, alt: str = '', short: str | None = None) -> s.Brand
Also exported#
Theme, Canvas, PaneHeader, TopBar and Glyph are the schema models of the same names, re-exported for app code; see the schema.