@bitcobblers/wod-wiki-ui 0.50.2189 → 0.50.2191

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.
package/dist/index.mjs CHANGED
@@ -1,9 +1,9 @@
1
- import { cn, WidgetProblemBadge, WidgetChart, toChartResult } from './chunk-UAIJTD74.mjs';
2
- export { AGG_OPTIONS, CANONICAL_BLOCK_TYPES, CLAUSE_META, CONTENT_GROUPING_DIMENSIONS, ComposerRegistry, DEFAULT_DIAGNOSTICS_DEBOUNCE_MS, DEFAULT_OUTPUT_FILTERS, DEFAULT_PRIMARY_FILTER, DefaultFrontmatterWidget, EditorDialog, GROUPBY_OPTIONS, GoalRings, KIND_OPTIONS, METRIC_OPTIONS, MULTI_VALUE_TYPES, MetricBadge, OUTPUT_TYPE_CLASS, OUTPUT_TYPE_DOT, OutputFilterPills, OutputStatementsTable, PLANE_OPTIONS, ProposedMetricBadge, QueryBlockView, QueryValue, ROLLUP_OPTIONS, RowsResultsChrome, RowsTable, SERIES_COLORS, SOURCE_OPTIONS, SUGGESTION_BINDINGS, StackedBar, TARGET_OPTIONS, TIME_OPTIONS, TONE_CLASS, TokenSlotPill, TopList, UNIT_OPTIONS, WHERE_AGGREGATORS, WHERE_METRICS, WidgetChart, WidgetProblemBadge, WqlBars, WqlComposer, WqlDiagnosticsStrip, WqlEmptyState, WqlQueryInspectorModal, WqlTable, WqlTimeseries, ZoneDistribution, activeCursorSection, addRuntimePanel, autocompleteTooltipTheme, blockContentId, blockTypesFromBlocks, canonicalSuggestionItems, cn, compactNumber, composerRegistry, configureOverlayActions, cursorFocusExtension, customCompletionSources, dateRangeSlot, dispatchAddRuntimePanel, dispatchExpandRuntimePanel, dispatchGutterHighlights, dispatchRemoveRuntimePanel, editQueryClause, editingWidgetsField, editorPreset, editorTheme, embedPreviewDecorations, expandRuntimePanel, extractBlockQueries, extractMetricsByCategory, fenceCompletion, findQueryBlockSection, findWorkoutErrors, forceSectionParse, formatClockTime, formatMMSS, formatTimestamp, frontmatterDetailed, frontmatterPreview, frontmatterPreviewField, frontmatterReferenceIndex, frontmatterSource, frontmatterSuggestions, frontmatterVisibility, getClauseMeta, getCursorFocusState, getOverlayActions, getSuggestionBinding, gutterHighlightsField, gutterUnified, handleFenceAutoWrap, hashCode, inlineButtonDecoration, invalidateSuggestions, lineIdsExtension, linkOpen, linkPreview, loadSuggestions, markdownSyntaxHiding, markdownTablePreview, matchFilterTypeahead, mergeSeries, mergeSuggestionItems, mergeTagSuggestions, navigationFacet, nestedCompletion, nestedCompletionOptions, normalizeOutputFilter, notebookSlugsFromBlocks, patchBlockQuery, presentBadges, previewDecorations, queryBlockPreview, readSessionRpe, referenceIndexReload, registeredSectionWidgets, removeRuntimePanel, resolveCodeLanguage, resolveQueryDraft, runtimePanelField, saveBlockQuerySource, sectionAtPos, sectionField, sectionGeometry, sectionIdentity, sectionIdentityHistory, sectionMove, sectionRestore, sectionSaved, sessionQueryInsert, sessionQueryWql, setFrontmatterDetailed, setFrontmatterSource, setGutterHighlights, setSuggestionBinding, smartIncrement, summarizeAggregate, summarizeFind, summarizePipeline, tagsFromStaticBlocks, toggleWidgetEdit, tooltipTimestamp, urlAtPos, useChartShape, useComposerSlots, useSuggestions, useVisualViewportRect, useWqlStageCounts, widgetBlockPreview, wodAutoWrap, wodAutocompletion, wodCompletionSource, wodEditorKeymap, wodLinter, wodOverlayPanel, workoutLintSource, wqlToPills, wrapInTimeFence } from './chunk-UAIJTD74.mjs';
1
+ import { cn, WidgetProblemBadge, WidgetChart, createExecutorDocumentHost, outputToRunResult } from './chunk-GM2PO7LK.mjs';
2
+ export { AGG_OPTIONS, CANONICAL_BLOCK_TYPES, CLAUSE_META, CONTENT_GROUPING_DIMENSIONS, ComposerRegistry, DEFAULT_DIAGNOSTICS_DEBOUNCE_MS, DEFAULT_OUTPUT_FILTERS, DEFAULT_PRIMARY_FILTER, DefaultFrontmatterWidget, EditorDialog, GROUPBY_OPTIONS, GoalRings, KIND_OPTIONS, METRIC_OPTIONS, MULTI_VALUE_TYPES, MetricBadge, OUTPUT_TYPE_CLASS, OUTPUT_TYPE_DOT, OutputFilterPills, OutputStatementsTable, PLANE_OPTIONS, ProposedMetricBadge, QueryBlockView, QueryValue, ROLLUP_OPTIONS, RowsResultsChrome, RowsTable, SERIES_COLORS, SOURCE_OPTIONS, SUGGESTION_BINDINGS, StackedBar, TARGET_OPTIONS, TIME_OPTIONS, TONE_CLASS, TokenSlotPill, TopList, UNIT_OPTIONS, WHERE_AGGREGATORS, WHERE_METRICS, WidgetChart, WidgetProblemBadge, WqlBars, WqlComposer, WqlDiagnosticsStrip, WqlEmptyState, WqlQueryInspectorModal, WqlTable, WqlTimeseries, ZoneDistribution, activeCursorSection, addRuntimePanel, autocompleteTooltipTheme, blockContentId, blockTypesFromBlocks, canonicalSuggestionItems, cn, compactNumber, composerRegistry, configureOverlayActions, createExecutorDocumentHost, cursorFocusExtension, customCompletionSources, dateRangeSlot, dispatchAddRuntimePanel, dispatchExpandRuntimePanel, dispatchGutterHighlights, dispatchRemoveRuntimePanel, editQueryClause, editingWidgetsField, editorPreset, editorTheme, embedPreviewDecorations, expandRuntimePanel, extractBlockQueries, extractMetricsByCategory, fenceCompletion, findQueryBlockSection, findWorkoutErrors, forceSectionParse, formatClockTime, formatMMSS, formatTimestamp, frontmatterDetailed, frontmatterPreview, frontmatterPreviewField, frontmatterReferenceIndex, frontmatterSource, frontmatterSuggestions, frontmatterVisibility, getClauseMeta, getCursorFocusState, getOverlayActions, getSuggestionBinding, gutterHighlightsField, gutterUnified, handleFenceAutoWrap, hashCode, inlineButtonDecoration, invalidateSuggestions, lineIdsExtension, linkOpen, linkPreview, loadSuggestions, markdownSyntaxHiding, markdownTablePreview, matchFilterTypeahead, mergeSeries, mergeSuggestionItems, mergeTagSuggestions, navigationFacet, nestedCompletion, nestedCompletionOptions, normalizeOutputFilter, notebookSlugsFromBlocks, outputToRunResult, patchBlockQuery, presentBadges, previewDecorations, queryBlockPreview, readSessionRpe, referenceIndexReload, registeredSectionWidgets, removeRuntimePanel, resolveCodeLanguage, resolveQueryDraft, runtimePanelField, saveBlockQuerySource, sectionAtPos, sectionField, sectionGeometry, sectionIdentity, sectionIdentityHistory, sectionMove, sectionRestore, sectionSaved, sessionQueryInsert, sessionQueryWql, setFrontmatterDetailed, setFrontmatterSource, setGutterHighlights, setSuggestionBinding, smartIncrement, summarizeAggregate, summarizeFind, summarizePipeline, tagsFromStaticBlocks, toChartResult, toggleWidgetEdit, tooltipTimestamp, urlAtPos, useChartShape, useComposerSlots, useSuggestions, useVisualViewportRect, useWqlStageCounts, widgetBlockPreview, wodAutoWrap, wodAutocompletion, wodCompletionSource, wodEditorKeymap, wodLinter, wodOverlayPanel, workoutLintSource, wqlToPills, wrapInTimeFence } from './chunk-GM2PO7LK.mjs';
3
3
  import { Eye, ExternalLink, Edit3, ChevronUp, ChevronDown, ChevronRight, Copy, Trash2 } from 'lucide-react';
4
4
  import { jsxs, jsx, Fragment as Fragment$1 } from 'react/jsx-runtime';
5
5
  import { useMemo, useState, useEffect, Fragment, useCallback } from 'react';
6
- import { defaultTokenValues, substituteTokens, parseQuery, isDashboardWidgetType, unknownWidgetTypeMessage, unknownTokensMessage, resolveWidgetType, isFindQuery, isPipelineQuery, SharedQueryDocumentRunner } from '@bitcobblers/wod-wiki-wql';
6
+ import { defaultTokenValues, substituteTokens, parseQuery, isDashboardWidgetType, unknownWidgetTypeMessage, unknownTokensMessage, parseDocument, resolveWidgetType, isFindQuery, isPipelineQuery, SharedQueryDocumentRunner } from '@bitcobblers/wod-wiki-wql';
7
7
 
8
8
  // src/contracts/dedupe.ts
9
9
  var CODEMIRROR_SINGLETON_DEPS = [
@@ -178,53 +178,6 @@ function DashboardTokenControls({ tokens, values, onChange }) {
178
178
  }
179
179
  );
180
180
  }
181
- function createExecutorDocumentHost(executor, range = {}) {
182
- return {
183
- runAggregate: (queryText, overrides) => executor.runQuery(queryText, {
184
- context: overrides.context,
185
- rangeStart: range.rangeStart,
186
- rangeEnd: range.rangeEnd,
187
- preferredUnit: range.preferredUnit
188
- }),
189
- runFind: async (queryText, context) => {
190
- const parsed = parseQuery(queryText);
191
- if (!isFindQuery(parsed)) return { kind: "find", error: `not a content query: ${queryText}` };
192
- const res = await executor.runFind(parsed, {
193
- context,
194
- range: range.rangeStart !== void 0 ? { start: range.rangeStart, end: range.rangeEnd ?? Number.MAX_SAFE_INTEGER, endExclusive: false } : void 0
195
- });
196
- return {
197
- kind: "find",
198
- notes: res.notes,
199
- blocks: res.blocks,
200
- efforts: res.efforts,
201
- containers: res.containers,
202
- runs: res.runs,
203
- table: res.table
204
- };
205
- },
206
- runPipeline: async (queryText, context) => {
207
- const res = await executor.runPipeline?.(queryText, {
208
- context,
209
- rangeStart: range.rangeStart,
210
- rangeEnd: range.rangeEnd,
211
- preferredUnit: range.preferredUnit
212
- });
213
- return res ?? { error: "pipeline queries are not supported by this executor" };
214
- },
215
- rollupEnsure: range.rollupEnsure
216
- };
217
- }
218
- function outputToRunResult(parsed, out) {
219
- const base = toChartResult({ parsed, series: out.series, unit: out.unit, error: out.error, table: out.table });
220
- return Object.assign(base, {
221
- ...out.notes ? { notes: out.notes } : {},
222
- ...out.blocks ? { blocks: out.blocks } : {},
223
- ...out.efforts ? { efforts: out.efforts } : {},
224
- ...out.containers ? { containers: out.containers } : {},
225
- ...out.runs ? { runs: out.runs } : {}
226
- });
227
- }
228
181
  function spanClass(widget) {
229
182
  if (widget.spanFull) return "col-span-1 md:col-span-2 lg:col-span-4";
230
183
  if (widget.spanCols != null) {
@@ -265,7 +218,9 @@ function DashboardView({
265
218
  onResizeWidget,
266
219
  rangeStart,
267
220
  rangeEnd,
268
- preferredUnit
221
+ preferredUnit,
222
+ onOpenNote,
223
+ noteHref
269
224
  }) {
270
225
  const values = useMemo(
271
226
  () => ({ ...defaultTokenValues(document.tokens), ...tokenValues }),
@@ -306,9 +261,12 @@ function DashboardView({
306
261
  next[widget.key] = { error: unknownTokensMessage(missing) };
307
262
  continue;
308
263
  }
309
- const parsed = parseQuery(query);
310
- if (parsed.error) {
311
- next[widget.key] = { error: parsed.error };
264
+ const docParse = parseDocument(query);
265
+ const isMultiLineDoc = !docParse.doc.degenerate && docParse.doc.assignments.length > 0;
266
+ const parsed = isMultiLineDoc ? { family: "aggregate", raw: query, agg: "sum", metric: docParse.doc.show[0] ?? "value", filters: [], groupBy: [] } : parseQuery(query);
267
+ const syntaxErr = isMultiLineDoc ? docParse.diagnostics[0] : parsed.error;
268
+ if (syntaxErr) {
269
+ next[widget.key] = { error: syntaxErr };
312
270
  continue;
313
271
  }
314
272
  const resolvedType = resolveWidgetType(widget.type);
@@ -317,7 +275,7 @@ function DashboardView({
317
275
  next[widget.key] = { error: "content queries return things \u2014 use a table or list widget instead" };
318
276
  continue;
319
277
  }
320
- } else if (!isPipelineQuery(parsed) && resolvedType === "list") {
278
+ } else if (!isPipelineQuery(parsed) && !isMultiLineDoc && resolvedType === "list") {
321
279
  next[widget.key] = { error: "aggregate queries return numbers \u2014 use a chart or value widget instead" };
322
280
  continue;
323
281
  }
@@ -455,7 +413,9 @@ function DashboardView({
455
413
  result: run?.result,
456
414
  label: widget.title,
457
415
  unit: preferredUnit,
458
- attributes
416
+ attributes,
417
+ onOpenNote,
418
+ noteHref
459
419
  }
460
420
  )
461
421
  },
@@ -529,17 +489,8 @@ function useAnalyticsQueries(queries, weeks, executor, refreshKey = 0, preferred
529
489
  const res = await runner.run(q.query);
530
490
  const out = res.outputs[0];
531
491
  const error = out?.error ?? res.diagnostics[0];
532
- const result = toChartResult({
533
- parsed: parseQuery(q.query),
534
- series: out?.series,
535
- unit: out?.unit,
536
- error,
537
- table: out?.table
538
- });
539
- if (out?.notes) result.notes = out.notes;
540
- if (out?.blocks) result.blocks = out.blocks;
541
- if (out?.efforts) result.efforts = out.efforts;
542
- if (out?.runs) result.runs = out.runs;
492
+ const parsed = parseQuery(q.query);
493
+ const result = out ? outputToRunResult(parsed, { ...out, ...error ? { error } : {} }) : outputToRunResult(parsed, { name: q.key, kind: "aggregate", error });
543
494
  return [q.key, result];
544
495
  })
545
496
  );
@@ -680,13 +631,13 @@ function AnalyticsUnitPreference({ className, unit: unitProp, forced = false, st
680
631
  }
681
632
 
682
633
  // src/version.ts
683
- var VERSION = "0.50.2189";
684
- var GIT_SHA = "5da2c23";
685
- var BUILD_TIME = "2026-11-01T16:27:05.203Z";
634
+ var VERSION = "0.50.2191";
635
+ var GIT_SHA = "95c89db";
636
+ var BUILD_TIME = "2026-11-03T03:31:31.921Z";
686
637
  var SEMVER = {
687
- version: "0.50.2189",
688
- gitSha: "5da2c23",
689
- buildTime: "2026-11-01T16:27:05.203Z"
638
+ version: "0.50.2191",
639
+ gitSha: "95c89db",
640
+ buildTime: "2026-11-03T03:31:31.921Z"
690
641
  };
691
642
 
692
643
  export { ANALYTICS_UNIT_STORAGE_KEY, AnalyticsUnitPreference, BUILD_TIME, CODEMIRROR_SINGLETON_DEPS, DEFAULT_ANALYTICS_UNIT, DashboardTokenControls, DashboardView, GIT_SHA, RangeSelector, SEMVER, VERSION, WidgetEditButton, WidgetFrame, WidgetToolButton, getDashboardEffectiveUnit, getEffectiveAnalyticsUnit, readStoredUnit, useAnalyticsQueries, useAnalyticsUnitPreference };
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/contracts/dedupe.ts","../src/widgets/WidgetFrame.tsx","../src/widgets/DashboardTokenControls.tsx","../src/widgets/DashboardView.tsx","../src/widgets/RangeSelector.tsx","../src/widgets/useAnalyticsQueries.ts","../src/widgets/useAnalyticsUnitPreference.tsx","../src/version.ts"],"names":["jsx","jsxs","useState","useEffect","Fragment","SharedQueryDocumentRunner","parseQuery"],"mappings":"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","file":"index.mjs","sourcesContent":["export const CODEMIRROR_SINGLETON_DEPS = [\n '@codemirror/autocomplete',\n '@codemirror/commands',\n '@codemirror/lang-markdown',\n '@codemirror/language',\n '@codemirror/lint',\n '@codemirror/search',\n '@codemirror/state',\n '@codemirror/theme-one-dark',\n '@codemirror/view',\n '@lezer/common',\n '@lezer/highlight',\n '@lezer/lr',\n '@lezer/markdown',\n] as const;\n","import type { ReactNode } from 'react';\nimport { Edit3, ExternalLink, Eye } from 'lucide-react';\nimport { cn } from '../utils/cn';\n\nexport interface WidgetFrameProps {\n /** DOM anchor for the frame (e.g. `widget-<key>`) — L3 indexes scroll here. */\n id?: string;\n title: string;\n question: string;\n /** Raw WQL for the widget. Hidden by default — only rendered with showQuery. */\n query?: string;\n span?: string;\n /** Show the raw WQL query box (teaching/authoring surfaces). View-first default hides it. */\n showQuery?: boolean;\n /**\n * Read-only inspection affordance — an always-visible button (prebuilt\n * seeds, teaching surfaces) opening the host's query inspector. Distinct\n * from editing: present without a toolbar.\n */\n onInspect?: () => void;\n /** Always-visible affordance: open this widget's resolved query in the\n * WQL explorer (host navigates). */\n onOpenInExplorer?: () => void;\n /**\n * Edit-mode action cluster (edit / duplicate / remove / reorder / size),\n * rendered top-right and always visible while present. The host builds it;\n * the frame only positions it.\n */\n toolbar?: ReactNode;\n children: ReactNode;\n}\n\nexport function WidgetFrame({ id, title, question, query, span, showQuery = false, onInspect, onOpenInExplorer, toolbar, children }: WidgetFrameProps) {\n return (\n <div\n id={id}\n className={cn(\n 'relative group/frame bg-card border border-border rounded-lg p-4 flex flex-col min-h-0',\n span,\n )}\n >\n <div className=\"flex items-start justify-between gap-3 mb-2\">\n <div className=\"min-w-0\">\n <h3 className=\"text-sm font-semibold text-foreground\">{title}</h3>\n {question && <p className=\"mt-1 text-xs text-muted-foreground\">{question}</p>}\n </div>\n {(onInspect || onOpenInExplorer) && (\n <div className=\"flex shrink-0 items-center gap-1\">\n {onInspect && (\n <button\n type=\"button\"\n onClick={onInspect}\n title=\"Inspect widget query\"\n aria-label={`Inspect query for ${title || 'widget'}`}\n className=\"inline-flex items-center justify-center p-1 max-lg:min-h-11 max-lg:min-w-11 rounded bg-muted/80 text-muted-foreground hover:text-foreground transition-colors\"\n >\n <Eye className=\"w-3.5 h-3.5\" />\n </button>\n )}\n {onOpenInExplorer && (\n <button\n type=\"button\"\n onClick={onOpenInExplorer}\n title=\"Open in explorer\"\n aria-label={`Open ${title || 'widget'} in explorer`}\n className=\"inline-flex items-center justify-center p-1 max-lg:min-h-11 max-lg:min-w-11 rounded bg-muted/80 text-muted-foreground hover:text-foreground transition-colors\"\n >\n <ExternalLink className=\"w-3.5 h-3.5\" />\n </button>\n )}\n </div>\n )}\n </div>\n {toolbar && <div className=\"mb-3\">{toolbar}</div>}\n {/* Long WQL wraps in place - an in-card horizontal scrollbar reads as\n a layout bug (dogfood #4). `break-all` covers spaceless fragments\n like `sum:totalVolume{effort:bench-press}`. */}\n {showQuery && query != null && query !== '' && (\n <div className=\"font-mono text-[11px] text-primary/90 bg-background/60 rounded px-2 py-1 mb-3 whitespace-pre-wrap break-all\">\n {query}\n </div>\n )}\n <div className=\"flex-1 min-h-0\">{children}</div>\n </div>\n );\n}\n\n/** Standard edit-mode tool-button chrome for the widget toolbar. */\nexport function WidgetToolButton({\n label,\n onClick,\n disabled,\n active,\n children,\n}: {\n label: string;\n onClick: () => void;\n disabled?: boolean;\n active?: boolean;\n children: ReactNode;\n}) {\n return (\n <button\n type=\"button\"\n onClick={onClick}\n title={label}\n aria-label={label}\n disabled={disabled}\n className={cn(\n 'inline-flex items-center justify-center p-1 max-lg:min-h-11 max-lg:min-w-11 rounded bg-muted/80 text-muted-foreground hover:text-foreground disabled:opacity-40 disabled:hover:text-muted-foreground transition-colors',\n active && 'text-primary bg-primary/10',\n )}\n >\n {children}\n </button>\n );\n}\n\n/** The primary \"open the full editor\" affordance inside the toolbar. */\nexport function WidgetEditButton({ title, onClick }: { title: string; onClick: () => void }) {\n return (\n <button\n type=\"button\"\n onClick={onClick}\n title=\"Edit widget\"\n aria-label={`Edit widget ${title || ''}`.trim()}\n className=\"inline-flex items-center justify-center p-1 max-lg:min-h-11 max-lg:min-w-11 rounded bg-muted/80 text-muted-foreground hover:text-foreground transition-colors\"\n >\n <Edit3 className=\"w-3.5 h-3.5\" />\n </button>\n );\n}\n","import { useEffect, useState } from 'react';\nimport type { DashboardToken } from '@bitcobblers/wod-wiki-wql';\nimport { cn } from '../utils/cn';\n\nexport interface DashboardTokenControlsProps {\n tokens: DashboardToken[];\n /** Current value per token name (defaults applied by the caller). */\n values: Record<string, string>;\n /**\n * Present when the note is editable — a change commits exactly once, on an\n * explicit action: a list-token segment click, or a scalar-token blur /\n * Enter. Keystrokes only edit the local draft — they never rewrite the\n * note or re-run widgets mid-edit.\n */\n onChange?: (name: string, value: string) => void;\n}\n\n/**\n * Scalar token input — owns a local draft so typing stays in-process; the\n * committed value (blur or Enter) is the only write. External value changes\n * (e.g. a written note re-resolved) re-sync the draft.\n */\nfunction ScalarTokenInput({\n name,\n value,\n disabled,\n onCommit,\n}: {\n name: string;\n value: string;\n disabled?: boolean;\n onCommit: (value: string) => void;\n}) {\n const [draft, setDraft] = useState(value);\n useEffect(() => setDraft(value), [value]);\n\n const commit = () => {\n if (draft !== value) onCommit(draft);\n };\n\n return (\n <input\n type=\"text\"\n value={draft}\n disabled={disabled}\n onChange={(e) => setDraft(e.target.value)}\n onBlur={commit}\n onKeyDown={(e) => {\n if (e.key === 'Enter') {\n commit();\n e.currentTarget.blur();\n }\n }}\n aria-label={`Token ${name}`}\n className=\"w-20 rounded-lg border border-border bg-card px-2 py-1 text-xs font-mono text-foreground focus:outline-none focus:border-primary disabled:opacity-60\"\n />\n );\n}\n\n/**\n * DashboardTokenControls — the control row a dashboard note's frontmatter\n * tokens render as (#899): scalar tokens become inputs, list tokens segmented\n * controls (first entry = default). Read-only views (no onChange) render the\n * current values as static text.\n */\nexport function DashboardTokenControls({ tokens, values, onChange }: DashboardTokenControlsProps) {\n if (tokens.length === 0) return null;\n const readOnly = !onChange;\n\n return (\n <div\n data-testid=\"dashboard-token-controls\"\n className=\"flex flex-wrap items-center gap-x-5 gap-y-2 mb-4\"\n >\n {tokens.map((token) => {\n const current = values[token.name] ?? token.values[0];\n return (\n <div key={token.name} className=\"flex items-center gap-2\">\n <span className=\"font-mono text-[11px] text-muted-foreground\">${token.name}</span>\n {token.isList ? (\n <div className=\"inline-flex items-center gap-1 rounded-lg border border-border bg-card p-1\">\n {token.values.map((option) => (\n <button\n key={option}\n type=\"button\"\n disabled={readOnly}\n onClick={() => {\n // A segment click IS the explicit commit — no draft.\n if (option !== current) onChange?.(token.name, option);\n }}\n className={cn(\n 'text-xs px-2.5 py-1 rounded-md transition-colors',\n option === current\n ? 'bg-primary text-primary-foreground font-medium'\n : 'text-muted-foreground hover:text-foreground hover:bg-muted',\n readOnly && 'cursor-default',\n )}\n >\n {option}\n </button>\n ))}\n </div>\n ) : readOnly ? (\n <span className=\"text-xs text-foreground font-mono\">{current}</span>\n ) : (\n <ScalarTokenInput\n name={token.name}\n value={current}\n onCommit={(next) => onChange?.(token.name, next)}\n />\n )}\n </div>\n );\n })}\n </div>\n );\n}\n","import { Fragment, useEffect, useMemo, useState } from 'react';\nimport { ChevronDown, ChevronRight, ChevronUp, Copy, Trash2 } from 'lucide-react';\nimport { isFindQuery, isPipelineQuery, parseQuery, type QueryResult, type AnyParsedQuery, defaultTokenValues, isDashboardWidgetType, resolveWidgetType, substituteTokens, unknownTokensMessage, unknownWidgetTypeMessage, type DashboardDocument, type DashboardWidget, SharedQueryDocumentRunner, type QueryDocumentRunnerHost, type DocumentOutput } from '@bitcobblers/wod-wiki-wql';\nimport type { QueryExecutor } from '../contracts/query';\nimport { WidgetFrame, WidgetToolButton, WidgetEditButton } from './WidgetFrame';\nimport { WidgetChart, WidgetProblemBadge } from './WidgetChart';\nimport { toChartResult } from './chartData';\nimport { DashboardTokenControls } from './DashboardTokenControls';\nimport { cn } from '../utils/cn';\n\n/** A widget's grid placement — column span 1..4, or a forced full row. */\nexport interface WidgetSpanOption {\n spanCols?: number;\n spanFull?: boolean;\n}\n\n/** A note heading rendered as a full-width grid row between widgets, with its\n * own DOM anchor (L3 scroll target). Order within `headings` is preserved. */\nexport interface DashboardHeadingAnchor {\n id: string;\n /** Markdown heading level (2+; level 1 renders as the page title surface). */\n level: number;\n text: string;\n /** Insert before this widget index; `widgets.length` = after the last. */\n beforeWidget: number;\n}\n\nexport interface DashboardViewProps {\n /** Parsed dashboard note (buildDashboardDocument). */\n document: DashboardDocument;\n /** Note headings to render between the widgets (document order). */\n headings?: DashboardHeadingAnchor[];\n /** Injected QueryExecutor for executing WQL queries. Zero singleton coupling. */\n executor?: QueryExecutor;\n /** Optional rollup fact warmer callback. */\n onEnsureRollupFacts?: () => Promise<void>;\n /** Current token values; defaults to each token's declared default. */\n tokenValues?: Record<string, string>;\n /** Present when the note is editable — committed control changes write back to frontmatter. */\n onTokenChange?: (name: string, value: string) => void;\n /**\n * Edit mode: per-widget arrangement chrome (edit / duplicate / remove /\n * reorder / size) renders on every card. View mode stays chrome-free —\n * the host gates editing on this flag plus the handler set below.\n */\n editMode?: boolean;\n /** Open the shared authoring composer for this widget (edit flow). */\n onEditWidget?: (widget: DashboardWidget) => void;\n /** Read-only query inspection (prebuilt seeds, teaching surfaces). */\n onInspectWidget?: (widget: DashboardWidget) => void;\n /** Open the widget's token-resolved query in the WQL explorer (host navigates). */\n onOpenInExplorer?: (widget: DashboardWidget, query: string) => void;\n onDuplicateWidget?: (widget: DashboardWidget) => void;\n onRemoveWidget?: (widget: DashboardWidget) => void;\n onMoveWidget?: (widget: DashboardWidget, delta: -1 | 1) => void;\n onResizeWidget?: (widget: DashboardWidget, span: WidgetSpanOption) => void;\n /** Optional execution range (ms epoch). Omit to use each query's own window. */\n rangeStart?: number;\n rangeEnd?: number;\n preferredUnit?: string;\n}\n\n/** Chart-input result plus the content-plane payloads list widgets read. */\ntype WidgetRunResult = QueryResult & {\n notes?: unknown[];\n blocks?: unknown[];\n efforts?: unknown[];\n runs?: unknown[];\n containers?: Record<string, { kind: 'page'; id: string; slug?: string; date?: string } | { kind: 'note'; id: string }>;\n};\n\ninterface WidgetRun {\n result?: WidgetRunResult;\n error?: string;\n}\n\n/**\n * Executor→runner host adapter (ticket 19 cutover): the shared document\n * runner drives family dispatch; the injected QueryExecutor stays the only\n * execution seam. Range/unit ride every aggregate call; missing pipeline\n * capability surfaces as an output diagnostic — never a silent empty result.\n */\nexport function createExecutorDocumentHost(\n executor: QueryExecutor,\n range: { rangeStart?: number; rangeEnd?: number; preferredUnit?: string; rollupEnsure?: () => Promise<void> } = {},\n): QueryDocumentRunnerHost {\n return {\n runAggregate: (queryText, overrides) =>\n executor.runQuery(queryText, {\n context: overrides.context,\n rangeStart: range.rangeStart,\n rangeEnd: range.rangeEnd,\n preferredUnit: range.preferredUnit,\n }),\n runFind: async (queryText, context) => {\n const parsed = parseQuery(queryText);\n if (!isFindQuery(parsed)) return { kind: 'find', error: `not a content query: ${queryText}` };\n const res = await executor.runFind(parsed, {\n context,\n range: range.rangeStart !== undefined\n ? { start: range.rangeStart, end: range.rangeEnd ?? Number.MAX_SAFE_INTEGER, endExclusive: false }\n : undefined,\n });\n return {\n kind: 'find',\n notes: res.notes,\n blocks: res.blocks,\n efforts: res.efforts,\n containers: res.containers,\n runs: res.runs,\n table: res.table,\n };\n },\n runPipeline: async (queryText, context) => {\n const res = await executor.runPipeline?.(queryText, {\n context,\n rangeStart: range.rangeStart,\n rangeEnd: range.rangeEnd,\n preferredUnit: range.preferredUnit,\n });\n return res ?? { error: 'pipeline queries are not supported by this executor' };\n },\n rollupEnsure: range.rollupEnsure,\n };\n}\n\n/** Document output → widget chart input (content payloads ride along). */\nfunction outputToRunResult(parsed: AnyParsedQuery, out: DocumentOutput): WidgetRunResult {\n const base = toChartResult({ parsed, series: out.series, unit: out.unit, error: out.error, table: out.table });\n return Object.assign(base, {\n ...(out.notes ? { notes: out.notes } : {}),\n ...(out.blocks ? { blocks: out.blocks } : {}),\n ...(out.efforts ? { efforts: out.efforts } : {}),\n ...(out.containers ? { containers: out.containers } : {}),\n ...(out.runs ? { runs: out.runs } : {}),\n });\n}\n\n/**\n * Grid span classes for a widget on the 1 / 2 / 4-column responsive grid.\n * An explicit fence span (`-N` / `-full`) wins; otherwise the type default.\n */\nfunction spanClass(widget: DashboardWidget): string {\n if (widget.spanFull) return 'col-span-1 md:col-span-2 lg:col-span-4';\n if (widget.spanCols != null) {\n if (widget.spanCols <= 1) return 'col-span-1';\n if (widget.spanCols === 2) return 'col-span-1 md:col-span-2';\n if (widget.spanCols === 3) return 'col-span-1 md:col-span-2 lg:col-span-3';\n return 'col-span-1 md:col-span-2 lg:col-span-4';\n }\n const resolved = resolveWidgetType(widget.type);\n if (resolved === 'value') return 'col-span-1';\n if (resolved === 'table') return 'col-span-1 md:col-span-2';\n return 'col-span-1 md:col-span-2 lg:col-span-4';\n}\n\n/** Size options in the edit-mode toolbar — column span or a full row. */\nconst SIZE_OPTIONS: Array<{ label: string; span: WidgetSpanOption; testId: string }> = [\n { label: 'Span 1 column', span: { spanCols: 1 }, testId: 'widget-size-1' },\n { label: 'Span 2 columns', span: { spanCols: 2 }, testId: 'widget-size-2' },\n { label: 'Span 4 columns', span: { spanCols: 4 }, testId: 'widget-size-4' },\n { label: 'Full row', span: { spanFull: true }, testId: 'widget-size-full' },\n];\n\nfunction sizeIsActive(widget: DashboardWidget, span: WidgetSpanOption): boolean {\n if (span.spanFull) return widget.spanFull === true;\n return !widget.spanFull && (widget.spanCols ?? null) === (span.spanCols ?? null);\n}\n\n/**\n * DashboardView — the state-free dashboard renderer (#900, format locked in #899).\n *\n * Takes a parsed DashboardDocument + current token values, runs all widget\n * queries concurrently via the injected QueryExecutor, and renders the\n * responsive CSS grid. All authoring interactions are host-owned: the view\n * surfaces the affordances (edit-mode toolbar, inspect button) and reports\n * through the injected callbacks — it never owns dialog or write state.\n */\nexport function DashboardView({\n document,\n headings,\n executor,\n onEnsureRollupFacts,\n tokenValues,\n onTokenChange,\n editMode = false,\n onEditWidget,\n onInspectWidget,\n onOpenInExplorer,\n onDuplicateWidget,\n onRemoveWidget,\n onMoveWidget,\n onResizeWidget,\n rangeStart,\n rangeEnd,\n preferredUnit,\n}: DashboardViewProps) {\n const values = useMemo(\n () => ({ ...defaultTokenValues(document.tokens), ...tokenValues }),\n [document.tokens, tokenValues],\n );\n\n const resolved = useMemo(\n () =>\n document.widgets.map((widget) => {\n const { query, missing } = substituteTokens(widget.body, values);\n // Fence-tag attributes carry presentation params (`goal=$token`) —\n // they ride the same token values as the body, and a missing token\n // blocks the widget instead of rendering a NaN target.\n const attributes: Record<string, string> = {};\n const allMissing = new Set(missing);\n for (const [key, value] of Object.entries(widget.attributes)) {\n const sub = substituteTokens(value, values);\n attributes[key] = sub.query;\n for (const m of sub.missing) allMissing.add(m);\n }\n return { widget, query, missing: [...allMissing], attributes };\n }),\n [document.widgets, values],\n );\n\n const [runs, setRuns] = useState<Record<string, WidgetRun>>({});\n const [loading, setLoading] = useState(true);\n\n const resolvedKey = useMemo(\n () => resolved.map((r) => `${r.widget.key}:${r.query}`).join('|'),\n [resolved],\n );\n\n useEffect(() => {\n let cancelled = false;\n setLoading(true);\n\n async function load() {\n const next: Record<string, WidgetRun> = {};\n const runnable: Array<{ widget: DashboardWidget; query: string; parsed: AnyParsedQuery }> = [];\n\n for (const { widget, query, missing } of resolved) {\n if (widget.type !== '' && !isDashboardWidgetType(widget.type)) {\n next[widget.key] = { error: unknownWidgetTypeMessage(widget.type) };\n continue;\n }\n if (missing.length > 0) {\n next[widget.key] = { error: unknownTokensMessage(missing) };\n continue;\n }\n const parsed = parseQuery(query);\n if (parsed.error) {\n next[widget.key] = { error: parsed.error };\n continue;\n }\n const resolvedType = resolveWidgetType(widget.type);\n if (isFindQuery(parsed)) {\n if (resolvedType !== 'table' && resolvedType !== 'list') {\n next[widget.key] = { error: 'content queries return things — use a table or list widget instead' };\n continue;\n }\n } else if (!isPipelineQuery(parsed) && resolvedType === 'list') {\n next[widget.key] = { error: 'aggregate queries return numbers — use a chart or value widget instead' };\n continue;\n }\n runnable.push({ widget, query, parsed });\n }\n\n if (!executor) {\n if (!cancelled) {\n setRuns(next);\n setLoading(false);\n }\n return;\n }\n\n // One runner host per dashboard run; each widget document gets its own\n // captured execution context (ticket 12) — overlapping runs never\n // borrow each other's snapshot. Rollup-ensure is AST-driven inside the\n // runner (calc.* heads incl. pipeline stages), not string sniffing.\n const host = createExecutorDocumentHost(executor, {\n rangeStart,\n rangeEnd,\n preferredUnit,\n rollupEnsure: onEnsureRollupFacts,\n });\n\n await Promise.all(\n runnable.map(async ({ widget, query, parsed }) => {\n try {\n const runner = new SharedQueryDocumentRunner(host);\n const res = await runner.run(query);\n const out = res.outputs[0];\n if (!out) {\n next[widget.key] = { error: res.diagnostics[0] ?? 'widget produced no output' };\n return;\n }\n const error = out.error ?? res.diagnostics[0];\n next[widget.key] = {\n result: outputToRunResult(parsed, out),\n ...(error ? { error } : {}),\n };\n } catch (err) {\n next[widget.key] = { error: err instanceof Error ? err.message : String(err) };\n }\n }),\n );\n\n if (!cancelled) {\n setRuns(next);\n setLoading(false);\n }\n }\n\n void load();\n return () => {\n cancelled = true;\n };\n }, [resolvedKey, resolved, executor, onEnsureRollupFacts, rangeStart, rangeEnd, preferredUnit]);\n\n return (\n <div data-testid=\"dashboard-view\">\n <DashboardTokenControls tokens={document.tokens} values={values} onChange={onTokenChange} />\n\n {loading && <div className=\"text-sm text-muted-foreground mb-3\">Loading widgets…</div>}\n\n <div className=\"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4\">\n {resolved.map(({ widget, query, attributes }, index) => {\n const run = runs[widget.key];\n const canArrange = editMode && (onEditWidget || onDuplicateWidget || onRemoveWidget || onMoveWidget || onResizeWidget);\n const before = (headings ?? []).filter((h) => h.beforeWidget === index);\n return (\n <Fragment key={widget.key}>\n {before.map((h) => (\n <div\n key={h.id}\n id={h.id}\n className={cn('col-span-full font-semibold text-foreground', h.level <= 2 ? 'text-base mt-2' : 'text-sm')}\n >\n {h.text}\n </div>\n ))}\n <WidgetFrame\n key={widget.key}\n id={`widget-${widget.key}`}\n title={widget.title ?? ''}\n question={widget.question ?? ''}\n query={query}\n span={spanClass(widget)}\n onInspect={onInspectWidget ? () => onInspectWidget(widget) : undefined}\n onOpenInExplorer={onOpenInExplorer ? () => onOpenInExplorer(widget, query) : undefined}\n toolbar={\n canArrange ? (\n <div className=\"flex flex-wrap items-center gap-1.5\" data-testid={`widget-toolbar-${widget.key}`}>\n {onEditWidget && <WidgetEditButton title={widget.title ?? ''} onClick={() => onEditWidget(widget)} />}\n {onMoveWidget && (\n <>\n <WidgetToolButton\n label={`Move ${widget.title || 'widget'} up`}\n disabled={index === 0}\n onClick={() => onMoveWidget(widget, -1)}\n >\n <ChevronUp className=\"w-3.5 h-3.5\" />\n </WidgetToolButton>\n <WidgetToolButton\n label={`Move ${widget.title || 'widget'} down`}\n disabled={index === resolved.length - 1}\n onClick={() => onMoveWidget(widget, 1)}\n >\n <ChevronDown className=\"w-3.5 h-3.5\" />\n </WidgetToolButton>\n </>\n )}\n {onResizeWidget && (\n <div className=\"flex items-center gap-0 rounded bg-muted/60 p-0.5\">\n {SIZE_OPTIONS.map((option) => (\n <button\n key={option.testId}\n type=\"button\"\n data-testid={`${option.testId}-${widget.key}`}\n title={option.label}\n aria-label={`${option.label} for ${widget.title || 'widget'}`}\n aria-pressed={sizeIsActive(widget, option.span)}\n disabled={sizeIsActive(widget, option.span)}\n onClick={() => onResizeWidget(widget, option.span)}\n className=\"inline-flex items-center justify-center px-1 py-0.5 max-lg:min-h-11 max-lg:min-w-11 text-xs font-mono rounded text-muted-foreground hover:text-foreground disabled:text-primary disabled:bg-primary/10\"\n >\n {option.span.spanFull ? <ChevronRight className=\"w-3 h-3\" /> : option.span.spanCols}\n </button>\n ))}\n </div>\n )}\n {onDuplicateWidget && (\n <WidgetToolButton\n label={`Duplicate ${widget.title || 'widget'}`}\n onClick={() => onDuplicateWidget(widget)}\n >\n <Copy className=\"w-3.5 h-3.5\" />\n </WidgetToolButton>\n )}\n {onRemoveWidget && (\n <WidgetToolButton\n label={`Remove ${widget.title || 'widget'}`}\n onClick={() => onRemoveWidget(widget)}\n >\n <Trash2 className=\"w-3.5 h-3.5\" />\n </WidgetToolButton>\n )}\n </div>\n ) : undefined\n }\n >\n {run?.error ? (\n <WidgetProblemBadge message={run.error} />\n ) : (\n <WidgetChart\n type={widget.type}\n result={run?.result}\n label={widget.title}\n unit={preferredUnit}\n attributes={attributes}\n />\n )}\n </WidgetFrame>\n </Fragment>\n );\n })}\n {(headings ?? []).filter((h) => h.beforeWidget >= resolved.length).map((h) => (\n <div\n key={h.id}\n id={h.id}\n className={cn('col-span-full font-semibold text-foreground', h.level <= 2 ? 'text-base mt-2' : 'text-sm')}\n >\n {h.text}\n </div>\n ))}\n </div>\n </div>\n );\n}\n","import { useState } from 'react';\nimport { cn } from '../utils/cn';\n\nexport type AnalyticsRangeWeeks = 4 | 8 | 16;\n\nexport interface RangeSelectorProps {\n weeks?: number;\n onWeeksChange?: (weeks: number) => void;\n className?: string;\n}\n\nexport function RangeSelector({ weeks: weeksProp, onWeeksChange, className }: RangeSelectorProps) {\n const [internalWeeks, setInternalWeeks] = useState<number>(16);\n const currentWeeks = weeksProp ?? internalWeeks;\n\n const handleSelect = (w: number) => {\n if (onWeeksChange) {\n onWeeksChange(w);\n } else {\n setInternalWeeks(w);\n }\n };\n\n // ponytail: fixed min-h-11 min-w-11 hit area satisfies mobile touch baseline; add responsive compact prop when dense desktop tables request it.\n return (\n <div className={cn('inline-flex items-center gap-1.5 rounded-lg border border-border bg-card p-1', className)}>\n {[4, 8, 16].map((w) => (\n <button\n key={w}\n type=\"button\"\n onClick={() => handleSelect(w)}\n className={cn(\n 'min-h-11 min-w-11 px-3 py-2 text-xs rounded-md transition-colors flex items-center justify-center font-medium',\n currentWeeks === w\n ? 'bg-primary text-primary-foreground'\n : 'text-muted-foreground hover:text-foreground hover:bg-muted',\n )}\n >\n {w}w\n </button>\n ))}\n </div>\n );\n}\n","import { useEffect, useState } from 'react';\nimport { parseQuery, SharedQueryDocumentRunner, type QueryResult } from '@bitcobblers/wod-wiki-wql';\nimport type { QueryExecutor } from '../contracts/query';\nimport { createExecutorDocumentHost } from './DashboardView';\nimport { toChartResult } from './chartData';\n\nconst DAY = 86_400_000;\nconst WEEK = 7 * DAY;\n\nexport interface AnalyticsQueryDef {\n key: string;\n query: string;\n}\n\nexport interface AnalyticsQueriesState {\n results: Record<string, QueryResult>;\n loading: boolean;\n}\n\nexport function useAnalyticsQueries(\n queries: AnalyticsQueryDef[],\n weeks: number,\n executor?: QueryExecutor,\n refreshKey = 0,\n preferredUnit?: string,\n onEnsureRollupFacts?: () => Promise<void>,\n): AnalyticsQueriesState {\n const [results, setResults] = useState<Record<string, QueryResult>>({});\n const [loading, setLoading] = useState(true);\n\n useEffect(() => {\n let cancelled = false;\n\n async function load() {\n if (!executor) {\n if (!cancelled) setLoading(false);\n return;\n }\n\n const now = Date.now();\n const rangeStart = now - weeks * WEEK;\n // Ticket 19 cutover: widget queries evaluate through the shared\n // document runner — family dispatch (aggregate/find/rows/pipeline),\n // AST-driven rollup-ensure, and one captured context per query run.\n const host = createExecutorDocumentHost(executor, {\n rangeStart,\n rangeEnd: now,\n preferredUnit,\n rollupEnsure: onEnsureRollupFacts,\n });\n const runner = new SharedQueryDocumentRunner(host);\n try {\n const settled = await Promise.all(\n queries.map(async (q) => {\n const res = await runner.run(q.query);\n const out = res.outputs[0];\n const error = out?.error ?? res.diagnostics[0];\n // The chart input keeps the raw query's own parse so consumers\n // reading `parsed.family`/`raw` see the authored family.\n const result = toChartResult({\n parsed: parseQuery(q.query),\n series: out?.series,\n unit: out?.unit,\n error,\n table: out?.table,\n }) as QueryResult & { notes?: unknown[]; blocks?: unknown[]; efforts?: unknown[]; runs?: unknown[] };\n if (out?.notes) result.notes = out.notes;\n if (out?.blocks) result.blocks = out.blocks;\n if (out?.efforts) result.efforts = out.efforts;\n if (out?.runs) result.runs = out.runs;\n return [q.key, result] as const;\n }),\n );\n if (!cancelled) setResults(Object.fromEntries(settled));\n } finally {\n if (!cancelled) setLoading(false);\n }\n }\n\n void load();\n\n return () => {\n cancelled = true;\n };\n }, [queries, weeks, executor, refreshKey, preferredUnit, onEnsureRollupFacts]);\n\n return { results, loading };\n}\n","import { useCallback, useEffect, useState } from 'react';\nimport { cn } from '../utils/cn';\nimport { parseQuery } from '@bitcobblers/wod-wiki-wql';\nimport type { AnalyticsQueryDef } from './useAnalyticsQueries';\nimport type { StorageLike } from '../contracts/storage';\n\nexport const ANALYTICS_UNIT_STORAGE_KEY = 'wod.analytics.unit';\nexport const DEFAULT_ANALYTICS_UNIT = 'kg';\nexport type AnalyticsUnit = 'kg' | 'lb';\n\nconst VALID_UNITS: readonly AnalyticsUnit[] = ['kg', 'lb'];\n\nconst memoryStore = new Map<string, string>();\nconst inMemoryStorage: StorageLike = {\n getItem: (key: string) => memoryStore.get(key) ?? null,\n setItem: (key: string, value: string) => {\n memoryStore.set(key, value);\n },\n removeItem: (key: string) => {\n memoryStore.delete(key);\n },\n};\n\nconst unitListeners = new Set<() => void>();\nfunction notifyUnitChange() {\n for (const l of unitListeners) l();\n}\n\nfunction resolveStorage(custom?: StorageLike): StorageLike {\n return custom ?? inMemoryStorage;\n}\n\nexport function readStoredUnit(storage?: StorageLike): AnalyticsUnit {\n const store = resolveStorage(storage);\n const stored = store.getItem(ANALYTICS_UNIT_STORAGE_KEY);\n return (VALID_UNITS as readonly string[]).includes(stored ?? '')\n ? (stored as AnalyticsUnit)\n : DEFAULT_ANALYTICS_UNIT;\n}\n\nexport function useAnalyticsUnitPreference(customStorage?: StorageLike) {\n const storage = resolveStorage(customStorage);\n const [unit, setUnitState] = useState<AnalyticsUnit>(() => readStoredUnit(storage));\n\n useEffect(() => {\n setUnitState(readStoredUnit(storage));\n const sync = () => setUnitState(readStoredUnit(storage));\n unitListeners.add(sync);\n return () => {\n unitListeners.delete(sync);\n };\n }, [storage]);\n\n const setUnit = useCallback(\n (next: AnalyticsUnit) => {\n storage.setItem(ANALYTICS_UNIT_STORAGE_KEY, next);\n setUnitState(next);\n notifyUnitChange();\n },\n [storage],\n );\n\n const toggleUnit = useCallback(() => {\n const next: AnalyticsUnit = unit === 'kg' ? 'lb' : 'kg';\n setUnit(next);\n }, [unit, setUnit]);\n\n return { unit, setUnit, toggleUnit };\n}\n\nexport function getEffectiveAnalyticsUnit(\n query: string,\n preferredUnit: AnalyticsUnit,\n): { unit: AnalyticsUnit; forced: boolean } {\n const parsed = parseQuery(query);\n const displayUnit = 'displayUnit' in parsed ? parsed.displayUnit : undefined;\n if (displayUnit === 'lb' || displayUnit === 'kg') {\n return { unit: displayUnit as AnalyticsUnit, forced: true };\n }\n return { unit: preferredUnit, forced: false };\n}\n\nexport function getDashboardEffectiveUnit(\n queries: AnalyticsQueryDef[],\n preferredUnit: AnalyticsUnit,\n): { unit: AnalyticsUnit; forced: boolean } {\n const units = queries\n .map((q) => {\n const parsed = parseQuery(q.query);\n return 'displayUnit' in parsed ? parsed.displayUnit : undefined;\n })\n .filter((u): u is string => u === 'kg' || u === 'lb');\n\n const unique = Array.from(new Set(units));\n if (unique.length === 1) {\n return { unit: unique[0] as AnalyticsUnit, forced: true };\n }\n return { unit: preferredUnit, forced: false };\n}\nexport interface AnalyticsUnitPreferenceProps {\n className?: string;\n unit?: AnalyticsUnit;\n forced?: boolean;\n storage?: StorageLike;\n}\n\nexport function AnalyticsUnitPreference({ className, unit: unitProp, forced = false, storage }: AnalyticsUnitPreferenceProps) {\n const { unit: storedUnit, setUnit } = useAnalyticsUnitPreference(storage);\n const activeUnit = unitProp ?? storedUnit;\n\n return (\n <div\n data-testid=\"analytics-unit-preference\"\n className={cn('inline-flex items-center gap-1 text-xs', className)}\n >\n <div\n className={cn(\n 'inline-flex items-center rounded-lg border border-border bg-card p-1',\n forced && 'opacity-70',\n )}\n title={forced ? 'Unit forced by query directive in this view' : 'Display unit preference'}\n >\n <button\n type=\"button\"\n disabled={forced}\n onClick={() => setUnit('kg')}\n className={cn(\n 'px-2.5 py-1 rounded-md transition-colors text-xs font-mono',\n activeUnit === 'kg'\n ? 'bg-primary text-primary-foreground font-medium'\n : 'text-muted-foreground hover:text-foreground hover:bg-muted',\n forced && 'cursor-default hover:bg-transparent hover:text-muted-foreground',\n )}\n >\n kg\n </button>\n <button\n type=\"button\"\n disabled={forced}\n onClick={() => setUnit('lb')}\n className={cn(\n 'px-2.5 py-1 rounded-md transition-colors text-xs font-mono',\n activeUnit === 'lb'\n ? 'bg-primary text-primary-foreground font-medium'\n : 'text-muted-foreground hover:text-foreground hover:bg-muted',\n forced && 'cursor-default hover:bg-transparent hover:text-muted-foreground',\n )}\n >\n lb\n </button>\n </div>\n {forced && (\n <span className=\"text-[11px] text-muted-foreground italic\">forced by note</span>\n )}\n </div>\n );\n}\n","/**\n * Auto-generated build version metadata.\n * Do not edit directly; updated by scripts/stamp-version.ts\n */\nexport const VERSION = '0.50.2189';\nexport const GIT_SHA = '5da2c23';\nexport const BUILD_TIME = '2026-11-01T16:27:05.203Z';\nexport const SEMVER = {\n version: '0.50.2189',\n gitSha: '5da2c23',\n buildTime: '2026-11-01T16:27:05.203Z',\n} as const;\n"]}
1
+ {"version":3,"sources":["../src/contracts/dedupe.ts","../src/widgets/WidgetFrame.tsx","../src/widgets/DashboardTokenControls.tsx","../src/widgets/DashboardView.tsx","../src/widgets/RangeSelector.tsx","../src/widgets/useAnalyticsQueries.ts","../src/widgets/useAnalyticsUnitPreference.tsx","../src/version.ts"],"names":["jsx","jsxs","useState","useEffect","Fragment","SharedQueryDocumentRunner","parseQuery"],"mappings":"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","file":"index.mjs","sourcesContent":["export const CODEMIRROR_SINGLETON_DEPS = [\n '@codemirror/autocomplete',\n '@codemirror/commands',\n '@codemirror/lang-markdown',\n '@codemirror/language',\n '@codemirror/lint',\n '@codemirror/search',\n '@codemirror/state',\n '@codemirror/theme-one-dark',\n '@codemirror/view',\n '@lezer/common',\n '@lezer/highlight',\n '@lezer/lr',\n '@lezer/markdown',\n] as const;\n","import type { ReactNode } from 'react';\nimport { Edit3, ExternalLink, Eye } from 'lucide-react';\nimport { cn } from '../utils/cn';\n\nexport interface WidgetFrameProps {\n /** DOM anchor for the frame (e.g. `widget-<key>`) — L3 indexes scroll here. */\n id?: string;\n title: string;\n question: string;\n /** Raw WQL for the widget. Hidden by default — only rendered with showQuery. */\n query?: string;\n span?: string;\n /** Show the raw WQL query box (teaching/authoring surfaces). View-first default hides it. */\n showQuery?: boolean;\n /**\n * Read-only inspection affordance — an always-visible button (prebuilt\n * seeds, teaching surfaces) opening the host's query inspector. Distinct\n * from editing: present without a toolbar.\n */\n onInspect?: () => void;\n /** Always-visible affordance: open this widget's resolved query in the\n * WQL explorer (host navigates). */\n onOpenInExplorer?: () => void;\n /**\n * Edit-mode action cluster (edit / duplicate / remove / reorder / size),\n * rendered top-right and always visible while present. The host builds it;\n * the frame only positions it.\n */\n toolbar?: ReactNode;\n children: ReactNode;\n}\n\nexport function WidgetFrame({ id, title, question, query, span, showQuery = false, onInspect, onOpenInExplorer, toolbar, children }: WidgetFrameProps) {\n return (\n <div\n id={id}\n className={cn(\n 'relative group/frame bg-card border border-border rounded-lg p-4 flex flex-col min-h-0',\n span,\n )}\n >\n <div className=\"flex items-start justify-between gap-3 mb-2\">\n <div className=\"min-w-0\">\n <h3 className=\"text-sm font-semibold text-foreground\">{title}</h3>\n {question && <p className=\"mt-1 text-xs text-muted-foreground\">{question}</p>}\n </div>\n {(onInspect || onOpenInExplorer) && (\n <div className=\"flex shrink-0 items-center gap-1\">\n {onInspect && (\n <button\n type=\"button\"\n onClick={onInspect}\n title=\"Inspect widget query\"\n aria-label={`Inspect query for ${title || 'widget'}`}\n className=\"inline-flex items-center justify-center p-1 max-lg:min-h-11 max-lg:min-w-11 rounded bg-muted/80 text-muted-foreground hover:text-foreground transition-colors\"\n >\n <Eye className=\"w-3.5 h-3.5\" />\n </button>\n )}\n {onOpenInExplorer && (\n <button\n type=\"button\"\n onClick={onOpenInExplorer}\n title=\"Open in explorer\"\n aria-label={`Open ${title || 'widget'} in explorer`}\n className=\"inline-flex items-center justify-center p-1 max-lg:min-h-11 max-lg:min-w-11 rounded bg-muted/80 text-muted-foreground hover:text-foreground transition-colors\"\n >\n <ExternalLink className=\"w-3.5 h-3.5\" />\n </button>\n )}\n </div>\n )}\n </div>\n {toolbar && <div className=\"mb-3\">{toolbar}</div>}\n {/* Long WQL wraps in place - an in-card horizontal scrollbar reads as\n a layout bug (dogfood #4). `break-all` covers spaceless fragments\n like `sum:totalVolume{effort:bench-press}`. */}\n {showQuery && query != null && query !== '' && (\n <div className=\"font-mono text-[11px] text-primary/90 bg-background/60 rounded px-2 py-1 mb-3 whitespace-pre-wrap break-all\">\n {query}\n </div>\n )}\n <div className=\"flex-1 min-h-0\">{children}</div>\n </div>\n );\n}\n\n/** Standard edit-mode tool-button chrome for the widget toolbar. */\nexport function WidgetToolButton({\n label,\n onClick,\n disabled,\n active,\n children,\n}: {\n label: string;\n onClick: () => void;\n disabled?: boolean;\n active?: boolean;\n children: ReactNode;\n}) {\n return (\n <button\n type=\"button\"\n onClick={onClick}\n title={label}\n aria-label={label}\n disabled={disabled}\n className={cn(\n 'inline-flex items-center justify-center p-1 max-lg:min-h-11 max-lg:min-w-11 rounded bg-muted/80 text-muted-foreground hover:text-foreground disabled:opacity-40 disabled:hover:text-muted-foreground transition-colors',\n active && 'text-primary bg-primary/10',\n )}\n >\n {children}\n </button>\n );\n}\n\n/** The primary \"open the full editor\" affordance inside the toolbar. */\nexport function WidgetEditButton({ title, onClick }: { title: string; onClick: () => void }) {\n return (\n <button\n type=\"button\"\n onClick={onClick}\n title=\"Edit widget\"\n aria-label={`Edit widget ${title || ''}`.trim()}\n className=\"inline-flex items-center justify-center p-1 max-lg:min-h-11 max-lg:min-w-11 rounded bg-muted/80 text-muted-foreground hover:text-foreground transition-colors\"\n >\n <Edit3 className=\"w-3.5 h-3.5\" />\n </button>\n );\n}\n","import { useEffect, useState } from 'react';\nimport type { DashboardToken } from '@bitcobblers/wod-wiki-wql';\nimport { cn } from '../utils/cn';\n\nexport interface DashboardTokenControlsProps {\n tokens: DashboardToken[];\n /** Current value per token name (defaults applied by the caller). */\n values: Record<string, string>;\n /**\n * Present when the note is editable — a change commits exactly once, on an\n * explicit action: a list-token segment click, or a scalar-token blur /\n * Enter. Keystrokes only edit the local draft — they never rewrite the\n * note or re-run widgets mid-edit.\n */\n onChange?: (name: string, value: string) => void;\n}\n\n/**\n * Scalar token input — owns a local draft so typing stays in-process; the\n * committed value (blur or Enter) is the only write. External value changes\n * (e.g. a written note re-resolved) re-sync the draft.\n */\nfunction ScalarTokenInput({\n name,\n value,\n disabled,\n onCommit,\n}: {\n name: string;\n value: string;\n disabled?: boolean;\n onCommit: (value: string) => void;\n}) {\n const [draft, setDraft] = useState(value);\n useEffect(() => setDraft(value), [value]);\n\n const commit = () => {\n if (draft !== value) onCommit(draft);\n };\n\n return (\n <input\n type=\"text\"\n value={draft}\n disabled={disabled}\n onChange={(e) => setDraft(e.target.value)}\n onBlur={commit}\n onKeyDown={(e) => {\n if (e.key === 'Enter') {\n commit();\n e.currentTarget.blur();\n }\n }}\n aria-label={`Token ${name}`}\n className=\"w-20 rounded-lg border border-border bg-card px-2 py-1 text-xs font-mono text-foreground focus:outline-none focus:border-primary disabled:opacity-60\"\n />\n );\n}\n\n/**\n * DashboardTokenControls — the control row a dashboard note's frontmatter\n * tokens render as (#899): scalar tokens become inputs, list tokens segmented\n * controls (first entry = default). Read-only views (no onChange) render the\n * current values as static text.\n */\nexport function DashboardTokenControls({ tokens, values, onChange }: DashboardTokenControlsProps) {\n if (tokens.length === 0) return null;\n const readOnly = !onChange;\n\n return (\n <div\n data-testid=\"dashboard-token-controls\"\n className=\"flex flex-wrap items-center gap-x-5 gap-y-2 mb-4\"\n >\n {tokens.map((token) => {\n const current = values[token.name] ?? token.values[0];\n return (\n <div key={token.name} className=\"flex items-center gap-2\">\n <span className=\"font-mono text-[11px] text-muted-foreground\">${token.name}</span>\n {token.isList ? (\n <div className=\"inline-flex items-center gap-1 rounded-lg border border-border bg-card p-1\">\n {token.values.map((option) => (\n <button\n key={option}\n type=\"button\"\n disabled={readOnly}\n onClick={() => {\n // A segment click IS the explicit commit — no draft.\n if (option !== current) onChange?.(token.name, option);\n }}\n className={cn(\n 'text-xs px-2.5 py-1 rounded-md transition-colors',\n option === current\n ? 'bg-primary text-primary-foreground font-medium'\n : 'text-muted-foreground hover:text-foreground hover:bg-muted',\n readOnly && 'cursor-default',\n )}\n >\n {option}\n </button>\n ))}\n </div>\n ) : readOnly ? (\n <span className=\"text-xs text-foreground font-mono\">{current}</span>\n ) : (\n <ScalarTokenInput\n name={token.name}\n value={current}\n onCommit={(next) => onChange?.(token.name, next)}\n />\n )}\n </div>\n );\n })}\n </div>\n );\n}\n","import { Fragment, useEffect, useMemo, useState } from 'react';\nimport { ChevronDown, ChevronRight, ChevronUp, Copy, Trash2 } from 'lucide-react';\nimport { isFindQuery, isPipelineQuery, parseQuery, parseDocument, type AnyParsedQuery, defaultTokenValues, isDashboardWidgetType, resolveWidgetType, substituteTokens, unknownTokensMessage, unknownWidgetTypeMessage, type DashboardDocument, type DashboardWidget, SharedQueryDocumentRunner } from '@bitcobblers/wod-wiki-wql';\nimport type { QueryExecutor } from '../contracts/query';\nimport { WidgetFrame, WidgetToolButton, WidgetEditButton } from './WidgetFrame';\nimport { WidgetChart, WidgetProblemBadge } from './WidgetChart';\nimport { createExecutorDocumentHost, outputToRunResult, type WidgetRunResult } from './chartData';\nimport { DashboardTokenControls } from './DashboardTokenControls';\nexport { createExecutorDocumentHost, outputToRunResult, type WidgetRunResult };\nimport { cn } from '../utils/cn';\n\n/** A widget's grid placement — column span 1..4, or a forced full row. */\nexport interface WidgetSpanOption {\n spanCols?: number;\n spanFull?: boolean;\n}\n\n/** A note heading rendered as a full-width grid row between widgets, with its\n * own DOM anchor (L3 scroll target). Order within `headings` is preserved. */\nexport interface DashboardHeadingAnchor {\n id: string;\n /** Markdown heading level (2+; level 1 renders as the page title surface). */\n level: number;\n text: string;\n /** Insert before this widget index; `widgets.length` = after the last. */\n beforeWidget: number;\n}\n\nexport interface DashboardViewProps {\n /** Parsed dashboard note (buildDashboardDocument). */\n document: DashboardDocument;\n /** Note headings to render between the widgets (document order). */\n headings?: DashboardHeadingAnchor[];\n /** Injected QueryExecutor for executing WQL queries. Zero singleton coupling. */\n executor?: QueryExecutor;\n /** Optional rollup fact warmer callback. */\n onEnsureRollupFacts?: () => Promise<void>;\n /** Current token values; defaults to each token's declared default. */\n tokenValues?: Record<string, string>;\n /** Present when the note is editable — committed control changes write back to frontmatter. */\n onTokenChange?: (name: string, value: string) => void;\n /**\n * Edit mode: per-widget arrangement chrome (edit / duplicate / remove /\n * reorder / size) renders on every card. View mode stays chrome-free —\n * the host gates editing on this flag plus the handler set below.\n */\n editMode?: boolean;\n /** Open the shared authoring composer for this widget (edit flow). */\n onEditWidget?: (widget: DashboardWidget) => void;\n /** Read-only query inspection (prebuilt seeds, teaching surfaces). */\n onInspectWidget?: (widget: DashboardWidget) => void;\n /** Open the widget's token-resolved query in the WQL explorer (host navigates). */\n onOpenInExplorer?: (widget: DashboardWidget, query: string) => void;\n onDuplicateWidget?: (widget: DashboardWidget) => void;\n onRemoveWidget?: (widget: DashboardWidget) => void;\n onMoveWidget?: (widget: DashboardWidget, delta: -1 | 1) => void;\n onResizeWidget?: (widget: DashboardWidget, span: WidgetSpanOption) => void;\n /** Optional execution range (ms epoch). Omit to use each query's own window. */\n rangeStart?: number;\n rangeEnd?: number;\n preferredUnit?: string;\n /** Optional click handler for opening a note/block from content-plane widgets. */\n onOpenNote?: (item: { id: string; title?: string; blockContentId?: string }) => void;\n /** Optional resolver returning the destination href for an item in content-plane widgets. */\n noteHref?: (item: { id: string; title?: string; blockContentId?: string }) => string;\n}\n\ninterface WidgetRun {\n result?: WidgetRunResult;\n error?: string;\n}\n/**\n * Grid span classes for a widget on the 1 / 2 / 4-column responsive grid.\n * An explicit fence span (`-N` / `-full`) wins; otherwise the type default.\n */\nfunction spanClass(widget: DashboardWidget): string {\n if (widget.spanFull) return 'col-span-1 md:col-span-2 lg:col-span-4';\n if (widget.spanCols != null) {\n if (widget.spanCols <= 1) return 'col-span-1';\n if (widget.spanCols === 2) return 'col-span-1 md:col-span-2';\n if (widget.spanCols === 3) return 'col-span-1 md:col-span-2 lg:col-span-3';\n return 'col-span-1 md:col-span-2 lg:col-span-4';\n }\n const resolved = resolveWidgetType(widget.type);\n if (resolved === 'value') return 'col-span-1';\n if (resolved === 'table') return 'col-span-1 md:col-span-2';\n return 'col-span-1 md:col-span-2 lg:col-span-4';\n}\n\n/** Size options in the edit-mode toolbar — column span or a full row. */\nconst SIZE_OPTIONS: Array<{ label: string; span: WidgetSpanOption; testId: string }> = [\n { label: 'Span 1 column', span: { spanCols: 1 }, testId: 'widget-size-1' },\n { label: 'Span 2 columns', span: { spanCols: 2 }, testId: 'widget-size-2' },\n { label: 'Span 4 columns', span: { spanCols: 4 }, testId: 'widget-size-4' },\n { label: 'Full row', span: { spanFull: true }, testId: 'widget-size-full' },\n];\n\nfunction sizeIsActive(widget: DashboardWidget, span: WidgetSpanOption): boolean {\n if (span.spanFull) return widget.spanFull === true;\n return !widget.spanFull && (widget.spanCols ?? null) === (span.spanCols ?? null);\n}\n\n/**\n * DashboardView — the state-free dashboard renderer (#900, format locked in #899).\n *\n * Takes a parsed DashboardDocument + current token values, runs all widget\n * queries concurrently via the injected QueryExecutor, and renders the\n * responsive CSS grid. All authoring interactions are host-owned: the view\n * surfaces the affordances (edit-mode toolbar, inspect button) and reports\n * through the injected callbacks — it never owns dialog or write state.\n */\nexport function DashboardView({\n document,\n headings,\n executor,\n onEnsureRollupFacts,\n tokenValues,\n onTokenChange,\n editMode = false,\n onEditWidget,\n onInspectWidget,\n onOpenInExplorer,\n onDuplicateWidget,\n onRemoveWidget,\n onMoveWidget,\n onResizeWidget,\n rangeStart,\n rangeEnd,\n preferredUnit,\n onOpenNote,\n noteHref,\n}: DashboardViewProps) {\n const values = useMemo(\n () => ({ ...defaultTokenValues(document.tokens), ...tokenValues }),\n [document.tokens, tokenValues],\n );\n\n const resolved = useMemo(\n () =>\n document.widgets.map((widget) => {\n const { query, missing } = substituteTokens(widget.body, values);\n // Fence-tag attributes carry presentation params (`goal=$token`) —\n // they ride the same token values as the body, and a missing token\n // blocks the widget instead of rendering a NaN target.\n const attributes: Record<string, string> = {};\n const allMissing = new Set(missing);\n for (const [key, value] of Object.entries(widget.attributes)) {\n const sub = substituteTokens(value, values);\n attributes[key] = sub.query;\n for (const m of sub.missing) allMissing.add(m);\n }\n return { widget, query, missing: [...allMissing], attributes };\n }),\n [document.widgets, values],\n );\n\n const [runs, setRuns] = useState<Record<string, WidgetRun>>({});\n const [loading, setLoading] = useState(true);\n\n const resolvedKey = useMemo(\n () => resolved.map((r) => `${r.widget.key}:${r.query}`).join('|'),\n [resolved],\n );\n\n useEffect(() => {\n let cancelled = false;\n setLoading(true);\n\n async function load() {\n const next: Record<string, WidgetRun> = {};\n const runnable: Array<{ widget: DashboardWidget; query: string; parsed: AnyParsedQuery }> = [];\n\n for (const { widget, query, missing } of resolved) {\n if (widget.type !== '' && !isDashboardWidgetType(widget.type)) {\n next[widget.key] = { error: unknownWidgetTypeMessage(widget.type) };\n continue;\n }\n if (missing.length > 0) {\n next[widget.key] = { error: unknownTokensMessage(missing) };\n continue;\n }\n const docParse = parseDocument(query);\n const isMultiLineDoc = !docParse.doc.degenerate && docParse.doc.assignments.length > 0;\n const parsed: AnyParsedQuery = isMultiLineDoc\n ? { family: 'aggregate', raw: query, agg: 'sum', metric: docParse.doc.show[0] ?? 'value', filters: [], groupBy: [] }\n : parseQuery(query);\n const syntaxErr = isMultiLineDoc ? docParse.diagnostics[0] : parsed.error;\n if (syntaxErr) {\n next[widget.key] = { error: syntaxErr };\n continue;\n }\n const resolvedType = resolveWidgetType(widget.type);\n if (isFindQuery(parsed)) {\n if (resolvedType !== 'table' && resolvedType !== 'list') {\n next[widget.key] = { error: 'content queries return things — use a table or list widget instead' };\n continue;\n }\n } else if (!isPipelineQuery(parsed) && !isMultiLineDoc && resolvedType === 'list') {\n next[widget.key] = { error: 'aggregate queries return numbers — use a chart or value widget instead' };\n continue;\n }\n runnable.push({ widget, query, parsed });\n }\n\n if (!executor) {\n if (!cancelled) {\n setRuns(next);\n setLoading(false);\n }\n return;\n }\n\n // One runner host per dashboard run; each widget document gets its own\n // captured execution context (ticket 12) — overlapping runs never\n // borrow each other's snapshot. Rollup-ensure is AST-driven inside the\n // runner (calc.* heads incl. pipeline stages), not string sniffing.\n const host = createExecutorDocumentHost(executor, {\n rangeStart,\n rangeEnd,\n preferredUnit,\n rollupEnsure: onEnsureRollupFacts,\n });\n\n await Promise.all(\n runnable.map(async ({ widget, query, parsed }) => {\n try {\n const runner = new SharedQueryDocumentRunner(host);\n const res = await runner.run(query);\n const out = res.outputs[0];\n if (!out) {\n next[widget.key] = { error: res.diagnostics[0] ?? 'widget produced no output' };\n return;\n }\n const error = out.error ?? res.diagnostics[0];\n next[widget.key] = {\n result: outputToRunResult(parsed, out),\n ...(error ? { error } : {}),\n };\n } catch (err) {\n next[widget.key] = { error: err instanceof Error ? err.message : String(err) };\n }\n }),\n );\n\n if (!cancelled) {\n setRuns(next);\n setLoading(false);\n }\n }\n\n void load();\n return () => {\n cancelled = true;\n };\n }, [resolvedKey, resolved, executor, onEnsureRollupFacts, rangeStart, rangeEnd, preferredUnit]);\n\n return (\n <div data-testid=\"dashboard-view\">\n <DashboardTokenControls tokens={document.tokens} values={values} onChange={onTokenChange} />\n\n {loading && <div className=\"text-sm text-muted-foreground mb-3\">Loading widgets…</div>}\n\n <div className=\"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4\">\n {resolved.map(({ widget, query, attributes }, index) => {\n const run = runs[widget.key];\n const canArrange = editMode && (onEditWidget || onDuplicateWidget || onRemoveWidget || onMoveWidget || onResizeWidget);\n const before = (headings ?? []).filter((h) => h.beforeWidget === index);\n return (\n <Fragment key={widget.key}>\n {before.map((h) => (\n <div\n key={h.id}\n id={h.id}\n className={cn('col-span-full font-semibold text-foreground', h.level <= 2 ? 'text-base mt-2' : 'text-sm')}\n >\n {h.text}\n </div>\n ))}\n <WidgetFrame\n key={widget.key}\n id={`widget-${widget.key}`}\n title={widget.title ?? ''}\n question={widget.question ?? ''}\n query={query}\n span={spanClass(widget)}\n onInspect={onInspectWidget ? () => onInspectWidget(widget) : undefined}\n onOpenInExplorer={onOpenInExplorer ? () => onOpenInExplorer(widget, query) : undefined}\n toolbar={\n canArrange ? (\n <div className=\"flex flex-wrap items-center gap-1.5\" data-testid={`widget-toolbar-${widget.key}`}>\n {onEditWidget && <WidgetEditButton title={widget.title ?? ''} onClick={() => onEditWidget(widget)} />}\n {onMoveWidget && (\n <>\n <WidgetToolButton\n label={`Move ${widget.title || 'widget'} up`}\n disabled={index === 0}\n onClick={() => onMoveWidget(widget, -1)}\n >\n <ChevronUp className=\"w-3.5 h-3.5\" />\n </WidgetToolButton>\n <WidgetToolButton\n label={`Move ${widget.title || 'widget'} down`}\n disabled={index === resolved.length - 1}\n onClick={() => onMoveWidget(widget, 1)}\n >\n <ChevronDown className=\"w-3.5 h-3.5\" />\n </WidgetToolButton>\n </>\n )}\n {onResizeWidget && (\n <div className=\"flex items-center gap-0 rounded bg-muted/60 p-0.5\">\n {SIZE_OPTIONS.map((option) => (\n <button\n key={option.testId}\n type=\"button\"\n data-testid={`${option.testId}-${widget.key}`}\n title={option.label}\n aria-label={`${option.label} for ${widget.title || 'widget'}`}\n aria-pressed={sizeIsActive(widget, option.span)}\n disabled={sizeIsActive(widget, option.span)}\n onClick={() => onResizeWidget(widget, option.span)}\n className=\"inline-flex items-center justify-center px-1 py-0.5 max-lg:min-h-11 max-lg:min-w-11 text-xs font-mono rounded text-muted-foreground hover:text-foreground disabled:text-primary disabled:bg-primary/10\"\n >\n {option.span.spanFull ? <ChevronRight className=\"w-3 h-3\" /> : option.span.spanCols}\n </button>\n ))}\n </div>\n )}\n {onDuplicateWidget && (\n <WidgetToolButton\n label={`Duplicate ${widget.title || 'widget'}`}\n onClick={() => onDuplicateWidget(widget)}\n >\n <Copy className=\"w-3.5 h-3.5\" />\n </WidgetToolButton>\n )}\n {onRemoveWidget && (\n <WidgetToolButton\n label={`Remove ${widget.title || 'widget'}`}\n onClick={() => onRemoveWidget(widget)}\n >\n <Trash2 className=\"w-3.5 h-3.5\" />\n </WidgetToolButton>\n )}\n </div>\n ) : undefined\n }\n >\n {run?.error ? (\n <WidgetProblemBadge message={run.error} />\n ) : (\n <WidgetChart\n type={widget.type}\n result={run?.result}\n label={widget.title}\n unit={preferredUnit}\n attributes={attributes}\n onOpenNote={onOpenNote}\n noteHref={noteHref}\n />\n )}\n </WidgetFrame>\n </Fragment>\n );\n })}\n {(headings ?? []).filter((h) => h.beforeWidget >= resolved.length).map((h) => (\n <div\n key={h.id}\n id={h.id}\n className={cn('col-span-full font-semibold text-foreground', h.level <= 2 ? 'text-base mt-2' : 'text-sm')}\n >\n {h.text}\n </div>\n ))}\n </div>\n </div>\n );\n}\n","import { useState } from 'react';\nimport { cn } from '../utils/cn';\n\nexport type AnalyticsRangeWeeks = 4 | 8 | 16;\n\nexport interface RangeSelectorProps {\n weeks?: number;\n onWeeksChange?: (weeks: number) => void;\n className?: string;\n}\n\nexport function RangeSelector({ weeks: weeksProp, onWeeksChange, className }: RangeSelectorProps) {\n const [internalWeeks, setInternalWeeks] = useState<number>(16);\n const currentWeeks = weeksProp ?? internalWeeks;\n\n const handleSelect = (w: number) => {\n if (onWeeksChange) {\n onWeeksChange(w);\n } else {\n setInternalWeeks(w);\n }\n };\n\n // ponytail: fixed min-h-11 min-w-11 hit area satisfies mobile touch baseline; add responsive compact prop when dense desktop tables request it.\n return (\n <div className={cn('inline-flex items-center gap-1.5 rounded-lg border border-border bg-card p-1', className)}>\n {[4, 8, 16].map((w) => (\n <button\n key={w}\n type=\"button\"\n onClick={() => handleSelect(w)}\n className={cn(\n 'min-h-11 min-w-11 px-3 py-2 text-xs rounded-md transition-colors flex items-center justify-center font-medium',\n currentWeeks === w\n ? 'bg-primary text-primary-foreground'\n : 'text-muted-foreground hover:text-foreground hover:bg-muted',\n )}\n >\n {w}w\n </button>\n ))}\n </div>\n );\n}\n","import { useEffect, useState } from 'react';\nimport { parseQuery, SharedQueryDocumentRunner, type QueryResult } from '@bitcobblers/wod-wiki-wql';\nimport type { QueryExecutor } from '../contracts/query';\nimport { createExecutorDocumentHost, outputToRunResult } from './chartData';\nconst DAY = 86_400_000;\nconst WEEK = 7 * DAY;\n\nexport interface AnalyticsQueryDef {\n key: string;\n query: string;\n}\n\nexport interface AnalyticsQueriesState {\n results: Record<string, QueryResult>;\n loading: boolean;\n}\n\nexport function useAnalyticsQueries(\n queries: AnalyticsQueryDef[],\n weeks: number,\n executor?: QueryExecutor,\n refreshKey = 0,\n preferredUnit?: string,\n onEnsureRollupFacts?: () => Promise<void>,\n): AnalyticsQueriesState {\n const [results, setResults] = useState<Record<string, QueryResult>>({});\n const [loading, setLoading] = useState(true);\n\n useEffect(() => {\n let cancelled = false;\n\n async function load() {\n if (!executor) {\n if (!cancelled) setLoading(false);\n return;\n }\n\n const now = Date.now();\n const rangeStart = now - weeks * WEEK;\n // Ticket 19 cutover: widget queries evaluate through the shared\n // document runner — family dispatch (aggregate/find/rows/pipeline),\n // AST-driven rollup-ensure, and one captured context per query run.\n const host = createExecutorDocumentHost(executor, {\n rangeStart,\n rangeEnd: now,\n preferredUnit,\n rollupEnsure: onEnsureRollupFacts,\n });\n const runner = new SharedQueryDocumentRunner(host);\n try {\n const settled = await Promise.all(\n queries.map(async (q) => {\n const res = await runner.run(q.query);\n const out = res.outputs[0];\n const error = out?.error ?? res.diagnostics[0];\n const parsed = parseQuery(q.query);\n const result = out\n ? outputToRunResult(parsed, { ...out, ...(error ? { error } : {}) })\n : outputToRunResult(parsed, { name: q.key, kind: 'aggregate', error });\n return [q.key, result] as const;\n }),\n );\n if (!cancelled) setResults(Object.fromEntries(settled));\n } finally {\n if (!cancelled) setLoading(false);\n }\n }\n\n void load();\n\n return () => {\n cancelled = true;\n };\n }, [queries, weeks, executor, refreshKey, preferredUnit, onEnsureRollupFacts]);\n\n return { results, loading };\n}\n","import { useCallback, useEffect, useState } from 'react';\nimport { cn } from '../utils/cn';\nimport { parseQuery } from '@bitcobblers/wod-wiki-wql';\nimport type { AnalyticsQueryDef } from './useAnalyticsQueries';\nimport type { StorageLike } from '../contracts/storage';\n\nexport const ANALYTICS_UNIT_STORAGE_KEY = 'wod.analytics.unit';\nexport const DEFAULT_ANALYTICS_UNIT = 'kg';\nexport type AnalyticsUnit = 'kg' | 'lb';\n\nconst VALID_UNITS: readonly AnalyticsUnit[] = ['kg', 'lb'];\n\nconst memoryStore = new Map<string, string>();\nconst inMemoryStorage: StorageLike = {\n getItem: (key: string) => memoryStore.get(key) ?? null,\n setItem: (key: string, value: string) => {\n memoryStore.set(key, value);\n },\n removeItem: (key: string) => {\n memoryStore.delete(key);\n },\n};\n\nconst unitListeners = new Set<() => void>();\nfunction notifyUnitChange() {\n for (const l of unitListeners) l();\n}\n\nfunction resolveStorage(custom?: StorageLike): StorageLike {\n return custom ?? inMemoryStorage;\n}\n\nexport function readStoredUnit(storage?: StorageLike): AnalyticsUnit {\n const store = resolveStorage(storage);\n const stored = store.getItem(ANALYTICS_UNIT_STORAGE_KEY);\n return (VALID_UNITS as readonly string[]).includes(stored ?? '')\n ? (stored as AnalyticsUnit)\n : DEFAULT_ANALYTICS_UNIT;\n}\n\nexport function useAnalyticsUnitPreference(customStorage?: StorageLike) {\n const storage = resolveStorage(customStorage);\n const [unit, setUnitState] = useState<AnalyticsUnit>(() => readStoredUnit(storage));\n\n useEffect(() => {\n setUnitState(readStoredUnit(storage));\n const sync = () => setUnitState(readStoredUnit(storage));\n unitListeners.add(sync);\n return () => {\n unitListeners.delete(sync);\n };\n }, [storage]);\n\n const setUnit = useCallback(\n (next: AnalyticsUnit) => {\n storage.setItem(ANALYTICS_UNIT_STORAGE_KEY, next);\n setUnitState(next);\n notifyUnitChange();\n },\n [storage],\n );\n\n const toggleUnit = useCallback(() => {\n const next: AnalyticsUnit = unit === 'kg' ? 'lb' : 'kg';\n setUnit(next);\n }, [unit, setUnit]);\n\n return { unit, setUnit, toggleUnit };\n}\n\nexport function getEffectiveAnalyticsUnit(\n query: string,\n preferredUnit: AnalyticsUnit,\n): { unit: AnalyticsUnit; forced: boolean } {\n const parsed = parseQuery(query);\n const displayUnit = 'displayUnit' in parsed ? parsed.displayUnit : undefined;\n if (displayUnit === 'lb' || displayUnit === 'kg') {\n return { unit: displayUnit as AnalyticsUnit, forced: true };\n }\n return { unit: preferredUnit, forced: false };\n}\n\nexport function getDashboardEffectiveUnit(\n queries: AnalyticsQueryDef[],\n preferredUnit: AnalyticsUnit,\n): { unit: AnalyticsUnit; forced: boolean } {\n const units = queries\n .map((q) => {\n const parsed = parseQuery(q.query);\n return 'displayUnit' in parsed ? parsed.displayUnit : undefined;\n })\n .filter((u): u is string => u === 'kg' || u === 'lb');\n\n const unique = Array.from(new Set(units));\n if (unique.length === 1) {\n return { unit: unique[0] as AnalyticsUnit, forced: true };\n }\n return { unit: preferredUnit, forced: false };\n}\nexport interface AnalyticsUnitPreferenceProps {\n className?: string;\n unit?: AnalyticsUnit;\n forced?: boolean;\n storage?: StorageLike;\n}\n\nexport function AnalyticsUnitPreference({ className, unit: unitProp, forced = false, storage }: AnalyticsUnitPreferenceProps) {\n const { unit: storedUnit, setUnit } = useAnalyticsUnitPreference(storage);\n const activeUnit = unitProp ?? storedUnit;\n\n return (\n <div\n data-testid=\"analytics-unit-preference\"\n className={cn('inline-flex items-center gap-1 text-xs', className)}\n >\n <div\n className={cn(\n 'inline-flex items-center rounded-lg border border-border bg-card p-1',\n forced && 'opacity-70',\n )}\n title={forced ? 'Unit forced by query directive in this view' : 'Display unit preference'}\n >\n <button\n type=\"button\"\n disabled={forced}\n onClick={() => setUnit('kg')}\n className={cn(\n 'px-2.5 py-1 rounded-md transition-colors text-xs font-mono',\n activeUnit === 'kg'\n ? 'bg-primary text-primary-foreground font-medium'\n : 'text-muted-foreground hover:text-foreground hover:bg-muted',\n forced && 'cursor-default hover:bg-transparent hover:text-muted-foreground',\n )}\n >\n kg\n </button>\n <button\n type=\"button\"\n disabled={forced}\n onClick={() => setUnit('lb')}\n className={cn(\n 'px-2.5 py-1 rounded-md transition-colors text-xs font-mono',\n activeUnit === 'lb'\n ? 'bg-primary text-primary-foreground font-medium'\n : 'text-muted-foreground hover:text-foreground hover:bg-muted',\n forced && 'cursor-default hover:bg-transparent hover:text-muted-foreground',\n )}\n >\n lb\n </button>\n </div>\n {forced && (\n <span className=\"text-[11px] text-muted-foreground italic\">forced by note</span>\n )}\n </div>\n );\n}\n","/**\n * Auto-generated build version metadata.\n * Do not edit directly; updated by scripts/stamp-version.ts\n */\nexport const VERSION = '0.50.2191';\nexport const GIT_SHA = '95c89db';\nexport const BUILD_TIME = '2026-11-03T03:31:31.921Z';\nexport const SEMVER = {\n version: '0.50.2191',\n gitSha: '95c89db',\n buildTime: '2026-11-03T03:31:31.921Z',\n} as const;\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bitcobblers/wod-wiki-ui",
3
- "version": "0.50.2189",
3
+ "version": "0.50.2191",
4
4
  "description": "Interactive presentation, CodeMirror editor extensions, and WQL diagram widgets",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -27,9 +27,9 @@
27
27
  "access": "public"
28
28
  },
29
29
  "dependencies": {
30
- "@bitcobblers/wod-wiki-core": "^0.50.2189",
31
- "@bitcobblers/wod-wiki-lang": "^0.50.2189",
32
- "@bitcobblers/wod-wiki-wql": "^0.50.2189",
30
+ "@bitcobblers/wod-wiki-core": "^0.50.2191",
31
+ "@bitcobblers/wod-wiki-lang": "^0.50.2191",
32
+ "@bitcobblers/wod-wiki-wql": "^0.50.2191",
33
33
  "clsx": "^2.1.1",
34
34
  "tailwind-merge": "^3.6.0",
35
35
  "@heroicons/react": "^2.2.0"