# Naming: which is which
URL: https://lsd.tools/docs/tools-glossary
Updated: 2026-07-18

> A 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 `Configurable`s, **not** `View`s — 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)`](/docs/api-view-watch) 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.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`](/docs/api-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 `watch`es 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](/docs/tools-rendering) and
[Deliver steps](/docs/tools-deliver) for the full story.

## Related

**Learn** — [The Tool object](/docs/tools-tool-object) · [Tables & data flow](/docs/tools-tables) ·
[Contributing UI](/docs/tools-panels-overlays-views)

**Start** — [How an LSD package fits together](/docs/start-architecture)

## FAQ
### `@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.
