@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.cjs CHANGED
@@ -204,20 +204,75 @@ function QueryValue({ result, unit: unitProp, label, thresholds }) {
204
204
  ] })
205
205
  ] });
206
206
  }
207
-
208
- // src/widgets/chartData.ts
207
+ function createExecutorDocumentHost(executor, range = {}) {
208
+ return {
209
+ runAggregate: (queryText, overrides) => executor.runQuery(queryText, {
210
+ context: overrides.context,
211
+ rangeStart: range.rangeStart,
212
+ rangeEnd: range.rangeEnd,
213
+ preferredUnit: range.preferredUnit
214
+ }),
215
+ runFind: async (queryText, context) => {
216
+ const parsed = wodWikiWql.parseQuery(queryText);
217
+ if (!wodWikiWql.isFindQuery(parsed)) return { kind: "find", error: `not a content query: ${queryText}` };
218
+ const res = await executor.runFind(parsed, {
219
+ context,
220
+ range: range.rangeStart !== void 0 ? { start: range.rangeStart, end: range.rangeEnd ?? Number.MAX_SAFE_INTEGER, endExclusive: false } : void 0
221
+ });
222
+ return {
223
+ kind: "find",
224
+ notes: res.notes,
225
+ blocks: res.blocks,
226
+ efforts: res.efforts,
227
+ containers: res.containers,
228
+ runs: res.runs,
229
+ table: res.table
230
+ };
231
+ },
232
+ runPipeline: async (queryText, context) => {
233
+ const res = await executor.runPipeline?.(queryText, {
234
+ context,
235
+ rangeStart: range.rangeStart,
236
+ rangeEnd: range.rangeEnd,
237
+ preferredUnit: range.preferredUnit
238
+ });
239
+ return res ?? { error: "pipeline queries are not supported by this executor" };
240
+ },
241
+ rollupEnsure: range.rollupEnsure
242
+ };
243
+ }
209
244
  function toChartResult(input) {
210
245
  const parsed = input.parsed;
246
+ const series = input.series ?? [];
247
+ const scalar = series.length === 1 && series[0].points.length === 1 ? series[0].points[0].value : void 0;
211
248
  return {
212
249
  parsed,
213
- series: input.series ?? [],
250
+ series,
214
251
  stages: { selected: 0, buckets: 0, aggregated: 0, groups: 0 },
215
252
  matched: [],
253
+ ...scalar !== void 0 ? { scalar } : {},
216
254
  ...input.unit !== void 0 ? { unit: input.unit } : {},
217
255
  ...input.error ? { error: input.error } : {},
218
256
  ...input.table ? { table: input.table } : {}
219
257
  };
220
258
  }
259
+ function outputToRunResult(parsed, out) {
260
+ const base = toChartResult({
261
+ parsed,
262
+ series: out.series,
263
+ unit: out.unit,
264
+ error: out.error,
265
+ table: out.table
266
+ });
267
+ return Object.assign(base, {
268
+ ...out.notes ? { notes: out.notes } : {},
269
+ ...out.blocks ? { blocks: out.blocks } : {},
270
+ ...out.efforts ? { efforts: out.efforts } : {},
271
+ ...out.containers ? { containers: out.containers } : {},
272
+ ...out.runs ? { runs: out.runs } : {},
273
+ ...out.chart ? { chart: out.chart } : {}
274
+ });
275
+ }
221
276
  function mergeSeries(series) {
222
277
  const map = /* @__PURE__ */ new Map();
223
278
  for (const s of series) {
@@ -830,16 +885,52 @@ function ZoneDistribution({ result, params, attributes }) {
830
885
  }
831
886
  );
832
887
  }
833
- function WidgetChart({ type, result, label, unit, params, attributes }) {
888
+ function inferChartTypeFromResult(result) {
889
+ if (!result || result.series.length === 0) return "table";
890
+ if (result.series.length === 1 && result.series[0].points.length === 1) return "value";
891
+ const hasTimeAxis = result.series.some((s) => s.points.length > 1);
892
+ return hasTimeAxis ? "timeseries" : "bar";
893
+ }
894
+ function WidgetChart({
895
+ type,
896
+ result,
897
+ label,
898
+ unit,
899
+ params,
900
+ attributes,
901
+ inferDefaultShape = false,
902
+ onOpenNote,
903
+ noteHref
904
+ }) {
834
905
  if (type !== "" && !wodWikiWql.isDashboardWidgetType(type)) {
835
906
  return /* @__PURE__ */ jsxRuntime.jsx(WidgetProblemBadge, { message: wodWikiWql.unknownWidgetTypeMessage(type) });
836
907
  }
837
- const resolved = wodWikiWql.resolveWidgetType(type);
838
- if (resolved === "table" && result?.table) {
839
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full h-full min-h-[160px]", children: /* @__PURE__ */ jsxRuntime.jsx(TabularTable, { table: result.table }) });
908
+ const resolved = type === "" && inferDefaultShape ? inferChartTypeFromResult(result) : wodWikiWql.resolveWidgetType(type);
909
+ const runResult = result;
910
+ const parsedFamily = runResult?.parsed?.family;
911
+ const hasContentItems = parsedFamily === "find" || Boolean(runResult?.notes?.length || runResult?.blocks?.length || runResult?.efforts?.length || runResult?.runs?.length);
912
+ if (resolved === "table" && runResult?.table) {
913
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full h-full min-h-[160px]", children: /* @__PURE__ */ jsxRuntime.jsx(TabularTable, { table: runResult.table }) });
914
+ }
915
+ if (resolved === "table" && hasContentItems && (!result || !("series" in result) || result.series.length === 0)) {
916
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full h-full min-h-[160px]", children: /* @__PURE__ */ jsxRuntime.jsx(
917
+ ContentRecordsTable,
918
+ {
919
+ result: runResult,
920
+ onOpenNote,
921
+ noteHref
922
+ }
923
+ ) });
840
924
  }
841
- if (resolved === "list" && result?.parsed?.family === "find") {
842
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full h-full min-h-[160px]", children: /* @__PURE__ */ jsxRuntime.jsx(ThingList, { result }) });
925
+ if (resolved === "list" && hasContentItems) {
926
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full h-full min-h-[160px]", children: /* @__PURE__ */ jsxRuntime.jsx(
927
+ ThingList,
928
+ {
929
+ result: runResult,
930
+ onOpenNote,
931
+ noteHref
932
+ }
933
+ ) });
843
934
  }
844
935
  if (!result || result.parsed.error || "series" in result && result.series.length === 0) {
845
936
  return /* @__PURE__ */ jsxRuntime.jsx(WqlEmptyState, { result });
@@ -966,9 +1057,138 @@ function TabularTable({ table }) {
966
1057
  ] }, group.key)) : table.rows.map((row, i) => /* @__PURE__ */ jsxRuntime.jsx("tr", { className: "border-b border-border/40 hover:bg-muted/20", children: table.columns.map((c) => /* @__PURE__ */ jsxRuntime.jsx("td", { className: "py-1 px-2 whitespace-nowrap", children: String(row[c.name] ?? "") }, c.name)) }, i)) })
967
1058
  ] }) });
968
1059
  }
969
- function ThingList({ result }) {
970
- const items = result.notes?.length ? result.notes : result.blocks?.length ? result.blocks : result.efforts?.length ? result.efforts : result.runs ?? [];
971
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-full overflow-auto space-y-1", "data-testid": "thing-list", children: items.map((item, i) => /* @__PURE__ */ jsxRuntime.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)) });
1060
+ function containerPageHref(container) {
1061
+ if (!container || container.kind !== "page") return void 0;
1062
+ const { slug, date } = container;
1063
+ if (slug) return `/page/${encodeURIComponent(slug)}`;
1064
+ if (date) return `/day/${encodeURIComponent(date)}`;
1065
+ return void 0;
1066
+ }
1067
+ function formatItemDate(item) {
1068
+ const raw = item.date ?? item.timestamp ?? item.createdAt;
1069
+ if (raw === void 0 || raw === null || raw === "") return "\u2014";
1070
+ if (typeof raw === "string" && /^\d{4}-\d{2}-\d{2}/.test(raw)) return raw.slice(0, 10);
1071
+ const ms = typeof raw === "number" ? raw : Date.parse(String(raw));
1072
+ if (!Number.isFinite(ms) || ms <= 0) return "\u2014";
1073
+ return new Date(ms).toISOString().slice(0, 10);
1074
+ }
1075
+ function ContentRecordsTable({
1076
+ result,
1077
+ onOpenNote,
1078
+ noteHref
1079
+ }) {
1080
+ const parsed = result?.parsed;
1081
+ const target = parsed?.target ?? (parsed?.source?.kind === "query" && parsed?.source?.query?.family === "find" ? parsed.source.query.target ?? "note" : result?.blocks?.length ? "block" : "note");
1082
+ const isBlock = target === "block" || Boolean(result?.blocks?.length && !result?.notes?.length);
1083
+ const rawItems = result?.notes?.length ? result.notes : result?.blocks?.length ? result.blocks : result?.efforts?.length ? result.efforts : result?.runs ?? [];
1084
+ const items = rawItems;
1085
+ if (items.length === 0) {
1086
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-xs text-muted-foreground py-4 text-center", "data-testid": "content-records-table-empty", children: [
1087
+ "No ",
1088
+ target,
1089
+ "s matched this query."
1090
+ ] });
1091
+ }
1092
+ const containers = result?.containers;
1093
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-full overflow-auto border-y border-border/60 bg-card/40", "data-testid": "content-records-table", children: /* @__PURE__ */ jsxRuntime.jsxs("table", { className: "w-full text-left border-collapse text-xs", children: [
1094
+ /* @__PURE__ */ jsxRuntime.jsx("thead", { children: /* @__PURE__ */ jsxRuntime.jsxs("tr", { className: "border-b border-border bg-muted/30 text-[11px] font-semibold uppercase tracking-wider text-muted-foreground", children: [
1095
+ /* @__PURE__ */ jsxRuntime.jsx("th", { className: "py-2 px-3", children: "Title" }),
1096
+ /* @__PURE__ */ jsxRuntime.jsx("th", { className: "py-2 px-3", children: "Kind" }),
1097
+ /* @__PURE__ */ jsxRuntime.jsx("th", { className: "py-2 px-3", children: "Tags / Details" }),
1098
+ /* @__PURE__ */ jsxRuntime.jsx("th", { className: "py-2 px-3 text-right", children: "Date" })
1099
+ ] }) }),
1100
+ /* @__PURE__ */ jsxRuntime.jsx("tbody", { className: "divide-y divide-border/40", children: items.map((item, i) => {
1101
+ const id = item.id ?? item.resultId ?? String(i);
1102
+ const typedItem = { ...item, id, title: item.title ?? item.label ?? item.noteTitle };
1103
+ const title = isBlock ? typedItem.title || typedItem.blockContentId || id : typedItem.title || id;
1104
+ const href = containers ? containerPageHref(containers[id]) ?? (noteHref ? noteHref(typedItem) : void 0) : noteHref ? noteHref(typedItem) : void 0;
1105
+ const kindLabel = item.type ?? item.dataType ?? item.baseAttributes?.discipline ?? target;
1106
+ 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";
1107
+ return /* @__PURE__ */ jsxRuntime.jsxs("tr", { className: "hover:bg-muted/30 transition-colors", children: [
1108
+ /* @__PURE__ */ jsxRuntime.jsx("td", { className: "py-2 px-3 font-medium truncate max-w-[260px]", children: href ? /* @__PURE__ */ jsxRuntime.jsx(
1109
+ "a",
1110
+ {
1111
+ href,
1112
+ onClick: (e) => {
1113
+ if (onOpenNote) {
1114
+ e.preventDefault();
1115
+ onOpenNote(typedItem);
1116
+ }
1117
+ },
1118
+ className: "text-primary hover:underline",
1119
+ children: title
1120
+ }
1121
+ ) : onOpenNote ? /* @__PURE__ */ jsxRuntime.jsx(
1122
+ "button",
1123
+ {
1124
+ type: "button",
1125
+ onClick: () => onOpenNote(typedItem),
1126
+ className: "text-left text-primary hover:underline",
1127
+ children: title
1128
+ }
1129
+ ) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-foreground", children: title }) }),
1130
+ /* @__PURE__ */ jsxRuntime.jsx("td", { className: "py-2 px-3 font-mono text-[11px] text-muted-foreground", children: kindLabel }),
1131
+ /* @__PURE__ */ jsxRuntime.jsx("td", { className: "py-2 px-3 font-mono text-[11px] text-muted-foreground truncate max-w-[200px]", children: detail }),
1132
+ /* @__PURE__ */ jsxRuntime.jsx("td", { className: "py-2 px-3 font-mono text-[11px] text-muted-foreground text-right tabular-nums", children: formatItemDate(item) })
1133
+ ] }, id);
1134
+ }) })
1135
+ ] }) });
1136
+ }
1137
+ function ThingList({
1138
+ result,
1139
+ onOpenNote,
1140
+ noteHref
1141
+ }) {
1142
+ const parsed = result?.parsed;
1143
+ const target = parsed?.target ?? (parsed?.source?.kind === "query" && parsed?.source?.query?.family === "find" ? parsed.source.query.target ?? "note" : result?.blocks?.length ? "block" : "note");
1144
+ const isBlock = target === "block" || Boolean(result?.blocks?.length && !result?.notes?.length);
1145
+ const rawItems = result?.notes?.length ? result.notes : result?.blocks?.length ? result.blocks : result?.efforts?.length ? result.efforts : result?.runs ?? [];
1146
+ const items = rawItems;
1147
+ if (items.length === 0) {
1148
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-xs text-muted-foreground py-2", "data-testid": "thing-list", children: [
1149
+ "No ",
1150
+ target,
1151
+ "s matched this query."
1152
+ ] });
1153
+ }
1154
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "h-full overflow-auto space-y-1", "data-testid": "thing-list", children: [
1155
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-[11px] font-medium text-muted-foreground mb-1", children: [
1156
+ items.length,
1157
+ " ",
1158
+ target,
1159
+ items.length === 1 ? "" : "s",
1160
+ " matched"
1161
+ ] }),
1162
+ /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "space-y-0.5 max-h-48 overflow-y-auto font-mono text-xs", children: items.map((item, i) => {
1163
+ const id = item.id ?? item.resultId ?? String(i);
1164
+ const typedItem = { ...item, id, title: item.title ?? item.label ?? item.noteTitle };
1165
+ const title = isBlock ? typedItem.title || typedItem.blockContentId || id : typedItem.title || id;
1166
+ const containers = result?.containers;
1167
+ const href = containers ? containerPageHref(containers[id]) ?? (noteHref ? noteHref(typedItem) : void 0) : noteHref ? noteHref(typedItem) : void 0;
1168
+ return /* @__PURE__ */ jsxRuntime.jsx("li", { className: "rounded hover:bg-muted/50 truncate", children: href ? /* @__PURE__ */ jsxRuntime.jsx(
1169
+ "a",
1170
+ {
1171
+ href,
1172
+ onClick: (e) => {
1173
+ if (onOpenNote) {
1174
+ e.preventDefault();
1175
+ onOpenNote(typedItem);
1176
+ }
1177
+ },
1178
+ className: "block py-0.5 px-1.5 text-primary hover:underline truncate",
1179
+ children: title
1180
+ }
1181
+ ) : onOpenNote ? /* @__PURE__ */ jsxRuntime.jsx(
1182
+ "button",
1183
+ {
1184
+ type: "button",
1185
+ onClick: () => onOpenNote(typedItem),
1186
+ className: "w-full text-left py-0.5 px-1.5 text-primary hover:underline truncate",
1187
+ children: title
1188
+ }
1189
+ ) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "block py-0.5 px-1.5 text-foreground truncate", children: title }) }, id);
1190
+ }) })
1191
+ ] });
972
1192
  }
973
1193
  function ScalarTokenInput({
974
1194
  name,
@@ -1044,53 +1264,6 @@ function DashboardTokenControls({ tokens, values: values2, onChange }) {
1044
1264
  }
1045
1265
  );
1046
1266
  }
1047
- function createExecutorDocumentHost(executor, range = {}) {
1048
- return {
1049
- runAggregate: (queryText, overrides) => executor.runQuery(queryText, {
1050
- context: overrides.context,
1051
- rangeStart: range.rangeStart,
1052
- rangeEnd: range.rangeEnd,
1053
- preferredUnit: range.preferredUnit
1054
- }),
1055
- runFind: async (queryText, context) => {
1056
- const parsed = wodWikiWql.parseQuery(queryText);
1057
- if (!wodWikiWql.isFindQuery(parsed)) return { kind: "find", error: `not a content query: ${queryText}` };
1058
- const res = await executor.runFind(parsed, {
1059
- context,
1060
- range: range.rangeStart !== void 0 ? { start: range.rangeStart, end: range.rangeEnd ?? Number.MAX_SAFE_INTEGER, endExclusive: false } : void 0
1061
- });
1062
- return {
1063
- kind: "find",
1064
- notes: res.notes,
1065
- blocks: res.blocks,
1066
- efforts: res.efforts,
1067
- containers: res.containers,
1068
- runs: res.runs,
1069
- table: res.table
1070
- };
1071
- },
1072
- runPipeline: async (queryText, context) => {
1073
- const res = await executor.runPipeline?.(queryText, {
1074
- context,
1075
- rangeStart: range.rangeStart,
1076
- rangeEnd: range.rangeEnd,
1077
- preferredUnit: range.preferredUnit
1078
- });
1079
- return res ?? { error: "pipeline queries are not supported by this executor" };
1080
- },
1081
- rollupEnsure: range.rollupEnsure
1082
- };
1083
- }
1084
- function outputToRunResult(parsed, out) {
1085
- const base = toChartResult({ parsed, series: out.series, unit: out.unit, error: out.error, table: out.table });
1086
- return Object.assign(base, {
1087
- ...out.notes ? { notes: out.notes } : {},
1088
- ...out.blocks ? { blocks: out.blocks } : {},
1089
- ...out.efforts ? { efforts: out.efforts } : {},
1090
- ...out.containers ? { containers: out.containers } : {},
1091
- ...out.runs ? { runs: out.runs } : {}
1092
- });
1093
- }
1094
1267
  function spanClass(widget) {
1095
1268
  if (widget.spanFull) return "col-span-1 md:col-span-2 lg:col-span-4";
1096
1269
  if (widget.spanCols != null) {
@@ -1131,7 +1304,9 @@ function DashboardView({
1131
1304
  onResizeWidget,
1132
1305
  rangeStart,
1133
1306
  rangeEnd,
1134
- preferredUnit
1307
+ preferredUnit,
1308
+ onOpenNote,
1309
+ noteHref
1135
1310
  }) {
1136
1311
  const values2 = React2.useMemo(
1137
1312
  () => ({ ...wodWikiWql.defaultTokenValues(document2.tokens), ...tokenValues }),
@@ -1172,9 +1347,12 @@ function DashboardView({
1172
1347
  next[widget.key] = { error: wodWikiWql.unknownTokensMessage(missing) };
1173
1348
  continue;
1174
1349
  }
1175
- const parsed = wodWikiWql.parseQuery(query);
1176
- if (parsed.error) {
1177
- next[widget.key] = { error: parsed.error };
1350
+ const docParse = wodWikiWql.parseDocument(query);
1351
+ const isMultiLineDoc = !docParse.doc.degenerate && docParse.doc.assignments.length > 0;
1352
+ const parsed = isMultiLineDoc ? { family: "aggregate", raw: query, agg: "sum", metric: docParse.doc.show[0] ?? "value", filters: [], groupBy: [] } : wodWikiWql.parseQuery(query);
1353
+ const syntaxErr = isMultiLineDoc ? docParse.diagnostics[0] : parsed.error;
1354
+ if (syntaxErr) {
1355
+ next[widget.key] = { error: syntaxErr };
1178
1356
  continue;
1179
1357
  }
1180
1358
  const resolvedType = wodWikiWql.resolveWidgetType(widget.type);
@@ -1183,7 +1361,7 @@ function DashboardView({
1183
1361
  next[widget.key] = { error: "content queries return things \u2014 use a table or list widget instead" };
1184
1362
  continue;
1185
1363
  }
1186
- } else if (!wodWikiWql.isPipelineQuery(parsed) && resolvedType === "list") {
1364
+ } else if (!wodWikiWql.isPipelineQuery(parsed) && !isMultiLineDoc && resolvedType === "list") {
1187
1365
  next[widget.key] = { error: "aggregate queries return numbers \u2014 use a chart or value widget instead" };
1188
1366
  continue;
1189
1367
  }
@@ -1321,7 +1499,9 @@ function DashboardView({
1321
1499
  result: run?.result,
1322
1500
  label: widget.title,
1323
1501
  unit: preferredUnit,
1324
- attributes
1502
+ attributes,
1503
+ onOpenNote,
1504
+ noteHref
1325
1505
  }
1326
1506
  )
1327
1507
  },
@@ -1395,17 +1575,8 @@ function useAnalyticsQueries(queries, weeks, executor, refreshKey = 0, preferred
1395
1575
  const res = await runner.run(q.query);
1396
1576
  const out = res.outputs[0];
1397
1577
  const error = out?.error ?? res.diagnostics[0];
1398
- const result = toChartResult({
1399
- parsed: wodWikiWql.parseQuery(q.query),
1400
- series: out?.series,
1401
- unit: out?.unit,
1402
- error,
1403
- table: out?.table
1404
- });
1405
- if (out?.notes) result.notes = out.notes;
1406
- if (out?.blocks) result.blocks = out.blocks;
1407
- if (out?.efforts) result.efforts = out.efforts;
1408
- if (out?.runs) result.runs = out.runs;
1578
+ const parsed = wodWikiWql.parseQuery(q.query);
1579
+ const result = out ? outputToRunResult(parsed, { ...out, ...error ? { error } : {} }) : outputToRunResult(parsed, { name: q.key, kind: "aggregate", error });
1409
1580
  return [q.key, result];
1410
1581
  })
1411
1582
  );
@@ -4551,21 +4722,31 @@ function QueryBlockView({
4551
4722
  widgetError,
4552
4723
  tokenValues,
4553
4724
  attributes,
4725
+ onAddToDashboard,
4554
4726
  onCaptureRpe,
4555
4727
  onOpenNote,
4556
4728
  noteHref
4557
4729
  }) {
4558
4730
  const [isModalOpen, setIsModalOpen] = React2.useState(false);
4559
4731
  const extracted = React2.useMemo(() => extractBlockQueries(query), [query]);
4732
+ const rawBody = React2.useMemo(() => {
4733
+ const trimmed = query.trim();
4734
+ if (trimmed.includes("\n")) {
4735
+ const docParsed = wodWikiWql.parseDocument(trimmed);
4736
+ if (!docParsed.doc.degenerate && docParsed.diagnostics.length === 0 && docParsed.doc.assignments.length > 0) {
4737
+ return trimmed;
4738
+ }
4739
+ }
4740
+ return extracted.length > 0 ? extracted[0].query : query;
4741
+ }, [extracted, query]);
4560
4742
  const { query: effectiveQuery, missing } = React2.useMemo(() => {
4561
- const raw = extracted.length > 0 ? extracted[0].query : query;
4562
- const subQuery = wodWikiWql.substituteTokens(raw, tokenValues ?? {});
4743
+ const subQuery = wodWikiWql.substituteTokens(rawBody, tokenValues ?? {});
4563
4744
  const missingSet = new Set(subQuery.missing);
4564
4745
  for (const value of Object.values(attributes ?? {})) {
4565
4746
  for (const m of wodWikiWql.substituteTokens(value, tokenValues ?? {}).missing) missingSet.add(m);
4566
4747
  }
4567
4748
  return { query: subQuery.query, missing: [...missingSet] };
4568
- }, [extracted, query, tokenValues, attributes]);
4749
+ }, [rawBody, tokenValues, attributes]);
4569
4750
  const resolvedAttributes = React2.useMemo(() => {
4570
4751
  const out = {};
4571
4752
  for (const [key, value] of Object.entries(attributes ?? {})) {
@@ -4573,11 +4754,12 @@ function QueryBlockView({
4573
4754
  }
4574
4755
  return out;
4575
4756
  }, [attributes, tokenValues]);
4757
+ const docParse = React2.useMemo(() => wodWikiWql.parseDocument(effectiveQuery), [effectiveQuery]);
4758
+ const isMultiLineDoc = !docParse.doc.degenerate && docParse.doc.assignments.length > 0;
4576
4759
  const parsed = React2.useMemo(() => wodWikiWql.parseQuery(effectiveQuery), [effectiveQuery]);
4760
+ const syntaxError = isMultiLineDoc ? docParse.diagnostics[0] : parsed.error;
4577
4761
  const unknownType = widgetType != null && widgetType !== "" && !wodWikiWql.isDashboardWidgetType(widgetType);
4578
- const [result, setResult] = React2.useState(void 0);
4579
- const [findResult, setFindResult] = React2.useState(void 0);
4580
- const [pipelineResult, setPipelineResult] = React2.useState(void 0);
4762
+ const [runResult, setRunResult] = React2.useState(void 0);
4581
4763
  const [runError, setRunError] = React2.useState(void 0);
4582
4764
  const [rowsRefreshKey, setRowsRefreshKey] = React2.useState(0);
4583
4765
  React2.useEffect(() => {
@@ -4592,59 +4774,45 @@ function QueryBlockView({
4592
4774
  }, [onResultSaved]);
4593
4775
  React2.useEffect(() => {
4594
4776
  let cancelled = false;
4777
+ let retryTimer;
4595
4778
  setRunError(void 0);
4596
- setResult(void 0);
4597
- setFindResult(void 0);
4598
- setPipelineResult(void 0);
4599
- if (parsed.error) return;
4600
- if (widgetError || unknownType || missing.length > 0) return;
4601
- if (!executor) return;
4602
- if (wodWikiWql.isFindQuery(parsed)) {
4603
- let retryTimer;
4604
- const isSession = parsed.target === "session";
4605
- const executeFind = (attemptCount) => {
4606
- void executor.runFind(parsed).then((res) => {
4607
- if (cancelled) return;
4608
- setFindResult(res);
4609
- if (isSession && (res.runs ?? []).length === 0 && attemptCount < 4) {
4610
- const delays = [50, 150, 350, 750];
4611
- retryTimer = setTimeout(() => {
4612
- if (!cancelled) executeFind(attemptCount + 1);
4613
- }, delays[attemptCount] ?? 500);
4614
- }
4615
- }).catch((err) => {
4616
- if (!cancelled) setRunError(err instanceof Error ? err.message : String(err));
4617
- });
4618
- };
4619
- executeFind(0);
4620
- return () => {
4621
- cancelled = true;
4622
- clearTimeout(retryTimer);
4623
- };
4624
- }
4625
- if (wodWikiWql.isPipelineQuery(parsed)) {
4626
- if (!executor.runPipeline) {
4627
- setRunError("pipeline queries are not supported by this executor");
4628
- return;
4629
- }
4630
- void executor.runPipeline(effectiveQuery).then((res) => {
4631
- if (!cancelled) setPipelineResult(res);
4779
+ setRunResult(void 0);
4780
+ if (syntaxError || widgetError || unknownType || missing.length > 0 || !executor) return;
4781
+ const isSessionFind = !isMultiLineDoc && wodWikiWql.isFindQuery(parsed) && parsed.target === "session";
4782
+ const host = createExecutorDocumentHost(executor);
4783
+ const executeRun = (attemptCount) => {
4784
+ const runner = new wodWikiWql.SharedQueryDocumentRunner(host);
4785
+ void runner.run(effectiveQuery).then((res) => {
4786
+ if (cancelled) return;
4787
+ const out = res.outputs[0];
4788
+ if (!out) {
4789
+ setRunError(res.diagnostics[0] ?? "widget produced no output");
4790
+ return;
4791
+ }
4792
+ const err = out.error ?? res.diagnostics[0];
4793
+ if (err) {
4794
+ setRunError(err);
4795
+ return;
4796
+ }
4797
+ 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;
4798
+ const shaped = outputToRunResult(effectiveParsed, out);
4799
+ setRunResult(shaped);
4800
+ if (isSessionFind && (shaped.runs ?? []).length === 0 && attemptCount < 4) {
4801
+ const delays = [50, 150, 350, 750];
4802
+ retryTimer = setTimeout(() => {
4803
+ if (!cancelled) executeRun(attemptCount + 1);
4804
+ }, delays[attemptCount] ?? 500);
4805
+ }
4632
4806
  }).catch((err) => {
4633
4807
  if (!cancelled) setRunError(err instanceof Error ? err.message : String(err));
4634
4808
  });
4635
- return () => {
4636
- cancelled = true;
4637
- };
4638
- }
4639
- void executor.runQuery(effectiveQuery).then((res) => {
4640
- if (!cancelled) setResult(res);
4641
- }).catch((err) => {
4642
- if (!cancelled) setRunError(err instanceof Error ? err.message : String(err));
4643
- });
4809
+ };
4810
+ executeRun(0);
4644
4811
  return () => {
4645
4812
  cancelled = true;
4813
+ clearTimeout(retryTimer);
4646
4814
  };
4647
- }, [effectiveQuery, parsed, widgetError, unknownType, missing, executor, rowsRefreshKey]);
4815
+ }, [effectiveQuery, parsed, isMultiLineDoc, syntaxError, widgetError, unknownType, missing, executor, rowsRefreshKey]);
4648
4816
  const canEdit = onSaveQuery !== void 0 && !readOnly;
4649
4817
  const onEdit = canEdit ? () => setIsModalOpen(true) : void 0;
4650
4818
  const resultId = React2.useMemo(() => {
@@ -4652,50 +4820,32 @@ function QueryBlockView({
4652
4820
  const resultFilter = parsed.filters.find((f) => f.key === "result");
4653
4821
  return resultFilter?.values[0]?.value;
4654
4822
  }, [parsed]);
4823
+ const fallbackLabel = React2.useMemo(() => {
4824
+ if (!wodWikiWql.isFindQuery(parsed) && !wodWikiWql.isPipelineQuery(parsed)) return parsed.metric;
4825
+ return parsed.raw;
4826
+ }, [parsed]);
4655
4827
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-testid": "query-block-view", children: [
4656
- widgetError ? /* @__PURE__ */ jsxRuntime.jsx(QueryBlockShell, { onEdit, readOnly, children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs text-destructive bg-destructive/10 border border-destructive/20 rounded p-2", children: widgetError }) }) : unknownType ? /* @__PURE__ */ jsxRuntime.jsx(QueryBlockShell, { onEdit, readOnly, children: /* @__PURE__ */ jsxRuntime.jsx(WidgetProblemBadge, { message: wodWikiWql.unknownWidgetTypeMessage(widgetType) }) }) : missing.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx(QueryBlockShell, { onEdit, readOnly, children: /* @__PURE__ */ jsxRuntime.jsx(WidgetProblemBadge, { message: wodWikiWql.unknownTokensMessage(missing) }) }) : parsed.error ? /* @__PURE__ */ jsxRuntime.jsx(QueryBlockShell, { onEdit, readOnly, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-xs text-destructive bg-destructive/10 border border-destructive/20 rounded p-2", children: [
4828
+ widgetError ? /* @__PURE__ */ jsxRuntime.jsx(QueryBlockShell, { onEdit, readOnly, onAddToDashboard, children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs text-destructive bg-destructive/10 border border-destructive/20 rounded p-2", children: widgetError }) }) : unknownType ? /* @__PURE__ */ jsxRuntime.jsx(QueryBlockShell, { onEdit, readOnly, onAddToDashboard, children: /* @__PURE__ */ jsxRuntime.jsx(WidgetProblemBadge, { message: wodWikiWql.unknownWidgetTypeMessage(widgetType) }) }) : missing.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx(QueryBlockShell, { onEdit, readOnly, onAddToDashboard, children: /* @__PURE__ */ jsxRuntime.jsx(WidgetProblemBadge, { message: wodWikiWql.unknownTokensMessage(missing) }) }) : syntaxError ? /* @__PURE__ */ jsxRuntime.jsx(QueryBlockShell, { onEdit, readOnly, onAddToDashboard, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-xs text-destructive bg-destructive/10 border border-destructive/20 rounded p-2", children: [
4657
4829
  "Query syntax error: ",
4658
- parsed.error
4659
- ] }) }) : runError ? /* @__PURE__ */ jsxRuntime.jsx(QueryBlockShell, { onEdit, readOnly, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-xs text-destructive bg-destructive/10 border border-destructive/20 rounded p-2", children: [
4830
+ syntaxError
4831
+ ] }) }) : runError ? /* @__PURE__ */ jsxRuntime.jsx(QueryBlockShell, { onEdit, readOnly, onAddToDashboard, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-xs text-destructive bg-destructive/10 border border-destructive/20 rounded p-2", children: [
4660
4832
  "Query execution error: ",
4661
4833
  runError
4662
- ] }) }) : wodWikiWql.isFindQuery(parsed) ? /* @__PURE__ */ jsxRuntime.jsx(QueryBlockShell, { onEdit, readOnly, children: findResult?.table ? /* @__PURE__ */ jsxRuntime.jsx(WidgetChart, { type: "table", result: findResult }) : resultId && findResult?.runs && findResult.runs.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx(
4663
- RowsResultsChrome,
4834
+ ] }) }) : /* @__PURE__ */ jsxRuntime.jsx(QueryBlockShell, { onEdit, readOnly, onAddToDashboard, children: /* @__PURE__ */ jsxRuntime.jsx(
4835
+ QueryBlockBody,
4664
4836
  {
4837
+ parsed,
4838
+ result: runResult,
4665
4839
  resultId,
4666
- sessionResult: findResult,
4840
+ widgetType,
4841
+ attributes: resolvedAttributes,
4842
+ fallbackLabel,
4667
4843
  executor,
4668
4844
  onCaptureRpe,
4669
- onCaptured: () => setRowsRefreshKey((k) => k + 1)
4670
- }
4671
- ) : findResult?.runs ? /* @__PURE__ */ jsxRuntime.jsx(RowsTable, { result: findResult }) : findResult ? /* @__PURE__ */ jsxRuntime.jsx(
4672
- FindResultList,
4673
- {
4674
- target: parsed.target,
4675
- notes: findResult.notes,
4676
- blocks: findResult.blocks,
4677
- containers: findResult.containers,
4845
+ onCaptured: () => setRowsRefreshKey((k) => k + 1),
4678
4846
  onOpenNote,
4679
4847
  noteHref
4680
4848
  }
4681
- ) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs text-muted-foreground py-2", children: "Loading rows\u2026" }) }) : wodWikiWql.isPipelineQuery(parsed) ? /* @__PURE__ */ jsxRuntime.jsx(QueryBlockShell, { onEdit, readOnly, children: /* @__PURE__ */ jsxRuntime.jsx(
4682
- PipelineResultView,
4683
- {
4684
- result: pipelineResult,
4685
- onOpenNote,
4686
- noteHref,
4687
- widgetType,
4688
- attributes: resolvedAttributes,
4689
- fallbackLabel: parsed.raw
4690
- }
4691
- ) }) : /* @__PURE__ */ jsxRuntime.jsx(QueryBlockShell, { onEdit, readOnly, children: /* @__PURE__ */ jsxRuntime.jsx(
4692
- AnalyticsChart,
4693
- {
4694
- result,
4695
- metric: parsed.metric,
4696
- widgetType,
4697
- attributes: resolvedAttributes
4698
- }
4699
4849
  ) }),
4700
4850
  canEdit && /* @__PURE__ */ jsxRuntime.jsx(
4701
4851
  WqlQueryInspectorModal,
@@ -4709,159 +4859,103 @@ function QueryBlockView({
4709
4859
  )
4710
4860
  ] });
4711
4861
  }
4712
- function AnalyticsChart({
4862
+ function QueryBlockBody({
4863
+ parsed,
4713
4864
  result,
4714
- metric,
4865
+ resultId,
4715
4866
  widgetType,
4716
- attributes
4717
- }) {
4718
- const shape = useChartShape(result);
4719
- if (widgetType != null && widgetType !== "") {
4720
- return /* @__PURE__ */ jsxRuntime.jsx(WidgetChart, { type: widgetType, result, label: metric, attributes });
4721
- }
4722
- if (shape.kind === "scalar") {
4723
- return /* @__PURE__ */ jsxRuntime.jsx(QueryValue, { result, label: metric });
4724
- }
4725
- if (shape.kind === "timeseries") {
4726
- return /* @__PURE__ */ jsxRuntime.jsx(WqlTimeseries, { result });
4727
- }
4728
- if (shape.kind === "bars") {
4729
- return /* @__PURE__ */ jsxRuntime.jsx(WqlBars, { result });
4730
- }
4731
- return /* @__PURE__ */ jsxRuntime.jsx(WqlEmptyState, { result });
4732
- }
4733
- function containerPageHref(container) {
4734
- if (!container || container.kind !== "page") return void 0;
4735
- const { slug, date } = container;
4736
- if (slug) return `/page/${encodeURIComponent(slug)}`;
4737
- if (date) return `/day/${encodeURIComponent(date)}`;
4738
- return void 0;
4739
- }
4740
- function FindResultList({
4741
- target,
4742
- notes,
4743
- blocks,
4744
- containers,
4867
+ attributes,
4868
+ fallbackLabel,
4869
+ executor,
4870
+ onCaptureRpe,
4871
+ onCaptured,
4745
4872
  onOpenNote,
4746
4873
  noteHref
4747
- }) {
4748
- const isBlock = target === "block";
4749
- const items = isBlock ? blocks : notes;
4750
- if (items.length === 0) {
4751
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-xs text-muted-foreground py-2", children: [
4752
- "No ",
4753
- target,
4754
- "s matched this query."
4755
- ] });
4756
- }
4757
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1", children: [
4758
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-[11px] font-medium text-muted-foreground mb-1", children: [
4759
- items.length,
4760
- " ",
4761
- target,
4762
- items.length === 1 ? "" : "s",
4763
- " matched"
4764
- ] }),
4765
- /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "space-y-0.5 max-h-48 overflow-y-auto font-mono text-xs", children: items.map((item) => {
4766
- const typedItem = item;
4767
- const title = isBlock ? typedItem.title || typedItem.blockContentId || item.id : typedItem.title || item.id;
4768
- const href = containers ? containerPageHref(containers[item.id]) ?? (noteHref ? noteHref(typedItem) : void 0) : noteHref ? noteHref(typedItem) : void 0;
4769
- return /* @__PURE__ */ jsxRuntime.jsx("li", { className: "rounded hover:bg-muted/50 truncate", children: href ? /* @__PURE__ */ jsxRuntime.jsx(
4770
- "a",
4771
- {
4772
- href,
4773
- onClick: (e) => {
4774
- if (onOpenNote) {
4775
- e.preventDefault();
4776
- onOpenNote(typedItem);
4777
- }
4778
- },
4779
- className: "block py-0.5 px-1.5 text-primary hover:underline truncate",
4780
- children: title
4781
- }
4782
- ) : onOpenNote ? /* @__PURE__ */ jsxRuntime.jsx(
4783
- "button",
4784
- {
4785
- type: "button",
4786
- onClick: () => onOpenNote(typedItem),
4787
- className: "w-full text-left py-0.5 px-1.5 text-primary hover:underline truncate",
4788
- children: title
4789
- }
4790
- ) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "block py-0.5 px-1.5 text-foreground truncate", children: title }) }, item.id);
4791
- }) })
4792
- ] });
4793
- }
4794
- function PipelineResultView({
4795
- result,
4796
- onOpenNote,
4797
- noteHref,
4798
- widgetType,
4799
- attributes,
4800
- fallbackLabel
4801
4874
  }) {
4802
4875
  if (!result) {
4803
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs text-muted-foreground py-2", children: "Loading\u2026" });
4876
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs text-muted-foreground py-2", children: wodWikiWql.isFindQuery(parsed) ? "Loading rows\u2026" : "Loading\u2026" });
4804
4877
  }
4805
- if (result.error) {
4806
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-xs text-destructive bg-destructive/10 border border-destructive/20 rounded p-2", children: [
4807
- "Query execution error: ",
4808
- result.error
4809
- ] });
4878
+ if (result.table && (widgetType == null || widgetType === "" || widgetType === "table")) {
4879
+ return /* @__PURE__ */ jsxRuntime.jsx(WidgetChart, { type: "table", result });
4810
4880
  }
4811
- const listTarget = result.parsed.source.kind === "query" && result.parsed.source.query.family === "find" ? result.parsed.source.query.target : "note";
4812
- if ((result.notes?.length ?? 0) + (result.blocks?.length ?? 0) > 0) {
4881
+ if (resultId && result.runs && result.runs.length > 0) {
4813
4882
  return /* @__PURE__ */ jsxRuntime.jsx(
4814
- FindResultList,
4883
+ RowsResultsChrome,
4815
4884
  {
4816
- target: listTarget,
4817
- notes: result.notes ?? [],
4818
- blocks: result.blocks ?? [],
4819
- containers: result.containers,
4820
- onOpenNote,
4821
- noteHref
4885
+ resultId,
4886
+ sessionResult: result,
4887
+ executor,
4888
+ onCaptureRpe,
4889
+ onCaptured
4822
4890
  }
4823
4891
  );
4824
4892
  }
4825
- if (result.runs && result.runs.length > 0) {
4826
- const rowsResult = { parsed: result.parsed, runs: result.runs, table: result.table };
4827
- return /* @__PURE__ */ jsxRuntime.jsx(RowsTable, { result: rowsResult });
4893
+ if (result.runs && (wodWikiWql.isFindQuery(parsed) || result.runs.length > 0)) {
4894
+ return /* @__PURE__ */ jsxRuntime.jsx(RowsTable, { result });
4828
4895
  }
4829
- const chartHead = widgetType != null && widgetType !== "" ? widgetType : result.chart?.head;
4830
- if (chartHead) {
4831
- const sinkAttributes = Object.fromEntries(
4832
- (result.chart?.filters ?? []).map((f) => [f.key, f.values.map((v) => v.value).join(",")])
4833
- );
4834
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full h-[220px] min-h-[160px]", children: /* @__PURE__ */ jsxRuntime.jsx(
4896
+ if (wodWikiWql.isFindQuery(parsed) || (result.notes?.length ?? 0) + (result.blocks?.length ?? 0) + (result.efforts?.length ?? 0) > 0 && (!result.series || result.series.length === 0)) {
4897
+ return /* @__PURE__ */ jsxRuntime.jsx(
4835
4898
  WidgetChart,
4836
4899
  {
4837
- type: chartHead,
4838
- result: toChartResult(result),
4839
- label: fallbackLabel,
4840
- attributes: { ...sinkAttributes, ...attributes }
4900
+ type: "list",
4901
+ result,
4902
+ onOpenNote,
4903
+ noteHref
4841
4904
  }
4842
- ) });
4843
- }
4844
- if (result.series && result.series.length > 0) {
4845
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full h-[220px] min-h-[160px]", children: /* @__PURE__ */ jsxRuntime.jsx(AnalyticsChart, { result: toChartResult(result), metric: fallbackLabel }) });
4905
+ );
4846
4906
  }
4847
- return /* @__PURE__ */ jsxRuntime.jsx(WqlEmptyState, { result: toChartResult(result) });
4907
+ const sinkHead = result.chart?.head;
4908
+ const effectiveType = widgetType != null && widgetType !== "" ? widgetType : sinkHead ?? "";
4909
+ const sinkAttributes = Object.fromEntries(
4910
+ (result.chart?.filters ?? []).map((f) => [f.key, f.values.map((v) => v.value).join(",")])
4911
+ );
4912
+ const mergedAttributes = { ...sinkAttributes, ...attributes };
4913
+ const needsChartHeight = wodWikiWql.isPipelineQuery(parsed) && (Boolean(effectiveType) || (result.series?.length ?? 0) > 0);
4914
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: needsChartHeight ? "w-full h-[220px] min-h-[160px]" : void 0, children: /* @__PURE__ */ jsxRuntime.jsx(
4915
+ WidgetChart,
4916
+ {
4917
+ type: effectiveType,
4918
+ result,
4919
+ label: fallbackLabel,
4920
+ attributes: mergedAttributes,
4921
+ inferDefaultShape: effectiveType === "",
4922
+ onOpenNote,
4923
+ noteHref
4924
+ }
4925
+ ) });
4848
4926
  }
4849
4927
  function QueryBlockShell({
4850
4928
  children,
4851
4929
  onEdit,
4852
- readOnly
4930
+ readOnly,
4931
+ onAddToDashboard
4853
4932
  }) {
4854
4933
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative group/block my-2 p-3 rounded-lg border border-border/80 bg-card/60 shadow-sm", children: [
4855
- onEdit && !readOnly && /* @__PURE__ */ jsxRuntime.jsx(
4856
- "button",
4857
- {
4858
- type: "button",
4859
- onClick: onEdit,
4860
- title: "Edit query in Omni-Composer",
4861
- 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",
4862
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Edit3, { className: "w-3.5 h-3.5" })
4863
- }
4864
- ),
4934
+ (onAddToDashboard || onEdit && !readOnly) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "absolute top-2 right-2 z-10 flex items-center gap-1", children: [
4935
+ onAddToDashboard && /* @__PURE__ */ jsxRuntime.jsx(
4936
+ "button",
4937
+ {
4938
+ type: "button",
4939
+ onClick: onAddToDashboard,
4940
+ "aria-label": "Add to dashboard",
4941
+ title: "Add to dashboard",
4942
+ "data-testid": "query-add-dashboard",
4943
+ className: "p-1 rounded bg-muted/80 text-muted-foreground hover:text-foreground",
4944
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChartBar, { className: "w-3.5 h-3.5" })
4945
+ }
4946
+ ),
4947
+ onEdit && !readOnly && /* @__PURE__ */ jsxRuntime.jsx(
4948
+ "button",
4949
+ {
4950
+ type: "button",
4951
+ onClick: onEdit,
4952
+ "aria-label": "Edit query in Omni-Composer",
4953
+ title: "Edit query in Omni-Composer",
4954
+ 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",
4955
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Edit3, { className: "w-3.5 h-3.5" })
4956
+ }
4957
+ )
4958
+ ] }),
4865
4959
  children
4866
4960
  ] });
4867
4961
  }
@@ -5273,6 +5367,7 @@ function deriveState(state, rawContent, sourceRevision, lineage, savedVersions,
5273
5367
  contentId: section.contentId,
5274
5368
  queryType: query?.type,
5275
5369
  queryError: query?.error,
5370
+ queryAttributes: query?.attributes,
5276
5371
  embed: section.embed
5277
5372
  };
5278
5373
  });
@@ -8351,22 +8446,26 @@ var cursorFocusExtension = [
8351
8446
  cursorFocusInternal
8352
8447
  ];
8353
8448
  var ReactQueryBlock = class extends view.WidgetType {
8354
- constructor(queryText, sectionId, widgetType, widgetError, options) {
8449
+ constructor(queryText, sectionId, widgetType, widgetError, attributes, tokenValues, options) {
8355
8450
  super();
8356
8451
  this.queryText = queryText;
8357
8452
  this.sectionId = sectionId;
8358
8453
  this.widgetType = widgetType;
8359
8454
  this.widgetError = widgetError;
8455
+ this.attributes = attributes;
8456
+ this.tokenValues = tokenValues;
8360
8457
  this.options = options;
8361
8458
  }
8362
8459
  queryText;
8363
8460
  sectionId;
8364
8461
  widgetType;
8365
8462
  widgetError;
8463
+ attributes;
8464
+ tokenValues;
8366
8465
  options;
8367
8466
  root = null;
8368
8467
  eq(other) {
8369
- 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;
8468
+ 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;
8370
8469
  }
8371
8470
  toDOM(view) {
8372
8471
  const container = document.createElement("div");
@@ -8387,6 +8486,8 @@ var ReactQueryBlock = class extends view.WidgetType {
8387
8486
  query: this.queryText,
8388
8487
  widgetType: this.widgetType,
8389
8488
  widgetError: this.widgetError,
8489
+ attributes: this.attributes,
8490
+ tokenValues: this.tokenValues,
8390
8491
  readOnly: this.options?.readOnly,
8391
8492
  executor: this.options?.executor,
8392
8493
  onResultSaved: this.options?.onResultSaved,
@@ -8414,6 +8515,8 @@ var ReactQueryBlock = class extends view.WidgetType {
8414
8515
  query: this.queryText,
8415
8516
  widgetType: this.widgetType,
8416
8517
  widgetError: this.widgetError,
8518
+ attributes: this.attributes,
8519
+ tokenValues: this.tokenValues,
8417
8520
  readOnly: this.options?.readOnly,
8418
8521
  executor: this.options?.executor,
8419
8522
  onResultSaved: this.options?.onResultSaved,
@@ -8441,8 +8544,11 @@ var ReactQueryBlock = class extends view.WidgetType {
8441
8544
  };
8442
8545
  function buildQueryBlockDecos(state$1, options) {
8443
8546
  const builder = new state.RangeSetBuilder();
8444
- const { sections } = state$1.field(sectionField);
8547
+ const { sections, rawContent: fullRawContent } = state$1.field(sectionField);
8445
8548
  const cursor2 = state$1.selection.main.head;
8549
+ const fmMeta = wodWikiCore.parseFrontmatter(fullRawContent).meta;
8550
+ const fmTokens = wodWikiWql.defaultTokenValues(wodWikiWql.extractDashboardTokens(fmMeta));
8551
+ const mergedTokens = { ...fmTokens, ...options?.tokenValues };
8446
8552
  for (const section of sections) {
8447
8553
  if (section.type !== "query") continue;
8448
8554
  if (cursor2 >= section.from && cursor2 <= section.to) continue;
@@ -8457,6 +8563,8 @@ function buildQueryBlockDecos(state$1, options) {
8457
8563
  section.id,
8458
8564
  section.queryType,
8459
8565
  section.queryError,
8566
+ section.queryAttributes,
8567
+ mergedTokens,
8460
8568
  options
8461
8569
  ),
8462
8570
  block: true
@@ -14303,13 +14411,13 @@ function editorPreset(optionsOrDialect = "markdown") {
14303
14411
  }
14304
14412
 
14305
14413
  // src/version.ts
14306
- var VERSION = "0.50.2189";
14307
- var GIT_SHA = "5da2c23";
14308
- var BUILD_TIME = "2026-11-01T16:27:05.203Z";
14414
+ var VERSION = "0.50.2191";
14415
+ var GIT_SHA = "95c89db";
14416
+ var BUILD_TIME = "2026-11-03T03:31:31.921Z";
14309
14417
  var SEMVER = {
14310
- version: "0.50.2189",
14311
- gitSha: "5da2c23",
14312
- buildTime: "2026-11-01T16:27:05.203Z"
14418
+ version: "0.50.2191",
14419
+ gitSha: "95c89db",
14420
+ buildTime: "2026-11-03T03:31:31.921Z"
14313
14421
  };
14314
14422
 
14315
14423
  Object.defineProperty(exports, "CONTENT_GROUPING_DIMENSIONS", {
@@ -14391,6 +14499,7 @@ exports.cn = cn;
14391
14499
  exports.compactNumber = compactNumber;
14392
14500
  exports.composerRegistry = composerRegistry;
14393
14501
  exports.configureOverlayActions = configureOverlayActions;
14502
+ exports.createExecutorDocumentHost = createExecutorDocumentHost;
14394
14503
  exports.cursorFocusExtension = cursorFocusExtension;
14395
14504
  exports.customCompletionSources = customCompletionSources;
14396
14505
  exports.dateRangeSlot = dateRangeSlot;
@@ -14447,6 +14556,7 @@ exports.nestedCompletion = nestedCompletion;
14447
14556
  exports.nestedCompletionOptions = nestedCompletionOptions;
14448
14557
  exports.normalizeOutputFilter = normalizeOutputFilter;
14449
14558
  exports.notebookSlugsFromBlocks = notebookSlugsFromBlocks;
14559
+ exports.outputToRunResult = outputToRunResult;
14450
14560
  exports.patchBlockQuery = patchBlockQuery;
14451
14561
  exports.presentBadges = presentBadges;
14452
14562
  exports.previewDecorations = previewDecorations;
@@ -14479,6 +14589,7 @@ exports.summarizeAggregate = summarizeAggregate;
14479
14589
  exports.summarizeFind = summarizeFind;
14480
14590
  exports.summarizePipeline = summarizePipeline;
14481
14591
  exports.tagsFromStaticBlocks = tagsFromStaticBlocks;
14592
+ exports.toChartResult = toChartResult;
14482
14593
  exports.toggleWidgetEdit = toggleWidgetEdit;
14483
14594
  exports.tooltipTimestamp = tooltipTimestamp;
14484
14595
  exports.urlAtPos = urlAtPos;