Sandbox — LSD is in active testing. All systems operational·payments are test-mode (no real charges).
Overview Use Cases News Docs Tool Store Support Community Get LSD
All docs
Docs/ Building Tools/ Naming: which is which
Building Tools

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.

TL;DRA few LSD words carry more than one meaning. The word View is both the one UI class (lsdtools.View) and the @tool.view decorator that contributes one; Table is the one constructor (there is no lowercase table factory); a panel is a View with no kind; workspace is the engine's run directory. This page pins each overloaded word 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 A decorator; the factory it wraps returns a View. The desktop mounts it as a sidebar panel, an overlay, or an editor tab (per location=). authoring a package
ViewContribution The internal object a @tool.view becomes at mount time. You rarely name it (it lives in lsdtools.advanced). escape hatch only

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 a View (params
    • @action, no kind). The desktop docks it in the sidebar.
  • The word "panel" in the desktop UI is just where that View is shown — there is no separate Panel class in the authoring API. (location="overlay" is the same idea, docked over the 3D viewer.)

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 with Table({...}), Table([{...}, …]), Table(df), or Table(pa_table); read files with Table.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, and views.table(...) is the data-view façade that wraps a Table for display.
  • pa.Table — raw pyarrow. .arrow on a Table is the zero-copy escape hatch out to it (and Table(pa_table) brings one back in). A step parameter annotated Table or pa.Table is an input port.

"Workspace"#

engine.workspace is the engine's run / cache directory — where step outputs land. It is a Workspace object that is a path string (so os.path.join(engine.workspace, ...) still works) but also answers .exists, .size(), .dir_for(entity), and .clean(). Engine() with no argument makes a throwaway temp one; Engine("project.lsd") puts it beside the project file.

(The desktop's "project" and the hub "workspace" are different, product-level concepts — not the one an SDK author meets.)

"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-written Entity subclass).
  • @tool.template (an EntityTemplate) — 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 with param() fields (steps, entities, the engine — all of them).
  • View — a Configurable that also has @action buttons and optional data (feed / to_text / to_image). With no kind it renders as a form (a panel); with a kind (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 runengine.add plus run_all() moves data, engine.add plus show() presents it, and watch is the wire between them.

  • It takes an Entity or an entity name (a name with no entity yet stays pending and resolves when one is added). Any order works — watch before or after either the view or the entity is added 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.

LearnThe Tool object · Tables & data flow · Contributing UI

StartHow an LSD package fits together

Frequently asked questions

`@tool.view` or the `View` class — which do I use?

Both name the same thing from two sides. `View` is the class — `param()` fields + `@action` + optional data — that you subclass or get from the `views` façade. `@tool.view` is the decorator whose factory returns a `View`; the desktop mounts it (a sidebar panel, an overlay, or an editor tab, per `location=`). You call `show()` on the View itself, or attach it with `engine.add(a_view)`.

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.

By LSD Team · Last updated Jul 18, 2026 Ask on Discord View as Markdown