Documentation
¶
Index ¶
- Constants
- Variables
- func DeclaredEnumSlots(sql string) (names []string)
- func DefaultRepository() *gloss.Repository
- func ExtractParamSlots(sql string) (slots []paramSlot, err error)
- func ExtractParams(sql string) (residual string, params map[string]string, err error)
- func NewCliCommand() *cli.Command
- func RegisterComponents(r *componentsql.Registry) (err error)
- func RegisterPasses(r *passreg.Registry) (err error)
- func RegisterVocabulary(r *sqlvocab.Registry) (err error)
- func SyncParamPrelude(sql string, slots []paramSlot, values map[string]string) (out string, changed bool)
- type CardDriver
- func (inst *CardDriver) ColumnClasses() []streamreadaccess.ColumnClass
- func (inst *CardDriver) Driver() *streamreadaccess.Driver
- func (inst *CardDriver) EnsureFor(schema *arrow.Schema) bool
- func (inst *CardDriver) Prepare(rec arrow.RecordBatch, row int64) error
- func (inst *CardDriver) Render()
- func (inst *CardDriver) SectionDigests() []leewaywidgets.SectionDigest
- func (inst *CardDriver) SetCellBlock(fn leewaywidgets.CellBlockFunc)
- func (inst *CardDriver) SetCellGloss(fn leewaywidgets.CellGlossFunc)
- func (inst *CardDriver) SetTagClickHandler(fn func(display, detail string))
- func (inst *CardDriver) TableDesc() *common.TableDesc
- type ChannelClaim
- type ChannelID
- type ChannelResult
- type ChannelSpec
- type ChartDriver
- type ClickHouseDocsSource
- func (inst *ClickHouseDocsSource) AbsoluteURL(url string) string
- func (inst *ClickHouseDocsSource) AbsolutiseLinks(md string) string
- func (inst *ClickHouseDocsSource) Close()
- func (inst *ClickHouseDocsSource) EmptyHint() string
- func (inst *ClickHouseDocsSource) ExplainError(err error) string
- func (inst *ClickHouseDocsSource) LinkCandidates(label string, url string) (out []string)
- func (inst *ClickHouseDocsSource) LinkClaimed(url string) bool
- func (inst *ClickHouseDocsSource) Lookup(name string) (entries []DocsEntry, ready bool, err error)
- type Client
- func (inst *Client) BuildStatement(sql string) (body string, params map[string]string)
- func (inst *Client) DatasetAliases() (aliases []string)
- func (inst *Client) Dispatch(sql string, affinity string) (dec dispatchDecision)
- func (inst *Client) ExecuteArrowStream(ctx context.Context, sql string, alloc memory.Allocator, opts *ExecOptions, ...) (rdr *ipc.Reader, rs *resultStream, summary Summary, err error)
- func (inst *Client) ExecuteWrite(ctx context.Context, sql string, signals map[string]string) (summary Summary, err error)
- func (inst *Client) ExposeConditions() (on bool)
- func (inst *Client) ExprSubstituted(sql string) (out string, err error)
- func (inst *Client) LastDecision() (dec dispatchDecision, ok bool)
- func (inst *Client) PassRegistry() *passreg.Registry
- func (inst *Client) ProbeStatement(ctx context.Context, sql string, params map[string]string, opts *ExecOptions, ...) (err error)
- func (inst *Client) RewriteTrace(sql string) (obs []passreg.ApplyObservation)
- func (inst *Client) SetExposeConditions(on bool)
- func (inst *Client) SetExprValues(values map[string]string)
- func (inst *Client) SetResolver(r endpointResolverI)
- func (inst *Client) SetStampIdentity(runId string, appId string, instanceKey uint64)
- func (inst *Client) SetURL(u string)
- func (inst *Client) URL() (u string)
- type ClientConfig
- type DetailContentFunc
- type DetailTimeline
- type DiagnosticsDriver
- type DistDriver
- type DocsEntry
- type DocsSourceI
- type ExecOptions
- type HistoryEntry
- type IcicleDriver
- type KanbanDriver
- type MapDriver
- type MirrorSync
- type NetworkDriver
- type Node
- type NodeID
- type PanelI
- type PanelID
- type PlayApp
- func (inst *PlayApp) ActivateTab(id string) (err error)
- func (inst *PlayApp) BindDataset(alias, handle string) error
- func (inst *PlayApp) BindTab(tabID string, cteName string) (err error)
- func (inst *PlayApp) CanCopy() bool
- func (inst *PlayApp) Close()
- func (inst *PlayApp) ComposeLaunch() (cfg launchcfg.PlayLaunch)
- func (inst *PlayApp) Frame(ctx app.FrameContextI) (err error)
- func (inst *PlayApp) InsertSqlAtCaret(text string)
- func (inst *PlayApp) MainSQL() string
- func (inst *PlayApp) MainSnapshot() (rec arrow.RecordBatch, schema *arrow.Schema, numRows int64, loading bool, ...)
- func (inst *PlayApp) NotifyDatasetRevision(alias string, revision uint64)
- func (inst *PlayApp) RenderDefaultDetailContent(rec arrow.RecordBatch, schema *arrow.Schema, row int64)
- func (inst *PlayApp) ReplaceSql(text string)
- func (inst *PlayApp) RequestRun()
- func (inst *PlayApp) RestorePersistedSql()
- func (inst *PlayApp) RestorePersistedTimelineBandsSql()
- func (inst *PlayApp) SetCapabilities(bus app.BusI, storage app.StorageI, logger zerolog.Logger)
- func (inst *PlayApp) SetColumnResolver(resolver passes.ColumnResolverI)
- func (inst *PlayApp) SetDatasetNotice(src []byte)
- func (inst *PlayApp) SetDefinitionMarkdown(src []byte)
- func (inst *PlayApp) SetDetailContent(fn DetailContentFunc)
- func (inst *PlayApp) SetDocsSource(src DocsSourceI)
- func (inst *PlayApp) SetLiveMain(on bool)
- func (inst *PlayApp) SetPreambleMarkdown(src []byte)
- func (inst *PlayApp) SetSecurityCeiling(class analysis.QuerySecurityClassE)
- func (inst *PlayApp) SetSignal(name string, value any)
- func (inst *PlayApp) SetTimelineBandsSql(sql string)
- func (inst *PlayApp) SetToolbarMinimal(on bool)
- func (inst *PlayApp) Tabs() *TabRegistry
- func (inst *PlayApp) UnbindDataset(alias string) error
- func (inst *PlayApp) WorkingsetDirty() (dirty bool)
- type PlayLauncher
- func (inst *PlayLauncher) ComposeWorkingset() (cfg []byte, dirty bool, err error)
- func (inst *PlayLauncher) Frame(ctx app.FrameContextI) (err error)
- func (inst *PlayLauncher) Manifest() (m app.Manifest)
- func (inst *PlayLauncher) Mount(ctx app.MountContextI) (err error)
- func (inst *PlayLauncher) Unmount(ctx app.MountContextI) (err error)
- type Projector
- type QueryStore
- func (inst *QueryStore) Cancel()
- func (inst *QueryStore) Close()
- func (inst *QueryStore) Execute(sql string, signals map[string]string, sourceBuffer string)
- func (inst *QueryStore) History() []HistoryEntry
- func (inst *QueryStore) IsLoading() bool
- func (inst *QueryStore) Progress() (p runstream.Progress, fresh bool)
- func (inst *QueryStore) SQL() string
- func (inst *QueryStore) Snapshot() (rec arrow.RecordBatch, schema *arrow.Schema, numRows int64, loading bool, ...)
- func (inst *QueryStore) Truncation() (reason string)
- type SankeyDriver
- type SeriesDriver
- type SignalEmitterI
- type SignalEnvI
- type SignalID
- type Summary
- type TabFrame
- type TabRegistry
- type TabSpec
- type TabZoneE
- type TimelineDriver
- type WorldDriver
Constants ¶
const AppId app.AppIdT = launchcfg.AppId
AppId is play's registered manifest id — the target another app names in a `windowhost.open` request (ADR-0135 §SD7).
The value lives in the leaf launchcfg package so a requester can name play without importing it (ADR-0017 §SD4); this re-export keeps the existing `play.AppId` spelling working.
Variables ¶
var ( SQLOverride = env.NewString(env.Spec{ Name: "BOXER_PLAY_SQL", Description: "initial SQL buffer for the play HMI; non-empty wins over the persisted-session restore", Category: env.CategoryE("boxer-play"), }) TimelineBandsSQLOverride = env.NewString(env.Spec{ Name: "BOXER_PLAY_TIMELINE_BANDS_SQL", Description: "panel-local bands SQL for the Timeline tab; non-empty wins over the persisted-session restore", Category: env.CategoryE("boxer-play"), }) AutoRun = env.NewString(env.Spec{ Name: "BOXER_PLAY_AUTORUN", Description: "non-empty enables auto-run of the initial SQL on mount", Category: env.CategoryE("boxer-play"), }) ScreenshotPath = env.NewPath(env.Spec{ Name: "BOXER_PLAY_SCREENSHOT", Description: "if set, the play HMI captures a screenshot to this path after the first frame", Category: env.CategoryE("boxer-play"), }) ExitOnShot = env.NewString(env.Spec{ Name: "BOXER_PLAY_EXIT_ON_SHOT", Description: "non-empty exits the play HMI after writing BOXER_PLAY_SCREENSHOT", Category: env.CategoryE("boxer-play"), }) PreviewAsSent = env.NewString(env.Spec{ Name: "BOXER_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("boxer-play"), }) AllowWrites = env.NewString(env.Spec{ Name: "BOXER_PLAY_ALLOW_WRITES", Description: "non-empty lets Run execute an INSERT … SELECT wrapper (ADR-0181 §SD8); unset, Run refuses the write with a copy-out hint. Governs every play-engined host, sqlapplet included", Category: env.CategoryE("boxer-play"), }) ObserveNode = env.NewString(env.Spec{ Name: "BOXER_PLAY_OBSERVE", Description: "graph node id to observe in the result panels after a Run (scripted screenshots); silently ignored when the node is absent from the split", Category: env.CategoryE("boxer-play"), }) ShotSettleFrames = env.NewInt(env.Spec{ Name: "BOXER_PLAY_SHOT_SETTLE", Description: "settle frames before BOXER_PLAY_SCREENSHOT fires; a positive value overrides the default (5), e.g. to wait out an async panel fetch", Category: env.CategoryE("boxer-play"), }) MapTable = env.NewString(env.Spec{ Name: "BOXER_PLAY_MAP_TABLE", Description: "initial table for the Map panel; empty keeps the default (planes_mercator_sample100)", Category: env.CategoryE("boxer-play"), }) MapZoom = env.NewFloat(env.Spec{ Name: "BOXER_PLAY_MAP_ZOOM", Description: "initial Map zoom level; a positive value overrides the default (4)", Category: env.CategoryE("boxer-play"), }) MapCenter = env.NewString(env.Spec{ Name: "BOXER_PLAY_MAP_CENTER", Description: "initial Map center as \"lat,lon\" (WGS84); empty or unparseable keeps the default (40,0)", Category: env.CategoryE("boxer-play"), }) MapSize = env.NewString(env.Spec{ Name: "BOXER_PLAY_MAP_SIZE", Description: "pin a fixed Map widget size as \"WxH\" logical points (deterministic scripted screenshots); empty or unparseable keeps the default (the map fills the Map tab)", Category: env.CategoryE("boxer-play"), }) WindowSize = env.NewString(env.Spec{ Name: "BOXER_PLAY_WINDOW_SIZE", Description: "open the play window at \"WxH\" logical points (scripted screenshots); empty or unparseable keeps the host's archetype default", Category: env.CategoryE("boxer-play"), }) )
BOXER_PLAY_* drive optional one-shot/scripted-screenshot behaviours on the play HMI. Registered with the boxer-wide env registry per ADR-0009, so every knob shows up in the generated doc/env-vars.md catalog. The typed handles cache after the first read — fine here: the knobs are set before launch and never change.
Functions ¶
func DeclaredEnumSlots ¶ added in v0.0.20
DeclaredEnumSlots names the slots a buffer declares an option list for, in a stable order.
Exported for the applet corpus gate: a published document's knobs are part of what it promises, and a marker with a typo in it renders as an ordinary text field with nothing to say why. A book can therefore assert that the knobs it documents as lists really are lists. The option values stay unexported — what they should be is the document's business, not the gate's.
func DefaultRepository ¶ added in v0.0.20
func DefaultRepository() *gloss.Repository
DefaultRepository is the process-wide gloss rule repository over the default catalog: what the self-registered PlayLauncher hands its windows, and what NewPlayApp / NewLivePlayApp use for a nil argument. A deployment that links play registers its rule sets on it before the first window mounts — an init of its own package does — or hands its own repository to the constructor / PlayLauncher.Rules instead. Rule sets are code, checked in beside the glosses they bind (ADR-0186, its 2026-08-15 Update).
func ExtractParamSlots ¶
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 ¶
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 RegisterComponents ¶ added in v0.0.20
func RegisterComponents(r *componentsql.Registry) (err error)
RegisterComponents publishes the generated stores play can read through LW_COMPONENT into a component registry (ADR-0189 §SD7).
Explicit rather than by package init: a registry populated by import has an extent that depends on the link set, so the same query would resolve a kind in one binary and not another. Listing the stores here keeps what play knows about reviewable at the wiring site, next to RegisterPasses.
Only stores whose tables play actually reads belong here. sysmetrics is the first — its rows are what ADR-0184's tee writes, and what loadstudy reads.
func RegisterPasses ¶ added in v0.0.14
RegisterPasses adds play's host-scoped entries to the shared pre-execute stage, beyond the standard set (passreg/defaults): a play-hosting process canonicalises every executed statement. Hosts (the standalone binary, the carousel) call this at their wiring site next to defaults.RegisterDefaults, keeping the process's rewrite set reviewable there (ADR-0108 §SD4).
CanonicalizeFull runs first (Order 50, ahead of the standard entries), so the stage's later passes consume canonical shapes — the nanopass contract that downstream passes target canonical form. The quoted spellings it emits stay matchable and executable: identsql's expander and the column-handle resolver compare identifiers through DecodeIdentifier, and ClickHouse accepts quoted function and table-function names ("left"('a', 1), FROM "numbers"(1)) — verified against the server. The converse cost is that later passes' own output (macro expansions) ships uncanonicalised; that output is machine-generated and already uniform.
The rewrite is result-schema-neutral: ClickHouse derives result column names from the parsed AST, so a sugared spelling and its canonical form name their columns identically ([1,2] and array(1,2) both name "[1, 2]"; quoted function names do not leak into names). Like every entry of the stage it rewrites the shipped body only — editor and preview surfaces keep the user's original text.
func RegisterVocabulary ¶ added in v0.0.20
RegisterVocabulary publishes every SQL function this build declares into one vocabulary registry (ADR-0190 §SD4).
The rosters say what a family contains; this says where each family runs and what to call it — the two facts that are a property of the *host*, not of the declaring package. Explicit for the same reason RegisterComponents is: a registry populated by import knows about a different vocabulary in one binary than in another.
It is the single source both surfaces read. The Vocabulary tab's declared set and the completion engine's signatures come from here, so a roster cannot reach one and not the other.
The LW_ID_* family is registered twice on purpose — once as the server UDFs lwsqlsurface installs, once as the client macro identsql expands — because it genuinely is both, and reporting only one of the two would make the other answer wrong (ADR-0174 §SD1).
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.
It is also the play app's single leeway-schema reconstruction point: the leeway physical column names carry the whole authored structure (sections, membership roles, co-section groups, canonical types, encoding hints), and EnsureFor recovers the common.TableDesc from them via DiscoverTableFromColumnNames. That TableDesc is exposed through [TableDesc] so schema-only consumers (the Schema pane) share this one derivation instead of re-running discovery — the Driver they don't need is built once anyway for the Detail card.
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) ColumnClasses ¶ added in v0.0.14
func (inst *CardDriver) ColumnClasses() []streamreadaccess.ColumnClass
ColumnClasses returns the per-Arrow-column leeway classification for the current result — each column's section, role bucket (value / support / membership), backbone flag, and collection shape — or nil when the schema is not leeway-shaped. Like TableDesc it is derived once per schema in EnsureFor (the play app's single leeway-schema reconstruction point); the Table pane reads it to group and filter result columns by section for its leeway display modes. Keyed by ArrowIdx — a schema column absent from the slice is un-classified (non-leeway, implicit, or projected-in) and shown verbatim.
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) Prepare ¶ added in v0.0.14
func (inst *CardDriver) Prepare(rec arrow.RecordBatch, row int64) error
Prepare walks a single-row slice of rec through the Driver, buffering the leeway card rows in the emitter without drawing them (the emitter runs with DeferRender set). Call it before SectionDigests and before Render. A no-op on a non-leeway or out-of-range input. Prepare drives every frame — the emitter re-bases its widget-id counter at each drive, so the deferred Render stays id-stable frame to frame.
func (*CardDriver) Render ¶
func (inst *CardDriver) Render()
Render draws the rows buffered by the last Prepare into the current ui scope. Call it after Prepare (and after any SectionDigests read), inside a ScrollArea or Vertical container.
func (*CardDriver) SectionDigests ¶ added in v0.0.14
func (inst *CardDriver) SectionDigests() []leewaywidgets.SectionDigest
SectionDigests returns the per-tagged-section summaries (memberships split primary / secondary + co-attribute values) buffered by the last Prepare, or nil when the schema is not leeway-shaped. The Detail timeline reuses these to label its temporal flags with the same content the card draws below.
func (*CardDriver) SetCellBlock ¶ added in v0.0.20
func (inst *CardDriver) SetCellBlock(fn leewaywidgets.CellBlockFunc)
SetCellBlock installs the per-value block face on the card emitter (ADR-0186, block faces on the card) — the host's renderer for the values whose gloss has one. Passing nil clears it. A no-op until EnsureFor has built an emitter.
func (*CardDriver) SetCellGloss ¶ added in v0.0.20
func (inst *CardDriver) SetCellGloss(fn leewaywidgets.CellGlossFunc)
SetCellGloss installs the per-value gloss on the card emitter (ADR-0186 §SD4) — the host's inline face over the marshalled text of each value. Passing nil clears it. A no-op until EnsureFor has built an emitter.
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.
func (*CardDriver) TableDesc ¶ added in v0.0.13
func (inst *CardDriver) TableDesc() *common.TableDesc
TableDesc returns the leeway schema reconstructed from the current result's physical column names, or nil when the schema is not leeway-shaped. This is the play app's single leeway-schema derivation — the Schema pane renders it rather than re-running discovery. EnsureFor must have been called for the current schema first (it is, every frame, via the Detail card).
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 ChartDriver ¶ added in v0.0.20
type ChartDriver struct {
// contains filtered or unexported fields
}
ChartDriver owns the Chart tab state: the folded model, its cache key, and the panel-local mark and scale choices.
func NewChartDriver ¶ added in v0.0.20
func NewChartDriver(ids *c.WidgetIdStack) (inst *ChartDriver)
type ClickHouseDocsSource ¶ added in v0.0.20
type ClickHouseDocsSource struct {
// Query is the parametrized lookup, run once per name. It must select
// four columns aliased name, type, description, source, accept the
// looked-up name as {n:String}, and order so the caller's preferred kind
// sorts first — see defaultDocsQuery for the shape decodeDocRows expects.
Query string
// SiteBase absolutises the corpus's site-relative links (`](/…)`) and
// anchors LinkClaimed/AbsoluteURL. Empty disables link absolutisation;
// LinkClaimed then only matches relative link forms, never an absolute
// site prefix.
SiteBase string
// contains filtered or unexported fields
}
ClickHouseDocsSource wraps a ClickHouse Client as a DocsSourceI. It is the source an unconfigured PlayApp with a live client already uses (NewLivePlayApp), and it is meant to be reused rather than reimplemented: a ClickHouse-backed re-user with its own documentation table points Query (and SiteBase, if that table's prose links elsewhere) at it and installs the result with PlayApp.SetDocsSource, instead of writing a new DocsSourceI. A re-user wanting BOTH ClickHouse's builtins and its own vocabulary in one pane can `UNION ALL` them into a single Query — this type never needs to know about more than one source.
func NewClickHouseDocsSource ¶ added in v0.0.20
func NewClickHouseDocsSource(client *Client) *ClickHouseDocsSource
NewClickHouseDocsSource builds the default Docs source: ClickHouse's own system.documentation, queried against client exactly as NewLivePlayApp wires it — the ordinary lane machinery, off the render thread, memoised, through whatever endpoint client resolves to. Query and SiteBase carry today's defaults and may be overridden before installing (SetDocsSource).
func (*ClickHouseDocsSource) AbsoluteURL ¶ added in v0.0.20
func (inst *ClickHouseDocsSource) AbsoluteURL(url string) string
AbsoluteURL turns a corpus link target into something a browser can open. Relative forms are resolved against SiteBase rather than against the entity's own page, whose location the corpus does not record — good enough to land the reader on the right site, and honest about being a fallback for the case where nothing here could answer.
func (*ClickHouseDocsSource) AbsolutiseLinks ¶ added in v0.0.20
func (inst *ClickHouseDocsSource) AbsolutiseLinks(md string) string
AbsolutiseLinks rewrites the corpus's site-relative link targets onto SiteBase.
ClickHouse authors these for its own site — `[DateTime](/sql-reference/…)` — and the widget has no base to resolve them against, so left alone they render as hyperlinks that go nowhere. This is a textual pre-pass rather than a resolver because the markdown widget's resolver seam covers wikilinks and embeds, not plain CommonMark links.
Deliberately narrow: only `](/` is rewritten. A protocol-relative `](//host` is already absolute and must not gain a prefix, and an in-document anchor `](#section)` resolves within the page.
func (*ClickHouseDocsSource) Close ¶ added in v0.0.20
func (inst *ClickHouseDocsSource) Close()
Close tears down the lookup lane. Idempotent, nil-safe.
func (*ClickHouseDocsSource) EmptyHint ¶ added in v0.0.20
func (inst *ClickHouseDocsSource) EmptyHint() string
EmptyHint names this source so a reader knows what a blank pane is waiting on.
func (*ClickHouseDocsSource) ExplainError ¶ added in v0.0.20
func (inst *ClickHouseDocsSource) ExplainError(err error) string
ExplainError explains a failed lookup, separating the one cause a reader can act on from the rest.
`system.documentation` arrived in ClickHouse 26.x. Against an older server the query fails with an unknown-table error, and saying "this endpoint does not ship the documentation table" is a different message from "the lookup failed" — the first is a fact about the server, the second a fault.
The match is gated on THIS instance's own Query mentioning system.documentation, not on the error text alone: a re-user who repurposed Query for a different table would otherwise have every unknown-table error on THEIR table misreported as "needs ClickHouse 26.x".
func (*ClickHouseDocsSource) LinkCandidates ¶ added in v0.0.20
func (inst *ClickHouseDocsSource) LinkCandidates(label string, url string) (out []string)
LinkCandidates ranks the names a claimed link might be naming, best first, for the pane to try in order.
The LABEL leads, because it is what the author wrote to name the thing: "[`UInt8`](/sql-reference/data-types/int-uint)" points at a page covering a dozen types and only the label says which one. Measured over the corpus the label and the URL's last segment each resolve about 60% of links on their own, and they fail on different links — the page-per-family targets (`int-uint`, `special-data-types/expression`) are exactly where the label carries the answer.
The fragment comes second: doc URLs point at a section of a page, and the section is usually the entity (`.../date-time-functions#tohour`).
func (*ClickHouseDocsSource) LinkClaimed ¶ added in v0.0.20
func (inst *ClickHouseDocsSource) LinkClaimed(url string) bool
LinkClaimed reports whether a link target is a ClickHouse documentation page, and so belongs in this pane rather than in a browser.
It runs once per link per frame during layout, so it is a cheap syntactic test and never a lookup: whether the target names something this server documents is decided on the click, where a query is affordable. Claiming a page that turns out to be undocumented is recoverable — the pane says so and offers the original URL — whereas consulting the cache here would make a link's appearance depend on what happened to be cached, and links would change shape as the reader scrolled.
Absolute URLs are claimed only for SiteBase; a link out to GitHub or an RFC is exactly the case that should still leave for a browser.
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
func (*Client) BuildStatement ¶ added in v0.0.12
BuildStatement performs the client-side rewrite of a raw editor buffer into the statement body and URL params that ExecuteArrowStream ships:
- 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.
- 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.
- 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) DatasetAliases ¶ added in v0.0.20
DatasetAliases are the ad-hoc dataset aliases bound in this session, sorted.
They are tables no catalogue enumerates — they exist because this session bound them (ADR-0134 §SD4) — which is why the completion engine's table answers are routed per buffer rather than per build (ADR-0190 §SD12).
func (*Client) Dispatch ¶ added in v0.0.17
Dispatch resolves where the statement authored as sql will execute.
It resolves from the residual — the SQL the request will actually carry — by running the same client-side rewrites the request runs. That costs one extra rewrite per run (the schema probes behind it are cached), and buys the property that no resolver ever judges a form the server will not see.
Callers hand the result to ExecuteArrowStream or ProbeStatement. Because the decision is a function of the authored buffer alone, two issuers that start from the same buffer — a run and the diagnostic probe describing it — cannot reach different endpoints.
func (*Client) ExecuteArrowStream ¶
func (inst *Client) ExecuteArrowStream(ctx context.Context, sql string, alloc memory.Allocator, opts *ExecOptions, signals map[string]string, dec dispatchDecision) (rdr *ipc.Reader, rs *resultStream, 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).
signals carries the caller's resolved signal values (ADR-0097 slice 5a), URL-keyed (`param_<name>` → raw); nil/empty means none. They ride the same `param_*` channel as the SET-bound constants BuildStatement harvests from the body's prelude, and a SET-bound name SHADOWS a same-named signal (slice-5 D1: a SET pins a signal into a constant) — the harvested params are applied second.
dec is required and names the endpoint (play_dispatch.go): obtain it from Client.Dispatch. It is a parameter rather than something read here so that every issuer of a request is enumerated by the compiler, and so a run and the diagnostic probe describing it provably share one decision.
func (*Client) ExecuteWrite ¶ added in v0.0.20
func (inst *Client) ExecuteWrite(ctx context.Context, sql string, signals map[string]string) (summary Summary, err error)
ExecuteWrite ships a write statement (the INSERT wrapper) and returns the server's summary. The body is BuildStatement's — every pre-execute rewrite applied, target adoption included, and no FORMAT appended — and the response is drained to its terminal, because ClickHouse can raise a failure after the status line and a write path that stops at the header would report such a run as OK (the documented chclient.Exec gap; this path must not inherit it).
func (*Client) ExposeConditions ¶ added in v0.0.14
ExposeConditions reports whether the selection-condition rewrite is on.
func (*Client) ExprSubstituted ¶ added in v0.0.20
ExprSubstituted is sql with its SQL-valued placeholders replaced — the body ADR-0187 §SD5 classifies.
It substitutes and stops. That is the whole point of its existing separately from [Client.buildResidual]: the class must be judged AFTER the user's expression enters and BEFORE the pass registry runs, because ADR-0132 §SD5 classifies the authored buffer precisely so a `keelson('…')` macro is not mistaken for the `url()` it may expand into. Running the passes first would reverse that decision silently.
The prelude is left in place, so the only difference from the buffer the classifier reads today is the substitution.
An unparseable buffer comes back unchanged with the error; the caller then classifies what the user wrote, which the classifier already fails closed on.
func (*Client) LastDecision ¶ added in v0.0.17
LastDecision returns the most recently resolved decision, and whether anything has been resolved yet. It is what the toolbar reports: where the last query actually went and why — a fact about a run that happened, not a prediction about the buffer currently being typed. Predicting would mean running the client-side rewrites on the render thread every frame, and those can reach the network.
func (*Client) PassRegistry ¶ added in v0.0.14
PassRegistry exposes the registry Execute applies at StagePreExecute — the Passes tab draws its catalog (ADR-0119 M3).
func (*Client) ProbeStatement ¶ added in v0.0.13
func (inst *Client) ProbeStatement(ctx context.Context, sql string, params map[string]string, opts *ExecOptions, dec dispatchDecision) (err error)
ProbeStatement POSTs sql verbatim (params riding the URL exactly as in ExecuteArrowStream) and reports only whether the server accepted it — no FORMAT rewrite, no Arrow decode. The diagnostics EXPLAIN probe consumes the verdict, not the rows: a FORMAT appended to `EXPLAIN AST <stmt>` would bind to the inner statement and leave EXPLAIN's own output undecodable, so the probe must stay off the Arrow pipeline. Non-200 responses fold the server's diagnostic into the error exactly like ExecuteArrowStream ("clickhouse http <code>: <body>"), which classifyProbeError keys on.
dec is required (play_dispatch.go). A probe that resolved its own endpoint from its own `EXPLAIN AST …` text would answer about a server the run it describes never talks to, so the caller passes the decision the run uses.
func (*Client) RewriteTrace ¶ added in v0.0.17
func (inst *Client) RewriteTrace(sql string) (obs []passreg.ApplyObservation)
RewriteTrace runs the client-side rewrite of sql for its per-step outcomes and discards the statement. Callers get what the registry stage and play's own steps did to this buffer — including passes that failed and were skipped, which otherwise appear only as a warn line in the process log (ADR-0108 §SD3 makes every unit degrade rather than fail, so a skipped rewrite is invisible in the result).
It costs a full rewrite — the passes re-parse — so callers memoise it by buffer rather than recomputing per frame. Safe from any goroutine: the state the rewrite reads is either immutable after wiring or guarded (Client.mu, the exposeConditions atomic).
func (*Client) SetExposeConditions ¶ added in v0.0.14
SetExposeConditions turns the opt-in selection-condition rewrite (ADR-0121) on or off.
func (*Client) SetExprValues ¶ added in v0.0.20
SetExprValues publishes the LIVE-tier expression values (ADR-0187 §SD3) — the ones a panel or the pane holds in the signal store rather than in the buffer's `-- play: expr` lines.
They cannot travel the way an ordinary live value does. A `param_*` entry on the request URL is a VALUE to ClickHouse, so a predicate sent that way is a string; the only route into the query is the splice, and the splice reads text. Hence a client binding, in the shape SetExposeConditions and bindDataset already established: written from the render thread, read wherever a query is built.
Whole-map replacement, so a name that left the live tier stops being substituted rather than lingering as a stale binding.
func (*Client) SetResolver ¶ added in v0.0.17
func (inst *Client) SetResolver(r endpointResolverI)
SetResolver installs the endpoint resolver. A nil resolver restores the static one, which always answers with the pinned endpoint.
func (*Client) SetStampIdentity ¶ added in v0.0.14
SetStampIdentity records the process/run identity the stamps carry: the runtime's run id (MountContextI.RunId — joins captured runs to the runtime-start fact) and the app id (the Manifest Id, the same value the MembRuntimeApp membership carries elsewhere). Callable any time; empty values simply leave those stamp fields out. The launcher wires it at Mount; the standalone CLI never does, and its runs stamp lane + fingerprints only.
type ClientConfig ¶
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 DetailTimeline ¶ added in v0.0.14
type DetailTimeline struct {
// contains filtered or unexported fields
}
DetailTimeline renders the Detail pane's per-row temporal overview strip. It owns a single non-interactive timeline widget reused across rows; the annotation/interval sets + pinned range are rebuilt only when the (result, row) changes — the early-cutoff the Timeline tab's driver uses.
func NewDetailTimeline ¶ added in v0.0.14
func NewDetailTimeline(ids *c.WidgetIdStack) (inst *DetailTimeline)
NewDetailTimeline builds the driver and its widget. The widget is non-interactive (a read-only overview; the annotation stagger separates colliding flags without a zoom, and a pan/zoom-capturing widget at the top of the Detail pane would fight the pane's own scroll). WithIntervalColors pins the lane-bar palette to the qualitative cycle so a range bar matches its legend swatch. The range is pinned per row via SetRange, so the widget's interval-only auto-fit never applies.
type DiagnosticsDriver ¶ added in v0.0.13
type DiagnosticsDriver struct {
// contains filtered or unexported fields
}
DiagnosticsDriver owns the probe lane and its render-thread state. All methods are render-thread-only; the lane does the async work.
func NewDiagnosticsDriver ¶ added in v0.0.13
func NewDiagnosticsDriver(client *Client) *DiagnosticsDriver
NewDiagnosticsDriver wires the probe against the live endpoint. A nil client (tests, legacy CLI) leaves the lane nil and the verdict at probeNone — the pane then shows the grammar error without a server check.
type DistDriver ¶ added in v0.0.20
type DistDriver struct {
// contains filtered or unexported fields
}
DistDriver owns the Distribution tab state: the folded model, its cache key, and the panel-local view + baseline state.
func NewDistDriver ¶ added in v0.0.20
func NewDistDriver(ids *c.WidgetIdStack) (inst *DistDriver)
type DocsEntry ¶ added in v0.0.20
type DocsEntry struct {
Name string
// Kind labels this entry when a name carries more than one — the pane
// offers a selector built from it. A source with no such ambiguity can
// leave it empty; a single unlabelled entry renders with no selector.
Kind string
// Body is the entry's prose, as Markdown.
Body string
// Source is shown under the body as provenance (e.g. a file path);
// empty when unknown.
Source string
}
DocsEntry is one documentation result for a name, source-agnostic: the pane renders whatever a DocsSourceI returns without knowing where it came from.
type DocsSourceI ¶ added in v0.0.20
type DocsSourceI interface {
// Lookup starts or continues resolving name. The Docs pane polls it at
// most once per frame — docsDriver's single-slot debounce is the only
// caller — and it must not block: ready is false while resolution is
// still in flight (or has not started), and the caller retries next
// frame. A name with no documentation is ready=true with entries empty,
// not an error.
Lookup(name string) (entries []DocsEntry, ready bool, err error)
// LinkClaimed reports whether a link target names something this source
// documents — kept in the pane, resolved via LinkCandidates — versus an
// ordinary hyperlink left for a browser. Runs once per link per frame
// during layout: a syntactic test, never a lookup.
LinkClaimed(url string) bool
// LinkCandidates ranks the names a claimed link might be naming, best
// first, for the pane to try against Lookup in order.
LinkCandidates(label, url string) []string
// AbsolutiseLinks rewrites an entry's own site-relative link targets,
// once, before its body is parsed as markdown, so they resolve outside
// the pane. A source whose bodies carry no relative links can return md
// unchanged.
AbsolutiseLinks(md string) string
// AbsoluteURL turns a link target into something a browser can open —
// the escape hatch offered when a claimed link resolves to nothing.
AbsoluteURL(url string) string
// EmptyHint is the pane's idle-state explanation, shown before anything
// is looked up or typed.
EmptyHint() string
// ExplainError turns a failed Lookup into pane copy, so a source can
// separate a cause its reader can act on from the rest.
ExplainError(err error) string
// Close releases whatever Lookup holds open (e.g. a query lane). Called
// once, from PlayApp.Close or when SetDocsSource replaces it; a source
// with nothing to release can no-op.
Close()
}
DocsSourceI is how the Docs pane finds documentation for a name and makes sense of its corpus's own links. Install one with PlayApp.SetDocsSource; ClickHouseDocsSource wraps ClickHouse's own system.documentation and is what an unconfigured PlayApp with a live client already uses.
type ExecOptions ¶ added in v0.0.12
type ExecOptions struct {
QueryID string
ReplaceRunningQuery bool
// Label is the human lane name ("main", "map", "diagnostics", …) the
// QueryID embeds — carried separately so the SD7 log_comment stamp
// can record it without parsing it back out of the id.
Label string
// OnProgress, when set, opts the request into ClickHouse's in-band
// progress headers (ADR-0115 plane A): the server streams
// X-ClickHouse-Progress lines inside the open response-header block,
// and the engine's streaming transport fires this callback for each one
// while the query still runs. Called from the transport goroutine —
// keep it cheap and thread-safe. Live delivery needs a plain-http
// endpoint; elsewhere the run degrades to the final summary with no
// mid-run ticks. nil = off (no wire change).
//
// The ticks are a callback rather than progress frames because they
// arrive inside the response-header block — before the result stream
// exists. Frames are how a party that is NOT the connection holder sees
// progress, and this lane is the connection holder.
OnProgress func(p runstream.Progress)
// WrapStatement, when set, wraps the WIRE body only — applied to the
// residual after the client-side rewrites and before the FORMAT step
// (a FORMAT inside a wrapper's parens would bind to the inner
// statement). Everything else — the routing decision, the URL params,
// the pre-execute rewrites, the row cap — is derived from the plain
// statement, so a wrapped run routes and rewrites exactly as the
// statement it wraps would: the ADR-0141 probe precedent ("resolve from
// the statement it wraps") generalized to lanes. The Flow tab's EXPLAIN
// lenses are the consumer: index structure and schema are
// endpoint-local, so the EXPLAIN must interrogate the endpoint the
// query would actually run on. The wrapper is machine-built and must
// not carry its own FORMAT clause.
WrapStatement func(residual string) string
}
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
// SigParams snapshots the URL-keyed signal values the run shipped
// (ADR-0097 slice-5 D4): the run's true inputs are the buffer (with its
// SET-bound constants) PLUS these. Restoring a history entry seeds the
// signal store from this map. nil when the run read no signals.
SigParams map[string]string
ErrorText string
// Buffer is the editor buffer the run came from, when that is not the SQL
// that executed — a multi-statement buffer ships only the statement under
// the caret (ADR-0130 L3). Restoring the entry restores this, so the
// siblings come back with it. Empty means SQL is the whole buffer.
Buffer string
}
type IcicleDriver ¶ added in v0.0.20
type IcicleDriver struct {
// contains filtered or unexported fields
}
IcicleDriver owns the Icicle tab state: the built tree and its cache, the laid-out geometry and its cache, the draw options, and the locally-pinned selection.
func NewIcicleDriver ¶ added in v0.0.20
func NewIcicleDriver(ids *c.WidgetIdStack) (inst *IcicleDriver)
NewIcicleDriver builds the driver. It takes no client: unlike Sankey and Network the panel reads the ACTIVE RESULT, so it has no lane of its own.
type KanbanDriver ¶ added in v0.0.14
type KanbanDriver struct {
// contains filtered or unexported fields
}
KanbanDriver owns the Kanban tab state: the board model, its build cache, and the lane for the optional lanes node (§SD6).
func NewKanbanDriver ¶ added in v0.0.14
func NewKanbanDriver(ids *c.WidgetIdStack, client *Client) (inst *KanbanDriver)
NewKanbanDriver builds the driver. client may be nil (tests, an unwired host): the lanes lane is then absent and the board reads lanes off the rows.
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 (fetchR15WalkersCameras, cached, keyed by this map's handle); once the camera has settled it EMITS the viewport as the six reserved vp_* signals (ADR-0096 §SD6 realized via ADR-0097 slice 5c) and demands a panel-authored node whose SQL carries the matching {vp_*:UInt32} slots — a pan changes only the compiled params, never the SQL text. The result packs to RGBA and draws as a mapRaster overlay pinned to bounds recovered from the served vp_* values.
The table, sampling, and colour render stay panel controls spliced into the node template (this is a power-user SQL playground — the editor already grants arbitrary query access); a control change is a template change and re-executes via the lane's (SQL, params) memo key. Not yet wired: the keepBuffer margin, the progressive sampling ladder, and hover→info queries — 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(sig SignalEnvI, emit SignalEmitterI)
Render draws the controls, the last-good raster overlay, and the map; and, once the viewport has settled (debounced), publishes it as the vp_* signals and demands the raster node compiled against the frame's signal snapshot (sig; emits land next frame — the 5a frame consistency). Order matters: the mapRaster overlay is a register-drain node, so it must be emitted BEFORE the walkersMap that drains it.
type MirrorSync ¶
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 NetworkDriver ¶ added in v0.0.14
type NetworkDriver struct {
// contains filtered or unexported fields
}
NetworkDriver owns the Network tab state: the two input lanes, the cached layout (recomputed only on a topology or rank-direction change, so a selection click never re-lays-out), and the pan/zoom view.
func NewNetworkDriver ¶ added in v0.0.14
func NewNetworkDriver(ids *c.WidgetIdStack, client *Client) (inst *NetworkDriver)
NewNetworkDriver builds the driver. client may be nil (tests, an unwired host): the lanes are then absent and the panel shows its empty-state.
type Node ¶ added in v0.0.12
type Node struct {
ID NodeID
Compile func(sig SignalEnvI) (c compiledNode, err error)
}
Node is a query node. Compile produces the pushed-down SQL plus the signal values it reads from the current signal env (ADR-0097: editor SQL → nanopass pipeline → param resolution). In slice 1 Compile is supplied directly; the splitter and a real nanopass pipeline land in slice 3.
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.
//
// It must also stay CHEAP — a schema scan, not a data pass. Since the dock
// strip carries the verdict (the 2026-07-27 Update) it runs once per
// registered tab per frame, hidden tabs included, not only for the panes a
// frame draws. Data-dependent work belongs in Render, where the record is
// at hand and the pane is known to be visible.
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 NewLivePlayApp ¶ added in v0.0.13
func NewLivePlayApp(client *Client, initialSQL string, maxHistory int, rules *gloss.Repository) *PlayApp
NewLivePlayApp builds a PlayApp wired to a live ClickHouse Client — the same query-graph wiring PlayLauncher.Mount uses — and returns it ready for a re-user to customize before mounting (e.g. SetDetailContent to override the Detail body, or SetDocsSource to point the Docs pane at a different corpus). It is the supported constructor for embedding the playground behind a domain-specific AppI: the live query graph type is unexported, so an external module cannot call NewPlayApp directly. maxHistory bounds each lane's result-history ring (the shipped launcher uses 100). See doc/howto/play-pluggable-detail.md and doc/howto/play-pluggable-docs.md.
It also installs the client's pre-execute SQL pipeline — the standard pass set (ADR-0108, e.g. LW_ID_* macro expansion) plus the schema-aware leeway name resolver (ADR-0116) — and feeds that resolver to the Diagnostics pane. That wiring is unexported (it sets the client's private pass registry), so an embedder cannot reproduce it; folding it in here is what lets every embedder pre-process SQL identically to the standalone CLI and the launcher instead of re-implementing launcher internals. A nil client — the result-less test shells that never run a query — skips the install.
rules is the ADR-0186 rule repository — the standing gloss rules and the catalog — the embedder built at wiring time; nil takes DefaultRepository.
func NewPlayApp ¶
func NewPlayApp(client *Client, graph *queryGraph, initialSQL string, rules *gloss.Repository) *PlayApp
NewPlayApp builds the playground over a client, its query graph, the buffer it opens with, and the gloss rule repository (ADR-0186) — the standing rules and the catalog the host hands in; nil means play's own DefaultRepository.
func (*PlayApp) ActivateTab ¶ added in v0.0.14
ActivateTab focuses the dock tab with the given registry slug ("editor", "table", … — the ids from Tabs().Specs()) on the next dock send. An unknown slug is an error, matching the registry's validation style. The SQL delivery ops focus the editor for you; reach for this to raise a different pane.
func (*PlayApp) BindDataset ¶ added in v0.0.15
BindDataset binds a stable alias — as written in the buffer, keelson('<alias>') — to an ephemeral dataset handle, so the client rewrites the alias to the handle before the request leaves play (ADR-0134 §SD4). Both must be bare identifiers. The binding is instance state, applied by the embedder between construction and mount.
func (*PlayApp) BindTab ¶ added in v0.0.14
BindTab points a panel tab at a split node by CTE name (ADR-0097 slice 6c). An unknown tab id is an error, matching ActivateTab's validation style; an unknown node name is deliberately NOT one — bindings key on CTE names, sit inert while a split lacks the name, and revive when it returns.
func (*PlayApp) CanCopy ¶ added in v0.0.20
CanCopy reports whether the clipboard is reachable from this instance — what an affordance that only exists to copy is gated on.
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) ComposeLaunch ¶ added in v0.0.20
func (inst *PlayApp) ComposeLaunch() (cfg launchcfg.PlayLaunch)
ComposeLaunch renders the window's current user-authored state as the launch that would reproduce it (ADR-0148 §SD2/§SD8): the editor buffer, the Timeline bands buffer, the Live toggle, and the active pane.
AutoRun composes false unconditionally — re-executing a buffer is something a user asks for, not something a restoration does — and At is stamped now, which is also why nothing in the save path byte-compares two composed configs.
Endpoint is deliberately left empty: which target a buffer ran against is a property of the run that authored it, not of the state, and a restored window resolves keelson reads through the auto-routing resolver (ADR-0141) anyway.
Caches, result history, selections and pane-local view state are out of scope by SD1 — a workingset carries what the user authored or chose.
func (*PlayApp) Frame ¶ added in v0.0.20
func (inst *PlayApp) Frame(ctx app.FrameContextI) (err error)
Frame renders one frame with the per-frame capabilities the host offers on ctx. It is the entry point for every host: the launcher below, and equally any library that builds a PlayApp of its own and re-hosts it.
Two capabilities arrive this way, both in the ADR-0155 §SD1 shape — an optional interface type-asserted off the context, so a host that cannot offer one owes nothing and nothing errors:
- app.WindowFocusI gates the process-global Ctrl+Enter chord. The chord is drained once from egui's shared queue and is visible to every open instance alike, so without the gate one press runs a query in all of them. A host that cannot say is taken as focused: single-surface hosts and tests have only the one instance, and it is the active one.
- colwidth.HostI carries the column-width store (ADR-0151). It is acquired here rather than at Mount because it is a frame-context capability; ensureColWidthRes does the once-only work.
The render pass itself is unexported precisely because of this: a re-host holding a frame context has no reason to skip either capability, and every re-host that called the pass directly silently lost both. Making the context the only way in turns that from a rule to remember into a signature.
func (*PlayApp) InsertSqlAtCaret ¶ added in v0.0.14
InsertSqlAtCaret hands text to the SQL editor to splice at its caret on the next editor render (TextEditFluid.InsertAtCursor, ADR-0063) and focuses the Editor tab. The activation is a correctness step, not cosmetic: a hidden tab's body buffer is discarded uninterpreted, so an insert into an unfocused editor is silently lost — bundling it here keeps the visibility invariant the API's to hold, not each caller's to remember.
func (*PlayApp) MainSQL ¶ added in v0.0.14
MainSQL returns the executed SQL text behind the `main` node's current result — the query MainSnapshot's record was produced by — or "" when none has landed yet. It is the thread-safe companion to MainSnapshot for embedders that need the query behind the live result (e.g. deriving per-result query lineage): the read is guarded by the main lane's lock, so it is safe off the render loop. Being a second, independent lock acquisition, a result that completes between a MainSnapshot and a MainSQL call can momentarily pair rows from one query with the SQL of the next; for human-driven single-query flows that window is not observable.
func (*PlayApp) MainSnapshot ¶ added in v0.0.14
func (inst *PlayApp) MainSnapshot() (rec arrow.RecordBatch, schema *arrow.Schema, numRows int64, loading bool, elapsed time.Duration, summary Summary, executed time.Time, err error)
MainSnapshot returns a retained view of the `main` node's current result — the sink result the Table tab shows by default — with its metadata. It is the thread-safe embedder seam for reading the live resultset OFF the render loop (e.g. an on-demand report server behind a re-user's own pane): the read is guarded by the main lane's QueryStore lock, so it is safe from any goroutine, unlike the render-thread-only activeSnapshot (which observes the intermediate lane and per-frame split state). The caller MUST Release the returned record (nil-safe); rec is nil until the first result lands, with loading/err then reflecting the lane state. See doc/howto/play-pluggable-detail.md for the companion body/tab seams.
func (*PlayApp) NotifyDatasetRevision ¶ added in v0.0.15
NotifyDatasetRevision signals that a bound dataset was republished at a new revision. Under Live main it triggers the ordinary auto-run path so the applet re-queries the fresh data; an explicit Run always works regardless. This is the instance-level realization of ADR-0134 §SD5 freshness (a trigger, not a graph signal). The alias identifies which dataset changed and is advisory today — the trigger re-runs the whole main buffer.
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) ReplaceSql ¶ added in v0.0.14
ReplaceSql swaps the whole SQL editor buffer with text on the next editor render and focuses the Editor tab. A same-frame ReplaceSql supersedes an InsertSqlAtCaret. See InsertSqlAtCaret for the activation rationale.
func (*PlayApp) RequestRun ¶ added in v0.0.15
func (inst *PlayApp) RequestRun()
RequestRun schedules a run of the main buffer after the current frame — the same one-shot the Run button and the Live auto-run path use.
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.
Read-only bridge (ADR-0148 §SD8, added 2026-07-29): nothing writes this key any more — the editor buffer now survives as a workingset record the window host pulls at close. Mount consults the key only for a window that received no config at all, so a session that predates the workingset era still finds its buffer. Retire the key, this function, and the PersistedKeys entry one release on.
func (*PlayApp) RestorePersistedTimelineBandsSql ¶
func (inst *PlayApp) RestorePersistedTimelineBandsSql()
RestorePersistedTimelineBandsSql loads the bands-SQL editor buffer from the persist cap. Same best-effort semantics — and the same one-release read-only bridge — as RestorePersistedSql.
func (*PlayApp) SetCapabilities ¶
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) SetColumnResolver ¶ added in v0.0.14
func (inst *PlayApp) SetColumnResolver(resolver passes.ColumnResolverI)
SetColumnResolver wires the leeway column resolver into the Diagnostics pane, so it can warn — client-side, before a Run — about `section:column` handles that name no known section or column. Safe to call with nil (no resolver).
func (*PlayApp) SetDatasetNotice ¶ added in v0.0.20
SetDatasetNotice hands play a runtime notice about this instance's data preconditions — in practice, that a declared ad-hoc dataset alias has no dataset behind it yet (ADR-0134 §SD4). It renders above the preamble, inside the central panel, so the reader meets the reason the panes are empty before the prose explaining what the panes would mean.
Unlike PlayApp.SetPreambleMarkdown this is expected to be re-set while the instance is live, as the condition it reports appears and clears; empty source clears it. It reparses on every call, so a caller that re-derives the same text each frame should compare before setting.
It is the host's surface, not the document's: play has no idea what produces a given alias, and the embedder that declared the alias does.
func (*PlayApp) SetDefinitionMarkdown ¶ added in v0.0.20
SetDefinitionMarkdown hands play the document this instance was defined by: a sqlapplet's markdown source (ADR-0132 §SD1) — frontmatter as the manifest, prose as the explanation, the first `sql` fence as this buffer. It turns on the top bar's "Definition" toggle and the drawer behind it. Call it between construction and mount, like the tab registry; empty source leaves the affordance off, which is what an ordinary playground wants.
Parsing happens here rather than per frame. Images resolve through no vault — the seam carries the document's bytes and not the book's FS, so a doc that embeds one degrades to markdown's glyph-hyperlink fallback.
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.
func (*PlayApp) SetDocsSource ¶ added in v0.0.20
func (inst *PlayApp) SetDocsSource(src DocsSourceI)
SetDocsSource overrides the Docs pane's lookup, replacing whatever source is currently installed (closing it first). Passing nil restores ClickHouse's own system.documentation via NewClickHouseDocsSource — what an unconfigured PlayApp with a live client already uses. Takes effect on the next frame; there is nothing else to unregister. See doc/howto/play-pluggable-docs.md.
func (*PlayApp) SetLiveMain ¶ added in v0.0.14
SetLiveMain presets the `main` lane's Live toggle (ADR-0097 slice 5e, D2). The toggle stays user-reachable in the top bar; presetting it on lets an embedder open with signal-driven re-runs active (e.g. a sqlapplet whose buffer reads `{selection_id:UInt64}`, ADR-0132 §SD3).
func (*PlayApp) SetPreambleMarkdown ¶ added in v0.0.20
SetPreambleMarkdown hands play the explanatory passage an applet author wants standing over the results — a sqlapplet's `md preamble` aux fence. It renders above every result pane, inside the central panel, so it reads as a header on the data rather than as another piece of toolbar. Call it between construction and mount; empty source renders nothing.
Separate from PlayApp.SetDefinitionMarkdown on purpose: the definition is the whole document a reader opens deliberately, the preamble is the couple of sentences they should not have to open anything to see. An applet may have either, both, or neither.
func (*PlayApp) SetSecurityCeiling ¶ added in v0.0.20
func (inst *PlayApp) SetSecurityCeiling(class analysis.QuerySecurityClassE)
SetSecurityCeiling declares the strongest class this instance's queries may reach (ADR-0187 §SD5). An applet passes its mint-time class; play leaves it unset, and the zero value refuses nothing.
It is a ceiling on the SUBSTITUTED body, so it constrains what a knob may turn the document into — not what the document already is. Raising the document's own class is the author's business and the mint-time classification already covers it.
func (*PlayApp) SetSignal ¶ added in v0.0.15
SetSignal publishes value under signal name through the same emit path panels use (play_graph.go): last-write-wins within a frame, visible from the next frame's snapshot. Supported value kinds are the signal-store encodable set (string, integers, float64, bool); others are dropped with a warning by the emitter.
func (*PlayApp) SetTimelineBandsSql ¶ added in v0.0.14
SetTimelineBandsSql seeds the Timeline panel's bands editor (the panel-local SQL of ADR-0097 slice 5d). An embedder whose own definition carries bands SQL — e.g. a sqlapplet aux fence (ADR-0132 §SD1) — applies it between construction and mount; empty is a valid value (no bands).
func (*PlayApp) SetToolbarMinimal ¶ added in v0.0.14
SetToolbarMinimal attenuates the top bar to the applet surface (ADR-0132 §SD3): Load .sql, the endpoint switcher, and the prelude/conditions toggles disappear; Run/Cancel, the Live toggle, and the unfilled-inputs hint stay; the "Open in Playground" escape hatch (ADR-0135 §SD7) appears when a bus is wired. Call it between construction and mount, like the tab registry. The other escape hatch, "Definition", rides PlayApp.SetDefinitionMarkdown rather than this flag — it is offered wherever a document exists to show.
func (*PlayApp) Tabs ¶ added in v0.0.13
func (inst *PlayApp) Tabs() *TabRegistry
Tabs is the instance's dock-tab set (ADR-0097 slice 6a, D4): an embedder customizes it — Add/Replace/Remove — between construction and mounting; the first Render freezes it. See TabSpec for the registration shape.
func (*PlayApp) UnbindDataset ¶ added in v0.0.20
UnbindDataset drops the alias→handle binding BindDataset installed, so the alias passes through unrewritten again (and a query naming it fails readably as an unknown keelson table). The embedder calls it when the dataset behind the alias is withdrawn — the ADR-0188 §SD3 `retracted` event — and binds afresh when a new dataset appears under the alias. Unbinding an alias that is not bound is a no-op.
func (*PlayApp) WorkingsetDirty ¶ added in v0.0.20
WorkingsetDirty reports whether user intent occurred in this window since Mount (ADR-0148 §SD4): an edit to either SQL buffer, a manual Run, a Live toggle, or a pane raised through the delivery seam. It is the whole save gate — a window nobody acted in leaves the stored record alone.
type PlayLauncher ¶
type PlayLauncher struct {
// Rules is the gloss rule repository every window this launcher opens is
// built over (ADR-0186); nil takes DefaultRepository. The factory
// registered in init leaves it nil, so a deployment that links play
// registers its rule sets on DefaultRepository.
Rules *gloss.Repository
// 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) ComposeWorkingset ¶ added in v0.0.20
func (inst *PlayLauncher) ComposeWorkingset() (cfg []byte, dirty bool, err error)
ComposeWorkingset implements app.WorkingsetComposerI for the launcher (ADR-0148 §SD4). The host calls it at close, before Unmount — which is load-bearing: Unmount releases the inner app, so the nil guard below is the honest answer for anything that asks afterwards, not a fallback.
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 ¶
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, signals map[string]string, sourceBuffer string)
Execute kicks off an async query. signals carries the URL-keyed signal values resolved for this run (ADR-0097 slice 5a; nil = none) — they ride the request URL and are snapshotted into the history entry (D4). Subsequent calls while a query is running are ignored; call Cancel first.
sourceBuffer is the editor buffer sql was derived from, recorded on the history entry so a restore brings back the whole buffer rather than the fragment that ran (ADR-0130 L3 run-under-cursor). Pass "" when sql IS the buffer — every lane but `main` does.
func (*QueryStore) History ¶
func (inst *QueryStore) History() []HistoryEntry
func (*QueryStore) IsLoading ¶
func (inst *QueryStore) IsLoading() bool
func (*QueryStore) Progress ¶ added in v0.0.14
func (inst *QueryStore) Progress() (p runstream.Progress, fresh bool)
Progress returns the in-flight run's latest in-band progress tick; fresh is false when no run is loading or no tick has arrived yet.
func (*QueryStore) SQL ¶ added in v0.0.14
func (inst *QueryStore) SQL() string
SQL returns the SQL text of the run that produced the current Snapshot result, or "" before the first run finishes. Guarded by the same lock as Snapshot; as a separate call it can race a concurrent finish (see PlayApp.MainSQL).
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.
func (*QueryStore) Truncation ¶ added in v0.0.17
func (inst *QueryStore) Truncation() (reason string)
Truncation reports the reason the last result is a prefix, or "" when it is whole (or when the run failed, which err already says). Read it beside Snapshot: a capped result looks exactly like a complete one otherwise, which is the confusion R9 exists to prevent.
type SankeyDriver ¶ added in v0.0.20
type SankeyDriver struct {
// contains filtered or unexported fields
}
SankeyDriver owns the Sankey tab state: the two input lanes, the cached layout (recomputed only when the diagram changes, so a hover never re-lays out), the draw options, and the locally-pinned selection.
func NewSankeyDriver ¶ added in v0.0.20
func NewSankeyDriver(ids *c.WidgetIdStack, client *Client) (inst *SankeyDriver)
NewSankeyDriver builds the driver. client may be nil (tests, an unwired host): the lanes are then absent and the panel shows its empty state.
type SeriesDriver ¶ added in v0.0.20
type SeriesDriver struct {
// contains filtered or unexported fields
}
SeriesDriver owns the Series tab state: the folded series, its cache key, and the panel-local view state.
func NewSeriesDriver ¶ added in v0.0.20
func NewSeriesDriver(ids *c.WidgetIdStack, deliver func(sql string)) (inst *SeriesDriver)
type SignalEmitterI ¶ added in v0.0.12
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
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
MemoryUsage uint64
}
Summary mirrors ClickHouse's X-ClickHouse-Summary JSON-ish header values.
Parsing the header is the engine adapter's job (chserver.ParseSummary); this is play's display shape, and [summaryFrom] is the one place the two meet.
type TabFrame ¶ added in v0.0.13
type TabFrame struct {
Rec arrow.RecordBatch
Schema *arrow.Schema
NumRows int64
Loading bool
Elapsed time.Duration
Summary Summary
Executed time.Time
Err error
Sig SignalEnvI
Emit SignalEmitterI
}
TabFrame is the per-frame view a tab body renders from: the active result snapshot plus the frame's signal env and emitter. It decouples tab bodies from Render's locals; per-tab STATE stays wherever it lives today and migrates behind factories opportunistically (slice-6 D2).
type TabRegistry ¶ added in v0.0.13
type TabRegistry struct {
// contains filtered or unexported fields
}
TabRegistry is a PlayApp instance's tab set (D4): mutate between construction and the first Render via Add/Replace/Remove; the first Render freezes it. Not safe for concurrent use — it is render-thread state.
func (*TabRegistry) Add ¶ added in v0.0.13
func (inst *TabRegistry) Add(spec TabSpec) (err error)
Add appends a tab. New body-zone tabs render after the built-ins.
func (*TabRegistry) Remove ¶ added in v0.0.13
func (inst *TabRegistry) Remove(id string) (err error)
Remove drops the tab with the given id.
func (*TabRegistry) Replace ¶ added in v0.0.13
func (inst *TabRegistry) Replace(id string, spec TabSpec) (err error)
Replace swaps the tab with spec.ID == id, keeping its position. The replacement may change every field including DockID (a different pane identity for the persisted layout).
func (*TabRegistry) SetZone ¶ added in v0.0.20
func (inst *TabRegistry) SetZone(id string, z TabZoneE) (err error)
SetZone moves a registered tab to another layout zone, leaving the rest of its spec alone.
It exists so an embedder can place a pane without rebuilding the spec that declares it: a document says where its panes go, and everything else about them — the panel, the dock identity, the shape contract — belongs to whoever registered the tab. Replace would make the caller restate all of it, and a caller restating a spec it did not author is a caller that will eventually restate it wrongly.
Like the other mutators, valid only before the first Render.
func (*TabRegistry) Specs ¶ added in v0.0.13
func (inst *TabRegistry) Specs() (out []TabSpec)
Specs returns a copy of the registered tabs in registration order — the read surface for embedders (asserting their registrations) and, later, the binding UI. Mutate the set only through Add/Replace/Remove.
type TabSpec ¶ added in v0.0.13
type TabSpec struct {
ID string
DockID uint64
Title string
Zone TabZoneE
NoScroll bool
Lazy bool
Panel PanelI
Render func(f *TabFrame)
// ShapeContract marks a panel whose acceptance turns on the RESULT'S
// COLUMN SHAPE — the Timeline's `_tl_*` contract, the World's country
// column, the Kanban's `lane`+`title`, the Network's `edges` CTE. Their
// rejection is worth advertising on the dock strip (the `×` mark of the
// 2026-07-27 Update); the universal panes accept any schema and reject
// only on interaction state ("select a row"), where a mark would be
// permanent and carry no information.
ShapeContract bool
// Writes are the signal names this tab may publish — the write-back half
// of the reactive surface, declared so the strip can mark a pane that
// drives the current query BEFORE the first interaction (provenance knows
// a writer only afterwards). It lives here rather than on PanelI because
// the Map writes `vp_*` without being a PanelI at all. The companions the
// dispatcher stamps (`selection_node`, `selection_id`) are implied by
// declaring `selection` — see declaredWrites.
Writes []SignalID
}
TabSpec declares one dock tab. ID is the stable human slug ("table", "map") keying the focus knobs and, later, per-channel bindings; DockID is the frozen dock identity (D3). Panel is the PanelI for result panels and nil for chrome. NoScroll opts out of the dock's default per-tab ScrollArea — for panes that consume wheel/zoom gestures themselves (Map) or size from the available remainder (World). Lazy routes the body through a widgets/lazypane gate: while the host discards the tab's buffer (inactive tab), only a probe + loading placeholder is emitted; the real body lands one frame after activation. Opt in for heavy bodies only — a lazy tab shows a one-frame loading tick on switch.
type TabZoneE ¶ added in v0.0.13
type TabZoneE uint8
TabZoneE places a tab in the initial dock layout (before the user drags — the persisted dock state wins afterwards). The zero value is the body zone, where embedder tabs land by default.
const ( TabZoneBody TabZoneE = iota // the main body leaf (result views) TabZoneEditor // the editor leaf (Editor, History) // TabZoneTools is the leaf split right of the editor: the tool panes, // read WHILE editing. A tool pane is chrome (nil Panel) whose input is // the buffer or what is derived from it — the caret, the split, the // pre-execute pipeline — as opposed to a result panel, which is fed the // query result over a channel (ADR-0097 Update 2026-08-01). It was // TabZonePreview when Preview was its only occupant. TabZoneTools TabZoneSide // split right of the body leaf (Detail) // TabZoneBottom is the leaf split BELOW the body: a pane read alongside // the body rather than instead of it, spanning the full width because the // split is taken before the side zone narrows the body. // // Nothing registers here by default, so play's own layout is unchanged; it // exists for a document that says where its panes go (ADR-0132's `tabs:` // zone suffix). The three-pane shape it buys — a picture, its detail // beside it, the rows underneath — is what a corpus browser is, and it was // not expressible while every result panel landed in one leaf of tabs. TabZoneBottom )
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 publish the `selection` signal (slice 5b) so the Detail tab updates. Bucket clicks (Points-mode rug aggregation) carry no per-row id and are ignored.
Bands: a panel-authored node (SQL held on PlayApp, accessed through bandsSQLPtr) demanded on its own lane each frame. Its `{tl_min}`/`{tl_max}` slots — and any other referenced signals — resolve against the frame snapshot; the Timeline publishes the events extent as those signals after each rebuild (slice 5d), 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, 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). Selection publishes through the SignalEmitterI renderContract receives per frame (slice 5b — no PlayApp 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 it elsewhere. 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.
type WorldDriver ¶ added in v0.0.13
type WorldDriver struct {
// contains filtered or unexported fields
}
WorldDriver owns the World tab state: the worldmap widget, the value-column choice, and the per-result extraction cache.
func NewWorldDriver ¶ added in v0.0.13
func NewWorldDriver(ids *c.WidgetIdStack) *WorldDriver
Source Files
¶
- app_register.go
- play_affordance.go
- play_bindings.go
- play_card_driver.go
- play_chart_panel.go
- play_cli.go
- play_client.go
- play_completion_catalog.go
- play_completion_panel.go
- play_concat.go
- play_cost_chart.go
- play_datasets.go
- play_definition.go
- play_delivery.go
- play_detail.go
- play_detail_panel.go
- play_detail_rich.go
- play_detail_timeline.go
- play_diagnostics.go
- play_dispatch.go
- play_dispatch_policy.go
- play_dispatch_reach.go
- play_dist_panel.go
- play_docs.go
- play_docs_clickhouse.go
- play_docs_panel.go
- play_docs_source.go
- play_editor_styled.go
- play_experiments_tab.go
- play_extract_params.go
- play_files_panel.go
- play_flow_highlight.go
- play_flow_lens.go
- play_flow_lineage.go
- play_flow_model.go
- play_flow_panel.go
- play_format.go
- play_gloss.go
- play_gloss_panel.go
- play_graph.go
- play_graph_executor.go
- play_graph_lane.go
- play_graph_view.go
- play_graph_viz.go
- play_hierarchy.go
- play_icicle_panel.go
- play_kanban_panel.go
- play_layeredgraph_panel.go
- play_map.go
- play_observation.go
- play_pane_box.go
- play_panel_dispatch.go
- play_panes_menu.go
- play_param_ceiling.go
- play_param_inject.go
- play_param_render.go
- play_param_reset.go
- play_param_slots.go
- play_param_splice.go
- play_param_widget.go
- play_param_widget_enum.go
- play_param_widget_expr.go
- play_param_widget_range.go
- play_parse.go
- play_passes.go
- play_passes_cost.go
- play_passes_tab.go
- play_pathrows.go
- play_pin.go
- play_progress.go
- play_projection.go
- play_projection_panel.go
- play_querystate.go
- play_renderer.go
- play_runs_history.go
- play_runstream.go
- play_sankey_panel.go
- play_schema_infer.go
- play_schema_panel.go
- play_schema_provider.go
- play_series_fixture.go
- play_series_labels.go
- play_series_overlays.go
- play_series_panel.go
- play_signals_chrome.go
- play_snippets.go
- play_split.go
- play_sqlsurface_install.go
- play_stamp.go
- play_statements.go
- play_store.go
- play_subquery.go
- play_tab_marks.go
- play_table_attr.go
- play_table_leeway.go
- play_table_panel.go
- play_table_sort.go
- play_tabs.go
- play_text.go
- play_timeline.go
- play_timeline_bands.go
- play_timeline_panel.go
- play_treemap_panel.go
- play_ts_collision.go
- play_ts_executor.go
- play_ts_recognize.go
- play_ts_registry.go
- play_ts_transforms.go
- play_vocab_model.go
- play_vocab_panel.go
- play_vocab_probe.go
- play_vocab_tree.go
- play_workingset.go
- play_world_panel.go
- play_write_run.go
Directories
¶
| Path | Synopsis |
|---|---|
|
Package launchcfg is play's launch config (ADR-0135 §SD7): the typed arguments another app can open a play window with over `windowhost.open`.
|
Package launchcfg is play's launch config (ADR-0135 §SD7): the typed arguments another app can open a play window with over `windowhost.open`. |