@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.
@@ -452,6 +452,7 @@ function deriveState(state, rawContent, sourceRevision, lineage, savedVersions,
452
452
  contentId: section.contentId,
453
453
  queryType: query?.type,
454
454
  queryError: query?.error,
455
+ queryAttributes: query?.attributes,
455
456
  embed: section.embed
456
457
  };
457
458
  });
@@ -4136,6 +4137,108 @@ function RowsResultsChrome({
4136
4137
  )
4137
4138
  ] });
4138
4139
  }
4140
+
4141
+ // src/widgets/chartPalette.ts
4142
+ var SERIES_COLORS = [
4143
+ "hsl(var(--metric-time))",
4144
+ "hsl(var(--metric-effort))",
4145
+ "hsl(var(--metric-rep))",
4146
+ "hsl(var(--metric-rounds))",
4147
+ "hsl(var(--metric-distance))",
4148
+ "hsl(var(--metric-resistance))",
4149
+ "hsl(var(--metric-action))",
4150
+ "hsl(var(--primary))"
4151
+ ];
4152
+ function createExecutorDocumentHost(executor, range = {}) {
4153
+ return {
4154
+ runAggregate: (queryText, overrides) => executor.runQuery(queryText, {
4155
+ context: overrides.context,
4156
+ rangeStart: range.rangeStart,
4157
+ rangeEnd: range.rangeEnd,
4158
+ preferredUnit: range.preferredUnit
4159
+ }),
4160
+ runFind: async (queryText, context) => {
4161
+ const parsed = wodWikiWql.parseQuery(queryText);
4162
+ if (!wodWikiWql.isFindQuery(parsed)) return { kind: "find", error: `not a content query: ${queryText}` };
4163
+ const res = await executor.runFind(parsed, {
4164
+ context,
4165
+ range: range.rangeStart !== void 0 ? { start: range.rangeStart, end: range.rangeEnd ?? Number.MAX_SAFE_INTEGER, endExclusive: false } : void 0
4166
+ });
4167
+ return {
4168
+ kind: "find",
4169
+ notes: res.notes,
4170
+ blocks: res.blocks,
4171
+ efforts: res.efforts,
4172
+ containers: res.containers,
4173
+ runs: res.runs,
4174
+ table: res.table
4175
+ };
4176
+ },
4177
+ runPipeline: async (queryText, context) => {
4178
+ const res = await executor.runPipeline?.(queryText, {
4179
+ context,
4180
+ rangeStart: range.rangeStart,
4181
+ rangeEnd: range.rangeEnd,
4182
+ preferredUnit: range.preferredUnit
4183
+ });
4184
+ return res ?? { error: "pipeline queries are not supported by this executor" };
4185
+ },
4186
+ rollupEnsure: range.rollupEnsure
4187
+ };
4188
+ }
4189
+ function toChartResult(input) {
4190
+ const parsed = input.parsed;
4191
+ const series = input.series ?? [];
4192
+ const scalar = series.length === 1 && series[0].points.length === 1 ? series[0].points[0].value : void 0;
4193
+ return {
4194
+ parsed,
4195
+ series,
4196
+ stages: { selected: 0, buckets: 0, aggregated: 0, groups: 0 },
4197
+ matched: [],
4198
+ ...scalar !== void 0 ? { scalar } : {},
4199
+ ...input.unit !== void 0 ? { unit: input.unit } : {},
4200
+ ...input.error ? { error: input.error } : {},
4201
+ ...input.table ? { table: input.table } : {}
4202
+ };
4203
+ }
4204
+ function outputToRunResult(parsed, out) {
4205
+ const base = toChartResult({
4206
+ parsed,
4207
+ series: out.series,
4208
+ unit: out.unit,
4209
+ error: out.error,
4210
+ table: out.table
4211
+ });
4212
+ return Object.assign(base, {
4213
+ ...out.notes ? { notes: out.notes } : {},
4214
+ ...out.blocks ? { blocks: out.blocks } : {},
4215
+ ...out.efforts ? { efforts: out.efforts } : {},
4216
+ ...out.containers ? { containers: out.containers } : {},
4217
+ ...out.runs ? { runs: out.runs } : {},
4218
+ ...out.chart ? { chart: out.chart } : {}
4219
+ });
4220
+ }
4221
+ function mergeSeries(series) {
4222
+ const map = /* @__PURE__ */ new Map();
4223
+ for (const s of series) {
4224
+ for (const p of s.points) {
4225
+ if (!map.has(p.ts)) map.set(p.ts, { ts: p.ts });
4226
+ map.get(p.ts)[s.label] = p.value;
4227
+ }
4228
+ }
4229
+ return Array.from(map.values()).sort((a, b) => a.ts - b.ts);
4230
+ }
4231
+ function compactNumber(value) {
4232
+ const n = typeof value === "number" ? value : Number(value);
4233
+ if (Math.abs(n) >= 1e3) return `${(n / 1e3).toFixed(1)}k`;
4234
+ return String(n);
4235
+ }
4236
+ function formatTimestamp(ts) {
4237
+ return new Date(ts).toLocaleDateString("en-US", { month: "short", day: "numeric" });
4238
+ }
4239
+ function tooltipTimestamp(ts) {
4240
+ return new Date(ts).toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric" });
4241
+ }
4139
4242
  function useChartShape(result) {
4140
4243
  return React2.useMemo(() => {
4141
4244
  if (!result) return { kind: "empty" };
@@ -4186,53 +4289,6 @@ function QueryValue({ result, unit: unitProp, label, thresholds }) {
4186
4289
  ] })
4187
4290
  ] });
4188
4291
  }
4189
-
4190
- // src/widgets/chartData.ts
4191
- function toChartResult(input) {
4192
- const parsed = input.parsed;
4193
- return {
4194
- parsed,
4195
- series: input.series ?? [],
4196
- stages: { selected: 0, buckets: 0, aggregated: 0, groups: 0 },
4197
- matched: [],
4198
- ...input.unit !== void 0 ? { unit: input.unit } : {},
4199
- ...input.error ? { error: input.error } : {},
4200
- ...input.table ? { table: input.table } : {}
4201
- };
4202
- }
4203
- function mergeSeries(series) {
4204
- const map = /* @__PURE__ */ new Map();
4205
- for (const s of series) {
4206
- for (const p of s.points) {
4207
- if (!map.has(p.ts)) map.set(p.ts, { ts: p.ts });
4208
- map.get(p.ts)[s.label] = p.value;
4209
- }
4210
- }
4211
- return Array.from(map.values()).sort((a, b) => a.ts - b.ts);
4212
- }
4213
- function compactNumber(value) {
4214
- const n = typeof value === "number" ? value : Number(value);
4215
- if (Math.abs(n) >= 1e3) return `${(n / 1e3).toFixed(1)}k`;
4216
- return String(n);
4217
- }
4218
- function formatTimestamp(ts) {
4219
- return new Date(ts).toLocaleDateString("en-US", { month: "short", day: "numeric" });
4220
- }
4221
- function tooltipTimestamp(ts) {
4222
- return new Date(ts).toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric" });
4223
- }
4224
-
4225
- // src/widgets/chartPalette.ts
4226
- var SERIES_COLORS = [
4227
- "hsl(var(--metric-time))",
4228
- "hsl(var(--metric-effort))",
4229
- "hsl(var(--metric-rep))",
4230
- "hsl(var(--metric-rounds))",
4231
- "hsl(var(--metric-distance))",
4232
- "hsl(var(--metric-resistance))",
4233
- "hsl(var(--metric-action))",
4234
- "hsl(var(--primary))"
4235
- ];
4236
4292
  function WqlTimeseries({ result, unit: unitProp }) {
4237
4293
  const shape = useChartShape(result);
4238
4294
  const data2 = React2.useMemo(() => mergeSeries(result.series), [result]);
@@ -4812,16 +4868,52 @@ function ZoneDistribution({ result, params, attributes }) {
4812
4868
  }
4813
4869
  );
4814
4870
  }
4815
- function WidgetChart({ type, result, label, unit, params, attributes }) {
4871
+ function inferChartTypeFromResult(result) {
4872
+ if (!result || result.series.length === 0) return "table";
4873
+ if (result.series.length === 1 && result.series[0].points.length === 1) return "value";
4874
+ const hasTimeAxis = result.series.some((s) => s.points.length > 1);
4875
+ return hasTimeAxis ? "timeseries" : "bar";
4876
+ }
4877
+ function WidgetChart({
4878
+ type,
4879
+ result,
4880
+ label,
4881
+ unit,
4882
+ params,
4883
+ attributes,
4884
+ inferDefaultShape = false,
4885
+ onOpenNote,
4886
+ noteHref
4887
+ }) {
4816
4888
  if (type !== "" && !wodWikiWql.isDashboardWidgetType(type)) {
4817
4889
  return /* @__PURE__ */ jsxRuntime.jsx(WidgetProblemBadge, { message: wodWikiWql.unknownWidgetTypeMessage(type) });
4818
4890
  }
4819
- const resolved = wodWikiWql.resolveWidgetType(type);
4820
- if (resolved === "table" && result?.table) {
4821
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full h-full min-h-[160px]", children: /* @__PURE__ */ jsxRuntime.jsx(TabularTable, { table: result.table }) });
4891
+ const resolved = type === "" && inferDefaultShape ? inferChartTypeFromResult(result) : wodWikiWql.resolveWidgetType(type);
4892
+ const runResult = result;
4893
+ const parsedFamily = runResult?.parsed?.family;
4894
+ const hasContentItems = parsedFamily === "find" || Boolean(runResult?.notes?.length || runResult?.blocks?.length || runResult?.efforts?.length || runResult?.runs?.length);
4895
+ if (resolved === "table" && runResult?.table) {
4896
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full h-full min-h-[160px]", children: /* @__PURE__ */ jsxRuntime.jsx(TabularTable, { table: runResult.table }) });
4897
+ }
4898
+ if (resolved === "table" && hasContentItems && (!result || !("series" in result) || result.series.length === 0)) {
4899
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full h-full min-h-[160px]", children: /* @__PURE__ */ jsxRuntime.jsx(
4900
+ ContentRecordsTable,
4901
+ {
4902
+ result: runResult,
4903
+ onOpenNote,
4904
+ noteHref
4905
+ }
4906
+ ) });
4822
4907
  }
4823
- if (resolved === "list" && result?.parsed?.family === "find") {
4824
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full h-full min-h-[160px]", children: /* @__PURE__ */ jsxRuntime.jsx(ThingList, { result }) });
4908
+ if (resolved === "list" && hasContentItems) {
4909
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full h-full min-h-[160px]", children: /* @__PURE__ */ jsxRuntime.jsx(
4910
+ ThingList,
4911
+ {
4912
+ result: runResult,
4913
+ onOpenNote,
4914
+ noteHref
4915
+ }
4916
+ ) });
4825
4917
  }
4826
4918
  if (!result || result.parsed.error || "series" in result && result.series.length === 0) {
4827
4919
  return /* @__PURE__ */ jsxRuntime.jsx(WqlEmptyState, { result });
@@ -4935,9 +5027,138 @@ function TabularTable({ table }) {
4935
5027
  ] }, 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)) })
4936
5028
  ] }) });
4937
5029
  }
4938
- function ThingList({ result }) {
4939
- const items = result.notes?.length ? result.notes : result.blocks?.length ? result.blocks : result.efforts?.length ? result.efforts : result.runs ?? [];
4940
- 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)) });
5030
+ function containerPageHref(container) {
5031
+ if (!container || container.kind !== "page") return void 0;
5032
+ const { slug, date } = container;
5033
+ if (slug) return `/page/${encodeURIComponent(slug)}`;
5034
+ if (date) return `/day/${encodeURIComponent(date)}`;
5035
+ return void 0;
5036
+ }
5037
+ function formatItemDate(item) {
5038
+ const raw = item.date ?? item.timestamp ?? item.createdAt;
5039
+ if (raw === void 0 || raw === null || raw === "") return "\u2014";
5040
+ if (typeof raw === "string" && /^\d{4}-\d{2}-\d{2}/.test(raw)) return raw.slice(0, 10);
5041
+ const ms = typeof raw === "number" ? raw : Date.parse(String(raw));
5042
+ if (!Number.isFinite(ms) || ms <= 0) return "\u2014";
5043
+ return new Date(ms).toISOString().slice(0, 10);
5044
+ }
5045
+ function ContentRecordsTable({
5046
+ result,
5047
+ onOpenNote,
5048
+ noteHref
5049
+ }) {
5050
+ const parsed = result?.parsed;
5051
+ const target = parsed?.target ?? (parsed?.source?.kind === "query" && parsed?.source?.query?.family === "find" ? parsed.source.query.target ?? "note" : result?.blocks?.length ? "block" : "note");
5052
+ const isBlock = target === "block" || Boolean(result?.blocks?.length && !result?.notes?.length);
5053
+ const rawItems = result?.notes?.length ? result.notes : result?.blocks?.length ? result.blocks : result?.efforts?.length ? result.efforts : result?.runs ?? [];
5054
+ const items = rawItems;
5055
+ if (items.length === 0) {
5056
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-xs text-muted-foreground py-4 text-center", "data-testid": "content-records-table-empty", children: [
5057
+ "No ",
5058
+ target,
5059
+ "s matched this query."
5060
+ ] });
5061
+ }
5062
+ const containers = result?.containers;
5063
+ 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: [
5064
+ /* @__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: [
5065
+ /* @__PURE__ */ jsxRuntime.jsx("th", { className: "py-2 px-3", children: "Title" }),
5066
+ /* @__PURE__ */ jsxRuntime.jsx("th", { className: "py-2 px-3", children: "Kind" }),
5067
+ /* @__PURE__ */ jsxRuntime.jsx("th", { className: "py-2 px-3", children: "Tags / Details" }),
5068
+ /* @__PURE__ */ jsxRuntime.jsx("th", { className: "py-2 px-3 text-right", children: "Date" })
5069
+ ] }) }),
5070
+ /* @__PURE__ */ jsxRuntime.jsx("tbody", { className: "divide-y divide-border/40", children: items.map((item, i) => {
5071
+ const id = item.id ?? item.resultId ?? String(i);
5072
+ const typedItem = { ...item, id, title: item.title ?? item.label ?? item.noteTitle };
5073
+ const title = isBlock ? typedItem.title || typedItem.blockContentId || id : typedItem.title || id;
5074
+ const href = containers ? containerPageHref(containers[id]) ?? (noteHref ? noteHref(typedItem) : void 0) : noteHref ? noteHref(typedItem) : void 0;
5075
+ const kindLabel = item.type ?? item.dataType ?? item.baseAttributes?.discipline ?? target;
5076
+ 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";
5077
+ return /* @__PURE__ */ jsxRuntime.jsxs("tr", { className: "hover:bg-muted/30 transition-colors", children: [
5078
+ /* @__PURE__ */ jsxRuntime.jsx("td", { className: "py-2 px-3 font-medium truncate max-w-[260px]", children: href ? /* @__PURE__ */ jsxRuntime.jsx(
5079
+ "a",
5080
+ {
5081
+ href,
5082
+ onClick: (e) => {
5083
+ if (onOpenNote) {
5084
+ e.preventDefault();
5085
+ onOpenNote(typedItem);
5086
+ }
5087
+ },
5088
+ className: "text-primary hover:underline",
5089
+ children: title
5090
+ }
5091
+ ) : onOpenNote ? /* @__PURE__ */ jsxRuntime.jsx(
5092
+ "button",
5093
+ {
5094
+ type: "button",
5095
+ onClick: () => onOpenNote(typedItem),
5096
+ className: "text-left text-primary hover:underline",
5097
+ children: title
5098
+ }
5099
+ ) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-foreground", children: title }) }),
5100
+ /* @__PURE__ */ jsxRuntime.jsx("td", { className: "py-2 px-3 font-mono text-[11px] text-muted-foreground", children: kindLabel }),
5101
+ /* @__PURE__ */ jsxRuntime.jsx("td", { className: "py-2 px-3 font-mono text-[11px] text-muted-foreground truncate max-w-[200px]", children: detail }),
5102
+ /* @__PURE__ */ jsxRuntime.jsx("td", { className: "py-2 px-3 font-mono text-[11px] text-muted-foreground text-right tabular-nums", children: formatItemDate(item) })
5103
+ ] }, id);
5104
+ }) })
5105
+ ] }) });
5106
+ }
5107
+ function ThingList({
5108
+ result,
5109
+ onOpenNote,
5110
+ noteHref
5111
+ }) {
5112
+ const parsed = result?.parsed;
5113
+ const target = parsed?.target ?? (parsed?.source?.kind === "query" && parsed?.source?.query?.family === "find" ? parsed.source.query.target ?? "note" : result?.blocks?.length ? "block" : "note");
5114
+ const isBlock = target === "block" || Boolean(result?.blocks?.length && !result?.notes?.length);
5115
+ const rawItems = result?.notes?.length ? result.notes : result?.blocks?.length ? result.blocks : result?.efforts?.length ? result.efforts : result?.runs ?? [];
5116
+ const items = rawItems;
5117
+ if (items.length === 0) {
5118
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-xs text-muted-foreground py-2", "data-testid": "thing-list", children: [
5119
+ "No ",
5120
+ target,
5121
+ "s matched this query."
5122
+ ] });
5123
+ }
5124
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "h-full overflow-auto space-y-1", "data-testid": "thing-list", children: [
5125
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-[11px] font-medium text-muted-foreground mb-1", children: [
5126
+ items.length,
5127
+ " ",
5128
+ target,
5129
+ items.length === 1 ? "" : "s",
5130
+ " matched"
5131
+ ] }),
5132
+ /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "space-y-0.5 max-h-48 overflow-y-auto font-mono text-xs", children: items.map((item, i) => {
5133
+ const id = item.id ?? item.resultId ?? String(i);
5134
+ const typedItem = { ...item, id, title: item.title ?? item.label ?? item.noteTitle };
5135
+ const title = isBlock ? typedItem.title || typedItem.blockContentId || id : typedItem.title || id;
5136
+ const containers = result?.containers;
5137
+ const href = containers ? containerPageHref(containers[id]) ?? (noteHref ? noteHref(typedItem) : void 0) : noteHref ? noteHref(typedItem) : void 0;
5138
+ return /* @__PURE__ */ jsxRuntime.jsx("li", { className: "rounded hover:bg-muted/50 truncate", children: href ? /* @__PURE__ */ jsxRuntime.jsx(
5139
+ "a",
5140
+ {
5141
+ href,
5142
+ onClick: (e) => {
5143
+ if (onOpenNote) {
5144
+ e.preventDefault();
5145
+ onOpenNote(typedItem);
5146
+ }
5147
+ },
5148
+ className: "block py-0.5 px-1.5 text-primary hover:underline truncate",
5149
+ children: title
5150
+ }
5151
+ ) : onOpenNote ? /* @__PURE__ */ jsxRuntime.jsx(
5152
+ "button",
5153
+ {
5154
+ type: "button",
5155
+ onClick: () => onOpenNote(typedItem),
5156
+ className: "w-full text-left py-0.5 px-1.5 text-primary hover:underline truncate",
5157
+ children: title
5158
+ }
5159
+ ) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "block py-0.5 px-1.5 text-foreground truncate", children: title }) }, id);
5160
+ }) })
5161
+ ] });
4941
5162
  }
4942
5163
 
4943
5164
  // src/utils/blockQueryPatcher.ts
@@ -7186,21 +7407,31 @@ function QueryBlockView({
7186
7407
  widgetError,
7187
7408
  tokenValues,
7188
7409
  attributes,
7410
+ onAddToDashboard,
7189
7411
  onCaptureRpe,
7190
7412
  onOpenNote,
7191
7413
  noteHref
7192
7414
  }) {
7193
7415
  const [isModalOpen, setIsModalOpen] = React2.useState(false);
7194
7416
  const extracted = React2.useMemo(() => extractBlockQueries(query), [query]);
7417
+ const rawBody = React2.useMemo(() => {
7418
+ const trimmed = query.trim();
7419
+ if (trimmed.includes("\n")) {
7420
+ const docParsed = wodWikiWql.parseDocument(trimmed);
7421
+ if (!docParsed.doc.degenerate && docParsed.diagnostics.length === 0 && docParsed.doc.assignments.length > 0) {
7422
+ return trimmed;
7423
+ }
7424
+ }
7425
+ return extracted.length > 0 ? extracted[0].query : query;
7426
+ }, [extracted, query]);
7195
7427
  const { query: effectiveQuery, missing } = React2.useMemo(() => {
7196
- const raw = extracted.length > 0 ? extracted[0].query : query;
7197
- const subQuery = wodWikiWql.substituteTokens(raw, tokenValues ?? {});
7428
+ const subQuery = wodWikiWql.substituteTokens(rawBody, tokenValues ?? {});
7198
7429
  const missingSet = new Set(subQuery.missing);
7199
7430
  for (const value of Object.values(attributes ?? {})) {
7200
7431
  for (const m of wodWikiWql.substituteTokens(value, tokenValues ?? {}).missing) missingSet.add(m);
7201
7432
  }
7202
7433
  return { query: subQuery.query, missing: [...missingSet] };
7203
- }, [extracted, query, tokenValues, attributes]);
7434
+ }, [rawBody, tokenValues, attributes]);
7204
7435
  const resolvedAttributes = React2.useMemo(() => {
7205
7436
  const out = {};
7206
7437
  for (const [key, value] of Object.entries(attributes ?? {})) {
@@ -7208,11 +7439,12 @@ function QueryBlockView({
7208
7439
  }
7209
7440
  return out;
7210
7441
  }, [attributes, tokenValues]);
7442
+ const docParse = React2.useMemo(() => wodWikiWql.parseDocument(effectiveQuery), [effectiveQuery]);
7443
+ const isMultiLineDoc = !docParse.doc.degenerate && docParse.doc.assignments.length > 0;
7211
7444
  const parsed = React2.useMemo(() => wodWikiWql.parseQuery(effectiveQuery), [effectiveQuery]);
7445
+ const syntaxError = isMultiLineDoc ? docParse.diagnostics[0] : parsed.error;
7212
7446
  const unknownType = widgetType != null && widgetType !== "" && !wodWikiWql.isDashboardWidgetType(widgetType);
7213
- const [result, setResult] = React2.useState(void 0);
7214
- const [findResult, setFindResult] = React2.useState(void 0);
7215
- const [pipelineResult, setPipelineResult] = React2.useState(void 0);
7447
+ const [runResult, setRunResult] = React2.useState(void 0);
7216
7448
  const [runError, setRunError] = React2.useState(void 0);
7217
7449
  const [rowsRefreshKey, setRowsRefreshKey] = React2.useState(0);
7218
7450
  React2.useEffect(() => {
@@ -7227,59 +7459,45 @@ function QueryBlockView({
7227
7459
  }, [onResultSaved]);
7228
7460
  React2.useEffect(() => {
7229
7461
  let cancelled = false;
7462
+ let retryTimer;
7230
7463
  setRunError(void 0);
7231
- setResult(void 0);
7232
- setFindResult(void 0);
7233
- setPipelineResult(void 0);
7234
- if (parsed.error) return;
7235
- if (widgetError || unknownType || missing.length > 0) return;
7236
- if (!executor) return;
7237
- if (wodWikiWql.isFindQuery(parsed)) {
7238
- let retryTimer;
7239
- const isSession = parsed.target === "session";
7240
- const executeFind = (attemptCount) => {
7241
- void executor.runFind(parsed).then((res) => {
7242
- if (cancelled) return;
7243
- setFindResult(res);
7244
- if (isSession && (res.runs ?? []).length === 0 && attemptCount < 4) {
7245
- const delays = [50, 150, 350, 750];
7246
- retryTimer = setTimeout(() => {
7247
- if (!cancelled) executeFind(attemptCount + 1);
7248
- }, delays[attemptCount] ?? 500);
7249
- }
7250
- }).catch((err) => {
7251
- if (!cancelled) setRunError(err instanceof Error ? err.message : String(err));
7252
- });
7253
- };
7254
- executeFind(0);
7255
- return () => {
7256
- cancelled = true;
7257
- clearTimeout(retryTimer);
7258
- };
7259
- }
7260
- if (wodWikiWql.isPipelineQuery(parsed)) {
7261
- if (!executor.runPipeline) {
7262
- setRunError("pipeline queries are not supported by this executor");
7263
- return;
7264
- }
7265
- void executor.runPipeline(effectiveQuery).then((res) => {
7266
- if (!cancelled) setPipelineResult(res);
7464
+ setRunResult(void 0);
7465
+ if (syntaxError || widgetError || unknownType || missing.length > 0 || !executor) return;
7466
+ const isSessionFind = !isMultiLineDoc && wodWikiWql.isFindQuery(parsed) && parsed.target === "session";
7467
+ const host = createExecutorDocumentHost(executor);
7468
+ const executeRun = (attemptCount) => {
7469
+ const runner = new wodWikiWql.SharedQueryDocumentRunner(host);
7470
+ void runner.run(effectiveQuery).then((res) => {
7471
+ if (cancelled) return;
7472
+ const out = res.outputs[0];
7473
+ if (!out) {
7474
+ setRunError(res.diagnostics[0] ?? "widget produced no output");
7475
+ return;
7476
+ }
7477
+ const err = out.error ?? res.diagnostics[0];
7478
+ if (err) {
7479
+ setRunError(err);
7480
+ return;
7481
+ }
7482
+ 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;
7483
+ const shaped = outputToRunResult(effectiveParsed, out);
7484
+ setRunResult(shaped);
7485
+ if (isSessionFind && (shaped.runs ?? []).length === 0 && attemptCount < 4) {
7486
+ const delays = [50, 150, 350, 750];
7487
+ retryTimer = setTimeout(() => {
7488
+ if (!cancelled) executeRun(attemptCount + 1);
7489
+ }, delays[attemptCount] ?? 500);
7490
+ }
7267
7491
  }).catch((err) => {
7268
7492
  if (!cancelled) setRunError(err instanceof Error ? err.message : String(err));
7269
7493
  });
7270
- return () => {
7271
- cancelled = true;
7272
- };
7273
- }
7274
- void executor.runQuery(effectiveQuery).then((res) => {
7275
- if (!cancelled) setResult(res);
7276
- }).catch((err) => {
7277
- if (!cancelled) setRunError(err instanceof Error ? err.message : String(err));
7278
- });
7494
+ };
7495
+ executeRun(0);
7279
7496
  return () => {
7280
7497
  cancelled = true;
7498
+ clearTimeout(retryTimer);
7281
7499
  };
7282
- }, [effectiveQuery, parsed, widgetError, unknownType, missing, executor, rowsRefreshKey]);
7500
+ }, [effectiveQuery, parsed, isMultiLineDoc, syntaxError, widgetError, unknownType, missing, executor, rowsRefreshKey]);
7283
7501
  const canEdit = onSaveQuery !== void 0 && !readOnly;
7284
7502
  const onEdit = canEdit ? () => setIsModalOpen(true) : void 0;
7285
7503
  const resultId = React2.useMemo(() => {
@@ -7287,50 +7505,32 @@ function QueryBlockView({
7287
7505
  const resultFilter = parsed.filters.find((f) => f.key === "result");
7288
7506
  return resultFilter?.values[0]?.value;
7289
7507
  }, [parsed]);
7508
+ const fallbackLabel = React2.useMemo(() => {
7509
+ if (!wodWikiWql.isFindQuery(parsed) && !wodWikiWql.isPipelineQuery(parsed)) return parsed.metric;
7510
+ return parsed.raw;
7511
+ }, [parsed]);
7290
7512
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-testid": "query-block-view", children: [
7291
- 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: [
7513
+ 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: [
7292
7514
  "Query syntax error: ",
7293
- parsed.error
7294
- ] }) }) : 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: [
7515
+ syntaxError
7516
+ ] }) }) : 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: [
7295
7517
  "Query execution error: ",
7296
7518
  runError
7297
- ] }) }) : 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(
7298
- RowsResultsChrome,
7519
+ ] }) }) : /* @__PURE__ */ jsxRuntime.jsx(QueryBlockShell, { onEdit, readOnly, onAddToDashboard, children: /* @__PURE__ */ jsxRuntime.jsx(
7520
+ QueryBlockBody,
7299
7521
  {
7522
+ parsed,
7523
+ result: runResult,
7300
7524
  resultId,
7301
- sessionResult: findResult,
7525
+ widgetType,
7526
+ attributes: resolvedAttributes,
7527
+ fallbackLabel,
7302
7528
  executor,
7303
7529
  onCaptureRpe,
7304
- onCaptured: () => setRowsRefreshKey((k) => k + 1)
7305
- }
7306
- ) : findResult?.runs ? /* @__PURE__ */ jsxRuntime.jsx(RowsTable, { result: findResult }) : findResult ? /* @__PURE__ */ jsxRuntime.jsx(
7307
- FindResultList,
7308
- {
7309
- target: parsed.target,
7310
- notes: findResult.notes,
7311
- blocks: findResult.blocks,
7312
- containers: findResult.containers,
7530
+ onCaptured: () => setRowsRefreshKey((k) => k + 1),
7313
7531
  onOpenNote,
7314
7532
  noteHref
7315
7533
  }
7316
- ) : /* @__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(
7317
- PipelineResultView,
7318
- {
7319
- result: pipelineResult,
7320
- onOpenNote,
7321
- noteHref,
7322
- widgetType,
7323
- attributes: resolvedAttributes,
7324
- fallbackLabel: parsed.raw
7325
- }
7326
- ) }) : /* @__PURE__ */ jsxRuntime.jsx(QueryBlockShell, { onEdit, readOnly, children: /* @__PURE__ */ jsxRuntime.jsx(
7327
- AnalyticsChart,
7328
- {
7329
- result,
7330
- metric: parsed.metric,
7331
- widgetType,
7332
- attributes: resolvedAttributes
7333
- }
7334
7534
  ) }),
7335
7535
  canEdit && /* @__PURE__ */ jsxRuntime.jsx(
7336
7536
  WqlQueryInspectorModal,
@@ -7344,179 +7544,127 @@ function QueryBlockView({
7344
7544
  )
7345
7545
  ] });
7346
7546
  }
7347
- function AnalyticsChart({
7547
+ function QueryBlockBody({
7548
+ parsed,
7348
7549
  result,
7349
- metric,
7550
+ resultId,
7350
7551
  widgetType,
7351
- attributes
7352
- }) {
7353
- const shape = useChartShape(result);
7354
- if (widgetType != null && widgetType !== "") {
7355
- return /* @__PURE__ */ jsxRuntime.jsx(WidgetChart, { type: widgetType, result, label: metric, attributes });
7356
- }
7357
- if (shape.kind === "scalar") {
7358
- return /* @__PURE__ */ jsxRuntime.jsx(QueryValue, { result, label: metric });
7359
- }
7360
- if (shape.kind === "timeseries") {
7361
- return /* @__PURE__ */ jsxRuntime.jsx(WqlTimeseries, { result });
7362
- }
7363
- if (shape.kind === "bars") {
7364
- return /* @__PURE__ */ jsxRuntime.jsx(WqlBars, { result });
7365
- }
7366
- return /* @__PURE__ */ jsxRuntime.jsx(WqlEmptyState, { result });
7367
- }
7368
- function containerPageHref(container) {
7369
- if (!container || container.kind !== "page") return void 0;
7370
- const { slug, date } = container;
7371
- if (slug) return `/page/${encodeURIComponent(slug)}`;
7372
- if (date) return `/day/${encodeURIComponent(date)}`;
7373
- return void 0;
7374
- }
7375
- function FindResultList({
7376
- target,
7377
- notes,
7378
- blocks,
7379
- containers,
7552
+ attributes,
7553
+ fallbackLabel,
7554
+ executor,
7555
+ onCaptureRpe,
7556
+ onCaptured,
7380
7557
  onOpenNote,
7381
7558
  noteHref
7382
- }) {
7383
- const isBlock = target === "block";
7384
- const items = isBlock ? blocks : notes;
7385
- if (items.length === 0) {
7386
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-xs text-muted-foreground py-2", children: [
7387
- "No ",
7388
- target,
7389
- "s matched this query."
7390
- ] });
7391
- }
7392
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1", children: [
7393
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-[11px] font-medium text-muted-foreground mb-1", children: [
7394
- items.length,
7395
- " ",
7396
- target,
7397
- items.length === 1 ? "" : "s",
7398
- " matched"
7399
- ] }),
7400
- /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "space-y-0.5 max-h-48 overflow-y-auto font-mono text-xs", children: items.map((item) => {
7401
- const typedItem = item;
7402
- const title = isBlock ? typedItem.title || typedItem.blockContentId || item.id : typedItem.title || item.id;
7403
- const href = containers ? containerPageHref(containers[item.id]) ?? (noteHref ? noteHref(typedItem) : void 0) : noteHref ? noteHref(typedItem) : void 0;
7404
- return /* @__PURE__ */ jsxRuntime.jsx("li", { className: "rounded hover:bg-muted/50 truncate", children: href ? /* @__PURE__ */ jsxRuntime.jsx(
7405
- "a",
7406
- {
7407
- href,
7408
- onClick: (e) => {
7409
- if (onOpenNote) {
7410
- e.preventDefault();
7411
- onOpenNote(typedItem);
7412
- }
7413
- },
7414
- className: "block py-0.5 px-1.5 text-primary hover:underline truncate",
7415
- children: title
7416
- }
7417
- ) : onOpenNote ? /* @__PURE__ */ jsxRuntime.jsx(
7418
- "button",
7419
- {
7420
- type: "button",
7421
- onClick: () => onOpenNote(typedItem),
7422
- className: "w-full text-left py-0.5 px-1.5 text-primary hover:underline truncate",
7423
- children: title
7424
- }
7425
- ) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "block py-0.5 px-1.5 text-foreground truncate", children: title }) }, item.id);
7426
- }) })
7427
- ] });
7428
- }
7429
- function PipelineResultView({
7430
- result,
7431
- onOpenNote,
7432
- noteHref,
7433
- widgetType,
7434
- attributes,
7435
- fallbackLabel
7436
7559
  }) {
7437
7560
  if (!result) {
7438
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs text-muted-foreground py-2", children: "Loading\u2026" });
7561
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs text-muted-foreground py-2", children: wodWikiWql.isFindQuery(parsed) ? "Loading rows\u2026" : "Loading\u2026" });
7439
7562
  }
7440
- if (result.error) {
7441
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-xs text-destructive bg-destructive/10 border border-destructive/20 rounded p-2", children: [
7442
- "Query execution error: ",
7443
- result.error
7444
- ] });
7563
+ if (result.table && (widgetType == null || widgetType === "" || widgetType === "table")) {
7564
+ return /* @__PURE__ */ jsxRuntime.jsx(WidgetChart, { type: "table", result });
7445
7565
  }
7446
- const listTarget = result.parsed.source.kind === "query" && result.parsed.source.query.family === "find" ? result.parsed.source.query.target : "note";
7447
- if ((result.notes?.length ?? 0) + (result.blocks?.length ?? 0) > 0) {
7566
+ if (resultId && result.runs && result.runs.length > 0) {
7448
7567
  return /* @__PURE__ */ jsxRuntime.jsx(
7449
- FindResultList,
7568
+ RowsResultsChrome,
7450
7569
  {
7451
- target: listTarget,
7452
- notes: result.notes ?? [],
7453
- blocks: result.blocks ?? [],
7454
- containers: result.containers,
7455
- onOpenNote,
7456
- noteHref
7570
+ resultId,
7571
+ sessionResult: result,
7572
+ executor,
7573
+ onCaptureRpe,
7574
+ onCaptured
7457
7575
  }
7458
7576
  );
7459
7577
  }
7460
- if (result.runs && result.runs.length > 0) {
7461
- const rowsResult = { parsed: result.parsed, runs: result.runs, table: result.table };
7462
- return /* @__PURE__ */ jsxRuntime.jsx(RowsTable, { result: rowsResult });
7578
+ if (result.runs && (wodWikiWql.isFindQuery(parsed) || result.runs.length > 0)) {
7579
+ return /* @__PURE__ */ jsxRuntime.jsx(RowsTable, { result });
7463
7580
  }
7464
- const chartHead = widgetType != null && widgetType !== "" ? widgetType : result.chart?.head;
7465
- if (chartHead) {
7466
- const sinkAttributes = Object.fromEntries(
7467
- (result.chart?.filters ?? []).map((f) => [f.key, f.values.map((v) => v.value).join(",")])
7468
- );
7469
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full h-[220px] min-h-[160px]", children: /* @__PURE__ */ jsxRuntime.jsx(
7581
+ if (wodWikiWql.isFindQuery(parsed) || (result.notes?.length ?? 0) + (result.blocks?.length ?? 0) + (result.efforts?.length ?? 0) > 0 && (!result.series || result.series.length === 0)) {
7582
+ return /* @__PURE__ */ jsxRuntime.jsx(
7470
7583
  WidgetChart,
7471
7584
  {
7472
- type: chartHead,
7473
- result: toChartResult(result),
7474
- label: fallbackLabel,
7475
- attributes: { ...sinkAttributes, ...attributes }
7585
+ type: "list",
7586
+ result,
7587
+ onOpenNote,
7588
+ noteHref
7476
7589
  }
7477
- ) });
7478
- }
7479
- if (result.series && result.series.length > 0) {
7480
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full h-[220px] min-h-[160px]", children: /* @__PURE__ */ jsxRuntime.jsx(AnalyticsChart, { result: toChartResult(result), metric: fallbackLabel }) });
7590
+ );
7481
7591
  }
7482
- return /* @__PURE__ */ jsxRuntime.jsx(WqlEmptyState, { result: toChartResult(result) });
7592
+ const sinkHead = result.chart?.head;
7593
+ const effectiveType = widgetType != null && widgetType !== "" ? widgetType : sinkHead ?? "";
7594
+ const sinkAttributes = Object.fromEntries(
7595
+ (result.chart?.filters ?? []).map((f) => [f.key, f.values.map((v) => v.value).join(",")])
7596
+ );
7597
+ const mergedAttributes = { ...sinkAttributes, ...attributes };
7598
+ const needsChartHeight = wodWikiWql.isPipelineQuery(parsed) && (Boolean(effectiveType) || (result.series?.length ?? 0) > 0);
7599
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: needsChartHeight ? "w-full h-[220px] min-h-[160px]" : void 0, children: /* @__PURE__ */ jsxRuntime.jsx(
7600
+ WidgetChart,
7601
+ {
7602
+ type: effectiveType,
7603
+ result,
7604
+ label: fallbackLabel,
7605
+ attributes: mergedAttributes,
7606
+ inferDefaultShape: effectiveType === "",
7607
+ onOpenNote,
7608
+ noteHref
7609
+ }
7610
+ ) });
7483
7611
  }
7484
7612
  function QueryBlockShell({
7485
7613
  children,
7486
7614
  onEdit,
7487
- readOnly
7615
+ readOnly,
7616
+ onAddToDashboard
7488
7617
  }) {
7489
7618
  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: [
7490
- onEdit && !readOnly && /* @__PURE__ */ jsxRuntime.jsx(
7491
- "button",
7492
- {
7493
- type: "button",
7494
- onClick: onEdit,
7495
- title: "Edit query in Omni-Composer",
7496
- 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",
7497
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Edit3, { className: "w-3.5 h-3.5" })
7498
- }
7499
- ),
7619
+ (onAddToDashboard || onEdit && !readOnly) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "absolute top-2 right-2 z-10 flex items-center gap-1", children: [
7620
+ onAddToDashboard && /* @__PURE__ */ jsxRuntime.jsx(
7621
+ "button",
7622
+ {
7623
+ type: "button",
7624
+ onClick: onAddToDashboard,
7625
+ "aria-label": "Add to dashboard",
7626
+ title: "Add to dashboard",
7627
+ "data-testid": "query-add-dashboard",
7628
+ className: "p-1 rounded bg-muted/80 text-muted-foreground hover:text-foreground",
7629
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChartBar, { className: "w-3.5 h-3.5" })
7630
+ }
7631
+ ),
7632
+ onEdit && !readOnly && /* @__PURE__ */ jsxRuntime.jsx(
7633
+ "button",
7634
+ {
7635
+ type: "button",
7636
+ onClick: onEdit,
7637
+ "aria-label": "Edit query in Omni-Composer",
7638
+ title: "Edit query in Omni-Composer",
7639
+ 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",
7640
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Edit3, { className: "w-3.5 h-3.5" })
7641
+ }
7642
+ )
7643
+ ] }),
7500
7644
  children
7501
7645
  ] });
7502
7646
  }
7503
7647
  var ReactQueryBlock = class extends view.WidgetType {
7504
- constructor(queryText, sectionId, widgetType, widgetError, options) {
7648
+ constructor(queryText, sectionId, widgetType, widgetError, attributes, tokenValues, options) {
7505
7649
  super();
7506
7650
  this.queryText = queryText;
7507
7651
  this.sectionId = sectionId;
7508
7652
  this.widgetType = widgetType;
7509
7653
  this.widgetError = widgetError;
7654
+ this.attributes = attributes;
7655
+ this.tokenValues = tokenValues;
7510
7656
  this.options = options;
7511
7657
  }
7512
7658
  queryText;
7513
7659
  sectionId;
7514
7660
  widgetType;
7515
7661
  widgetError;
7662
+ attributes;
7663
+ tokenValues;
7516
7664
  options;
7517
7665
  root = null;
7518
7666
  eq(other) {
7519
- 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;
7667
+ 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;
7520
7668
  }
7521
7669
  toDOM(view) {
7522
7670
  const container = document.createElement("div");
@@ -7537,6 +7685,8 @@ var ReactQueryBlock = class extends view.WidgetType {
7537
7685
  query: this.queryText,
7538
7686
  widgetType: this.widgetType,
7539
7687
  widgetError: this.widgetError,
7688
+ attributes: this.attributes,
7689
+ tokenValues: this.tokenValues,
7540
7690
  readOnly: this.options?.readOnly,
7541
7691
  executor: this.options?.executor,
7542
7692
  onResultSaved: this.options?.onResultSaved,
@@ -7564,6 +7714,8 @@ var ReactQueryBlock = class extends view.WidgetType {
7564
7714
  query: this.queryText,
7565
7715
  widgetType: this.widgetType,
7566
7716
  widgetError: this.widgetError,
7717
+ attributes: this.attributes,
7718
+ tokenValues: this.tokenValues,
7567
7719
  readOnly: this.options?.readOnly,
7568
7720
  executor: this.options?.executor,
7569
7721
  onResultSaved: this.options?.onResultSaved,
@@ -7591,8 +7743,11 @@ var ReactQueryBlock = class extends view.WidgetType {
7591
7743
  };
7592
7744
  function buildQueryBlockDecos(state$1, options) {
7593
7745
  const builder = new state.RangeSetBuilder();
7594
- const { sections } = state$1.field(sectionField);
7746
+ const { sections, rawContent: fullRawContent } = state$1.field(sectionField);
7595
7747
  const cursor2 = state$1.selection.main.head;
7748
+ const fmMeta = wodWikiCore.parseFrontmatter(fullRawContent).meta;
7749
+ const fmTokens = wodWikiWql.defaultTokenValues(wodWikiWql.extractDashboardTokens(fmMeta));
7750
+ const mergedTokens = { ...fmTokens, ...options?.tokenValues };
7596
7751
  for (const section of sections) {
7597
7752
  if (section.type !== "query") continue;
7598
7753
  if (cursor2 >= section.from && cursor2 <= section.to) continue;
@@ -7607,6 +7762,8 @@ function buildQueryBlockDecos(state$1, options) {
7607
7762
  section.id,
7608
7763
  section.queryType,
7609
7764
  section.queryError,
7765
+ section.queryAttributes,
7766
+ mergedTokens,
7610
7767
  options
7611
7768
  ),
7612
7769
  block: true