@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.
@@ -4,16 +4,16 @@ import { tags, styleTags, Tag as Tag$1 } from '@lezer/highlight';
4
4
  import { RangeSetBuilder, Facet, StateEffect, StateField, Prec, EditorState, EditorSelection, countColumn } from '@codemirror/state';
5
5
  import { linter, forEachDiagnostic } from '@codemirror/lint';
6
6
  import { whiteboardScriptLanguage, EFFORT_DISCIPLINES, createParser, whiteboardScriptCompletionSource, metricPresentation, whiteboardScript } from '@bitcobblers/wod-wiki-lang';
7
- import { mapNoteBlockChanges, validateNoteBlocks, reconcileNoteBlocks, parseDocumentSections, actionableSectionKind, patchFrontmatterProperty, MetricType } from '@bitcobblers/wod-wiki-core';
7
+ import { mapNoteBlockChanges, validateNoteBlocks, reconcileNoteBlocks, parseDocumentSections, actionableSectionKind, patchFrontmatterProperty, MetricType, parseFrontmatter } from '@bitcobblers/wod-wiki-core';
8
8
  export { blockContentId } from '@bitcobblers/wod-wiki-core';
9
9
  import { invertedEffects, history, defaultKeymap, historyKeymap, indentWithTab, cursorPageUp, cursorPageDown, cursorDocStart, cursorDocEnd } from '@codemirror/commands';
10
- import { WQL_FIND_TARGETS, WQL_SOURCE_VALUES, WQL_RESULT_PLANES, WQL_AGGREGATORS, WQL_VIRTUAL_DIMS, WQL_TAG_KEYS, WQL_METRIC_AGGREGATES, WQL_METRIC_FAMILIES, WQL_CALC_TARGETS, WQL_DISPLAY_UNITS, WQL_INTENSITY_TIERS, parseQueryWidgetSuffix, wqlLanguage, parseFrontmatterBody, wqlCompletionSource, parseQuery, isFindQuery, isDashboardWidgetType, unknownWidgetTypeMessage, resolveWidgetType, PLANNED_WIDGET_TYPES, notebookSlugOfSourceId, isPipelineQuery, wqlGroupingDimensions, isAggregateQuery, serialize, allowedFilterTypesForTarget, CONTENT_GROUPING_DIMENSIONS, substituteTokens, unknownTokensMessage, wql, WQL_NOTE_DEFAULT_SOURCES, wqlHighlightStyle, supportsWqlFilterKey } from '@bitcobblers/wod-wiki-wql';
10
+ import { WQL_FIND_TARGETS, WQL_SOURCE_VALUES, WQL_RESULT_PLANES, WQL_AGGREGATORS, WQL_VIRTUAL_DIMS, WQL_TAG_KEYS, WQL_METRIC_AGGREGATES, WQL_METRIC_FAMILIES, WQL_CALC_TARGETS, WQL_DISPLAY_UNITS, WQL_INTENSITY_TIERS, parseQueryWidgetSuffix, wqlLanguage, parseFrontmatterBody, wqlCompletionSource, parseQuery, isFindQuery, isDashboardWidgetType, unknownWidgetTypeMessage, resolveWidgetType, PLANNED_WIDGET_TYPES, notebookSlugOfSourceId, isPipelineQuery, wqlGroupingDimensions, isAggregateQuery, serialize, allowedFilterTypesForTarget, CONTENT_GROUPING_DIMENSIONS, parseDocument, substituteTokens, unknownTokensMessage, wql, SharedQueryDocumentRunner, WQL_NOTE_DEFAULT_SOURCES, wqlHighlightStyle, defaultTokenValues, extractDashboardTokens, supportsWqlFilterKey } from '@bitcobblers/wod-wiki-wql';
11
11
  export { CONTENT_GROUPING_DIMENSIONS } from '@bitcobblers/wod-wiki-wql';
12
12
  import { snippet, autocompletion, CompletionContext, closeBrackets, closeBracketsKeymap, completionStatus, startCompletion, moveCompletionSelection, setSelectedCompletion, currentCompletions, acceptCompletion, closeCompletion, ifNotIn, completeFromList, snippetCompletion } from '@codemirror/autocomplete';
13
13
  import { createRoot } from 'react-dom/client';
14
14
  import * as React2 from 'react';
15
15
  import React2__default, { createElement, useMemo, useState, useCallback, useEffect, useId, useRef, useSyncExternalStore } from 'react';
16
- import { Tag, ListFilter, ChartNoAxesCombined, Search, X, AlertTriangle, CheckCircle2, AlertCircle, Edit3, ArrowUp, ArrowDown, Check } from 'lucide-react';
16
+ import { Tag, ListFilter, ChartNoAxesCombined, Search, X, AlertTriangle, CheckCircle2, AlertCircle, ChartBar, Edit3, ArrowUp, ArrowDown, Check } from 'lucide-react';
17
17
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
18
18
  import { clsx } from 'clsx';
19
19
  import { twMerge } from 'tailwind-merge';
@@ -433,6 +433,7 @@ function deriveState(state, rawContent, sourceRevision, lineage, savedVersions,
433
433
  contentId: section.contentId,
434
434
  queryType: query?.type,
435
435
  queryError: query?.error,
436
+ queryAttributes: query?.attributes,
436
437
  embed: section.embed
437
438
  };
438
439
  });
@@ -4126,6 +4127,108 @@ function RowsResultsChrome({
4126
4127
  )
4127
4128
  ] });
4128
4129
  }
4130
+
4131
+ // src/widgets/chartPalette.ts
4132
+ var SERIES_COLORS = [
4133
+ "hsl(var(--metric-time))",
4134
+ "hsl(var(--metric-effort))",
4135
+ "hsl(var(--metric-rep))",
4136
+ "hsl(var(--metric-rounds))",
4137
+ "hsl(var(--metric-distance))",
4138
+ "hsl(var(--metric-resistance))",
4139
+ "hsl(var(--metric-action))",
4140
+ "hsl(var(--primary))"
4141
+ ];
4142
+ function createExecutorDocumentHost(executor, range = {}) {
4143
+ return {
4144
+ runAggregate: (queryText, overrides) => executor.runQuery(queryText, {
4145
+ context: overrides.context,
4146
+ rangeStart: range.rangeStart,
4147
+ rangeEnd: range.rangeEnd,
4148
+ preferredUnit: range.preferredUnit
4149
+ }),
4150
+ runFind: async (queryText, context) => {
4151
+ const parsed = parseQuery(queryText);
4152
+ if (!isFindQuery(parsed)) return { kind: "find", error: `not a content query: ${queryText}` };
4153
+ const res = await executor.runFind(parsed, {
4154
+ context,
4155
+ range: range.rangeStart !== void 0 ? { start: range.rangeStart, end: range.rangeEnd ?? Number.MAX_SAFE_INTEGER, endExclusive: false } : void 0
4156
+ });
4157
+ return {
4158
+ kind: "find",
4159
+ notes: res.notes,
4160
+ blocks: res.blocks,
4161
+ efforts: res.efforts,
4162
+ containers: res.containers,
4163
+ runs: res.runs,
4164
+ table: res.table
4165
+ };
4166
+ },
4167
+ runPipeline: async (queryText, context) => {
4168
+ const res = await executor.runPipeline?.(queryText, {
4169
+ context,
4170
+ rangeStart: range.rangeStart,
4171
+ rangeEnd: range.rangeEnd,
4172
+ preferredUnit: range.preferredUnit
4173
+ });
4174
+ return res ?? { error: "pipeline queries are not supported by this executor" };
4175
+ },
4176
+ rollupEnsure: range.rollupEnsure
4177
+ };
4178
+ }
4179
+ function toChartResult(input) {
4180
+ const parsed = input.parsed;
4181
+ const series = input.series ?? [];
4182
+ const scalar = series.length === 1 && series[0].points.length === 1 ? series[0].points[0].value : void 0;
4183
+ return {
4184
+ parsed,
4185
+ series,
4186
+ stages: { selected: 0, buckets: 0, aggregated: 0, groups: 0 },
4187
+ matched: [],
4188
+ ...scalar !== void 0 ? { scalar } : {},
4189
+ ...input.unit !== void 0 ? { unit: input.unit } : {},
4190
+ ...input.error ? { error: input.error } : {},
4191
+ ...input.table ? { table: input.table } : {}
4192
+ };
4193
+ }
4194
+ function outputToRunResult(parsed, out) {
4195
+ const base = toChartResult({
4196
+ parsed,
4197
+ series: out.series,
4198
+ unit: out.unit,
4199
+ error: out.error,
4200
+ table: out.table
4201
+ });
4202
+ return Object.assign(base, {
4203
+ ...out.notes ? { notes: out.notes } : {},
4204
+ ...out.blocks ? { blocks: out.blocks } : {},
4205
+ ...out.efforts ? { efforts: out.efforts } : {},
4206
+ ...out.containers ? { containers: out.containers } : {},
4207
+ ...out.runs ? { runs: out.runs } : {},
4208
+ ...out.chart ? { chart: out.chart } : {}
4209
+ });
4210
+ }
4211
+ function mergeSeries(series) {
4212
+ const map = /* @__PURE__ */ new Map();
4213
+ for (const s of series) {
4214
+ for (const p of s.points) {
4215
+ if (!map.has(p.ts)) map.set(p.ts, { ts: p.ts });
4216
+ map.get(p.ts)[s.label] = p.value;
4217
+ }
4218
+ }
4219
+ return Array.from(map.values()).sort((a, b) => a.ts - b.ts);
4220
+ }
4221
+ function compactNumber(value) {
4222
+ const n = typeof value === "number" ? value : Number(value);
4223
+ if (Math.abs(n) >= 1e3) return `${(n / 1e3).toFixed(1)}k`;
4224
+ return String(n);
4225
+ }
4226
+ function formatTimestamp(ts) {
4227
+ return new Date(ts).toLocaleDateString("en-US", { month: "short", day: "numeric" });
4228
+ }
4229
+ function tooltipTimestamp(ts) {
4230
+ return new Date(ts).toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric" });
4231
+ }
4129
4232
  function useChartShape(result) {
4130
4233
  return useMemo(() => {
4131
4234
  if (!result) return { kind: "empty" };
@@ -4176,53 +4279,6 @@ function QueryValue({ result, unit: unitProp, label, thresholds }) {
4176
4279
  ] })
4177
4280
  ] });
4178
4281
  }
4179
-
4180
- // src/widgets/chartData.ts
4181
- function toChartResult(input) {
4182
- const parsed = input.parsed;
4183
- return {
4184
- parsed,
4185
- series: input.series ?? [],
4186
- stages: { selected: 0, buckets: 0, aggregated: 0, groups: 0 },
4187
- matched: [],
4188
- ...input.unit !== void 0 ? { unit: input.unit } : {},
4189
- ...input.error ? { error: input.error } : {},
4190
- ...input.table ? { table: input.table } : {}
4191
- };
4192
- }
4193
- function mergeSeries(series) {
4194
- const map = /* @__PURE__ */ new Map();
4195
- for (const s of series) {
4196
- for (const p of s.points) {
4197
- if (!map.has(p.ts)) map.set(p.ts, { ts: p.ts });
4198
- map.get(p.ts)[s.label] = p.value;
4199
- }
4200
- }
4201
- return Array.from(map.values()).sort((a, b) => a.ts - b.ts);
4202
- }
4203
- function compactNumber(value) {
4204
- const n = typeof value === "number" ? value : Number(value);
4205
- if (Math.abs(n) >= 1e3) return `${(n / 1e3).toFixed(1)}k`;
4206
- return String(n);
4207
- }
4208
- function formatTimestamp(ts) {
4209
- return new Date(ts).toLocaleDateString("en-US", { month: "short", day: "numeric" });
4210
- }
4211
- function tooltipTimestamp(ts) {
4212
- return new Date(ts).toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric" });
4213
- }
4214
-
4215
- // src/widgets/chartPalette.ts
4216
- var SERIES_COLORS = [
4217
- "hsl(var(--metric-time))",
4218
- "hsl(var(--metric-effort))",
4219
- "hsl(var(--metric-rep))",
4220
- "hsl(var(--metric-rounds))",
4221
- "hsl(var(--metric-distance))",
4222
- "hsl(var(--metric-resistance))",
4223
- "hsl(var(--metric-action))",
4224
- "hsl(var(--primary))"
4225
- ];
4226
4282
  function WqlTimeseries({ result, unit: unitProp }) {
4227
4283
  const shape = useChartShape(result);
4228
4284
  const data2 = useMemo(() => mergeSeries(result.series), [result]);
@@ -4802,16 +4858,52 @@ function ZoneDistribution({ result, params, attributes }) {
4802
4858
  }
4803
4859
  );
4804
4860
  }
4805
- function WidgetChart({ type, result, label, unit, params, attributes }) {
4861
+ function inferChartTypeFromResult(result) {
4862
+ if (!result || result.series.length === 0) return "table";
4863
+ if (result.series.length === 1 && result.series[0].points.length === 1) return "value";
4864
+ const hasTimeAxis = result.series.some((s) => s.points.length > 1);
4865
+ return hasTimeAxis ? "timeseries" : "bar";
4866
+ }
4867
+ function WidgetChart({
4868
+ type,
4869
+ result,
4870
+ label,
4871
+ unit,
4872
+ params,
4873
+ attributes,
4874
+ inferDefaultShape = false,
4875
+ onOpenNote,
4876
+ noteHref
4877
+ }) {
4806
4878
  if (type !== "" && !isDashboardWidgetType(type)) {
4807
4879
  return /* @__PURE__ */ jsx(WidgetProblemBadge, { message: unknownWidgetTypeMessage(type) });
4808
4880
  }
4809
- const resolved = resolveWidgetType(type);
4810
- if (resolved === "table" && result?.table) {
4811
- return /* @__PURE__ */ jsx("div", { className: "w-full h-full min-h-[160px]", children: /* @__PURE__ */ jsx(TabularTable, { table: result.table }) });
4881
+ const resolved = type === "" && inferDefaultShape ? inferChartTypeFromResult(result) : resolveWidgetType(type);
4882
+ const runResult = result;
4883
+ const parsedFamily = runResult?.parsed?.family;
4884
+ const hasContentItems = parsedFamily === "find" || Boolean(runResult?.notes?.length || runResult?.blocks?.length || runResult?.efforts?.length || runResult?.runs?.length);
4885
+ if (resolved === "table" && runResult?.table) {
4886
+ return /* @__PURE__ */ jsx("div", { className: "w-full h-full min-h-[160px]", children: /* @__PURE__ */ jsx(TabularTable, { table: runResult.table }) });
4887
+ }
4888
+ if (resolved === "table" && hasContentItems && (!result || !("series" in result) || result.series.length === 0)) {
4889
+ return /* @__PURE__ */ jsx("div", { className: "w-full h-full min-h-[160px]", children: /* @__PURE__ */ jsx(
4890
+ ContentRecordsTable,
4891
+ {
4892
+ result: runResult,
4893
+ onOpenNote,
4894
+ noteHref
4895
+ }
4896
+ ) });
4812
4897
  }
4813
- if (resolved === "list" && result?.parsed?.family === "find") {
4814
- return /* @__PURE__ */ jsx("div", { className: "w-full h-full min-h-[160px]", children: /* @__PURE__ */ jsx(ThingList, { result }) });
4898
+ if (resolved === "list" && hasContentItems) {
4899
+ return /* @__PURE__ */ jsx("div", { className: "w-full h-full min-h-[160px]", children: /* @__PURE__ */ jsx(
4900
+ ThingList,
4901
+ {
4902
+ result: runResult,
4903
+ onOpenNote,
4904
+ noteHref
4905
+ }
4906
+ ) });
4815
4907
  }
4816
4908
  if (!result || result.parsed.error || "series" in result && result.series.length === 0) {
4817
4909
  return /* @__PURE__ */ jsx(WqlEmptyState, { result });
@@ -4938,9 +5030,138 @@ function TabularTable({ table }) {
4938
5030
  ] }, group.key)) : table.rows.map((row, i) => /* @__PURE__ */ jsx("tr", { className: "border-b border-border/40 hover:bg-muted/20", children: table.columns.map((c) => /* @__PURE__ */ jsx("td", { className: "py-1 px-2 whitespace-nowrap", children: String(row[c.name] ?? "") }, c.name)) }, i)) })
4939
5031
  ] }) });
4940
5032
  }
4941
- function ThingList({ result }) {
4942
- const items = result.notes?.length ? result.notes : result.blocks?.length ? result.blocks : result.efforts?.length ? result.efforts : result.runs ?? [];
4943
- return /* @__PURE__ */ jsx("div", { className: "h-full overflow-auto space-y-1", "data-testid": "thing-list", children: items.map((item, i) => /* @__PURE__ */ jsx("div", { className: "py-1 px-2 text-xs border-b border-border/40 truncate", children: item.title ?? item.label ?? item.noteTitle ?? item.id ?? item.resultId }, item.id ?? item.resultId ?? i)) });
5033
+ function containerPageHref(container) {
5034
+ if (!container || container.kind !== "page") return void 0;
5035
+ const { slug, date } = container;
5036
+ if (slug) return `/page/${encodeURIComponent(slug)}`;
5037
+ if (date) return `/day/${encodeURIComponent(date)}`;
5038
+ return void 0;
5039
+ }
5040
+ function formatItemDate(item) {
5041
+ const raw = item.date ?? item.timestamp ?? item.createdAt;
5042
+ if (raw === void 0 || raw === null || raw === "") return "\u2014";
5043
+ if (typeof raw === "string" && /^\d{4}-\d{2}-\d{2}/.test(raw)) return raw.slice(0, 10);
5044
+ const ms = typeof raw === "number" ? raw : Date.parse(String(raw));
5045
+ if (!Number.isFinite(ms) || ms <= 0) return "\u2014";
5046
+ return new Date(ms).toISOString().slice(0, 10);
5047
+ }
5048
+ function ContentRecordsTable({
5049
+ result,
5050
+ onOpenNote,
5051
+ noteHref
5052
+ }) {
5053
+ const parsed = result?.parsed;
5054
+ const target = parsed?.target ?? (parsed?.source?.kind === "query" && parsed?.source?.query?.family === "find" ? parsed.source.query.target ?? "note" : result?.blocks?.length ? "block" : "note");
5055
+ const isBlock = target === "block" || Boolean(result?.blocks?.length && !result?.notes?.length);
5056
+ const rawItems = result?.notes?.length ? result.notes : result?.blocks?.length ? result.blocks : result?.efforts?.length ? result.efforts : result?.runs ?? [];
5057
+ const items = rawItems;
5058
+ if (items.length === 0) {
5059
+ return /* @__PURE__ */ jsxs("div", { className: "text-xs text-muted-foreground py-4 text-center", "data-testid": "content-records-table-empty", children: [
5060
+ "No ",
5061
+ target,
5062
+ "s matched this query."
5063
+ ] });
5064
+ }
5065
+ const containers = result?.containers;
5066
+ return /* @__PURE__ */ jsx("div", { className: "h-full overflow-auto border-y border-border/60 bg-card/40", "data-testid": "content-records-table", children: /* @__PURE__ */ jsxs("table", { className: "w-full text-left border-collapse text-xs", children: [
5067
+ /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { className: "border-b border-border bg-muted/30 text-[11px] font-semibold uppercase tracking-wider text-muted-foreground", children: [
5068
+ /* @__PURE__ */ jsx("th", { className: "py-2 px-3", children: "Title" }),
5069
+ /* @__PURE__ */ jsx("th", { className: "py-2 px-3", children: "Kind" }),
5070
+ /* @__PURE__ */ jsx("th", { className: "py-2 px-3", children: "Tags / Details" }),
5071
+ /* @__PURE__ */ jsx("th", { className: "py-2 px-3 text-right", children: "Date" })
5072
+ ] }) }),
5073
+ /* @__PURE__ */ jsx("tbody", { className: "divide-y divide-border/40", children: items.map((item, i) => {
5074
+ const id = item.id ?? item.resultId ?? String(i);
5075
+ const typedItem = { ...item, id, title: item.title ?? item.label ?? item.noteTitle };
5076
+ const title = isBlock ? typedItem.title || typedItem.blockContentId || id : typedItem.title || id;
5077
+ const href = containers ? containerPageHref(containers[id]) ?? (noteHref ? noteHref(typedItem) : void 0) : noteHref ? noteHref(typedItem) : void 0;
5078
+ const kindLabel = item.type ?? item.dataType ?? item.baseAttributes?.discipline ?? target;
5079
+ const detail = Array.isArray(item.tags) && item.tags.length > 0 ? item.tags.map((t) => `#${t}`).join(" ") : item.baseAttributes?.met ? `MET ${item.baseAttributes.met}` : item.sourceId ?? "\u2014";
5080
+ return /* @__PURE__ */ jsxs("tr", { className: "hover:bg-muted/30 transition-colors", children: [
5081
+ /* @__PURE__ */ jsx("td", { className: "py-2 px-3 font-medium truncate max-w-[260px]", children: href ? /* @__PURE__ */ jsx(
5082
+ "a",
5083
+ {
5084
+ href,
5085
+ onClick: (e) => {
5086
+ if (onOpenNote) {
5087
+ e.preventDefault();
5088
+ onOpenNote(typedItem);
5089
+ }
5090
+ },
5091
+ className: "text-primary hover:underline",
5092
+ children: title
5093
+ }
5094
+ ) : onOpenNote ? /* @__PURE__ */ jsx(
5095
+ "button",
5096
+ {
5097
+ type: "button",
5098
+ onClick: () => onOpenNote(typedItem),
5099
+ className: "text-left text-primary hover:underline",
5100
+ children: title
5101
+ }
5102
+ ) : /* @__PURE__ */ jsx("span", { className: "text-foreground", children: title }) }),
5103
+ /* @__PURE__ */ jsx("td", { className: "py-2 px-3 font-mono text-[11px] text-muted-foreground", children: kindLabel }),
5104
+ /* @__PURE__ */ jsx("td", { className: "py-2 px-3 font-mono text-[11px] text-muted-foreground truncate max-w-[200px]", children: detail }),
5105
+ /* @__PURE__ */ jsx("td", { className: "py-2 px-3 font-mono text-[11px] text-muted-foreground text-right tabular-nums", children: formatItemDate(item) })
5106
+ ] }, id);
5107
+ }) })
5108
+ ] }) });
5109
+ }
5110
+ function ThingList({
5111
+ result,
5112
+ onOpenNote,
5113
+ noteHref
5114
+ }) {
5115
+ const parsed = result?.parsed;
5116
+ const target = parsed?.target ?? (parsed?.source?.kind === "query" && parsed?.source?.query?.family === "find" ? parsed.source.query.target ?? "note" : result?.blocks?.length ? "block" : "note");
5117
+ const isBlock = target === "block" || Boolean(result?.blocks?.length && !result?.notes?.length);
5118
+ const rawItems = result?.notes?.length ? result.notes : result?.blocks?.length ? result.blocks : result?.efforts?.length ? result.efforts : result?.runs ?? [];
5119
+ const items = rawItems;
5120
+ if (items.length === 0) {
5121
+ return /* @__PURE__ */ jsxs("div", { className: "text-xs text-muted-foreground py-2", "data-testid": "thing-list", children: [
5122
+ "No ",
5123
+ target,
5124
+ "s matched this query."
5125
+ ] });
5126
+ }
5127
+ return /* @__PURE__ */ jsxs("div", { className: "h-full overflow-auto space-y-1", "data-testid": "thing-list", children: [
5128
+ /* @__PURE__ */ jsxs("div", { className: "text-[11px] font-medium text-muted-foreground mb-1", children: [
5129
+ items.length,
5130
+ " ",
5131
+ target,
5132
+ items.length === 1 ? "" : "s",
5133
+ " matched"
5134
+ ] }),
5135
+ /* @__PURE__ */ jsx("ul", { className: "space-y-0.5 max-h-48 overflow-y-auto font-mono text-xs", children: items.map((item, i) => {
5136
+ const id = item.id ?? item.resultId ?? String(i);
5137
+ const typedItem = { ...item, id, title: item.title ?? item.label ?? item.noteTitle };
5138
+ const title = isBlock ? typedItem.title || typedItem.blockContentId || id : typedItem.title || id;
5139
+ const containers = result?.containers;
5140
+ const href = containers ? containerPageHref(containers[id]) ?? (noteHref ? noteHref(typedItem) : void 0) : noteHref ? noteHref(typedItem) : void 0;
5141
+ return /* @__PURE__ */ jsx("li", { className: "rounded hover:bg-muted/50 truncate", children: href ? /* @__PURE__ */ jsx(
5142
+ "a",
5143
+ {
5144
+ href,
5145
+ onClick: (e) => {
5146
+ if (onOpenNote) {
5147
+ e.preventDefault();
5148
+ onOpenNote(typedItem);
5149
+ }
5150
+ },
5151
+ className: "block py-0.5 px-1.5 text-primary hover:underline truncate",
5152
+ children: title
5153
+ }
5154
+ ) : onOpenNote ? /* @__PURE__ */ jsx(
5155
+ "button",
5156
+ {
5157
+ type: "button",
5158
+ onClick: () => onOpenNote(typedItem),
5159
+ className: "w-full text-left py-0.5 px-1.5 text-primary hover:underline truncate",
5160
+ children: title
5161
+ }
5162
+ ) : /* @__PURE__ */ jsx("span", { className: "block py-0.5 px-1.5 text-foreground truncate", children: title }) }, id);
5163
+ }) })
5164
+ ] });
4944
5165
  }
4945
5166
 
4946
5167
  // src/utils/blockQueryPatcher.ts
@@ -7345,21 +7566,31 @@ function QueryBlockView({
7345
7566
  widgetError,
7346
7567
  tokenValues,
7347
7568
  attributes,
7569
+ onAddToDashboard,
7348
7570
  onCaptureRpe,
7349
7571
  onOpenNote,
7350
7572
  noteHref
7351
7573
  }) {
7352
7574
  const [isModalOpen, setIsModalOpen] = useState(false);
7353
7575
  const extracted = useMemo(() => extractBlockQueries(query), [query]);
7576
+ const rawBody = useMemo(() => {
7577
+ const trimmed = query.trim();
7578
+ if (trimmed.includes("\n")) {
7579
+ const docParsed = parseDocument(trimmed);
7580
+ if (!docParsed.doc.degenerate && docParsed.diagnostics.length === 0 && docParsed.doc.assignments.length > 0) {
7581
+ return trimmed;
7582
+ }
7583
+ }
7584
+ return extracted.length > 0 ? extracted[0].query : query;
7585
+ }, [extracted, query]);
7354
7586
  const { query: effectiveQuery, missing } = useMemo(() => {
7355
- const raw = extracted.length > 0 ? extracted[0].query : query;
7356
- const subQuery = substituteTokens(raw, tokenValues ?? {});
7587
+ const subQuery = substituteTokens(rawBody, tokenValues ?? {});
7357
7588
  const missingSet = new Set(subQuery.missing);
7358
7589
  for (const value of Object.values(attributes ?? {})) {
7359
7590
  for (const m of substituteTokens(value, tokenValues ?? {}).missing) missingSet.add(m);
7360
7591
  }
7361
7592
  return { query: subQuery.query, missing: [...missingSet] };
7362
- }, [extracted, query, tokenValues, attributes]);
7593
+ }, [rawBody, tokenValues, attributes]);
7363
7594
  const resolvedAttributes = useMemo(() => {
7364
7595
  const out = {};
7365
7596
  for (const [key, value] of Object.entries(attributes ?? {})) {
@@ -7367,11 +7598,12 @@ function QueryBlockView({
7367
7598
  }
7368
7599
  return out;
7369
7600
  }, [attributes, tokenValues]);
7601
+ const docParse = useMemo(() => parseDocument(effectiveQuery), [effectiveQuery]);
7602
+ const isMultiLineDoc = !docParse.doc.degenerate && docParse.doc.assignments.length > 0;
7370
7603
  const parsed = useMemo(() => parseQuery(effectiveQuery), [effectiveQuery]);
7604
+ const syntaxError = isMultiLineDoc ? docParse.diagnostics[0] : parsed.error;
7371
7605
  const unknownType = widgetType != null && widgetType !== "" && !isDashboardWidgetType(widgetType);
7372
- const [result, setResult] = useState(void 0);
7373
- const [findResult, setFindResult] = useState(void 0);
7374
- const [pipelineResult, setPipelineResult] = useState(void 0);
7606
+ const [runResult, setRunResult] = useState(void 0);
7375
7607
  const [runError, setRunError] = useState(void 0);
7376
7608
  const [rowsRefreshKey, setRowsRefreshKey] = useState(0);
7377
7609
  useEffect(() => {
@@ -7386,59 +7618,45 @@ function QueryBlockView({
7386
7618
  }, [onResultSaved]);
7387
7619
  useEffect(() => {
7388
7620
  let cancelled = false;
7621
+ let retryTimer;
7389
7622
  setRunError(void 0);
7390
- setResult(void 0);
7391
- setFindResult(void 0);
7392
- setPipelineResult(void 0);
7393
- if (parsed.error) return;
7394
- if (widgetError || unknownType || missing.length > 0) return;
7395
- if (!executor) return;
7396
- if (isFindQuery(parsed)) {
7397
- let retryTimer;
7398
- const isSession = parsed.target === "session";
7399
- const executeFind = (attemptCount) => {
7400
- void executor.runFind(parsed).then((res) => {
7401
- if (cancelled) return;
7402
- setFindResult(res);
7403
- if (isSession && (res.runs ?? []).length === 0 && attemptCount < 4) {
7404
- const delays = [50, 150, 350, 750];
7405
- retryTimer = setTimeout(() => {
7406
- if (!cancelled) executeFind(attemptCount + 1);
7407
- }, delays[attemptCount] ?? 500);
7408
- }
7409
- }).catch((err) => {
7410
- if (!cancelled) setRunError(err instanceof Error ? err.message : String(err));
7411
- });
7412
- };
7413
- executeFind(0);
7414
- return () => {
7415
- cancelled = true;
7416
- clearTimeout(retryTimer);
7417
- };
7418
- }
7419
- if (isPipelineQuery(parsed)) {
7420
- if (!executor.runPipeline) {
7421
- setRunError("pipeline queries are not supported by this executor");
7422
- return;
7423
- }
7424
- void executor.runPipeline(effectiveQuery).then((res) => {
7425
- if (!cancelled) setPipelineResult(res);
7623
+ setRunResult(void 0);
7624
+ if (syntaxError || widgetError || unknownType || missing.length > 0 || !executor) return;
7625
+ const isSessionFind = !isMultiLineDoc && isFindQuery(parsed) && parsed.target === "session";
7626
+ const host = createExecutorDocumentHost(executor);
7627
+ const executeRun = (attemptCount) => {
7628
+ const runner = new SharedQueryDocumentRunner(host);
7629
+ void runner.run(effectiveQuery).then((res) => {
7630
+ if (cancelled) return;
7631
+ const out = res.outputs[0];
7632
+ if (!out) {
7633
+ setRunError(res.diagnostics[0] ?? "widget produced no output");
7634
+ return;
7635
+ }
7636
+ const err = out.error ?? res.diagnostics[0];
7637
+ if (err) {
7638
+ setRunError(err);
7639
+ return;
7640
+ }
7641
+ const effectiveParsed = isMultiLineDoc ? out.kind === "find" ? { family: "find", raw: effectiveQuery, target: "note", filters: [] } : { family: "aggregate", raw: effectiveQuery, agg: "sum", metric: out.name, filters: [], groupBy: out.groupBy ?? [] } : parsed;
7642
+ const shaped = outputToRunResult(effectiveParsed, out);
7643
+ setRunResult(shaped);
7644
+ if (isSessionFind && (shaped.runs ?? []).length === 0 && attemptCount < 4) {
7645
+ const delays = [50, 150, 350, 750];
7646
+ retryTimer = setTimeout(() => {
7647
+ if (!cancelled) executeRun(attemptCount + 1);
7648
+ }, delays[attemptCount] ?? 500);
7649
+ }
7426
7650
  }).catch((err) => {
7427
7651
  if (!cancelled) setRunError(err instanceof Error ? err.message : String(err));
7428
7652
  });
7429
- return () => {
7430
- cancelled = true;
7431
- };
7432
- }
7433
- void executor.runQuery(effectiveQuery).then((res) => {
7434
- if (!cancelled) setResult(res);
7435
- }).catch((err) => {
7436
- if (!cancelled) setRunError(err instanceof Error ? err.message : String(err));
7437
- });
7653
+ };
7654
+ executeRun(0);
7438
7655
  return () => {
7439
7656
  cancelled = true;
7657
+ clearTimeout(retryTimer);
7440
7658
  };
7441
- }, [effectiveQuery, parsed, widgetError, unknownType, missing, executor, rowsRefreshKey]);
7659
+ }, [effectiveQuery, parsed, isMultiLineDoc, syntaxError, widgetError, unknownType, missing, executor, rowsRefreshKey]);
7442
7660
  const canEdit = onSaveQuery !== void 0 && !readOnly;
7443
7661
  const onEdit = canEdit ? () => setIsModalOpen(true) : void 0;
7444
7662
  const resultId = useMemo(() => {
@@ -7446,50 +7664,32 @@ function QueryBlockView({
7446
7664
  const resultFilter = parsed.filters.find((f) => f.key === "result");
7447
7665
  return resultFilter?.values[0]?.value;
7448
7666
  }, [parsed]);
7667
+ const fallbackLabel = useMemo(() => {
7668
+ if (!isFindQuery(parsed) && !isPipelineQuery(parsed)) return parsed.metric;
7669
+ return parsed.raw;
7670
+ }, [parsed]);
7449
7671
  return /* @__PURE__ */ jsxs("div", { "data-testid": "query-block-view", children: [
7450
- widgetError ? /* @__PURE__ */ jsx(QueryBlockShell, { onEdit, readOnly, children: /* @__PURE__ */ jsx("div", { className: "text-xs text-destructive bg-destructive/10 border border-destructive/20 rounded p-2", children: widgetError }) }) : unknownType ? /* @__PURE__ */ jsx(QueryBlockShell, { onEdit, readOnly, children: /* @__PURE__ */ jsx(WidgetProblemBadge, { message: unknownWidgetTypeMessage(widgetType) }) }) : missing.length > 0 ? /* @__PURE__ */ jsx(QueryBlockShell, { onEdit, readOnly, children: /* @__PURE__ */ jsx(WidgetProblemBadge, { message: unknownTokensMessage(missing) }) }) : parsed.error ? /* @__PURE__ */ jsx(QueryBlockShell, { onEdit, readOnly, children: /* @__PURE__ */ jsxs("div", { className: "text-xs text-destructive bg-destructive/10 border border-destructive/20 rounded p-2", children: [
7672
+ widgetError ? /* @__PURE__ */ jsx(QueryBlockShell, { onEdit, readOnly, onAddToDashboard, children: /* @__PURE__ */ jsx("div", { className: "text-xs text-destructive bg-destructive/10 border border-destructive/20 rounded p-2", children: widgetError }) }) : unknownType ? /* @__PURE__ */ jsx(QueryBlockShell, { onEdit, readOnly, onAddToDashboard, children: /* @__PURE__ */ jsx(WidgetProblemBadge, { message: unknownWidgetTypeMessage(widgetType) }) }) : missing.length > 0 ? /* @__PURE__ */ jsx(QueryBlockShell, { onEdit, readOnly, onAddToDashboard, children: /* @__PURE__ */ jsx(WidgetProblemBadge, { message: unknownTokensMessage(missing) }) }) : syntaxError ? /* @__PURE__ */ jsx(QueryBlockShell, { onEdit, readOnly, onAddToDashboard, children: /* @__PURE__ */ jsxs("div", { className: "text-xs text-destructive bg-destructive/10 border border-destructive/20 rounded p-2", children: [
7451
7673
  "Query syntax error: ",
7452
- parsed.error
7453
- ] }) }) : runError ? /* @__PURE__ */ jsx(QueryBlockShell, { onEdit, readOnly, children: /* @__PURE__ */ jsxs("div", { className: "text-xs text-destructive bg-destructive/10 border border-destructive/20 rounded p-2", children: [
7674
+ syntaxError
7675
+ ] }) }) : runError ? /* @__PURE__ */ jsx(QueryBlockShell, { onEdit, readOnly, onAddToDashboard, children: /* @__PURE__ */ jsxs("div", { className: "text-xs text-destructive bg-destructive/10 border border-destructive/20 rounded p-2", children: [
7454
7676
  "Query execution error: ",
7455
7677
  runError
7456
- ] }) }) : isFindQuery(parsed) ? /* @__PURE__ */ jsx(QueryBlockShell, { onEdit, readOnly, children: findResult?.table ? /* @__PURE__ */ jsx(WidgetChart, { type: "table", result: findResult }) : resultId && findResult?.runs && findResult.runs.length > 0 ? /* @__PURE__ */ jsx(
7457
- RowsResultsChrome,
7678
+ ] }) }) : /* @__PURE__ */ jsx(QueryBlockShell, { onEdit, readOnly, onAddToDashboard, children: /* @__PURE__ */ jsx(
7679
+ QueryBlockBody,
7458
7680
  {
7681
+ parsed,
7682
+ result: runResult,
7459
7683
  resultId,
7460
- sessionResult: findResult,
7684
+ widgetType,
7685
+ attributes: resolvedAttributes,
7686
+ fallbackLabel,
7461
7687
  executor,
7462
7688
  onCaptureRpe,
7463
- onCaptured: () => setRowsRefreshKey((k) => k + 1)
7464
- }
7465
- ) : findResult?.runs ? /* @__PURE__ */ jsx(RowsTable, { result: findResult }) : findResult ? /* @__PURE__ */ jsx(
7466
- FindResultList,
7467
- {
7468
- target: parsed.target,
7469
- notes: findResult.notes,
7470
- blocks: findResult.blocks,
7471
- containers: findResult.containers,
7689
+ onCaptured: () => setRowsRefreshKey((k) => k + 1),
7472
7690
  onOpenNote,
7473
7691
  noteHref
7474
7692
  }
7475
- ) : /* @__PURE__ */ jsx("div", { className: "text-xs text-muted-foreground py-2", children: "Loading rows\u2026" }) }) : isPipelineQuery(parsed) ? /* @__PURE__ */ jsx(QueryBlockShell, { onEdit, readOnly, children: /* @__PURE__ */ jsx(
7476
- PipelineResultView,
7477
- {
7478
- result: pipelineResult,
7479
- onOpenNote,
7480
- noteHref,
7481
- widgetType,
7482
- attributes: resolvedAttributes,
7483
- fallbackLabel: parsed.raw
7484
- }
7485
- ) }) : /* @__PURE__ */ jsx(QueryBlockShell, { onEdit, readOnly, children: /* @__PURE__ */ jsx(
7486
- AnalyticsChart,
7487
- {
7488
- result,
7489
- metric: parsed.metric,
7490
- widgetType,
7491
- attributes: resolvedAttributes
7492
- }
7493
7693
  ) }),
7494
7694
  canEdit && /* @__PURE__ */ jsx(
7495
7695
  WqlQueryInspectorModal,
@@ -7503,179 +7703,127 @@ function QueryBlockView({
7503
7703
  )
7504
7704
  ] });
7505
7705
  }
7506
- function AnalyticsChart({
7706
+ function QueryBlockBody({
7707
+ parsed,
7507
7708
  result,
7508
- metric,
7709
+ resultId,
7509
7710
  widgetType,
7510
- attributes
7511
- }) {
7512
- const shape = useChartShape(result);
7513
- if (widgetType != null && widgetType !== "") {
7514
- return /* @__PURE__ */ jsx(WidgetChart, { type: widgetType, result, label: metric, attributes });
7515
- }
7516
- if (shape.kind === "scalar") {
7517
- return /* @__PURE__ */ jsx(QueryValue, { result, label: metric });
7518
- }
7519
- if (shape.kind === "timeseries") {
7520
- return /* @__PURE__ */ jsx(WqlTimeseries, { result });
7521
- }
7522
- if (shape.kind === "bars") {
7523
- return /* @__PURE__ */ jsx(WqlBars, { result });
7524
- }
7525
- return /* @__PURE__ */ jsx(WqlEmptyState, { result });
7526
- }
7527
- function containerPageHref(container) {
7528
- if (!container || container.kind !== "page") return void 0;
7529
- const { slug, date } = container;
7530
- if (slug) return `/page/${encodeURIComponent(slug)}`;
7531
- if (date) return `/day/${encodeURIComponent(date)}`;
7532
- return void 0;
7533
- }
7534
- function FindResultList({
7535
- target,
7536
- notes,
7537
- blocks,
7538
- containers,
7711
+ attributes,
7712
+ fallbackLabel,
7713
+ executor,
7714
+ onCaptureRpe,
7715
+ onCaptured,
7539
7716
  onOpenNote,
7540
7717
  noteHref
7541
- }) {
7542
- const isBlock = target === "block";
7543
- const items = isBlock ? blocks : notes;
7544
- if (items.length === 0) {
7545
- return /* @__PURE__ */ jsxs("div", { className: "text-xs text-muted-foreground py-2", children: [
7546
- "No ",
7547
- target,
7548
- "s matched this query."
7549
- ] });
7550
- }
7551
- return /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
7552
- /* @__PURE__ */ jsxs("div", { className: "text-[11px] font-medium text-muted-foreground mb-1", children: [
7553
- items.length,
7554
- " ",
7555
- target,
7556
- items.length === 1 ? "" : "s",
7557
- " matched"
7558
- ] }),
7559
- /* @__PURE__ */ jsx("ul", { className: "space-y-0.5 max-h-48 overflow-y-auto font-mono text-xs", children: items.map((item) => {
7560
- const typedItem = item;
7561
- const title = isBlock ? typedItem.title || typedItem.blockContentId || item.id : typedItem.title || item.id;
7562
- const href = containers ? containerPageHref(containers[item.id]) ?? (noteHref ? noteHref(typedItem) : void 0) : noteHref ? noteHref(typedItem) : void 0;
7563
- return /* @__PURE__ */ jsx("li", { className: "rounded hover:bg-muted/50 truncate", children: href ? /* @__PURE__ */ jsx(
7564
- "a",
7565
- {
7566
- href,
7567
- onClick: (e) => {
7568
- if (onOpenNote) {
7569
- e.preventDefault();
7570
- onOpenNote(typedItem);
7571
- }
7572
- },
7573
- className: "block py-0.5 px-1.5 text-primary hover:underline truncate",
7574
- children: title
7575
- }
7576
- ) : onOpenNote ? /* @__PURE__ */ jsx(
7577
- "button",
7578
- {
7579
- type: "button",
7580
- onClick: () => onOpenNote(typedItem),
7581
- className: "w-full text-left py-0.5 px-1.5 text-primary hover:underline truncate",
7582
- children: title
7583
- }
7584
- ) : /* @__PURE__ */ jsx("span", { className: "block py-0.5 px-1.5 text-foreground truncate", children: title }) }, item.id);
7585
- }) })
7586
- ] });
7587
- }
7588
- function PipelineResultView({
7589
- result,
7590
- onOpenNote,
7591
- noteHref,
7592
- widgetType,
7593
- attributes,
7594
- fallbackLabel
7595
7718
  }) {
7596
7719
  if (!result) {
7597
- return /* @__PURE__ */ jsx("div", { className: "text-xs text-muted-foreground py-2", children: "Loading\u2026" });
7720
+ return /* @__PURE__ */ jsx("div", { className: "text-xs text-muted-foreground py-2", children: isFindQuery(parsed) ? "Loading rows\u2026" : "Loading\u2026" });
7598
7721
  }
7599
- if (result.error) {
7600
- return /* @__PURE__ */ jsxs("div", { className: "text-xs text-destructive bg-destructive/10 border border-destructive/20 rounded p-2", children: [
7601
- "Query execution error: ",
7602
- result.error
7603
- ] });
7722
+ if (result.table && (widgetType == null || widgetType === "" || widgetType === "table")) {
7723
+ return /* @__PURE__ */ jsx(WidgetChart, { type: "table", result });
7604
7724
  }
7605
- const listTarget = result.parsed.source.kind === "query" && result.parsed.source.query.family === "find" ? result.parsed.source.query.target : "note";
7606
- if ((result.notes?.length ?? 0) + (result.blocks?.length ?? 0) > 0) {
7725
+ if (resultId && result.runs && result.runs.length > 0) {
7607
7726
  return /* @__PURE__ */ jsx(
7608
- FindResultList,
7727
+ RowsResultsChrome,
7609
7728
  {
7610
- target: listTarget,
7611
- notes: result.notes ?? [],
7612
- blocks: result.blocks ?? [],
7613
- containers: result.containers,
7614
- onOpenNote,
7615
- noteHref
7729
+ resultId,
7730
+ sessionResult: result,
7731
+ executor,
7732
+ onCaptureRpe,
7733
+ onCaptured
7616
7734
  }
7617
7735
  );
7618
7736
  }
7619
- if (result.runs && result.runs.length > 0) {
7620
- const rowsResult = { parsed: result.parsed, runs: result.runs, table: result.table };
7621
- return /* @__PURE__ */ jsx(RowsTable, { result: rowsResult });
7737
+ if (result.runs && (isFindQuery(parsed) || result.runs.length > 0)) {
7738
+ return /* @__PURE__ */ jsx(RowsTable, { result });
7622
7739
  }
7623
- const chartHead = widgetType != null && widgetType !== "" ? widgetType : result.chart?.head;
7624
- if (chartHead) {
7625
- const sinkAttributes = Object.fromEntries(
7626
- (result.chart?.filters ?? []).map((f) => [f.key, f.values.map((v) => v.value).join(",")])
7627
- );
7628
- return /* @__PURE__ */ jsx("div", { className: "w-full h-[220px] min-h-[160px]", children: /* @__PURE__ */ jsx(
7740
+ if (isFindQuery(parsed) || (result.notes?.length ?? 0) + (result.blocks?.length ?? 0) + (result.efforts?.length ?? 0) > 0 && (!result.series || result.series.length === 0)) {
7741
+ return /* @__PURE__ */ jsx(
7629
7742
  WidgetChart,
7630
7743
  {
7631
- type: chartHead,
7632
- result: toChartResult(result),
7633
- label: fallbackLabel,
7634
- attributes: { ...sinkAttributes, ...attributes }
7744
+ type: "list",
7745
+ result,
7746
+ onOpenNote,
7747
+ noteHref
7635
7748
  }
7636
- ) });
7637
- }
7638
- if (result.series && result.series.length > 0) {
7639
- return /* @__PURE__ */ jsx("div", { className: "w-full h-[220px] min-h-[160px]", children: /* @__PURE__ */ jsx(AnalyticsChart, { result: toChartResult(result), metric: fallbackLabel }) });
7749
+ );
7640
7750
  }
7641
- return /* @__PURE__ */ jsx(WqlEmptyState, { result: toChartResult(result) });
7751
+ const sinkHead = result.chart?.head;
7752
+ const effectiveType = widgetType != null && widgetType !== "" ? widgetType : sinkHead ?? "";
7753
+ const sinkAttributes = Object.fromEntries(
7754
+ (result.chart?.filters ?? []).map((f) => [f.key, f.values.map((v) => v.value).join(",")])
7755
+ );
7756
+ const mergedAttributes = { ...sinkAttributes, ...attributes };
7757
+ const needsChartHeight = isPipelineQuery(parsed) && (Boolean(effectiveType) || (result.series?.length ?? 0) > 0);
7758
+ return /* @__PURE__ */ jsx("div", { className: needsChartHeight ? "w-full h-[220px] min-h-[160px]" : void 0, children: /* @__PURE__ */ jsx(
7759
+ WidgetChart,
7760
+ {
7761
+ type: effectiveType,
7762
+ result,
7763
+ label: fallbackLabel,
7764
+ attributes: mergedAttributes,
7765
+ inferDefaultShape: effectiveType === "",
7766
+ onOpenNote,
7767
+ noteHref
7768
+ }
7769
+ ) });
7642
7770
  }
7643
7771
  function QueryBlockShell({
7644
7772
  children,
7645
7773
  onEdit,
7646
- readOnly
7774
+ readOnly,
7775
+ onAddToDashboard
7647
7776
  }) {
7648
7777
  return /* @__PURE__ */ jsxs("div", { className: "relative group/block my-2 p-3 rounded-lg border border-border/80 bg-card/60 shadow-sm", children: [
7649
- onEdit && !readOnly && /* @__PURE__ */ jsx(
7650
- "button",
7651
- {
7652
- type: "button",
7653
- onClick: onEdit,
7654
- title: "Edit query in Omni-Composer",
7655
- className: "absolute top-2 right-2 p-1 rounded bg-muted/80 text-muted-foreground hover:text-foreground opacity-0 group-hover/block:opacity-100 focus-visible:opacity-100 [@media(pointer:coarse)]:opacity-100 transition-opacity z-10",
7656
- children: /* @__PURE__ */ jsx(Edit3, { className: "w-3.5 h-3.5" })
7657
- }
7658
- ),
7778
+ (onAddToDashboard || onEdit && !readOnly) && /* @__PURE__ */ jsxs("div", { className: "absolute top-2 right-2 z-10 flex items-center gap-1", children: [
7779
+ onAddToDashboard && /* @__PURE__ */ jsx(
7780
+ "button",
7781
+ {
7782
+ type: "button",
7783
+ onClick: onAddToDashboard,
7784
+ "aria-label": "Add to dashboard",
7785
+ title: "Add to dashboard",
7786
+ "data-testid": "query-add-dashboard",
7787
+ className: "p-1 rounded bg-muted/80 text-muted-foreground hover:text-foreground",
7788
+ children: /* @__PURE__ */ jsx(ChartBar, { className: "w-3.5 h-3.5" })
7789
+ }
7790
+ ),
7791
+ onEdit && !readOnly && /* @__PURE__ */ jsx(
7792
+ "button",
7793
+ {
7794
+ type: "button",
7795
+ onClick: onEdit,
7796
+ "aria-label": "Edit query in Omni-Composer",
7797
+ title: "Edit query in Omni-Composer",
7798
+ className: "p-1 rounded bg-muted/80 text-muted-foreground hover:text-foreground opacity-0 group-hover/block:opacity-100 focus-visible:opacity-100 [@media(pointer:coarse)]:opacity-100 transition-opacity",
7799
+ children: /* @__PURE__ */ jsx(Edit3, { className: "w-3.5 h-3.5" })
7800
+ }
7801
+ )
7802
+ ] }),
7659
7803
  children
7660
7804
  ] });
7661
7805
  }
7662
7806
  var ReactQueryBlock = class extends WidgetType {
7663
- constructor(queryText, sectionId, widgetType, widgetError, options) {
7807
+ constructor(queryText, sectionId, widgetType, widgetError, attributes, tokenValues, options) {
7664
7808
  super();
7665
7809
  this.queryText = queryText;
7666
7810
  this.sectionId = sectionId;
7667
7811
  this.widgetType = widgetType;
7668
7812
  this.widgetError = widgetError;
7813
+ this.attributes = attributes;
7814
+ this.tokenValues = tokenValues;
7669
7815
  this.options = options;
7670
7816
  }
7671
7817
  queryText;
7672
7818
  sectionId;
7673
7819
  widgetType;
7674
7820
  widgetError;
7821
+ attributes;
7822
+ tokenValues;
7675
7823
  options;
7676
7824
  root = null;
7677
7825
  eq(other) {
7678
- return this.queryText === other.queryText && this.sectionId === other.sectionId && this.widgetType === other.widgetType && this.widgetError === other.widgetError && this.options?.readOnly === other.options?.readOnly && this.options?.executor === other.options?.executor && this.options?.onOpenNote === other.options?.onOpenNote && this.options?.noteHref === other.options?.noteHref;
7826
+ return this.queryText === other.queryText && this.sectionId === other.sectionId && this.widgetType === other.widgetType && this.widgetError === other.widgetError && JSON.stringify(this.attributes ?? {}) === JSON.stringify(other.attributes ?? {}) && JSON.stringify(this.tokenValues ?? {}) === JSON.stringify(other.tokenValues ?? {}) && this.options?.readOnly === other.options?.readOnly && this.options?.executor === other.options?.executor && this.options?.onOpenNote === other.options?.onOpenNote && this.options?.noteHref === other.options?.noteHref;
7679
7827
  }
7680
7828
  toDOM(view) {
7681
7829
  const container = document.createElement("div");
@@ -7696,6 +7844,8 @@ var ReactQueryBlock = class extends WidgetType {
7696
7844
  query: this.queryText,
7697
7845
  widgetType: this.widgetType,
7698
7846
  widgetError: this.widgetError,
7847
+ attributes: this.attributes,
7848
+ tokenValues: this.tokenValues,
7699
7849
  readOnly: this.options?.readOnly,
7700
7850
  executor: this.options?.executor,
7701
7851
  onResultSaved: this.options?.onResultSaved,
@@ -7723,6 +7873,8 @@ var ReactQueryBlock = class extends WidgetType {
7723
7873
  query: this.queryText,
7724
7874
  widgetType: this.widgetType,
7725
7875
  widgetError: this.widgetError,
7876
+ attributes: this.attributes,
7877
+ tokenValues: this.tokenValues,
7726
7878
  readOnly: this.options?.readOnly,
7727
7879
  executor: this.options?.executor,
7728
7880
  onResultSaved: this.options?.onResultSaved,
@@ -7750,8 +7902,11 @@ var ReactQueryBlock = class extends WidgetType {
7750
7902
  };
7751
7903
  function buildQueryBlockDecos(state, options) {
7752
7904
  const builder = new RangeSetBuilder();
7753
- const { sections } = state.field(sectionField);
7905
+ const { sections, rawContent: fullRawContent } = state.field(sectionField);
7754
7906
  const cursor2 = state.selection.main.head;
7907
+ const fmMeta = parseFrontmatter(fullRawContent).meta;
7908
+ const fmTokens = defaultTokenValues(extractDashboardTokens(fmMeta));
7909
+ const mergedTokens = { ...fmTokens, ...options?.tokenValues };
7755
7910
  for (const section of sections) {
7756
7911
  if (section.type !== "query") continue;
7757
7912
  if (cursor2 >= section.from && cursor2 <= section.to) continue;
@@ -7766,6 +7921,8 @@ function buildQueryBlockDecos(state, options) {
7766
7921
  section.id,
7767
7922
  section.queryType,
7768
7923
  section.queryError,
7924
+ section.queryAttributes,
7925
+ mergedTokens,
7769
7926
  options
7770
7927
  ),
7771
7928
  block: true
@@ -13611,6 +13768,6 @@ function editorPreset(optionsOrDialect = "markdown") {
13611
13768
  return extensions;
13612
13769
  }
13613
13770
 
13614
- export { AGG_OPTIONS, CANONICAL_BLOCK_TYPES, CLAUSE_META, 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, 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, toChartResult, toggleWidgetEdit, tooltipTimestamp, urlAtPos, useChartShape, useComposerSlots, useSuggestions, useVisualViewportRect, useWqlStageCounts, widgetBlockPreview, wodAutoWrap, wodAutocompletion, wodCompletionSource, wodEditorKeymap, wodLinter, wodOverlayPanel, workoutLintSource, wqlToPills, wrapInTimeFence };
13615
- //# sourceMappingURL=chunk-UAIJTD74.mjs.map
13616
- //# sourceMappingURL=chunk-UAIJTD74.mjs.map
13771
+ export { AGG_OPTIONS, CANONICAL_BLOCK_TYPES, CLAUSE_META, 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, 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 };
13772
+ //# sourceMappingURL=chunk-GM2PO7LK.mjs.map
13773
+ //# sourceMappingURL=chunk-GM2PO7LK.mjs.map