All docs
Naming: which is which
A few LSD words carry more than one meaning — View, panel, table, workspace, entity. This page pins each to its exact meaning.
Why this page exists#
A handful of LSD words are overloaded. The meaning is always clear from context in the code, but the words alone can be ambiguous when you are new or reading an error. Here is each one, pinned.
"View"#
| Term | What it is | Where you meet it |
|---|---|---|
View (the class) |
The one UI class — param() fields, @action buttons, and optional data (feed / to_text / to_image). Has the show() verb; engine.add(a_view) then engine.show(). |
from lsdtools import View |
@tool.view |
Declares a factory returning a View for location="sidebar" or location="editor". Editor instance policy and entity acceptance are explicit. |
authoring a package |
MainViewType / SidebarViewContribution |
The declarations that editor and sidebar factories become when mounted. Both are available through lsdtools.extend; the old ViewContribution type is removed. |
extension authoring |
ViewerExtensionSpec.surfaces |
Declares content inside a viewer, including toolbars and overlays, through tool.viewer_extension(...). |
lsdtools.extend |
A @tool.view factory returns a View — the same class you subclass yourself or get from the
views façade. There is no separate wrapper: a view is the object you show().
"Panel"#
@tool.view(..., location="sidebar")— a decorator whose factory returns aView(params@action, nokind). The desktop docks it in the sidebar.
- The word "panel" in Desktop is a placement term; there is no separate
Panelclass in the authoring API. Viewer overlays have their own declared extension surface and do not use@tool.view(location="overlay").
So: a panel is a View with no kind. If you are authoring one, you write a View subclass.
"Table" vs "pa.Table" vs "views.table"#
Table— the one class and constructor: an immutable, zero-copy wrapper around one Arrow table with a small verb list (with_column,filter,join,group_by,sort,print…). Build one withTable({...}),Table([{...}, …]),Table(df), orTable(pa_table); read files withTable.read_csv/Table.read_parquet.- There is no lowercase
table(...)factory — the constructor absorbed it. Two look-alikes remain and are not the LSD class:pa.table(...)is pyarrow's own constructor, andviews.table(...)is the data-view façade that wraps aTablefor display. pa.Table— raw pyarrow..arrowon aTableis the zero-copy escape hatch out to it (andTable(pa_table)brings one back in). A step parameter annotatedTableorpa.Tableis an input port.
"Workspace"#
engine.workspace is the domain workspace: it owns the entity forest, identity resolution,
hierarchy and event bus. Add an entity with engine.workspace.add(entity), then run it with
engine.run(entity.id). Engine.add(view) attaches views; it does not add entities.
engine.artifact_workspace is the separate run / cache directory where step outputs land.
This ArtifactWorkspace is path-compatible and provides .exists, .size(), .dir_for(entity)
and .clean(). Engine() creates a temporary artifact directory; pass
Engine(artifact_workspace="./runs") to choose one. Open a saved project with
Engine.load("project.lsd"), not by passing a filename as the Engine constructor's workspace.
A participant's UI workspace is their arrangement of view tabs. It is distinct from the domain workspace, artifact directory, saved project document and Local package authoring directory.
"Entity"#
Entity— the base class: a node that owns an ordered list of steps. You subclass it (or use@tool.entity) to define a domain type.@tool.entity— registers a creatable entity type (a builder function, or a hand-writtenEntitysubclass).@tool.template(anEntityTemplate) — a recipe that turns dropped file(s) into a ready-wired entity in the import wizard.
Configurable → View#
A short inheritance chain underlies the authoring surface, which is why params behave the same everywhere:
Configurable— anything withparam()fields (steps, entities, the engine — all of them).View— aConfigurablethat also has@actionbuttons and optional data (feed/to_text/to_image). With nokindit renders as a form (a panel); with akind(table/chart/flowchart/custom) it represents data.
Steps are Configurables, not Views — they have parameters but no @action. That they render
in the same inspector as a View is because both are Configurable.
"watch" — the View↔Entity verb#
view.watch(entity) is the one verb that links a view to an entity: the
view holds that entity's output and refreshes on every re-run. It is the symmetry partner of an
entity's run — engine.workspace.add plus run_all() moves data, engine.add(view) plus
show() presents it, and watch is the wire between them.
- It takes an
Entity, a stable entity id, or an unambiguous display name. An unresolved string stays pending until that entity is added to the domain workspace. Watching can happen before or after the view is attached to the engine. - The framework does the bus wiring — you never call
bus.on(...)to feed a view. - Watching an entity that has already run pulls its output immediately (what makes drag-and-drop and a reopened project feel instant).
- It returns the view, so
views.table(title=...).watch(entity)reads as one phrase.
The desktop's drag-drop, the 3-D checkbox, and right-click "Show in ▸" are all sugar over this one
verb. (Do not confuse it with on_view_event — a view-local UI observer — or the low-level event bus.)
"ViewerPayload" — what a deliver returns#
A ViewerPayload is the object a @tool.deliver step returns to
show a result (symmetric to how a @tool.load step takes a src: Source). The framework writes any
tables to Parquet, persists a
*_payload.json sidecar, stamps entity_id / step_id, and emits the payload's topic — routing it
to every view that watches the entity. You never hand-roll ctx.emit("viewer/...").
| Payload | Topic | Shows |
|---|---|---|
LayerPayload |
viewer/layer/set |
3-D geometry — LayerPayload(kind=..., tables=..., style=...) |
ChartPayload |
viewer/chart/set |
a chart (usually spec-only) — ChartPayload(kind=..., title=..., series=...) |
LegendPayload |
viewer/legend/set |
a colour legend (spec-only) — LegendPayload(title=..., colormap=...) |
ViewerPayload |
your topic |
the base class — subclass it and register_payload it for a custom viewer topic |
A payload return is for the terminal deliver artifact. Live, mid-run signals (progress, a training
epoch) stay ctx.emit custom events. See Rendering & payloads and
Deliver steps for the full story.
Related#
Learn — The Tool object · Tables & data flow · Contributing UI
Start — How an LSD package fits together
Frequently asked questions
`@tool.view` or the `View` class — which do I use?
View is the class with param() fields, @action methods and optional data; subclass it or use the views facade. @tool.view declares a factory returning a View for an editor tab or sidebar. Viewer overlays use ViewerExtensionSpec.surfaces through tool.viewer_extension instead of location=overlay.
Is `table` the same as `Table`?
`Table` (capital T) is the one class and constructor — the zero-copy wrapper over one Arrow table; build one with `Table({...})`, `Table([...])`, or `Table.read_csv(...)`. There is no lowercase `table` factory. `pa.table` is raw pyarrow's constructor and `views.table(...)` is the data-view façade — different things; `.arrow` on a `Table` gets you to pyarrow and back.