ui exporting and docgen testing
This commit is contained in:
7
docs/data/.ui-reference.generated.json
Normal file
7
docs/data/.ui-reference.generated.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"files": {
|
||||
"data/en/ui-reference.md": "d010b68b9599f033a3920ef0590c95d10a895e7d4c5b98e2ba5d822c26a32dd4",
|
||||
"data/ui-inventory.json": "7b8895661a3023f3198eb17e5c15101537b4915faf1a9b398abaa49cd575edc4"
|
||||
},
|
||||
"generator": "common.slice reference"
|
||||
}
|
||||
@@ -28,6 +28,11 @@ make ui scan <app-dir> # what it uses, and how its copy compares
|
||||
|
||||
See [Export / Compile](#export) for run files, the two deliveries and drift.
|
||||
|
||||
**Reference:** [UI reference](#ui-reference) — every export's props, models, emits,
|
||||
slots, closure, packages and tokens, generated from the source by `make reference` in
|
||||
`soleprint/common/ui` (which also has `make check`: typecheck, tests, reference current,
|
||||
parts in agreement). Hand-written notes live beside the library in `common/ui/notes/`.
|
||||
|
||||
## soleprint-modelgen
|
||||
|
||||
Model generator. Reads schema definitions and produces model files.
|
||||
|
||||
485
docs/data/en/ui-reference.md
Normal file
485
docs/data/en/ui-reference.md
Normal file
@@ -0,0 +1,485 @@
|
||||
# common/ui — reference
|
||||
|
||||
> Generated from `common/ui/src` by `make reference` in common/ui (`python3 -m common.slice reference`).
|
||||
> Do not edit this page: the next build replaces it. Prose goes in `soleprint/common/ui/notes/<spot>.md`,
|
||||
> and `make reference-spots` lists every spot.
|
||||
|
||||
## What this is
|
||||
|
||||
A panel framework, not a dashboard: boxes with a title and a status light, splits that
|
||||
resize, a feed that pushes typed events, and renderers for what arrives. It was built for
|
||||
high-frequency operational views — a minimal Grafana — and every piece below exists
|
||||
because an app needed it.
|
||||
|
||||
**The contract is the framework; Vue is one binding of it.** The contract is four things:
|
||||
the token names, the element layer (`base.css`), the class names (`.panel`,
|
||||
`.panel-header`, `.split-pane`, …) and the status words. Those are not whole yet: a feed
|
||||
reports `idle`, `connecting`, `live`, `error`, while the panel's dot shows `idle`,
|
||||
`live`, `processing`, `error` — so `connecting` has no dot, and a consumer has to map it
|
||||
by hand. That is a gap in the contract, listed below. `common/theme/parts` is the second
|
||||
binding: the same contract as plain CSS and JS, for pages that must open from a file with
|
||||
no build. docgen's site is not a binding yet: it bakes its colours from its own style
|
||||
slots, a third token vocabulary.
|
||||
|
||||
## The layers, as use shaped them
|
||||
|
||||
| layer | what | the evidence |
|
||||
| --- | --- | --- |
|
||||
| contract | tokens, element layer, class names, status words | every consumer reads `tokens.css`; four apps pasted their own `base.css` before it shipped here |
|
||||
| chrome | `Panel` (+ maximize), `SplitPane`, `ParameterEditor`, `VideoPlayer` | `Panel` and `SplitPane` are the only exports all five consumers use; nested splits, not `LayoutGrid`, turned out to be the layout |
|
||||
| feed | `DataSource`, `SSEDataSource` | one stream, typed events, a terminal `done` |
|
||||
| renderers | `LogRenderer`, `TableRenderer`, `FrameRenderer`, `TimeSeriesRenderer`, `GraphRenderer` | opt-in; the only two external packages arrive through these |
|
||||
| app state | not here | mpr keeps its own Pinia stores; `pinia` is imported nowhere in this library |
|
||||
| delivery | slices, not this whole folder | see below |
|
||||
|
||||
## How to take it
|
||||
|
||||
Not as this folder. A consumer takes a **slice**: the closure of the exports it uses,
|
||||
as readable source, with a barrel exporting only those and a `tokens.css` cut down to
|
||||
what is read — `make ui scan <app>` computes it, `ctrl/ui.sh slice <run file>` writes it,
|
||||
and `check` reports drift both ways. The "Contents" table is the price list.
|
||||
|
||||
## How this document is made
|
||||
|
||||
Everything outside these notes is generated from `src/` by `make reference` (in
|
||||
`common/ui`), so a prop that is removed disappears from here on the next build. The notes
|
||||
are the only hand-written part, one file per spot in `soleprint/common/ui/notes/`; a note
|
||||
whose spot disappears fails `make check`. `make docs` — anywhere in soleprint — launches
|
||||
this site.
|
||||
|
||||
## What came in
|
||||
|
||||
`common/ui/src` — **24 file(s) seen, 24 read, 0 failed**; 19 exports and 15 types in the barrel.
|
||||
|
||||
## Contents
|
||||
|
||||
**Who uses what**, measured 2026-09-22 with `make ui scan <app>` over the five apps that
|
||||
consume this library. It is the one table here that is not generated — the apps live
|
||||
outside this repository — so re-run the scan before trusting a row.
|
||||
|
||||
| export | mpr detection | mts meetus | mts doocus | unt | nvi |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| `Panel`, `SplitPane` | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||
| `LogRenderer` | ✓ | | | ✓ | |
|
||||
| `VideoPlayer` | | ✓ | | | |
|
||||
| `ParameterEditor`, `ResizeHandle`, all renderers but Log, `SSEDataSource`, `useRegistry`, `useEditorExecution` | ✓ | | | | |
|
||||
| `LayoutGrid`, `StaticDataSource`, `useDataSource`, `BBoxDrawPlugin`, `CrosshairPlugin` | | | | | |
|
||||
|
||||
The last row has **no users at all**. Those exports stay in the library — they are
|
||||
small and a slice never carries them unless asked — but nothing should be built on them
|
||||
as if they were proven. `ResizeHandle` is imported by mpr and never rendered: it is what
|
||||
`SplitPane` replaced.
|
||||
|
||||
What each export costs a consumer that takes it: the files it pulls in, the packages it
|
||||
brings, and whether a plain-HTML part exists for it (`common/theme/parts`).
|
||||
|
||||
| export | kind | file | pulls in | packages | size | plain part |
|
||||
| --- | --- | --- | --- | --- | --- | --- |
|
||||
| [`DataSource`](#datasource) | datasource | `datasources/DataSource.ts` | — | vue | 1.3 KB | feed |
|
||||
| [`SSEDataSource`](#ssedatasource) | datasource | `datasources/SSEDataSource.ts` | DataSource.ts | vue | 3.8 KB | feed |
|
||||
| [`StaticDataSource`](#staticdatasource) | datasource | `datasources/StaticDataSource.ts` | DataSource.ts | vue | 2.4 KB | — |
|
||||
| [`useDataSource`](#usedatasource) | composable | `composables/useDataSource.ts` | DataSource.ts | vue | 2.0 KB | — |
|
||||
| [`useRegistry`](#useregistry) | composable | `composables/useRegistry.ts` | — | vue | 1.9 KB | — |
|
||||
| [`useEditorExecution`](#useeditorexecution) | composable | `composables/useEditorExecution.ts` | — | vue | 1.4 KB | — |
|
||||
| [`Panel`](#panel) | component | `components/Panel.vue` | — | vue | 4.3 KB | maximize, panel |
|
||||
| [`LayoutGrid`](#layoutgrid) | component | `components/LayoutGrid.vue` | — | vue | 0.5 KB | — |
|
||||
| [`ResizeHandle`](#resizehandle) | component | `components/ResizeHandle.vue` | — | vue | 1.3 KB | — |
|
||||
| [`SplitPane`](#splitpane) | component | `components/SplitPane.vue` | — | vue | 3.7 KB | split |
|
||||
| [`ParameterEditor`](#parametereditor) | component | `components/ParameterEditor.vue` | — | vue | 3.1 KB | params |
|
||||
| [`VideoPlayer`](#videoplayer) | component | `components/VideoPlayer.vue` | — | vue | 4.1 KB | — |
|
||||
| [`LogRenderer`](#logrenderer) | renderer | `renderers/LogRenderer.vue` | — | vue | 3.7 KB | — |
|
||||
| [`TimeSeriesRenderer`](#timeseriesrenderer) | renderer | `renderers/TimeSeriesRenderer.vue` | — | uplot, vue | 4.3 KB | — |
|
||||
| [`GraphRenderer`](#graphrenderer) | renderer | `renderers/GraphRenderer.vue` | — | @vue-flow/core, vue | 7.9 KB | — |
|
||||
| [`FrameRenderer`](#framerenderer) | renderer | `renderers/FrameRenderer.vue` | — | vue | 4.9 KB | — |
|
||||
| [`TableRenderer`](#tablerenderer) | renderer | `renderers/TableRenderer.vue` | — | vue | 2.4 KB | — |
|
||||
| [`BBoxDrawPlugin`](#bboxdrawplugin) | plugin | `plugins/BBoxDrawPlugin.ts` | InteractionPlugin.ts | — | 3.0 KB | — |
|
||||
| [`CrosshairPlugin`](#crosshairplugin) | plugin | `plugins/CrosshairPlugin.ts` | InteractionPlugin.ts | — | 2.5 KB | — |
|
||||
|
||||
## Components
|
||||
|
||||
### Panel
|
||||
|
||||
The one component everything uses — 13 instances in mpr, 7 across mts's two apps.
|
||||
|
||||
**Maximize came from mts.** mts's copy grew `v-model:maximized` and `maximizable` while
|
||||
spr's stood still, and it was brought upstream on 2026-09-22 — the framework spec's own
|
||||
rule ("a second application finds the holes") working as written. The reason it is the
|
||||
panel's job and not the video's is in the source: a fullscreen `<video>` paints over the
|
||||
panel's actions and overlays.
|
||||
|
||||
**`#actions` is where panel buttons go.** mts fills it in 6 of 7 panels; mpr fills it in
|
||||
none and absolutely-positions buttons inside the body instead. The slot is the answer.
|
||||
**`#overlay` is filled by no consumer**; it stays for the interaction plugins, which have
|
||||
no consumer either.
|
||||
|
||||
`components/Panel.vue` · 4.3 KB with its closure · packages: `vue` ^3.5 · plain part: `maximize`, `panel`
|
||||
|
||||
| prop | type | required | default | |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `title` | `string` | yes | — | — |
|
||||
| `status` | `'idle' \| 'live' \| 'processing' \| 'error'` | no | — | Omit to show no indicator at all — see the note on `.panel-status`. |
|
||||
| `maximizable` | `boolean` | no | `true` | Set false for panels that make no sense filling the window. |
|
||||
|
||||
| v-model | type | default |
|
||||
| --- | --- | --- |
|
||||
| `v-model:maximized` | `boolean` | `false` |
|
||||
|
||||
slots: `#actions`, `#default`, `#overlay`
|
||||
|
||||
reads: `--font-size-sm` `--font-ui` `--panel-border` `--panel-header-height` `--panel-radius` `--space-1` `--space-2` `--space-3` `--status-error` `--status-idle` `--status-live` `--status-processing` `--surface-1` `--surface-2` `--text-primary` `--text-secondary`
|
||||
|
||||
### LayoutGrid
|
||||
|
||||
`components/LayoutGrid.vue` · 0.5 KB with its closure · packages: `vue` ^3.5
|
||||
|
||||
| prop | type | required | default | |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `columns` | `number` | no | `2` | — |
|
||||
| `rows` | `number` | no | `2` | — |
|
||||
| `gap` | `string` | no | `'var(--space-2)'` | — |
|
||||
|
||||
slots: `#default`
|
||||
|
||||
### ResizeHandle
|
||||
|
||||
`components/ResizeHandle.vue` · 1.3 KB with its closure · packages: `vue` ^3.5
|
||||
|
||||
| prop | type | required | default | |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `direction` | `'horizontal' \| 'vertical'` | yes | — | — |
|
||||
|
||||
| emits | payload | |
|
||||
| --- | --- | --- |
|
||||
| `resize` | `[delta: number]` | — |
|
||||
|
||||
reads: `--text-dim`
|
||||
|
||||
### SplitPane
|
||||
|
||||
The real layout primitive. Every app lays itself out with nested splits — mpr three
|
||||
deep — and none uses `LayoutGrid`. `sizeMode="ratio"` is the common case (all of mts);
|
||||
`"px"` pins a sidebar (mpr). `resizable: false` has never been passed by anyone: a split
|
||||
that cannot move is a flex row.
|
||||
|
||||
`components/SplitPane.vue` · 3.7 KB with its closure · packages: `vue` ^3.5 · plain part: `split`
|
||||
|
||||
| prop | type | required | default | |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `direction` | `'horizontal' \| 'vertical'` | no | `'horizontal'` | Split direction |
|
||||
| `initialSize` | `number` | no | `1` | Initial size of the sized pane (px or flex ratio) |
|
||||
| `sizeMode` | `'px' \| 'ratio'` | no | `'ratio'` | Size mode: 'px' = sized pane fixed in pixels, 'ratio' = flex ratio |
|
||||
| `anchor` | `'first' \| 'second'` | no | `'first'` | Which pane is sized: 'first' or 'second'. Default: 'first'. |
|
||||
| `min` | `number` | no | `0.1` | Min size (px in px-mode, ratio in ratio-mode) |
|
||||
| `max` | `number` | no | `10` | Max size (px in px-mode, ratio in ratio-mode) |
|
||||
| `resizable` | `boolean` | no | `true` | Whether the divider is draggable |
|
||||
|
||||
slots: `#first`, `#second`
|
||||
|
||||
reads: `--text-dim`
|
||||
|
||||
### ParameterEditor
|
||||
|
||||
Schema-driven: the same field list a pipeline stage declares renders its sliders. Two
|
||||
gaps, both fixed in the plain binding (`params` part) and not yet here: a field with
|
||||
`options` (an enum) renders **nothing**, because only `int`/`float` and `bool` are
|
||||
filtered for; and the `reset` event is declared but never emitted — mpr wires a handler
|
||||
to it that cannot fire.
|
||||
|
||||
`components/ParameterEditor.vue` · 3.1 KB with its closure · packages: `vue` ^3.5 · plain part: `params`
|
||||
|
||||
| prop | type | required | default | |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `fields` | `ConfigField[]` | yes | — | — |
|
||||
| `values` | `Record<string, unknown>` | yes | — | — |
|
||||
|
||||
| emits | payload | |
|
||||
| --- | --- | --- |
|
||||
| `update` | `[name: string, value: unknown]` | — |
|
||||
| `reset` | `[]` | — |
|
||||
|
||||
reads: `--space-2` `--surface-3` `--text-dim` `--text-primary` `--text-secondary`
|
||||
|
||||
### VideoPlayer
|
||||
|
||||
Agnostic seekable media player.
|
||||
|
||||
mts's, and only mts uses it. It came upstream with its `#overlay` slot on 2026-09-22.
|
||||
It deliberately does no fullscreen of its own: put it in a `Panel` and maximize that.
|
||||
|
||||
`components/VideoPlayer.vue` · 4.1 KB with its closure · packages: `vue` ^3.5
|
||||
|
||||
| prop | type | required | default | |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `src` | `string` | yes | — | Media source URL |
|
||||
| `currentTime` | `number` | no | `0` | Playback position in seconds (v-model:currentTime) |
|
||||
| `playing` | `boolean` | no | `false` | Whether the media is playing (v-model:playing) |
|
||||
|
||||
| emits | payload | |
|
||||
| --- | --- | --- |
|
||||
| `update:currentTime` | `[value: number]` | — |
|
||||
| `update:playing` | `[value: boolean]` | — |
|
||||
| `duration` | `[value: number]` | Media duration once metadata has loaded |
|
||||
| `ended` | `[]` | Playback reached the end |
|
||||
|
||||
slots: `#overlay`
|
||||
|
||||
reads: `--surface-0`
|
||||
|
||||
## Renderers
|
||||
|
||||
### LogRenderer
|
||||
|
||||
Virtual scroll, and the only one in any of the apps: rows outside the viewport are not
|
||||
rendered, and auto-scroll pauses while the reader scrolls up. mts's long lists and mpr's
|
||||
React side render every row instead. `rowHeight` and `autoScroll` have never been passed
|
||||
by a consumer; the defaults are what is proven.
|
||||
|
||||
`renderers/LogRenderer.vue` · 3.7 KB with its closure · packages: `vue` ^3.5
|
||||
|
||||
| prop | type | required | default | |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `entries` | `LogEntry[]` | yes | — | — |
|
||||
| `rowHeight` | `number` | no | `24` | — |
|
||||
| `autoScroll` | `boolean` | no | `true` | — |
|
||||
|
||||
reads: `--font-mono` `--space-2` `--space-6` `--status-error` `--status-escalating` `--status-live` `--status-processing` `--text-dim`
|
||||
|
||||
### TimeSeriesRenderer
|
||||
|
||||
One of the only two exports that bring a package (`uplot`, with its stylesheet), and the
|
||||
reason a chart cannot be a plain part: baking uplot into every standalone page is the
|
||||
bundle this library refuses to be. A page that needs a real chart is a served page.
|
||||
|
||||
`renderers/TimeSeriesRenderer.vue` · 4.3 KB with its closure · packages: `uplot` ^1.6, `vue` ^3.5
|
||||
|
||||
| prop | type | required | default | |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `series` | `TimeSeriesSeries[]` | yes | — | Array of series configs (label + color) |
|
||||
| `data` | `uPlot.AlignedData` | yes | — | Data: [timestamps[], series1[], series2[], ...] |
|
||||
| `title` | `string` | no | — | Chart title (optional) |
|
||||
| `stacked` | `boolean` | no | `false` | Stacked area mode |
|
||||
|
||||
reads: `--font-mono` `--surface-2` `--surface-3` `--text-primary` `--text-secondary`
|
||||
|
||||
### GraphRenderer
|
||||
|
||||
`renderers/GraphRenderer.vue` · 7.9 KB with its closure · packages: `@vue-flow/core` ^1.48.2, `vue` ^3.5
|
||||
|
||||
| prop | type | required | default | |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `nodes` | `GraphNode[]` | yes | — | — |
|
||||
| `mode` | `GraphMode` | no | `'observe'` | Interaction mode — changes visual treatment and click behavior |
|
||||
| `activeStage` | `string \| null` | no | `null` | Currently edited stage (highlighted in edit modes) |
|
||||
| `regionStages` | `string[]` | no | — | Stages that have a region editor (bbox/polygon) |
|
||||
|
||||
| emits | payload | |
|
||||
| --- | --- | --- |
|
||||
| `open-region-editor` | `[stage: string]` | — |
|
||||
| `open-stage-editor` | `[stage: string]` | — |
|
||||
| `node-click` | `[stage: string]` | — |
|
||||
|
||||
reads: `--font-mono` `--font-size-sm` `--panel-radius` `--status-processing` `--text-secondary`
|
||||
|
||||
### FrameRenderer
|
||||
|
||||
**Known bug:** box colours by confidence are returned as `'var(--conf-high)'` strings and
|
||||
handed to a canvas `strokeStyle`. Canvas does not resolve custom properties, so those
|
||||
boxes draw in the context's previous colour. That is why the "Tokens" table shows the
|
||||
`--conf-*` tokens as read by nobody: in effect, they are not.
|
||||
|
||||
`renderers/FrameRenderer.vue` · 4.9 KB with its closure · packages: `vue` ^3.5
|
||||
|
||||
| prop | type | required | default | |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `imageSrc` | `string` | yes | — | Base64 JPEG image |
|
||||
| `boxes` | `FrameBBox[]` | yes | — | Bounding boxes to overlay |
|
||||
| `overlays` | `FrameOverlay[]` | no | — | Debug overlay layers (edge images, line visualizations, etc.) |
|
||||
|
||||
reads: `--text-dim`
|
||||
|
||||
### TableRenderer
|
||||
|
||||
`renderers/TableRenderer.vue` · 2.4 KB with its closure · packages: `vue` ^3.5
|
||||
|
||||
| prop | type | required | default | |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `columns` | `TableColumn[]` | yes | — | — |
|
||||
| `rows` | `Record<string, unknown>[]` | yes | — | — |
|
||||
| `sortKey` | `string` | no | — | — |
|
||||
| `sortDir` | `'asc' \| 'desc'` | no | — | — |
|
||||
|
||||
| emits | payload | |
|
||||
| --- | --- | --- |
|
||||
| `sort` | `[key: string]` | — |
|
||||
|
||||
reads: `--font-mono` `--font-size-sm` `--panel-border` `--space-1` `--space-2` `--space-3` `--space-6` `--surface-2` `--surface-3` `--text-dim` `--text-primary` `--text-secondary`
|
||||
|
||||
## Datasources
|
||||
|
||||
### DataSource
|
||||
|
||||
`datasources/DataSource.ts` · 1.3 KB with its closure · packages: `vue` ^3.5 · plain part: `feed`
|
||||
|
||||
Base class for all data sources. A DataSource connects to some event stream, exposes reactive state, and lets consumers subscribe to typed events. Panels read from these reactively — they never touch the transport layer directly.
|
||||
|
||||
### SSEDataSource
|
||||
|
||||
One stream, many typed events: panels subscribe with `on(type, handler)` to the event
|
||||
types they draw, and the stream ends with a terminal `done`. `status` uses the same four
|
||||
words as a panel's dot, so wiring one to the other needs no mapping.
|
||||
|
||||
Two things the code does not do, both learned from mpr. **It does not throttle**: at a few
|
||||
hundred events a second, a handler that writes to the DOM per event stalls — the plain
|
||||
binding's `feed` part adds `onFrame()` (one call per animation frame, newest payload) for
|
||||
exactly this, and a Vue consumer has to coalesce the same way. **Nobody unsubscribes**:
|
||||
`on()` returns an unsubscribe function and every call site in mpr drops it, which only
|
||||
works because its panels live as long as the page.
|
||||
|
||||
`datasources/SSEDataSource.ts` · 3.8 KB with its closure · packages: `vue` ^3.5 · plain part: `feed`
|
||||
|
||||
DataSource backed by native EventSource (Server-Sent Events). Connects to a single SSE endpoint and demultiplexes events by type. Multiple panels can subscribe to different event types from the same source.
|
||||
|
||||
### StaticDataSource
|
||||
|
||||
`datasources/StaticDataSource.ts` · 2.4 KB with its closure · packages: `vue` ^3.5
|
||||
|
||||
DataSource that replays a fixture array of events. Used for development and testing without a running backend. Events are emitted in sequence with optional delays.
|
||||
|
||||
## Composables
|
||||
|
||||
### useDataSource
|
||||
|
||||
Composable that connects a component to a DataSource.
|
||||
|
||||
`composables/useDataSource.ts` · 2.0 KB with its closure · packages: `vue` ^3.5
|
||||
|
||||
Composable that connects a component to a DataSource. Connects on mount, disconnects on unmount. Returns reactive refs for data, status, and error.
|
||||
|
||||
### useRegistry
|
||||
|
||||
Generic registry composable — fetches typed data from a URL, caches it, exposes it reactively.
|
||||
|
||||
`composables/useRegistry.ts` · 1.9 KB with its closure · packages: `vue` ^3.5
|
||||
|
||||
### useEditorExecution
|
||||
|
||||
`composables/useEditorExecution.ts` · 1.4 KB with its closure · packages: `vue` ^3.5
|
||||
|
||||
Generic editor execution pattern — debounced apply with auto-apply toggle, loading/error/timing state tracking. The caller provides the actual execution function. This composable handles the orchestration: debounce, auto-apply, loading state, timing.
|
||||
|
||||
## Plugins
|
||||
|
||||
### BBoxDrawPlugin
|
||||
|
||||
BBoxDrawPlugin — draw bounding boxes on the frame viewer.
|
||||
|
||||
`plugins/BBoxDrawPlugin.ts` · 3.0 KB with its closure · packages: none
|
||||
|
||||
### CrosshairPlugin
|
||||
|
||||
CrosshairPlugin — synchronized vertical crosshair across time-series panels.
|
||||
|
||||
`plugins/CrosshairPlugin.ts` · 2.5 KB with its closure · packages: none
|
||||
|
||||
## Types
|
||||
|
||||
Types ride with the file that declares them: taking the export takes its types.
|
||||
|
||||
| type | declared in | |
|
||||
| --- | --- | --- |
|
||||
| `DataSourceStatus` | `datasources/DataSource.ts` | type |
|
||||
| `EditorExecutionOptions` | `composables/useEditorExecution.ts` | interface |
|
||||
| `ConfigField` | `components/ParameterEditor.vue` | interface |
|
||||
| `FrameBBox` | `renderers/FrameRenderer.vue` | interface |
|
||||
| `FrameOverlay` | `renderers/FrameRenderer.vue` | interface |
|
||||
| `LogEntry` | `renderers/LogRenderer.vue` | interface |
|
||||
| `GraphNode` | `renderers/GraphRenderer.vue` | interface |
|
||||
| `GraphMode` | `renderers/GraphRenderer.vue` | type |
|
||||
| `TableColumn` | `renderers/TableRenderer.vue` | interface |
|
||||
| `TimeSeriesSeries` | `renderers/TimeSeriesRenderer.vue` | interface |
|
||||
| `InteractionPlugin` | `plugins/InteractionPlugin.ts` | interface |
|
||||
| `PluginContext` | `plugins/InteractionPlugin.ts` | interface |
|
||||
| `BBoxResult` | `plugins/BBoxDrawPlugin.ts` | interface |
|
||||
| `BBoxCallback` | `plugins/BBoxDrawPlugin.ts` | type |
|
||||
| `CrosshairCallback` | `plugins/CrosshairPlugin.ts` | type |
|
||||
|
||||
## Tokens
|
||||
|
||||
**There are two definitions, and the ⚠ rows are where they disagree.** `src/tokens.css`
|
||||
is the library's own, dark-only; the theme contract in `common/theme` derives the same
|
||||
names from its own palette. They agree on every colour but `--surface-3` and on neither
|
||||
font stack. A Vue slice uses the library's values by default so swapping a consumer's
|
||||
copy for a slice changes no pixel; `theme = "soleprint"` opts into the contract, and
|
||||
that is a visible change. Retiring the library's file in favour of the contract is the
|
||||
open question — it changes what every current consumer renders.
|
||||
|
||||
"read by nobody" means no stylesheet in the library reads it. Apps read tokens directly
|
||||
too — `make ui scan` reports between 14 and 23 per app — and a slice carries those.
|
||||
|
||||
Every custom property the library defines or reads. `library` is `src/tokens.css`, what
|
||||
Vue consumers render today; `theme` is the contract plain pages use
|
||||
(`common/theme/tokens.css` + `themes/soleprint.css`). A row where the two differ is a
|
||||
visible change for a consumer that switches.
|
||||
|
||||
| token | library | theme | read by |
|
||||
| --- | --- | --- | --- |
|
||||
| `--border` | `#2e2e38` | `#2e2e38` | tokens.css |
|
||||
| `--conf-high` | `#3ecf8e` | `#3ecf8e` | *nobody* |
|
||||
| `--conf-low` | `#f06565` | `#f06565` | *nobody* |
|
||||
| `--conf-mid` | `#f5a623` | `#f5a623` | *nobody* |
|
||||
| `--font-mono` ⚠ | `'JetBrains Mono', 'Fira Code', monospace` | `"JetBrains Mono", "Cascadia Mono", Consolas, monospace` | GraphRenderer.vue, LogRenderer.vue, TableRenderer.vue, TimeSeriesRenderer.vue |
|
||||
| `--font-size-base` | `13px` | `13px` | base.css |
|
||||
| `--font-size-lg` | `15px` | `15px` | *nobody* |
|
||||
| `--font-size-sm` | `11px` | `11px` | GraphRenderer.vue, Panel.vue, TableRenderer.vue |
|
||||
| `--font-ui` ⚠ | `'Inter', system-ui, sans-serif` | `Inter, "Segoe UI", system-ui, -apple-system, Arial, sans-serif` | Panel.vue, base.css |
|
||||
| `--panel-border` | `1px solid #2e2e38` | `1px solid #2e2e38` | Panel.vue, TableRenderer.vue, base.css |
|
||||
| `--panel-header-height` | `36px` | `36px` | Panel.vue |
|
||||
| `--panel-radius` | `6px` | `6px` | GraphRenderer.vue, Panel.vue, base.css |
|
||||
| `--space-1` | `4px` | `4px` | Panel.vue, TableRenderer.vue, base.css |
|
||||
| `--space-2` | `8px` | `8px` | LogRenderer.vue, Panel.vue, ParameterEditor.vue, TableRenderer.vue |
|
||||
| `--space-3` | `12px` | `12px` | Panel.vue, TableRenderer.vue, base.css |
|
||||
| `--space-4` | `16px` | `16px` | *nobody* |
|
||||
| `--space-6` | `24px` | `24px` | LogRenderer.vue, TableRenderer.vue |
|
||||
| `--space-8` | `32px` | `32px` | *nobody* |
|
||||
| `--status-error` | `#f06565` | `#f06565` | LogRenderer.vue, Panel.vue |
|
||||
| `--status-escalating` | `#f5a623` | `#f5a623` | LogRenderer.vue, tokens.css |
|
||||
| `--status-idle` | `#555568` | `#555568` | Panel.vue |
|
||||
| `--status-live` | `#3ecf8e` | `#3ecf8e` | LogRenderer.vue, Panel.vue, tokens.css |
|
||||
| `--status-processing` | `#4f9cf9` | `#4f9cf9` | GraphRenderer.vue, LogRenderer.vue, Panel.vue, tokens.css |
|
||||
| `--surface-0` | `#0d0d0f` | `#0d0d0f` | VideoPlayer.vue, base.css, theme.css |
|
||||
| `--surface-1` | `#16161a` | `#16161a` | Panel.vue |
|
||||
| `--surface-2` | `#1e1e24` | `#1e1e24` | Panel.vue, TableRenderer.vue, TimeSeriesRenderer.vue, base.css |
|
||||
| `--surface-3` ⚠ | `#26262f` | `#2e2e38` | ParameterEditor.vue, TableRenderer.vue, TimeSeriesRenderer.vue, base.css |
|
||||
| `--text-dim` | `#555568` | `#555568` | FrameRenderer.vue, LogRenderer.vue, ParameterEditor.vue, ResizeHandle.vue, SplitPane.vue, TableRenderer.vue |
|
||||
| `--text-primary` | `#e8e8f0` | `#e8e8f0` | Panel.vue, ParameterEditor.vue, TableRenderer.vue, TimeSeriesRenderer.vue, base.css |
|
||||
| `--text-secondary` | `#8888a0` | `#8888a0` | GraphRenderer.vue, Panel.vue, ParameterEditor.vue, TableRenderer.vue, TimeSeriesRenderer.vue |
|
||||
|
||||
## What came out
|
||||
|
||||
nodes: 1 barrel · 6 component · 3 composable · 3 datasource · 19 export · 3 package · 6 part · 3 plugin · 5 renderer · 3 stylesheet · 30 token · 15 type
|
||||
|
||||
edges: 7 binds · 28 imports · 70 reads · 17 requires
|
||||
|
||||
- ok 24 file(s) read produced 24 file node(s)
|
||||
- ok 19 export(s) and 15 type(s) in the barrel, each with a node
|
||||
- ok 11 of 11 component(s) have their surface read
|
||||
- ok every note lands on a spot
|
||||
|
||||
## Where it stands
|
||||
|
||||
- **Done:** the reference is generated and checked; slices are cut per consumer with a
|
||||
two-way drift check; mts's additions (maximize, the video overlay) are upstream.
|
||||
- **Behind:** the copies in mpr, unt and nvi date from 2026-04 and lack maximize,
|
||||
`VideoPlayer`, `base.css` and `theme.css`. Their `.spr` stamp names a commit that no
|
||||
longer exists. Replacing each with a slice is the next step for each.
|
||||
- **Open:** one token definition instead of two (three, counting docgen's style slots);
|
||||
the `connecting` status the panel cannot show; `--accent`, which nvi and doocus read and
|
||||
nothing defines; the `FrameRenderer` colour bug; `ParameterEditor`'s enum fields.
|
||||
|
||||
## Rebuilding this
|
||||
|
||||
`make reference` in `common/ui`. `make check` fails when this page is stale, when a
|
||||
generated file was edited by hand, or when a note no longer has its spot. Extraction reads the
|
||||
source with patterns, not a TypeScript parser; `common/slice/selftest.py` pins what the
|
||||
patterns must find, so one that stops matching fails the suite rather than thinning this
|
||||
page.
|
||||
@@ -167,6 +167,13 @@
|
||||
"en": "Shared Components"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "ui-reference",
|
||||
"title": {
|
||||
"en": "↳ UI reference"
|
||||
},
|
||||
"sub": true
|
||||
},
|
||||
{
|
||||
"id": "export",
|
||||
"title": {
|
||||
|
||||
2701
docs/data/ui-inventory.json
Normal file
2701
docs/data/ui-inventory.json
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user