@bitcobblers/wod-wiki-ui 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,882 @@
1
+ import { QueryOptions, QueryResult, ParsedFindQuery, FindOptions, FindQueryResult, ParsedRowsQuery, RowsQueryResult, ParsedQuery, DashboardToken, DashboardDocument, DashboardWidget, Series, RowsRun } from '@bitcobblers/wod-wiki-wql';
2
+ export { FindOptions, FindQueryResult, ParsedFindQuery, ParsedQuery, ParsedRowsQuery, QueryOptions, QueryResult, RowsQueryResult, RowsRun } from '@bitcobblers/wod-wiki-wql';
3
+ import * as react from 'react';
4
+ import { ReactNode, ComponentType } from 'react';
5
+ import { Segment, StoredOutputStatement, BlockIndexRow, ScriptBlock, ICodeStatement, IMetric } from '@bitcobblers/wod-wiki-core';
6
+ import * as _codemirror_state from '@codemirror/state';
7
+ import { Extension, Facet, EditorState, StateField } from '@codemirror/state';
8
+ import { ViewPlugin, DecorationSet, ViewUpdate, EditorView, Decoration, WidgetType } from '@codemirror/view';
9
+ import { Diagnostic } from '@codemirror/lint';
10
+ import { CompletionContext, CompletionResult } from '@codemirror/autocomplete';
11
+ import { ClassValue } from 'clsx';
12
+
13
+ interface QueryExecutor {
14
+ runQuery(query: string, options?: QueryOptions): Promise<QueryResult>;
15
+ runFind(parsed: ParsedFindQuery, options?: FindOptions): Promise<FindQueryResult>;
16
+ runRows(parsed: ParsedRowsQuery, options?: {
17
+ anchorNow?: number;
18
+ }): Promise<RowsQueryResult>;
19
+ run?(parsed: ParsedQuery, options?: QueryOptions): Promise<QueryResult>;
20
+ }
21
+
22
+ interface StorageLike {
23
+ getItem(key: string): string | null;
24
+ setItem(key: string, value: string): void;
25
+ removeItem?(key: string): void;
26
+ }
27
+
28
+ declare const CODEMIRROR_SINGLETON_DEPS: readonly ["@codemirror/autocomplete", "@codemirror/commands", "@codemirror/lang-markdown", "@codemirror/language", "@codemirror/lint", "@codemirror/search", "@codemirror/state", "@codemirror/theme-one-dark", "@codemirror/view", "@lezer/common", "@lezer/highlight", "@lezer/lr", "@lezer/markdown"];
29
+
30
+ interface WidgetFrameProps {
31
+ title: string;
32
+ question: string;
33
+ query: string;
34
+ span?: string;
35
+ children: ReactNode;
36
+ }
37
+ declare function WidgetFrame({ title, question, query, span, children }: WidgetFrameProps): react.JSX.Element;
38
+
39
+ interface QueryValueProps {
40
+ result: QueryResult;
41
+ unit?: string;
42
+ label: string;
43
+ thresholds?: {
44
+ green: [number, number];
45
+ red: [number, number];
46
+ };
47
+ }
48
+ declare function QueryValue({ result, unit: unitProp, label, thresholds }: QueryValueProps): react.JSX.Element;
49
+
50
+ interface WqlTimeseriesProps {
51
+ result: QueryResult;
52
+ unit?: string;
53
+ }
54
+ declare function WqlTimeseries({ result, unit: unitProp }: WqlTimeseriesProps): react.JSX.Element;
55
+
56
+ interface WqlBarsProps {
57
+ result: QueryResult;
58
+ unit?: string;
59
+ }
60
+ declare function WqlBars({ result, unit: unitProp }: WqlBarsProps): react.JSX.Element;
61
+
62
+ interface WqlTableProps {
63
+ result: QueryResult;
64
+ unit?: string;
65
+ }
66
+ declare function WqlTable({ result, unit: unitProp }: WqlTableProps): react.JSX.Element;
67
+
68
+ interface WidgetChartProps {
69
+ /** Raw widget type from the fence suffix; '' picks the dashboard default (table). */
70
+ type: string;
71
+ result: QueryResult | undefined;
72
+ /** Fallback label for value widgets (widget title or metric). */
73
+ label?: string;
74
+ unit?: string;
75
+ /** Positional widget parameters from the block body (e.g. goal target or zone targets). */
76
+ params?: string[];
77
+ }
78
+ declare function WidgetChart({ type, result, label, unit, params }: WidgetChartProps): react.JSX.Element;
79
+ declare function WidgetProblemBadge({ message }: {
80
+ message: string;
81
+ }): react.JSX.Element;
82
+ declare function ProposedMetricBadge({ metric }: {
83
+ metric: string;
84
+ }): react.JSX.Element;
85
+
86
+ interface DashboardTokenControlsProps {
87
+ tokens: DashboardToken[];
88
+ /** Current value per token name (defaults applied by the caller). */
89
+ values: Record<string, string>;
90
+ /** Present when the note is editable — changes write back to frontmatter. */
91
+ onChange?: (name: string, value: string) => void;
92
+ }
93
+ /**
94
+ * DashboardTokenControls — the control row a dashboard note's frontmatter
95
+ * tokens render as (#899): scalar tokens become inputs, list tokens segmented
96
+ * controls (first entry = default). Read-only views (no onChange) render the
97
+ * current values as static text.
98
+ */
99
+ declare function DashboardTokenControls({ tokens, values, onChange }: DashboardTokenControlsProps): react.JSX.Element | null;
100
+
101
+ interface DashboardViewProps {
102
+ /** Parsed dashboard note (buildDashboardDocument). */
103
+ document: DashboardDocument;
104
+ /** Injected QueryExecutor for executing WQL queries. Zero singleton coupling. */
105
+ executor?: QueryExecutor;
106
+ /** Optional rollup fact warmer callback. */
107
+ onEnsureRollupFacts?: () => Promise<void>;
108
+ /** Current token values; defaults to each token's declared default. */
109
+ tokenValues?: Record<string, string>;
110
+ /** Present when the note is editable — control changes write back to frontmatter. */
111
+ onTokenChange?: (name: string, value: string) => void;
112
+ /** Present when the host offers query editing (composer modal owned by the host). */
113
+ onEditQuery?: (widget: DashboardWidget) => void;
114
+ /** Optional execution range (ms epoch). Omit to use each query's own window. */
115
+ rangeStart?: number;
116
+ rangeEnd?: number;
117
+ preferredUnit?: string;
118
+ }
119
+ /**
120
+ * DashboardView — the state-free dashboard renderer (#900, format locked in #899).
121
+ *
122
+ * Takes a parsed DashboardDocument + current token values, runs all widget
123
+ * queries concurrently via the injected QueryExecutor, and renders the responsive CSS grid.
124
+ */
125
+ declare function DashboardView({ document, executor, onEnsureRollupFacts, tokenValues, onTokenChange, onEditQuery, rangeStart, rangeEnd, preferredUnit, }: DashboardViewProps): react.JSX.Element;
126
+
127
+ interface WqlEmptyStateProps {
128
+ result: QueryResult | undefined;
129
+ className?: string;
130
+ }
131
+ declare function WqlEmptyState({ result, className }: WqlEmptyStateProps): react.JSX.Element | null;
132
+
133
+ interface TopListProps {
134
+ result: QueryResult;
135
+ unit?: string;
136
+ limit?: number;
137
+ }
138
+ declare function TopList({ result, unit: unitProp, limit }: TopListProps): react.JSX.Element;
139
+
140
+ interface StackedBarProps {
141
+ result: QueryResult;
142
+ unit?: string;
143
+ }
144
+ declare function StackedBar({ result, unit: unitProp }: StackedBarProps): react.JSX.Element;
145
+
146
+ interface GoalRingsProps {
147
+ result: QueryResult | undefined;
148
+ params?: string[];
149
+ label?: string;
150
+ unit?: string;
151
+ }
152
+ declare function GoalRings({ result, params, label, unit }: GoalRingsProps): react.JSX.Element;
153
+
154
+ interface ZoneDistributionProps {
155
+ result: QueryResult | undefined;
156
+ params?: string[];
157
+ unit?: string;
158
+ }
159
+ declare function ZoneDistribution({ result, params }: ZoneDistributionProps): react.JSX.Element;
160
+
161
+ type AnalyticsRangeWeeks = 4 | 8 | 16;
162
+ interface RangeSelectorProps {
163
+ weeks?: number;
164
+ onWeeksChange?: (weeks: number) => void;
165
+ className?: string;
166
+ }
167
+ declare function RangeSelector({ weeks: weeksProp, onWeeksChange, className }: RangeSelectorProps): react.JSX.Element;
168
+
169
+ interface AnalyticsQueryDef {
170
+ key: string;
171
+ query: string;
172
+ }
173
+ interface AnalyticsQueriesState {
174
+ results: Record<string, QueryResult>;
175
+ loading: boolean;
176
+ }
177
+ declare function useAnalyticsQueries(queries: AnalyticsQueryDef[], weeks: number, executor?: QueryExecutor, refreshKey?: number, preferredUnit?: string, onEnsureRollupFacts?: () => Promise<void>): AnalyticsQueriesState;
178
+
179
+ type ChartShape = {
180
+ kind: 'error';
181
+ message: string;
182
+ } | {
183
+ kind: 'empty';
184
+ } | {
185
+ kind: 'scalar';
186
+ value: number;
187
+ } | {
188
+ kind: 'bars';
189
+ } | {
190
+ kind: 'timeseries';
191
+ };
192
+ declare function useChartShape(result: QueryResult | undefined): ChartShape;
193
+
194
+ /** Series palette for analytics charts — maps onto the metric token set. */
195
+ declare const SERIES_COLORS: string[];
196
+
197
+ interface MergedPoint {
198
+ ts: number;
199
+ [label: string]: number | string;
200
+ }
201
+ declare function mergeSeries(series: Series[]): MergedPoint[];
202
+ declare function compactNumber(value: number): string;
203
+ declare function formatTimestamp(ts: number): string;
204
+ declare function tooltipTimestamp(ts: number): string;
205
+
206
+ declare const ANALYTICS_UNIT_STORAGE_KEY = "wod.analytics.unit";
207
+ declare const DEFAULT_ANALYTICS_UNIT = "kg";
208
+ type AnalyticsUnit = 'kg' | 'lb';
209
+ declare function readStoredUnit(storage?: StorageLike): AnalyticsUnit;
210
+ declare function useAnalyticsUnitPreference(customStorage?: StorageLike): {
211
+ unit: AnalyticsUnit;
212
+ setUnit: (next: AnalyticsUnit) => void;
213
+ toggleUnit: () => void;
214
+ };
215
+ declare function getEffectiveAnalyticsUnit(query: string, preferredUnit: AnalyticsUnit): {
216
+ unit: AnalyticsUnit;
217
+ forced: boolean;
218
+ };
219
+ declare function getDashboardEffectiveUnit(queries: AnalyticsQueryDef[], preferredUnit: AnalyticsUnit): {
220
+ unit: AnalyticsUnit;
221
+ forced: boolean;
222
+ };
223
+ interface AnalyticsUnitPreferenceProps {
224
+ className?: string;
225
+ unit?: AnalyticsUnit;
226
+ forced?: boolean;
227
+ storage?: StorageLike;
228
+ }
229
+ declare function AnalyticsUnitPreference({ className, unit: unitProp, forced, storage }: AnalyticsUnitPreferenceProps): react.JSX.Element;
230
+
231
+ interface RowsTableProps {
232
+ result: RowsQueryResult;
233
+ /** Optional per-run chrome rendered inside the section header (#948 RPE). */
234
+ renderRunHeaderExtra?: (run: RowsRun) => ReactNode;
235
+ /** Optional segment grid renderer (e.g. app's ReviewGrid). Default: clean plain table rows. */
236
+ renderSegments?: (segments: Segment[], run: RowsRun) => ReactNode;
237
+ }
238
+ declare function RowsTable({ result, renderRunHeaderExtra, renderSegments, }: RowsTableProps): react.JSX.Element;
239
+
240
+ interface QueryBlockViewProps {
241
+ /** Raw text between the ```query fences — the WQL query string or block source. */
242
+ query: string;
243
+ /** Injected QueryExecutor for executing WQL queries (zero singleton coupling). */
244
+ executor?: QueryExecutor;
245
+ /** Optional callback or subscription hook for when a result is saved, replacing hardcoded resultRecorder coupling. */
246
+ onResultSaved?: (callback: () => void) => (() => void) | void;
247
+ /** Present when the query block is editable — opens the WQL composer. */
248
+ onSaveQuery?: (nextQuery: string) => void;
249
+ /** When multiple queries appear in one block, the index of this query. */
250
+ queryIndex?: number;
251
+ /** When true, editing the query is disallowed. */
252
+ readOnly?: boolean;
253
+ /** Fence-suffix widget type override (e.g. `query:chart`, `query:value`). */
254
+ widgetType?: string;
255
+ /** Frontmatter/syntax parse error for the enclosing widget block. */
256
+ widgetError?: string;
257
+ /** Token values from the note's frontmatter, for `$token` substitution. */
258
+ tokenValues?: Record<string, string>;
259
+ /** Optional RPE capture handler. */
260
+ onCaptureRpe?: (resultId: string, rpe: number) => Promise<void>;
261
+ /** Optional custom segment grid renderer. */
262
+ renderSegments?: (segments: Segment[], run: RowsRun) => React.ReactNode;
263
+ }
264
+ declare function QueryBlockView({ query, executor, onResultSaved, onSaveQuery, queryIndex: _queryIndex, readOnly, widgetType, widgetError, tokenValues, onCaptureRpe, renderSegments, }: QueryBlockViewProps): react.JSX.Element;
265
+
266
+ declare function readSessionRpe(logs: StoredOutputStatement[] | undefined): number | undefined;
267
+ interface RowsResultsChromeProps {
268
+ /** The session result id from the written `rows:{result:…}` query. */
269
+ resultId: string;
270
+ /** The session-scoped rows result (one run). */
271
+ sessionResult: RowsQueryResult;
272
+ /** Injected QueryExecutor for widened query execution. */
273
+ executor?: QueryExecutor;
274
+ /** Optional RPE capture handler. */
275
+ onCaptureRpe?: (resultId: string, rpe: number) => Promise<void>;
276
+ /** Re-run the parent query after a capture so the grid reflects the RPE. */
277
+ onCaptured?: () => void;
278
+ /** Optional segment grid renderer. */
279
+ renderSegments?: (segments: Segment[], run: RowsRun) => React.ReactNode;
280
+ }
281
+ declare function RowsResultsChrome({ resultId, sessionResult, executor, onCaptureRpe, onCaptured, renderSegments, }: RowsResultsChromeProps): react.JSX.Element;
282
+
283
+ interface WqlQueryInspectorModalProps {
284
+ isOpen: boolean;
285
+ onClose: () => void;
286
+ initialQuery: string;
287
+ onApply: (newQuery: string) => void;
288
+ title?: string;
289
+ subtitle?: string;
290
+ executor?: QueryExecutor;
291
+ }
292
+ declare function WqlQueryInspectorModal({ isOpen, onClose, initialQuery, onApply, title, subtitle, executor, }: WqlQueryInspectorModalProps): react.JSX.Element | null;
293
+
294
+ type AnyParsedQuery = ParsedQuery | ParsedFindQuery | ParsedRowsQuery;
295
+ type WqlExecutor = (ast: AnyParsedQuery) => Promise<FindQueryResult | QueryResult>;
296
+ type WqlStageCounts = {
297
+ kind: 'find';
298
+ selected: number;
299
+ matched: number;
300
+ } | {
301
+ kind: 'aggregate';
302
+ selected: number;
303
+ buckets: number;
304
+ aggregated: number;
305
+ groups: number;
306
+ };
307
+ declare const DEFAULT_DIAGNOSTICS_DEBOUNCE_MS = 150;
308
+ declare function useWqlStageCounts(ast: AnyParsedQuery, valid: boolean, execute: WqlExecutor | undefined, debounceMs?: number): WqlStageCounts | undefined;
309
+
310
+ /**
311
+ * Shared query-clause model + WQL compiler for the WqlComposer.
312
+ *
313
+ * Source-pivot model (issue #838, decision #836): a single `source` head
314
+ * slot — journal | collections | feeds | notes | blocks | efforts | metrics —
315
+ * pivots the query kind. Content sources compile the `find:` skeleton; `metrics`
316
+ * compiles the aggregate skeleton (`agg:metric{filters} by {dims}.rollup(p)
317
+ * [in unit] [where find:…]`). The find target and scope are derived from the
318
+ * source value (the legacy `target`/`scope` clause types are retired).
319
+ *
320
+ * Static option lists live here; dynamic typeahead data sources live in
321
+ * ./suggestionSources (issue #831). All vocab is sourced from the canonical
322
+ * modules (@bitcobblers/wod-wiki-engine) — never hardcoded in the composer (decision #824).
323
+ *
324
+ * Supports freeform token slots, placeholder guidance, and keyboard navigation.
325
+ */
326
+ type ClauseType = 'source' | 'text' | 'catalog' | 'tag' | 'effort' | 'discipline' | 'intensity' | 'origin' | 'type' | 'has' | 'time' | 'where' | 'agg' | 'metric' | 'groupby' | 'rollup' | 'unit' | 'result' | 'block' | 'note' | 'output';
327
+ interface QueryClause {
328
+ id: string;
329
+ /** Built-in ClauseType or a custom slot type id from the ComposerRegistry. */
330
+ type: string;
331
+ label: string;
332
+ value: string;
333
+ inputType?: 'radio' | 'freetext' | 'select';
334
+ placeholder?: string;
335
+ icon?: string;
336
+ prefix?: string;
337
+ }
338
+ /** Content sources compile `find:note in <scope>`; notes/blocks use `all`. */
339
+ declare const CONTENT_SOURCES: ("journal" | "collections" | "feeds" | "notes" | "blocks" | "efforts")[];
340
+ /** The plane a source value belongs to — everything but `metrics` is content. */
341
+ declare function sourcePlane(source: string): 'content' | 'metrics';
342
+ declare const SOURCE_OPTIONS: {
343
+ value: string;
344
+ label: string;
345
+ description: string;
346
+ }[];
347
+ declare const TIME_OPTIONS: {
348
+ value: string;
349
+ label: string;
350
+ }[];
351
+ /** Aggregate head vocab — canonical homes (decision #824). */
352
+ declare const AGG_OPTIONS: {
353
+ value: "max" | "min" | "sum" | "avg" | "count" | "last" | "delta";
354
+ label: "max" | "min" | "sum" | "avg" | "count" | "last" | "delta";
355
+ }[];
356
+ declare const ROLLUP_OPTIONS: {
357
+ value: "1d" | "1w";
358
+ label: string;
359
+ }[];
360
+ declare const GROUPBY_OPTIONS: {
361
+ value: "result" | "day" | "origin" | "page" | "round" | "metric" | "note" | "week" | "effort" | "discipline" | "intensity" | "block" | "session" | "grain" | "tags";
362
+ label: "result" | "day" | "origin" | "page" | "round" | "metric" | "note" | "week" | "effort" | "discipline" | "intensity" | "block" | "session" | "grain" | "tags";
363
+ }[];
364
+ declare const METRIC_OPTIONS: {
365
+ value: "totalVolume" | "totalDistance" | "tis" | "sessionLoad" | "reps" | "distance" | "resistance" | "elapsed" | "power" | "pace" | "calc.metMinutes" | "calc.acwr" | "calc.monotony" | "calc.strain" | "calc.e1rm" | "calc.ctl" | "calc.atl" | "calc.tsb" | "calc.soreness" | "calc.sleep" | "calc.hrv" | "calc.readiness" | "calc.mvcBw" | "calc.ef" | "calc.adherence" | "calc.pct1rm" | "calc.sends";
366
+ label: "totalVolume" | "totalDistance" | "tis" | "sessionLoad" | "reps" | "distance" | "resistance" | "elapsed" | "power" | "pace" | "calc.metMinutes" | "calc.acwr" | "calc.monotony" | "calc.strain" | "calc.e1rm" | "calc.ctl" | "calc.atl" | "calc.tsb" | "calc.soreness" | "calc.sleep" | "calc.hrv" | "calc.readiness" | "calc.mvcBw" | "calc.ef" | "calc.adherence" | "calc.pct1rm" | "calc.sends";
367
+ }[];
368
+ declare const UNIT_OPTIONS: {
369
+ value: "kg" | "lb";
370
+ label: "kg" | "lb";
371
+ }[];
372
+ /**
373
+ * Where-join editor vocab — the same source of truth the analytics composer
374
+ * completes against (src/parser/wql-language.ts, aggregators from the AST
375
+ * contract in services/analytics/query/wql.ts). Issue #831.
376
+ */
377
+ declare const WHERE_AGGREGATORS: readonly string[];
378
+ declare const WHERE_METRICS: readonly string[];
379
+ declare const WHERE_OPERATORS: readonly string[];
380
+ interface ClauseMeta {
381
+ label: string;
382
+ inputType: 'radio' | 'freetext' | 'select';
383
+ placeholder: string;
384
+ placeholderText: string;
385
+ icon: string;
386
+ description: string;
387
+ prefix?: string;
388
+ required?: boolean;
389
+ }
390
+ declare const CLAUSE_META: Record<ClauseType, ClauseMeta>;
391
+ /**
392
+ * Metadata lookup for pills, popovers, and menus: built-in clauses come from
393
+ * CLAUSE_META; custom slot types resolve through the ComposerRegistry;
394
+ * anything else gets a generic fallback so a stale clause still renders.
395
+ */
396
+ declare function getClauseMeta(type: string): ClauseMeta;
397
+ /**
398
+ * Maps a single clause to its WQL fragment syntax.
399
+ */
400
+ declare function clauseToWql(clause: QueryClause): {
401
+ key?: string;
402
+ filterStr?: string;
403
+ };
404
+ /**
405
+ * Composes a full WQL string from an array of QueryClauses.
406
+ *
407
+ * Content planes: find:<target>{<filters>} in <scope> [last <n><unit>] [where <join>]
408
+ * — target/scope derived from `source` (journal|collections|feeds → note in
409
+ * <source>; notes → note in all; blocks → block in all; anything else →
410
+ * note in <verbatim value>, the salvage path for exotic scopes).
411
+ * Metrics plane: <agg>:<metric>{<filters>} [by {<dims>}] [.rollup(<p>)] [in <unit>] [where <find join>]
412
+ */
413
+ declare function clausesToWql(clauses: QueryClause[]): string;
414
+ /**
415
+ * Restore composer clauses from a WQL string — the inverse of
416
+ * `clausesToWql`, used to hydrate composer state from the URL.
417
+ *
418
+ * Unlike `parseQuery`, this is a *salvage* parser: it mirrors the compiler's
419
+ * structure on both planes without validating the fragments, so composer-
420
+ * reachable states that are WQL-invalid (e.g. `text:hello world`, a `1m`
421
+ * rollup period, an empty metric) still restore exactly — the diagnostics
422
+ * strip then attributes the parse error to the offending slot.
423
+ *
424
+ * Returns null when the string cannot be a composer product: a negated
425
+ * filter, or an unknown/custom-less filter key.
426
+ */
427
+ declare function wqlToClauses(wql: string): QueryClause[] | null;
428
+ /**
429
+ * Set the aggregate metric, re-basing the clause list on the metrics plane
430
+ * when needed (sidebar / launcher flows, issue #839): content planes pivot
431
+ * via `pivotClauses` (shared filters survive), a missing source or metric
432
+ * clause is seeded, and the metric value lands on the first metric clause.
433
+ */
434
+ declare function setMetricClause(clauses: QueryClause[], metric: string): QueryClause[];
435
+ /**
436
+ * Re-base the clause list on a new source value (decision #836): shared
437
+ * filter clauses survive the pivot; kind-specific clauses are dropped
438
+ * (content plane: time/where and any metrics head; metrics plane: time/
439
+ * where). Pivoting to metrics seeds the head slots (agg=sum, metric empty
440
+ * for placeholder guidance).
441
+ */
442
+ declare function pivotClauses(clauses: QueryClause[], source: string): QueryClause[];
443
+ declare function defaultClauses(): QueryClause[];
444
+ /** Default clauses for aggregate/metrics queries (source=metrics, agg=sum, empty metric). */
445
+ declare function defaultMetricsClauses(): QueryClause[];
446
+ /**
447
+ * Value of the first clause of `type`, trimmed, or `fallback` when absent or
448
+ * empty. Mirrors the lookup/defaulting `clausesToWql` performs, so consumers
449
+ * (e.g. the Library shelf) share one defaulting rule with the compiler.
450
+ */
451
+ declare function clauseValue(clauses: QueryClause[], type: string, fallback?: string): string;
452
+
453
+ interface WqlValidationState {
454
+ /** True when the composed WQL parses without error. */
455
+ valid: boolean;
456
+ /** Parser error message when invalid. */
457
+ error?: string;
458
+ }
459
+ interface WqlComposerProps {
460
+ /** Seed clauses for uncontrolled usage. Defaults to source/time. */
461
+ initialClauses?: QueryClause[];
462
+ /** Controlled clauses. When provided, the component does not own clause state. */
463
+ clauses?: QueryClause[];
464
+ /** Fired whenever the clause list changes (add / edit / remove). */
465
+ onClausesChange?: (clauses: QueryClause[]) => void;
466
+ /** Fired (including on mount) with the composed WQL string. */
467
+ onWqlChange?: (wql: string) => void;
468
+ /** Fired (including on mount) with parse validation state. */
469
+ onValidationChange?: (state: WqlValidationState) => void;
470
+ /** Fired (including on mount) with the parsed AST. */
471
+ onAstChange?: (ast: AnyParsedQuery) => void;
472
+ /**
473
+ * Fired on Enter when no free text is pending — the run-on-submit signal
474
+ * for hosts like the analytics explorer. Receives the composed WQL.
475
+ */
476
+ onSubmit?: (wql: string) => void;
477
+ /** Render the diagnostics strip (badge, AST summary, stage counts). Default true. */
478
+ showDiagnostics?: boolean;
479
+ /**
480
+ * Executor for live stage counts in the diagnostics strip.
481
+ */
482
+ execute?: WqlExecutor;
483
+ /** Debounce for live execution feedback. Default 150ms. */
484
+ debounceMs?: number;
485
+ /** Extension point: extra content rendered inside the bar, after the free-text input. */
486
+ customSlots?: ReactNode;
487
+ /** Host actions appended on the diagnostics line. */
488
+ diagnosticsActions?: ReactNode;
489
+ /**
490
+ * Clause types kept in the model but NOT rendered as pills.
491
+ */
492
+ hiddenClauseTypes?: ClauseType[];
493
+ /** Focus the free-text input on mount. */
494
+ autoFocus?: boolean;
495
+ /** Free-text input placeholder. */
496
+ placeholder?: string;
497
+ className?: string;
498
+ }
499
+ declare function WqlComposer({ initialClauses, clauses: controlledClauses, onClausesChange, onWqlChange, onValidationChange, onAstChange, onSubmit, showDiagnostics, execute, debounceMs, customSlots, diagnosticsActions, hiddenClauseTypes, autoFocus, placeholder, className, }: WqlComposerProps): react.JSX.Element;
500
+
501
+ interface CustomSlotEditorProps<TValue> {
502
+ value: TValue | undefined;
503
+ onChange: (value: TValue) => void;
504
+ onClose: () => void;
505
+ }
506
+ interface CustomSlotDefinition<TValue = unknown> {
507
+ type: string;
508
+ label: string;
509
+ icon: string;
510
+ placeholder: string;
511
+ placeholderText: string;
512
+ description: string;
513
+ Editor: ComponentType<CustomSlotEditorProps<TValue>>;
514
+ wqlGenerator: (value: TValue) => string;
515
+ formatValue?: (value: TValue) => string;
516
+ parseValue?: (raw: string) => TValue | undefined;
517
+ validate?: (value: TValue) => string | null;
518
+ wqlValidator?: (wql: string) => string | null;
519
+ plane?: 'content' | 'metrics' | 'both';
520
+ }
521
+ declare class ComposerRegistry {
522
+ private readonly slots;
523
+ private readonly listeners;
524
+ private cachedList;
525
+ registerSlot<TValue>(def: CustomSlotDefinition<TValue>): () => void;
526
+ unregisterSlot(type: string): void;
527
+ getSlot(type: string): CustomSlotDefinition<any> | undefined;
528
+ listSlots(): CustomSlotDefinition<any>[];
529
+ getAllSlots(): CustomSlotDefinition<any>[];
530
+ subscribe(listener: () => void): () => void;
531
+ private notify;
532
+ }
533
+ declare const composerRegistry: ComposerRegistry;
534
+ declare function useComposerSlots(registry?: ComposerRegistry): CustomSlotDefinition<any>[];
535
+
536
+ interface TokenSlotPillProps {
537
+ clause: QueryClause;
538
+ isActive?: boolean;
539
+ invalid?: boolean;
540
+ invalidReason?: string;
541
+ onClick?: () => void;
542
+ onRemove?: () => void;
543
+ onChange?: (patch: Partial<QueryClause>) => void;
544
+ compact?: boolean;
545
+ placeholderOverride?: string;
546
+ }
547
+ declare function TokenSlotPill({ clause, isActive, invalid, invalidReason, onClick, onRemove, onChange, compact, placeholderOverride, }: TokenSlotPillProps): react.JSX.Element;
548
+ declare const MULTI_VALUE_TYPES: Record<string, true>;
549
+ declare function ClausePopover({ clause, onClose, onChange, }: {
550
+ clause: QueryClause;
551
+ onClose: () => void;
552
+ onChange: (patch: Partial<QueryClause>) => void;
553
+ }): react.JSX.Element;
554
+ declare function CustomSlotPopover<TValue>({ clause, definition, onClose, onChange, }: {
555
+ clause: QueryClause;
556
+ definition: CustomSlotDefinition<TValue>;
557
+ onClose: () => void;
558
+ onChange: (value: TValue) => void;
559
+ }): react.JSX.Element;
560
+ declare function AddFilterDropdown({ clauses, onAdd, }: {
561
+ clauses: QueryClause[];
562
+ onAdd: (clause: QueryClause) => void;
563
+ }): react.JSX.Element;
564
+ declare function AddCalcDropdown({ clauses, onAdd, }: {
565
+ clauses: QueryClause[];
566
+ onAdd: (clause: QueryClause) => void;
567
+ }): react.JSX.Element | null;
568
+
569
+ interface DateRange {
570
+ start: string;
571
+ end: string;
572
+ }
573
+ declare const dateRangeSlot: CustomSlotDefinition<DateRange>;
574
+
575
+ interface SuggestionItem {
576
+ value: string;
577
+ label?: string;
578
+ description?: string;
579
+ icon?: string;
580
+ }
581
+ type SuggestionCachePolicy = 'static' | {
582
+ ttlMs: number;
583
+ };
584
+ interface SuggestionBinding {
585
+ load: () => Promise<SuggestionItem[]>;
586
+ cache: SuggestionCachePolicy;
587
+ open: boolean;
588
+ emptyText: string;
589
+ }
590
+ declare function tagsFromStaticBlocks(blocks: BlockIndexRow[]): string[];
591
+ declare function catalogIdsFromBlocks(blocks: BlockIndexRow[]): string[];
592
+ declare function blockTypesFromBlocks(blocks: BlockIndexRow[]): string[];
593
+ declare function mergeTagSuggestions(userLabels: string[], corpusLabels: string[]): string[];
594
+ declare const SUGGESTION_BINDINGS: Record<string, SuggestionBinding>;
595
+ declare function setSuggestionBinding(type: string, binding: SuggestionBinding | undefined): void;
596
+ declare function getSuggestionBinding(type: string): SuggestionBinding | undefined;
597
+ declare function loadSuggestions(type: string): Promise<SuggestionItem[]>;
598
+ declare function invalidateSuggestions(type?: string): void;
599
+
600
+ interface ClauseSuggestions {
601
+ items: SuggestionItem[];
602
+ /** True while the first load for this slot is in flight. */
603
+ loading: boolean;
604
+ /** The slot's binding (cache policy, open flag, empty-state copy). */
605
+ binding?: SuggestionBinding;
606
+ }
607
+ declare function useSuggestions(type: string): ClauseSuggestions;
608
+
609
+ interface WqlDiagnostics {
610
+ valid: boolean;
611
+ /** The composed WQL string for the current clause set. */
612
+ wql: string;
613
+ ast: AnyParsedQuery;
614
+ error?: string;
615
+ offendingClauseId?: string;
616
+ }
617
+ interface WqlFindSummary {
618
+ target: string;
619
+ scope: string;
620
+ timeWindow?: string;
621
+ hasJoin: boolean;
622
+ filterCount: number;
623
+ }
624
+ declare function summarizeFind(ast: ParsedFindQuery): WqlFindSummary;
625
+ interface WqlAggregateSummary {
626
+ agg: string;
627
+ metric: string;
628
+ groupBy?: string;
629
+ rollup?: string;
630
+ unit?: string;
631
+ timeWindow?: string;
632
+ hasJoin: boolean;
633
+ filterCount: number;
634
+ }
635
+ declare function summarizeAggregate(ast: ParsedQuery): WqlAggregateSummary;
636
+ declare function diagnoseClauses(clauses: QueryClause[]): WqlDiagnostics;
637
+
638
+ interface WqlDiagnosticsStripProps {
639
+ diagnostics: WqlDiagnostics;
640
+ offendingLabel?: string;
641
+ stages?: WqlStageCounts;
642
+ actions?: ReactNode;
643
+ className?: string;
644
+ }
645
+ declare function WqlDiagnosticsStrip({ diagnostics, offendingLabel, stages, actions, className, }: WqlDiagnosticsStripProps): react.JSX.Element;
646
+
647
+ declare function editorTheme(isDark: boolean): Extension;
648
+
649
+ /**
650
+ * Extension that adds ID attributes to line elements in the editor.
651
+ */
652
+ declare const lineIdsExtension: ViewPlugin<{
653
+ decorations: DecorationSet;
654
+ update(update: ViewUpdate): void;
655
+ getDecorations(view: EditorView): _codemirror_state.RangeSet<Decoration>;
656
+ }, undefined>;
657
+
658
+ type NavigationHook = (url: string, view: EditorView) => void;
659
+ declare const navigationFacet: Facet<NavigationHook, NavigationHook | null>;
660
+ declare function urlAtPos(view: EditorView, pos: number): string | null;
661
+ declare const linkOpen: Extension;
662
+
663
+ declare function findWorkoutErrors(content: string, offsetInDoc: number): Diagnostic[];
664
+ declare function workoutLintSource(view: EditorView): Diagnostic[];
665
+ declare const wodLinter: _codemirror_state.Extension;
666
+
667
+ declare function fenceCompletion(context: CompletionContext): CompletionResult | null;
668
+ declare function wrapInTimeFence(view: EditorView): boolean;
669
+ declare function handleFenceAutoWrap(event: InputEvent, view: EditorView): boolean;
670
+ declare const wodAutoWrap: Extension;
671
+ declare const wodEditorKeymap: Extension;
672
+ declare const wodAutocompletion: Extension;
673
+
674
+ type EditorDialect = "time" | "log";
675
+ type EditorSectionType = "markdown" | "time" | "log" | "frontmatter" | "code" | "widget" | "embed" | "query";
676
+ type EditorSectionSubtype = "heading" | "paragraph" | "list" | "blockquote" | "table" | "unknown";
677
+ type EmbedType = "image" | "link" | "youtube";
678
+ interface EditorSection {
679
+ id: string;
680
+ type: EditorSectionType;
681
+ subtype?: EditorSectionSubtype;
682
+ from: number;
683
+ to: number;
684
+ startLine: number;
685
+ endLine: number;
686
+ contentFrom?: number;
687
+ contentTo?: number;
688
+ sport?: string;
689
+ widgetName?: string;
690
+ contentId?: string;
691
+ queryType?: string;
692
+ queryError?: string;
693
+ embed?: {
694
+ type: EmbedType;
695
+ url: string;
696
+ label: string;
697
+ videoId?: string;
698
+ height?: number;
699
+ };
700
+ }
701
+ interface SectionState {
702
+ sections: EditorSection[];
703
+ }
704
+ declare const forceSectionParse: _codemirror_state.StateEffectType<null>;
705
+ declare function blockContentId(content: string): string;
706
+ declare const sectionField: StateField<SectionState>;
707
+ declare function sectionAtPos(state: EditorState, pos: number): EditorSection | null;
708
+ declare function activeCursorSection(state: EditorState): EditorSection | null;
709
+
710
+ interface SectionRect {
711
+ sectionId: string;
712
+ top: number;
713
+ height: number;
714
+ type: EditorSection["type"];
715
+ subtype?: EditorSection["subtype"];
716
+ widgetName?: string;
717
+ }
718
+ type GeometryListener = (rects: SectionRect[], docVersion: number) => void;
719
+ declare class SectionGeometryPlugin {
720
+ private view;
721
+ rects: SectionRect[];
722
+ docVersion: number;
723
+ private listeners;
724
+ constructor(view: EditorView);
725
+ update(update: ViewUpdate): void;
726
+ addListener(fn: GeometryListener): () => void;
727
+ private measure;
728
+ private notify;
729
+ destroy(): void;
730
+ }
731
+ declare const sectionGeometry: ViewPlugin<SectionGeometryPlugin, undefined>;
732
+
733
+ declare const previewDecorations: Extension;
734
+
735
+ declare const embedPreviewDecorations: Extension;
736
+
737
+ declare class DefaultFrontmatterWidget extends WidgetType {
738
+ readonly props: Record<string, string>;
739
+ constructor(props: Record<string, string>);
740
+ eq(other: DefaultFrontmatterWidget): boolean;
741
+ toDOM(): HTMLElement;
742
+ }
743
+ declare const frontmatterPreviewField: StateField<DecorationSet>;
744
+ declare const frontmatterPreview: Extension;
745
+
746
+ declare function markdownSyntaxHiding(): Extension;
747
+
748
+ declare const markdownTablePreview: Extension;
749
+
750
+ declare const setGutterHighlights: _codemirror_state.StateEffectType<Set<number>>;
751
+ declare const gutterHighlightsField: StateField<Set<number>>;
752
+ declare const gutterUnified: (_codemirror_state.Extension | StateField<Set<number>>)[];
753
+ declare function dispatchGutterHighlights(view: EditorView, lineNumbers: number[]): void;
754
+
755
+ type ButtonAction = (action: string, params: Record<string, string>) => void;
756
+ declare function inlineButtonDecoration(onAction: ButtonAction): Extension;
757
+
758
+ interface RuntimePanelEntry {
759
+ sectionId: string;
760
+ block: ScriptBlock;
761
+ afterPos: number;
762
+ expanded?: boolean;
763
+ }
764
+ declare const addRuntimePanel: _codemirror_state.StateEffectType<RuntimePanelEntry>;
765
+ declare const removeRuntimePanel: _codemirror_state.StateEffectType<{
766
+ sectionId: string;
767
+ }>;
768
+ declare const expandRuntimePanel: _codemirror_state.StateEffectType<{
769
+ sectionId: string;
770
+ expanded: boolean;
771
+ }>;
772
+ declare const runtimePanelField: StateField<Map<string, RuntimePanelEntry>>;
773
+ declare function dispatchAddRuntimePanel(view: EditorView, sectionId: string, block: ScriptBlock, afterPos: number, expanded?: boolean): void;
774
+ declare function dispatchRemoveRuntimePanel(view: EditorView, sectionId: string): void;
775
+ declare function dispatchExpandRuntimePanel(view: EditorView, sectionId: string, expanded: boolean): void;
776
+
777
+ declare function sessionQueryWql(resultId: string): string;
778
+ declare function sessionQueryInsert(state: EditorState, blockId: string, resultId: string, runBlock?: {
779
+ id?: string;
780
+ contentId?: string;
781
+ }): {
782
+ from: number;
783
+ insert: string;
784
+ } | null;
785
+
786
+ declare const smartIncrement: Extension;
787
+
788
+ interface OverlayAction {
789
+ label: string;
790
+ icon: string;
791
+ action: (view: EditorView, section: EditorSection) => void;
792
+ }
793
+ declare function configureOverlayActions(actions: OverlayAction[]): void;
794
+ declare function getOverlayActions(): OverlayAction[];
795
+ declare const wodOverlayPanel: Extension;
796
+
797
+ interface CursorFocusState {
798
+ sectionId: string;
799
+ section: EditorSection;
800
+ lineFrom: number;
801
+ docLine: number;
802
+ statementIndex: number;
803
+ statement: ICodeStatement;
804
+ focusedMetric: IMetric | null;
805
+ focusedMetricIndex: number;
806
+ allStatements: ICodeStatement[];
807
+ }
808
+ declare function getCursorFocusState(state: EditorState): CursorFocusState | null;
809
+ declare const cursorFocusExtension: Extension;
810
+
811
+ interface QueryBlockPreviewOptions {
812
+ executor?: QueryExecutor;
813
+ onResultSaved?: (callback: () => void) => (() => void) | void;
814
+ readOnly?: boolean;
815
+ onSaveQuery?: (sectionId: string, nextQuery: string) => void;
816
+ }
817
+ declare function saveBlockQuerySource(view: EditorView, sectionId: string, newQuery: string, queryIndex?: number): {
818
+ ok: boolean;
819
+ patchedContent?: string;
820
+ message?: string;
821
+ };
822
+ declare function findQueryBlockSection(view: EditorView, sectionId: string): EditorSection | null;
823
+ declare function queryBlockPreview(options?: QueryBlockPreviewOptions): Extension;
824
+
825
+ /** Parsed JSON object passed into a widget component. */
826
+ type WidgetConfig = Record<string, unknown>;
827
+ /** Props passed to every registered widget component. */
828
+ interface WidgetProps {
829
+ /** Parsed JSON object from the fenced widget body. */
830
+ config: WidgetConfig;
831
+ /** Exact source text between the opening and closing fences. */
832
+ rawContent: string;
833
+ /** Stable section identifier generated by section-state.ts. */
834
+ sectionId: string;
835
+ }
836
+ /** Registry of widget names to React components. */
837
+ type WidgetRegistry = Map<string, react.ComponentType<WidgetProps>>;
838
+ /** Effect to toggle edit mode for a specific widget block */
839
+ declare const toggleWidgetEdit: _codemirror_state.StateEffectType<{
840
+ sectionId: string;
841
+ editing: boolean;
842
+ }>;
843
+ /** StateField to track which widgets are currently being edited as raw text */
844
+ declare const editingWidgetsField: StateField<Set<string>>;
845
+ /**
846
+ * Create a CM6 Extension that replaces widget fenced blocks with React components.
847
+ * Block decorations MUST come from a StateField (not a ViewPlugin) per CM6 constraints.
848
+ */
849
+ declare function widgetBlockPreview(registry: WidgetRegistry): Extension;
850
+
851
+ interface EditorPresetOptions {
852
+ dialect?: 'wod' | 'whiteboard' | 'wql' | 'markdown' | string;
853
+ isDark?: boolean;
854
+ readOnly?: boolean;
855
+ lineNumbers?: boolean;
856
+ lineWrapping?: boolean;
857
+ executor?: QueryExecutor;
858
+ onResultSaved?: (callback: () => void) => (() => void) | void;
859
+ extensions?: Extension[];
860
+ }
861
+ declare function editorPreset(optionsOrDialect?: string | EditorPresetOptions): Extension[];
862
+
863
+ declare function cn(...inputs: ClassValue[]): string;
864
+ declare function hashCode(str: string): number;
865
+
866
+ /**
867
+ * blockQueryPatcher — utilities to extract and patch WQL queries inside ```query
868
+ * fenced block sources (#842).
869
+ *
870
+ * Supports both plain line queries and structured YAML key-value blocks.
871
+ * Preserves sibling keys, comments, formatting, and quotes when patching.
872
+ */
873
+ interface ExtractedQuery {
874
+ queryIndex: number;
875
+ query: string;
876
+ isYamlKey: boolean;
877
+ lineIndex: number;
878
+ }
879
+ declare function extractBlockQueries(blockContent: string): ExtractedQuery[];
880
+ declare function patchBlockQuery(blockContent: string, newQuery: string, queryIndex?: number): string;
881
+
882
+ export { AGG_OPTIONS, ANALYTICS_UNIT_STORAGE_KEY, AddCalcDropdown, AddFilterDropdown, type AnalyticsQueriesState, type AnalyticsQueryDef, type AnalyticsRangeWeeks, type AnalyticsUnit, AnalyticsUnitPreference, type AnalyticsUnitPreferenceProps, type ButtonAction, CLAUSE_META, CODEMIRROR_SINGLETON_DEPS, CONTENT_SOURCES, type ChartShape, type ClauseMeta, ClausePopover, type ClauseSuggestions, type ClauseType, ComposerRegistry, type CursorFocusState, type CustomSlotDefinition, type CustomSlotEditorProps, CustomSlotPopover, DEFAULT_ANALYTICS_UNIT, DEFAULT_DIAGNOSTICS_DEBOUNCE_MS, DashboardTokenControls, type DashboardTokenControlsProps, DashboardView, type DashboardViewProps, type DateRange, DefaultFrontmatterWidget, type EditorDialect, type EditorPresetOptions, type EditorSection, type EditorSectionSubtype, type EditorSectionType, type EmbedType, type ExtractedQuery, GROUPBY_OPTIONS, type GeometryListener, GoalRings, type GoalRingsProps, METRIC_OPTIONS, MULTI_VALUE_TYPES, type MergedPoint, type NavigationHook, type OverlayAction, ProposedMetricBadge, type QueryBlockPreviewOptions, QueryBlockView, type QueryBlockViewProps, type QueryClause, type QueryExecutor, QueryValue, type QueryValueProps, ROLLUP_OPTIONS, RangeSelector, type RangeSelectorProps, RowsResultsChrome, type RowsResultsChromeProps, RowsTable, type RowsTableProps, type RuntimePanelEntry, SERIES_COLORS, SOURCE_OPTIONS, SUGGESTION_BINDINGS, type SectionRect, type SectionState, StackedBar, type StackedBarProps, type StorageLike, type SuggestionBinding, type SuggestionCachePolicy, type SuggestionItem, TIME_OPTIONS, TokenSlotPill, type TokenSlotPillProps, TopList, type TopListProps, UNIT_OPTIONS, WHERE_AGGREGATORS, WHERE_METRICS, WHERE_OPERATORS, WidgetChart, type WidgetChartProps, type WidgetConfig, WidgetFrame, type WidgetFrameProps, WidgetProblemBadge, type WidgetProps, type WidgetRegistry, type WqlAggregateSummary, WqlBars, type WqlBarsProps, WqlComposer, type WqlComposerProps, type WqlDiagnostics, WqlDiagnosticsStrip, type WqlDiagnosticsStripProps, WqlEmptyState, type WqlEmptyStateProps, type WqlExecutor, type WqlFindSummary, WqlQueryInspectorModal, type WqlQueryInspectorModalProps, type WqlStageCounts, WqlTable, type WqlTableProps, WqlTimeseries, type WqlTimeseriesProps, type WqlValidationState, ZoneDistribution, type ZoneDistributionProps, activeCursorSection, addRuntimePanel, blockContentId, blockTypesFromBlocks, catalogIdsFromBlocks, clauseToWql, clauseValue, clausesToWql, cn, compactNumber, composerRegistry, configureOverlayActions, cursorFocusExtension, dateRangeSlot, defaultClauses, defaultMetricsClauses, diagnoseClauses, dispatchAddRuntimePanel, dispatchExpandRuntimePanel, dispatchGutterHighlights, dispatchRemoveRuntimePanel, editingWidgetsField, editorPreset, editorTheme, embedPreviewDecorations, expandRuntimePanel, extractBlockQueries, fenceCompletion, findQueryBlockSection, findWorkoutErrors, forceSectionParse, formatTimestamp, frontmatterPreview, frontmatterPreviewField, getClauseMeta, getCursorFocusState, getDashboardEffectiveUnit, getEffectiveAnalyticsUnit, getOverlayActions, getSuggestionBinding, gutterHighlightsField, gutterUnified, handleFenceAutoWrap, hashCode, inlineButtonDecoration, invalidateSuggestions, lineIdsExtension, linkOpen, loadSuggestions, markdownSyntaxHiding, markdownTablePreview, mergeSeries, mergeTagSuggestions, navigationFacet, patchBlockQuery, pivotClauses, previewDecorations, queryBlockPreview, readSessionRpe, readStoredUnit, removeRuntimePanel, runtimePanelField, saveBlockQuerySource, sectionAtPos, sectionField, sectionGeometry, sessionQueryInsert, sessionQueryWql, setGutterHighlights, setMetricClause, setSuggestionBinding, smartIncrement, sourcePlane, summarizeAggregate, summarizeFind, tagsFromStaticBlocks, toggleWidgetEdit, tooltipTimestamp, urlAtPos, useAnalyticsQueries, useAnalyticsUnitPreference, useChartShape, useComposerSlots, useSuggestions, useWqlStageCounts, widgetBlockPreview, wodAutoWrap, wodAutocompletion, wodEditorKeymap, wodLinter, wodOverlayPanel, workoutLintSource, wqlToClauses, wrapInTimeFence };