play

package
v0.0.12 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 9, 2026 License: MIT Imports: 73 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	SQLOverride = env.NewString(env.Spec{
		Name:        "SPINNAKER_PLAY_SQL",
		Description: "initial SQL buffer for the play HMI; non-empty wins over the persisted-session restore",
		Category:    env.CategoryE("spinnaker-play"),
	})

	TimelineBandsSQLOverride = env.NewString(env.Spec{
		Name:        "SPINNAKER_PLAY_TIMELINE_BANDS_SQL",
		Description: "panel-local bands SQL for the Timeline tab; non-empty wins over the persisted-session restore",
		Category:    env.CategoryE("spinnaker-play"),
	})

	AutoRun = env.NewString(env.Spec{
		Name:        "SPINNAKER_PLAY_AUTORUN",
		Description: "non-empty enables auto-run of the initial SQL on mount",
		Category:    env.CategoryE("spinnaker-play"),
	})

	ScreenshotPath = env.NewPath(env.Spec{
		Name:        "SPINNAKER_PLAY_SCREENSHOT",
		Description: "if set, the play HMI captures a screenshot to this path after the first frame",
		Category:    env.CategoryE("spinnaker-play"),
	})

	ExitOnShot = env.NewString(env.Spec{
		Name:        "SPINNAKER_PLAY_EXIT_ON_SHOT",
		Description: "non-empty exits the play HMI after writing SPINNAKER_PLAY_SCREENSHOT",
		Category:    env.CategoryE("spinnaker-play"),
	})

	PreviewAsSent = env.NewString(env.Spec{
		Name:        "SPINNAKER_PLAY_PREVIEW_AS_SENT",
		Description: "non-empty starts the Preview tab in 'as sent to server' mode (post-pass wire SQL) for scripted screenshots",
		Category:    env.CategoryE("spinnaker-play"),
	})
)

SPINNAKER_PLAY_* drive optional one-shot/scripted-screenshot behaviours on the play HMI. Registered with the boxer-wide env registry per ADR-0058.

Functions

func ExtractParamSlots

func ExtractParamSlots(sql string) (slots []paramSlot, err error)

ExtractParamSlots walks sql via the Grammar1 parser and returns one paramSlot per ColumnExprParamSlot CST node. Duplicate names are returned with the first occurrence's Type and Src. Hot-path callers should prefer extractSlotsAndParams, which parses once and produces both the slot list and the prelude value map.

func ExtractParams

func ExtractParams(sql string) (residual string, params map[string]string, err error)

ExtractParams parses sql and removes any top-level `SET` statement whose every setting name starts with `param_`, returning the residual SQL plus the harvested parameter values.

The values are the raw SQL literal texts of the right-hand side, with surrounding single quotes stripped from string literals so they can be shipped verbatim as ClickHouse HTTP `?param_<name>=<value>` URL fields.

Naming convention: ClickHouse maps URL key `param_<X>` to placeholder `{<X>:Type}` — the `param_` prefix is the URL-side marker, not part of the placeholder name. This pass passes SET names through verbatim, so `SET param_a=1; SELECT {a:UInt64}` is the canonical form. To use the placeholder `{param_a:Type}` literally, the SET must be `SET param_param_a=1`.

A SET statement that mixes `param_*` settings with non-`param_*` settings is rejected: partial deletion of individual settingExprs (with their commas) is fiddly and out of scope. SET statements that contain only non-`param_*` settings are left intact in the residual.

See ExecuteArrowStream's doc for the URL-length limits that bound how large the harvested values can collectively be.

func NewCliCommand

func NewCliCommand() *cli.Command

func SyncParamPrelude

func SyncParamPrelude(sql string, slots []paramSlot, values map[string]string) (out string, changed bool)

SyncParamPrelude rewrites the leading `SET param_*` block of sql so it exactly matches the widget-authored (name, value) pairs, in placeholder-occurrence order. Encoding is keyed off each slot's Type (numeric → verbatim if numeric-shape, compound → verbatim, other → single-quoted with escapes); see encodeParamLiteral.

Idempotent: returns (sql, false) when the existing prelude already matches the desired one. Returns (sql, false) on ExtractParams error — a transient keystroke that breaks the parse should not destroy the user's prelude.

Trailing prelude SETs whose name isn't in values are dropped on rewrite; this is how a deleted placeholder stops contributing to the prelude. Non-param SETs intermixed with param SETs are *not* preserved in the leading block — they may shift downward after a rewrite. In practice users keep non-param SETs in a trailing block; the play app's pristine output stays stable across re-syncs.

Types

type CardDriver

type CardDriver struct {
	// contains filtered or unexported fields
}

CardDriver bridges the current Arrow schema to the leeway streamreadaccess.Driver + a Table2CardEmitter.

Two-stage caching:

  • The Driver + TableDesc are rebuilt only when the Arrow schema object changes (cheap pointer compare).
  • Each Render call walks a single-row slice of the record batch, producing the same Begin*/End* sequence the HtmlCardEmitter consumes, but emitting ImZero2 widgets through the Table2CardEmitter.

func NewCardDriver

func NewCardDriver(ids *c.WidgetIdStack, alloc memory.Allocator) *CardDriver

NewCardDriver returns an empty driver. EnsureFor must be called before the first Render.

func (*CardDriver) Driver

func (inst *CardDriver) Driver() *streamreadaccess.Driver

Driver returns the underlying leeway streamreadaccess.Driver iff the schema is leeway-shaped (EnsureFor returned true). Otherwise nil. Used by the Projector to drive a FeatureExtractor over the same record batch the card view consumes, so we don't pay for a second schema-discovery round.

func (*CardDriver) EnsureFor

func (inst *CardDriver) EnsureFor(schema *arrow.Schema) bool

EnsureFor (re)builds the driver if the schema changed. Returns true iff the schema is leeway-shaped and Render can proceed.

func (*CardDriver) Render

func (inst *CardDriver) Render(rec arrow.RecordBatch, row int64) error

Render walks a single-row slice of rec through the Driver, which drives the Table2CardEmitter. The emitter pushes ImZero2 widgets into the current ui scope — call this inside a ScrollArea or Vertical container.

func (*CardDriver) SetTagClickHandler

func (inst *CardDriver) SetTagClickHandler(fn func(display, detail string))

SetTagClickHandler wires a clipboard / filter pivot callback through to the emitter. Passing nil clears it. Note: Table2CardEmitter renders chips as comma-joined strings, so the callback never fires in practice — kept for API parity with the older two-emitter model.

type ChannelClaim added in v0.0.12

type ChannelClaim any

ChannelClaim is a panel's interpretation of one channel's node output schema, opaque to the runtime — Timeline's Mode+slots, Detail's leeway-vs-ad-hoc choice. Computed in AcceptForChannel, consumed in Render.

type ChannelID added in v0.0.12

type ChannelID string

ChannelID identifies a typed input channel of a panel (ADR-0097 SD6/SD7, amended slice 4): the slot an eligible node fills. Single-input panels declare one channel; the Timeline declares events + bands.

type ChannelResult added in v0.0.12

type ChannelResult struct {
	Node  NodeID
	Rec   arrow.RecordBatch
	Claim ChannelClaim
}

ChannelResult is the node result bound to a channel, with the panel's resolved per-channel claim. Passed to Render in the filled map.

type ChannelSpec added in v0.0.12

type ChannelSpec struct {
	ID       ChannelID
	Required bool
	Label    string // human label for the Graph-view channel UI (slice 4c)
}

ChannelSpec declares one of a panel's input channels. A panel is renderable iff all its Required channels are filled.

type Client

type Client struct {
	// contains filtered or unexported fields
}

func NewClient

func NewClient(cfg ClientConfig, httpClient *http.Client) *Client

func (*Client) BuildStatement added in v0.0.12

func (inst *Client) BuildStatement(sql string) (body string, params map[string]string)

BuildStatement performs the client-side rewrite of a raw editor buffer into the statement body and URL params that ExecuteArrowStream ships:

  1. Harvest top-level `SET param_*=...` statements (ExtractParams) so they can ride the HTTP `param_*` channel rather than being inlined — values can be larger than fits comfortably in a single SQL literal, and the typed substitution from `{name:Type}` placeholders is what ClickHouse expects this way.
  2. Apply the registered pre-execute rewrites (ADR-0108 §SD6) — e.g. LW_ID_* macro expansion — best-effort: a pass that fails is skipped and the SQL from before it ships instead.
  3. Rewrite the query so it ends with `FORMAT ArrowStream`, replacing any existing FORMAT clause; falls back to a textual append when the SQL is outside Grammar1.

Every step degrades rather than fails, so a usable body always comes back and the server reports the real problem to the user. The Preview tab's "as sent" view calls this too, so what it shows can never drift from what executes.

func (*Client) ExecuteArrowStream

func (inst *Client) ExecuteArrowStream(ctx context.Context, sql string, alloc memory.Allocator, opts *ExecOptions) (rdr *ipc.Reader, body io.Closer, summary Summary, err error)

ExecuteArrowStream rewrites the query's FORMAT clause to `ArrowStream` via the nanopass pipeline, POSTs it, and returns an ipc.Reader over the response body and the body closer. The caller must close the body after fully draining the reader.

Top-level `SET param_*=...` statements in sql are extracted by ExtractParams and shipped on the URL query string (`?param_<name>=<value>`); the residual SQL goes in the body. ClickHouse rejects multi-statement bodies, so this split is what makes a script like `SET param_a=1; SELECT {a:UInt64}` work over a single HTTP request.

Size limits

We do not use multipart/form-data, so the only relevant cap is the request URI cap. Concretely:

  • ClickHouse's `http_max_uri_size` (default 1 MiB) bounds the *total* URL length, including the URL-encoded param names and `&` separators. Exceeding it returns HTTP 414 / "URI is too long" from the server.
  • Reverse proxies may impose tighter caps (nginx default `large_client_header_buffers` is 8 KiB). When deployed behind one, bump that knob or move to a temp-table strategy for large values.
  • For reference: ClickHouse's `http_max_field_value_size` (default 128 KiB) is the *multipart/form-data* per-field cap. It is stricter per-value than the URL channel, so switching to multipart only helps when the *number* of params (not the size of any one) is the bottleneck — and that switch is not implemented here.

For a single value above the URL cap, stage it in a temp table or raise `http_max_uri_size` server-side; there is no client-side fall-back.

opts may be nil; when set, its query_id / replace_running_query ride the URL alongside the params (see ExecOptions).

func (*Client) SetURL added in v0.0.12

func (inst *Client) SetURL(u string)

SetURL switches the target endpoint. Safe to call from the UI goroutine while a query runs on another: ExecuteArrowStream reads the target once at request-build time. An empty url is ignored (keeps the current target).

func (*Client) URL added in v0.0.12

func (inst *Client) URL() (u string)

URL returns the current target endpoint.

type ClientConfig

type ClientConfig struct {
	URL      string
	User     string
	Password string
}

type DetailContentFunc added in v0.0.12

type DetailContentFunc func(rec arrow.RecordBatch, schema *arrow.Schema, row int64)

DetailContentFunc renders the body of the Detail panel for one selected row, below the header (row position + entity identity) PlayApp always draws. It runs inside the pane's Vertical scope; rec/schema/row identify the row. A library re-using PlayApp installs one with SetDetailContent to replace the built-in leeway-card / ad-hoc body with a domain-specific view — or to wrap it, by calling RenderDefaultDetailContent and then appending its own widgets.

type ExecOptions added in v0.0.12

type ExecOptions struct {
	QueryID             string
	ReplaceRunningQuery bool
}

ExecOptions carries per-lane execution settings for ExecuteArrowStream. QueryID is a stable per-lane ClickHouse query_id: combined with ReplaceRunningQuery, a superseding run REPLACES its still-running predecessor server-side (ADR-0097 SD5 / ADR-0096 SD9). Context cancel alone only closes the HTTP connection, which ClickHouse by default does NOT treat as a kill for read-only queries — without this, superseded raster/bands queries pile up on the server. Endpoints that don't know these params ignore them (the keelson introspection /query reads only cols/query/param_*).

type HistoryEntry

type HistoryEntry struct {
	SQL       string
	Executed  time.Time
	Elapsed   time.Duration
	NumRows   int64
	ErrorText string
}

type MapDriver added in v0.0.10

type MapDriver struct {
	// contains filtered or unexported fields
}

MapDriver is the ADR-0096 geo-raster map panel: a walkers slippy map whose viewport drives an in-DB-rendered RGBA raster. Each frame it reads the previous frame's camera (fetchR15WalkersCamera, cached), and once the camera has settled it injects the viewport mercator bbox into a bbox-variant raster query run on a panel-local async lane (the play_timeline_bands precedent), packs the 4×UInt8 result to RGBA, and draws it as a mapRaster overlay pinned to the viewport's lat/lon bounds.

First cut: the render is the upstream "Altitude & Velocity" colour math generalised to an arbitrary viewport, so it assumes the adsb.exposed schema (mercator_x / mercator_y / altitude / ground_speed). The table + sampling are user controls; values are inlined as literals (this is a power-user SQL playground — the editor already grants arbitrary query access). Not yet wired: the keepBuffer margin, the progressive sampling ladder, hover→info queries, and a configurable render — all SD10 deferrals in the ADR.

func NewMapDriver added in v0.0.10

func NewMapDriver(ids *c.WidgetIdStack, client *Client) *MapDriver

func (*MapDriver) Render added in v0.0.10

func (inst *MapDriver) Render()

Render draws the controls, the last-good raster overlay, and the map; and kicks a (debounced) fetch when the viewport has settled. Order matters: the mapRaster overlay is a register-drain node, so it must be emitted BEFORE the walkersMap that drains it.

type MirrorSync

type MirrorSync struct {
	Canonical  string
	Mirror     string
	SyncedFrom string
	Prelude    string
	OK         bool
}

MirrorSync is the result of one recomposeMirror call: the new canonical SQL, the new mirror, the new syncedFrom snapshot, and the prelude string sliced off for the read-only label render. OK=false means ExtractParams failed OR the param SETs are not a leading prelude; the caller should leave state untouched and fall back to the unsliced editor.

type Node added in v0.0.12

type Node struct {
	ID      NodeID
	Compile func(sig SignalEnvI) (sql string, err error)
}

Node is a query node. Compile produces the pushed-down SQL from the current signal env (ADR-0097: editor SQL → nanopass pipeline → param substitution). In slice 1 Compile is supplied directly; the splitter and a real nanopass pipeline land in slice 3.

type NodeID added in v0.0.12

type NodeID string

NodeID identifies a query node in the graph.

type PanelI added in v0.0.12

type PanelI interface {
	ID() PanelID
	// Channels declares the panel's input channels in render/assignment order.
	Channels() []ChannelSpec
	// AcceptForChannel is the per-channel capability check (SD6): given a candidate
	// node's output schema for ch and the signal env, return a claim or a
	// human-facing reason (the empty-state text). Eligibility is reason == ""
	// — the dispatcher keys on the reason, and the claim may be any value the
	// panel wants back in Render (including nil). Pure: no side effects, no
	// rendering.
	AcceptForChannel(ch ChannelID, schema *arrow.Schema, sig SignalEnvI) (claim ChannelClaim, reason string)
	// Render draws the panel from its filled channels — called when every Required
	// channel is filled (and the panel is visible). May publish signal mutations
	// via emit.
	Render(filled map[ChannelID]ChannelResult, emit SignalEmitterI)
}

PanelI is the panel contract (ADR-0097 SD6/SD7, amended slice 4): a panel declares typed input channels, each filled by an eligible node. The single-channel case is the pre-slice-4 single-node observer, unchanged.

type PanelID added in v0.0.12

type PanelID string

PanelID identifies a panel (a dock tab that observes a node).

type PlayApp

type PlayApp struct {

	// Auto-run + screenshot (driven by env vars for one-shot captures).
	AutoRun        bool
	ScreenshotPath string
	ExitOnShot     bool
	// contains filtered or unexported fields
}

func NewPlayApp

func NewPlayApp(client *Client, graph *queryGraph, initialSQL string) *PlayApp

func (*PlayApp) Close added in v0.0.12

func (inst *PlayApp) Close()

Close tears down the app's async machinery (Unmount): cancels in-flight work, releases held results, and closes every lane. Late completions from still-running goroutines hit their generation/closed guards and are dropped. Idempotent; the app is unusable afterwards.

func (*PlayApp) PersistSql

func (inst *PlayApp) PersistSql()

PersistSql writes inst.sql under persistKeyLastSql when storage is wired. Called on Run + Unmount; errors are logged at debug level (audit-trail concern, not a user-visible failure).

func (*PlayApp) PersistTimelineBandsSql

func (inst *PlayApp) PersistTimelineBandsSql()

PersistTimelineBandsSql writes the current bands-SQL editor buffer to the persist cap. Called from Unmount so the user's bands query survives session restart; the value-write happens unconditionally so an empty buffer also persists (and overrides a previous value).

func (*PlayApp) Render

func (inst *PlayApp) Render() error

func (*PlayApp) RenderDefaultDetailContent added in v0.0.12

func (inst *PlayApp) RenderDefaultDetailContent(rec arrow.RecordBatch, schema *arrow.Schema, row int64)

RenderDefaultDetailContent is the built-in Detail body: the leeway card stack when the schema is leeway-shaped (co-sections, real tags, per-type formatters), else a prefix-grouped ad-hoc section layout for arbitrary SQL results. Exported so a custom DetailContentFunc can delegate to it and append its own widgets.

The leeway card view (Table2CardEmitter) renders into an egui_extras::TableBuilder that owns its own ScrollArea, so it must NOT be wrapped in an outer ScrollArea: that hands the table unbounded available height and egui_extras then crops its tail rows. The driver emits the plain section first and the tagged / co-sections after it, so the cropped rows are exactly the tagged sections — leaving "only plain value sections" visible. Render the card directly in the bounded dock tab, matching the leewaywidgets demo's renderActiveView. The ad-hoc fallback has no self-scrolling widget, so it keeps an explicit ScrollArea.

func (*PlayApp) RestorePersistedSql

func (inst *PlayApp) RestorePersistedSql()

RestorePersistedSql replaces inst.sql with the value stored under persistKeyLastSql when storage is wired and the value is non-empty. Best-effort: errors are logged at debug level and the existing inst.sql stays. The caller (PlayLauncher.Mount) decides precedence: today it lets SPINNAKER_PLAY_SQL win over persist, persist win over the literal default.

func (*PlayApp) RestorePersistedTimelineBandsSql

func (inst *PlayApp) RestorePersistedTimelineBandsSql()

RestorePersistedTimelineBandsSql loads the bands-SQL editor buffer from the persist cap. Same best-effort semantics as RestorePersistedSql.

func (*PlayApp) SetCapabilities

func (inst *PlayApp) SetCapabilities(bus app.BusI, storage app.StorageI, logger zerolog.Logger)

SetCapabilities is the host-side seam for wiring the runtime's M2 capabilities (ADR-0026). Called once from PlayLauncher.Mount with ctx.Bus() and ctx.Storage(). Either argument may be nil — the "Load .sql" button stays hidden when bus is nil; persist save/ restore is skipped when storage is nil.

func (*PlayApp) SetDetailContent added in v0.0.12

func (inst *PlayApp) SetDetailContent(fn DetailContentFunc)

SetDetailContent overrides the Detail panel's body renderer. Passing nil restores the built-in body (RenderDefaultDetailContent). The header PlayApp draws above the body is unaffected.

type PlayLauncher

type PlayLauncher struct {
	// contains filtered or unexported fields
}

PlayLauncher is the AppI wrapper for the SQL Playground. Late binding — ClickHouse connection details are read from environment variables at Mount, matching the legacy resolveApplication behaviour. A simple LegacyFuncApp wouldn't suffice because the env-var-driven configuration can't be captured cleanly at init time before the cli flag parser has run.

func (*PlayLauncher) Frame

func (inst *PlayLauncher) Frame(ctx app.FrameContextI) (err error)

func (*PlayLauncher) Manifest

func (inst *PlayLauncher) Manifest() (m app.Manifest)

func (*PlayLauncher) Mount

func (inst *PlayLauncher) Mount(ctx app.MountContextI) (err error)

func (*PlayLauncher) Unmount

func (inst *PlayLauncher) Unmount(ctx app.MountContextI) (err error)

type Projector

type Projector struct {
	// contains filtered or unexported fields
}

Projector owns the UMAP projection state for the current result batch. A single goroutine runs feature extraction + UMAP in the background; the render thread polls Snapshot() each frame to draw progress / scatter.

Lifecycle: Invalidate(schema, executed) is called every frame from the renderer; if the underlying result changed it cancels any in-flight run and resets to idle. Start(rec, schema, executed) spawns the goroutine (no-op if one is already running). Cancel() signals abort.

Concurrency: all mutable fields are guarded by mu. The cancel chan is non-nil iff a goroutine is in flight; Start refuses while non-nil. The goroutine clears it on exit so the next Start can proceed.

func NewProjector

func NewProjector(ids *c.WidgetIdStack, cards *CardDriver) *Projector

NewProjector binds the Projector to the play app's CardDriver. The Projector borrows the driver's streamreadaccess.Driver to feed the FeatureExtractor; it does not own the CardDriver and must not outlive it.

func (*Projector) Cancel

func (inst *Projector) Cancel()

Cancel signals the in-flight run to stop. The goroutine sees the closed channel on the next stepFunc fire (or the next phase boundary) and exits. While the goroutine winds down (UMAP can take seconds with no per-epoch hook) the status sits at Cancelling so the UI can show that the click was registered. Final transition to Cancelled happens in markCancelled(). No-op if nothing is running.

func (*Projector) Detach added in v0.0.12

func (inst *Projector) Detach()

Detach cancels any in-flight run and orphans it — Invalidate's semantics without installing a new dataset. For app teardown (PlayApp.Close): the winding-down goroutine's terminal writes become no-ops and it releases its retained record on exit.

func (*Projector) Invalidate

func (inst *Projector) Invalidate(schema *arrow.Schema, executed time.Time) (matches bool)

Invalidate is called every frame with the current (schema, executed). Returns true iff the projection state matches the current result and may be displayed. If the result changed since the last call, any in-flight computation is cancelled and the state is reset to idle.

Uses detachCurrentRunLocked (not signalCancelLocked) so a goroutine that is still winding down for the previous dataset becomes a no-op on its terminal status write — the new tab state (Idle) survives instead of being overwritten with the stale run's Cancelled / Failed / Done.

func (*Projector) Snapshot

func (inst *Projector) Snapshot() (snap projectorSnapshot)

Snapshot returns a value-copy of the current state. Safe to read on the render thread without holding the mutex. The coords slice is shared (not copied) — the goroutine treats it as immutable once published.

func (*Projector) Start

func (inst *Projector) Start(rec arrow.RecordBatch)

Start kicks off a projection run on the given record batch. No-op if a run is already in flight (Cancel first if you want to restart). The caller must have called Invalidate(schema, executed) earlier this frame so the cache key is set.

type QueryStore

type QueryStore struct {
	// contains filtered or unexported fields
}

func NewQueryStore

func NewQueryStore(client *Client, alloc memory.Allocator, maxHistory int, label string) *QueryStore

NewQueryStore builds a store; label names its lane in server-side observability (system.processes / query_log) via the stable query_id.

func (*QueryStore) Cancel

func (inst *QueryStore) Cancel()

Cancel aborts the in-flight query (if any).

func (*QueryStore) Close added in v0.0.12

func (inst *QueryStore) Close()

Close cancels any in-flight query, releases the held result, and marks the store closed so a late finish() is dropped rather than resurrecting state. Idempotent; the store is unusable afterwards.

func (*QueryStore) Execute

func (inst *QueryStore) Execute(sql string)

Execute kicks off an async query. Subsequent calls while a query is running are ignored; call Cancel first.

func (*QueryStore) History

func (inst *QueryStore) History() []HistoryEntry

func (*QueryStore) IsLoading

func (inst *QueryStore) IsLoading() bool

func (*QueryStore) Snapshot

func (inst *QueryStore) Snapshot() (rec arrow.RecordBatch, schema *arrow.Schema, numRows int64, loading bool, elapsed time.Duration, summary Summary, executed time.Time, err error)

Snapshot returns a retained view of the last result. Caller MUST call rec.Release() when done (nil-safe). Retaining under the read lock ensures a concurrent Execute→finish can't pull the record out from under us. executed is the time the most recent finish() completed — use it as an identity token for the current dataset (changes ⇒ new query). loading is read under the same lock as executed, so the pair is consistent: feed this loading to the FSM mirror rather than a separate IsLoading() call, which could observe the post-finish flag against this pre-finish snapshot.

type SignalEmitterI added in v0.0.12

type SignalEmitterI interface {
	Emit(id SignalID, value any)
}

SignalEmitterI lets a panel write a param's value — the viewof producer/ consumer duality (ADR-0097 SD8). A widget and a panel write the SAME named params; a node that references the param depends on it.

type SignalEnvI added in v0.0.12

type SignalEnvI interface {
	Get(id SignalID) (param env.Param, ok bool)
	Revision() uint64
}

SignalEnvI is the read-only view of the graph's signal (unbound-param) values at a single consistent revision (ADR-0097 SD4). Panels read it in AcceptForChannel.

type SignalID added in v0.0.12

type SignalID = string

SignalID is a param name. An unbound `{name:Type}` slot is a signal, and signals unify across nodes by name (ADR-0097 SD8): the same name is one shared input.

type Summary

type Summary struct {
	ReadRows        uint64
	ReadBytes       uint64
	WrittenRows     uint64
	WrittenBytes    uint64
	TotalRowsToRead uint64
	ResultRows      uint64
	ResultBytes     uint64
	ElapsedNs       uint64
}

Summary mirrors ClickHouse's X-ClickHouse-Summary JSON-ish header values.

func (Summary) String

func (inst Summary) String() string

type TimelineDriver

type TimelineDriver struct {
	// contains filtered or unexported fields
}

TimelineDriver bridges QueryStore Arrow results to the composite timeline widget. Per-frame: when the result identity changes (schema pointer or executed timestamp), the driver re-resolves the column contract and pushes the layout-event slices into the widget. Clicks on intervals / annotations route back into PlayApp.selectedRow so the Detail tab updates. Bucket clicks (Points-mode rug aggregation) carry no per-row id and are ignored.

Bands: a separate panel-local SQL (held on PlayApp, accessed through bandsSQLPtr) is run synchronously whenever the main result's _tl_time extent changes — the (minMS, maxMS) pair is substituted into the user-typed SQL via the timelineBandsPlaceholder* tokens, the resulting bands are cached by (minMS, maxMS, sql) under an LRU, and a closure over inst.bands feeds the widget's WithBackgroundBands producer each frame. See play_timeline_bands.go for the bands-specific helpers.

func NewTimelineDriver

func NewTimelineDriver(ids *c.WidgetIdStack, selectedRow *int64, client *Client, bandsSQLPtr *string, nowLinePtr *bool) (inst *TimelineDriver)

NewTimelineDriver constructs the driver and the underlying composite widget eagerly (the widget tolerates nil starter data and renders an empty axis until SetIntervals/SetPoints/SetAnnotations is called). The selectedRow pointer is captured so the WithOnSelection callback can push a row index back into PlayApp without a back-reference. The client is reused for bands-SQL submissions; bandsSQLPtr points at the PlayApp-owned, persisted bands SQL string (mutated by the TextEdit inside renderBandsControls). nowLinePtr points at the PlayApp-owned "now line" toggle (mutated by the toolbar checkbox); the driver pushes its current value into the widget via SetNowLine each frame so the flip survives data swaps without recreating the widget.

func (*TimelineDriver) RenderContractHelp

func (inst *TimelineDriver) RenderContractHelp()

RenderContractHelp emits a descriptive multi-line block listing the three column-shape modes the Timeline panel accepts (Points, Intervals, Annotations) and the slot constraints. Intended for empty-state and rejection-state surfaces so first-time users learn the contract from the panel itself instead of having to chase the how-to doc. Body line is body-sized; slot rows use the monospace style so column names line up visually; the closing note is small + weak so it doesn't compete with surrounding controls.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL