@bitcobblers/wod-wiki-ui 0.45.2167 → 0.47.2169

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
@@ -2447,7 +2447,9 @@ var CLAUSE_META = {
2447
2447
  note: { label: "Note", inputType: "freetext", placeholder: "note id\u2026", placeholderText: "note: [id]", icon: "\u{1F4D3}", description: "Scope to one note", prefix: "note:" },
2448
2448
  page: { label: "Page", inputType: "select", placeholder: "slug, date, true\u2026", placeholderText: "page: [slug|date]", icon: "\u{1F4C4}", description: "On :page \u2014 the page slug or YYYY-MM-DD date; on :note \u2014 page-ness (true/false)", prefix: "page:" },
2449
2449
  plane: { label: "Output Plane", inputType: "select", placeholder: "segment, load, event\u2026", placeholderText: "plane: [type]", icon: "\u{1F4CB}", description: "Output-statement plane on :session", prefix: "plane:" },
2450
- pipes: { label: "Pipes", inputType: "freetext", placeholder: "| order by date | limit 10", placeholderText: "| select | order | limit", icon: "\u21E5", description: "Presentation pipes \u2014 select/order/limit/offset" }
2450
+ pipes: { label: "Pipes", inputType: "freetext", placeholder: "| order by date | limit 10", placeholderText: "| select | order | limit", icon: "\u21E5", description: "Presentation pipes \u2014 select/order/limit/offset" },
2451
+ feed: { label: "Feed", inputType: "select", placeholder: "core, local, feed id\u2026", placeholderText: "feed: [id]", icon: "\u{1F4E1}", description: "Filter by publishing feed source", prefix: "feed:" },
2452
+ subscribed: { label: "Subscribed", inputType: "select", placeholder: "true, false", placeholderText: "subscribed: [true|false]", icon: "\u2B50", description: "Filter by notebook subscription state", prefix: "subscribed:" }
2451
2453
  };
2452
2454
  var CUSTOM_FALLBACK_ICON = "\u{1F9E9}";
2453
2455
  function getClauseMeta(type) {
@@ -5743,9 +5745,51 @@ var referenceIndexReload = state.StateEffect.define();
5743
5745
  var frontmatterVisibility = state.Facet.define({
5744
5746
  combine: (values2) => values2[0] ?? true
5745
5747
  });
5748
+ var setFrontmatterDetailed = state.StateEffect.define();
5749
+ var frontmatterDetailed = state.StateField.define({
5750
+ create: () => false,
5751
+ update(value, tr) {
5752
+ for (const effect of tr.effects) {
5753
+ if (effect.is(setFrontmatterDetailed)) return effect.value;
5754
+ }
5755
+ return value;
5756
+ }
5757
+ });
5758
+ var setFrontmatterSource = state.StateEffect.define();
5759
+ var frontmatterSource = state.StateField.define({
5760
+ create: () => false,
5761
+ update(value, tr) {
5762
+ let source = value;
5763
+ for (const effect of tr.effects) {
5764
+ if (effect.is(setFrontmatterSource)) source = effect.value;
5765
+ }
5766
+ if (source && tr.selection) {
5767
+ const section = tr.state.field(sectionField).sections.find((item) => item.type === "frontmatter");
5768
+ if (!section || !tr.state.selection.ranges.some((range) => range.from <= section.to && range.to >= section.from)) {
5769
+ source = false;
5770
+ }
5771
+ }
5772
+ return source;
5773
+ }
5774
+ });
5746
5775
  var pendingFocus = /* @__PURE__ */ new WeakMap();
5747
5776
  var widgetCleanup = /* @__PURE__ */ new WeakMap();
5748
5777
  var helperId = 0;
5778
+ var TAG_CATEGORY_SUMMARY_KEYS = {
5779
+ tag: true,
5780
+ tags: true,
5781
+ category: true,
5782
+ domain: true,
5783
+ format: true,
5784
+ equipment: true,
5785
+ effort: true,
5786
+ quality: true,
5787
+ intent: true,
5788
+ discipline: true,
5789
+ intensity: true,
5790
+ intensitytier: true,
5791
+ sound: true
5792
+ };
5749
5793
  function isTagProperty(key) {
5750
5794
  return ["tag", "tags", "category"].includes(key.trim().toLowerCase());
5751
5795
  }
@@ -5862,12 +5906,18 @@ function parseTagList(val) {
5862
5906
  }
5863
5907
  return [];
5864
5908
  }
5865
- function patchPropertyInView(view, _sectionId, path, value) {
5909
+ function patchPropertyInView(view, _sectionId, path, value, expandDetailed = false) {
5866
5910
  if (view.state.readOnly) return;
5867
5911
  const wholeDoc = view.state.doc.toString();
5868
5912
  const next = wodWikiCore.patchFrontmatterProperty(wholeDoc, path, value);
5869
- if (next === wholeDoc) return;
5870
- view.dispatch({ changes: { from: 0, to: wholeDoc.length, insert: next } });
5913
+ if (next === wholeDoc) {
5914
+ if (expandDetailed) view.dispatch({ effects: setFrontmatterDetailed.of(true) });
5915
+ return;
5916
+ }
5917
+ view.dispatch({
5918
+ changes: { from: 0, to: wholeDoc.length, insert: next },
5919
+ ...expandDetailed ? { effects: setFrontmatterDetailed.of(true) } : {}
5920
+ });
5871
5921
  }
5872
5922
  function renamePropertyInView(view, _sectionId, fromKey, toKey, value) {
5873
5923
  if (view.state.readOnly) return;
@@ -5887,12 +5937,22 @@ function detectDefinitionKind(meta) {
5887
5937
  if (meta.registrySource !== void 0 || meta.baseAttributes !== void 0) return "effort";
5888
5938
  return null;
5889
5939
  }
5890
- function readBaseAttributes(body) {
5940
+ function readBaseAttributes(body, meta = {}) {
5891
5941
  const out = {};
5942
+ if (meta["metrics.met"] !== void 0 && !Array.isArray(meta["metrics.met"])) {
5943
+ out.met = String(meta["metrics.met"]);
5944
+ }
5945
+ if (meta["discipline"] !== void 0 && !Array.isArray(meta["discipline"])) {
5946
+ out.discipline = String(meta["discipline"]);
5947
+ }
5948
+ if (meta["intensityTier"] !== void 0 && !Array.isArray(meta["intensityTier"])) {
5949
+ out.intensityTier = String(meta["intensityTier"]);
5950
+ }
5892
5951
  let inBase = false;
5893
5952
  for (const line of body.split(/\r?\n/)) {
5894
5953
  if (/^baseAttributes:\s*(#.*)?$/.test(line)) {
5895
5954
  inBase = true;
5955
+ out.hasBaseBlock = true;
5896
5956
  continue;
5897
5957
  }
5898
5958
  if (!inBase) continue;
@@ -6022,7 +6082,8 @@ function renderEffortValueDiagnostics(provider, token, onPick) {
6022
6082
  var METRIC_DRAFT_KINDS = {
6023
6083
  effort: { label: "Effort", path: "metrics.effort", kind: "effort" },
6024
6084
  equipment: { label: "Equipment", path: "metrics.equipment", kind: "equipment" },
6025
- rpe: { label: "RPE", path: "metrics.rpe", kind: "number" }
6085
+ rpe: { label: "RPE", path: "metrics.rpe", kind: "number" },
6086
+ custom: { label: "Custom / Equation", path: "metrics.", kind: "custom" }
6026
6087
  };
6027
6088
  function createMetricDraftRow(view, root, referenceProvider, commit) {
6028
6089
  const row = document.createElement("div");
@@ -6041,6 +6102,7 @@ function createMetricDraftRow(view, root, referenceProvider, commit) {
6041
6102
  row.appendChild(select);
6042
6103
  const valCol = document.createElement("div");
6043
6104
  valCol.className = "flex items-center flex-wrap gap-1.5 px-3 py-1.5 flex-1 min-w-0";
6105
+ let customKeyInput = null;
6044
6106
  const input = document.createElement("input");
6045
6107
  input.className = "flex-none w-44 max-w-full rounded-md border border-field-border bg-background/60 px-2 py-1 text-xs text-foreground outline-none placeholder:text-field-placeholder focus:border-ring focus:ring-1 focus:ring-ring";
6046
6108
  input.placeholder = "Value\u2026";
@@ -6063,6 +6125,10 @@ function createMetricDraftRow(view, root, referenceProvider, commit) {
6063
6125
  else if (spec.kind === "number") {
6064
6126
  const num = Number(raw);
6065
6127
  if (!Number.isNaN(num)) commit([spec.path], num);
6128
+ } else if (spec.kind === "custom") {
6129
+ const keyName = (customKeyInput?.value.trim() || "custom").replace(/^metrics\./i, "");
6130
+ const num = Number(raw);
6131
+ commit([`metrics.${keyName}`], !Number.isNaN(num) && raw !== "" ? num : raw);
6066
6132
  } else commit([spec.path], raw);
6067
6133
  }
6068
6134
  row.remove();
@@ -6070,6 +6136,31 @@ function createMetricDraftRow(view, root, referenceProvider, commit) {
6070
6136
  };
6071
6137
  const attachFor = () => {
6072
6138
  const spec = METRIC_DRAFT_KINDS[select.value];
6139
+ if (spec?.kind === "custom") {
6140
+ if (!customKeyInput) {
6141
+ customKeyInput = document.createElement("input");
6142
+ customKeyInput.className = "flex-none w-32 max-w-full rounded-md border border-field-border bg-background/60 px-2 py-1 text-xs text-foreground outline-none placeholder:text-field-placeholder focus:border-ring focus:ring-1 focus:ring-ring";
6143
+ customKeyInput.placeholder = "Metric key\u2026";
6144
+ customKeyInput.setAttribute("aria-label", "Metric key");
6145
+ customKeyInput.dataset.frontmatterField = "key";
6146
+ customKeyInput.addEventListener("keydown", (e) => {
6147
+ if (e.key === "Escape") {
6148
+ e.preventDefault();
6149
+ settle(false);
6150
+ } else if (e.key === "Enter") {
6151
+ e.preventDefault();
6152
+ if (input.value.trim()) settle(true);
6153
+ else input.focus();
6154
+ }
6155
+ });
6156
+ valCol.insertBefore(customKeyInput, input);
6157
+ }
6158
+ input.placeholder = "Value or equation\u2026";
6159
+ } else if (customKeyInput) {
6160
+ customKeyInput.remove();
6161
+ customKeyInput = null;
6162
+ input.placeholder = "Value\u2026";
6163
+ }
6073
6164
  if (spec && (spec.kind === "effort" || spec.kind === "equipment") && referenceProvider) {
6074
6165
  attachSuggestions(input, view, root, "value", spec.path, [], () => settle(true), spec.kind);
6075
6166
  }
@@ -6090,6 +6181,7 @@ function createMetricDraftRow(view, root, referenceProvider, commit) {
6090
6181
  });
6091
6182
  input.addEventListener("blur", () => queueMicrotask(() => {
6092
6183
  if (!input.isConnected) return;
6184
+ if (row.contains(document.activeElement)) return;
6093
6185
  settle(input.value.trim().length > 0);
6094
6186
  }));
6095
6187
  select.addEventListener("keydown", (e) => {
@@ -6100,6 +6192,82 @@ function createMetricDraftRow(view, root, referenceProvider, commit) {
6100
6192
  });
6101
6193
  return row;
6102
6194
  }
6195
+ function createSummaryDraftRow(view, commit) {
6196
+ const row = document.createElement("div");
6197
+ row.className = "flex items-center min-h-[34px] border-b border-dashed border-border last:border-b-0 group hover:bg-muted/10 transition-colors max-[479px]:flex-col max-[479px]:items-stretch";
6198
+ row.setAttribute("data-frontmatter-summary-draft", "true");
6199
+ row.dataset.frontmatterRow = "summary";
6200
+ const keyCol = document.createElement("div");
6201
+ keyCol.className = "flex items-center gap-2 px-3 py-1.5 w-36 shrink-0 border-r border-dashed border-border text-muted-foreground max-[479px]:w-auto max-[479px]:border-r-0 max-[479px]:border-b";
6202
+ keyCol.appendChild(createListIcon());
6203
+ const keyInput = document.createElement("input");
6204
+ keyInput.className = "w-full rounded-md border border-field-border bg-background/60 px-2 py-1 text-xs text-foreground outline-none placeholder:text-field-placeholder focus:border-ring focus:ring-1 focus:ring-ring";
6205
+ keyInput.placeholder = "Summary key\u2026";
6206
+ keyInput.setAttribute("aria-label", "Summary key");
6207
+ keyInput.dataset.frontmatterField = "key";
6208
+ keyInput.setAttribute(
6209
+ "aria-describedby",
6210
+ appendFieldHelper(keyCol, "Summary metric name. Press Enter to move to the equation.").id
6211
+ );
6212
+ keyCol.appendChild(keyInput);
6213
+ row.appendChild(keyCol);
6214
+ const valCol = document.createElement("div");
6215
+ valCol.className = "flex items-center flex-wrap gap-1.5 px-3 py-1.5 flex-1 min-w-0";
6216
+ const valInput = document.createElement("input");
6217
+ valInput.className = "flex-1 min-w-[160px] rounded-md border border-field-border bg-background/60 px-2 py-1 text-xs text-foreground outline-none placeholder:text-field-placeholder focus:border-ring focus:ring-1 focus:ring-ring";
6218
+ valInput.placeholder = "Equation (e.g. sum(work))\u2026";
6219
+ valInput.setAttribute("aria-label", "Summary equation");
6220
+ valInput.dataset.frontmatterField = "value";
6221
+ valInput.setAttribute(
6222
+ "aria-describedby",
6223
+ appendFieldHelper(valCol, "Summary equation. Press Enter to apply.").id
6224
+ );
6225
+ valCol.appendChild(valInput);
6226
+ row.appendChild(valCol);
6227
+ let settled = false;
6228
+ const settle = (doCommit) => {
6229
+ if (settled) return;
6230
+ settled = true;
6231
+ const rawKey = (keyInput.value.trim() || "summary").replace(/^summaries\./i, "");
6232
+ const rawExpr = valInput.value.trim();
6233
+ if (doCommit && rawExpr) {
6234
+ commit([`summaries.${rawKey}`], rawExpr);
6235
+ }
6236
+ row.remove();
6237
+ view.requestMeasure();
6238
+ };
6239
+ keyInput.addEventListener("keydown", (e) => {
6240
+ if (e.key === "Escape") {
6241
+ e.preventDefault();
6242
+ settle(false);
6243
+ return;
6244
+ }
6245
+ if (e.key === "Enter") {
6246
+ e.preventDefault();
6247
+ if (valInput.value.trim()) settle(true);
6248
+ else valInput.focus();
6249
+ }
6250
+ });
6251
+ valInput.addEventListener("keydown", (e) => {
6252
+ if (e.key === "Escape") {
6253
+ e.preventDefault();
6254
+ settle(false);
6255
+ return;
6256
+ }
6257
+ if (e.key === "Enter") {
6258
+ e.preventDefault();
6259
+ settle(true);
6260
+ }
6261
+ });
6262
+ const onBlur = () => queueMicrotask(() => {
6263
+ if (!row.isConnected) return;
6264
+ if (row.contains(document.activeElement)) return;
6265
+ settle(valInput.value.trim().length > 0);
6266
+ });
6267
+ keyInput.addEventListener("blur", onBlur);
6268
+ valInput.addEventListener("blur", onBlur);
6269
+ return row;
6270
+ }
6103
6271
  function createTagIcon() {
6104
6272
  const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
6105
6273
  svg.setAttribute("viewBox", "0 0 24 24");
@@ -6208,18 +6376,88 @@ function createCloseIcon(size = "md") {
6208
6376
  svg.appendChild(l2);
6209
6377
  return svg;
6210
6378
  }
6379
+ function isAliasProperty(key) {
6380
+ return ["alias", "aliases"].includes(key.trim().toLowerCase());
6381
+ }
6382
+ function createSummaryPill(value, hash2) {
6383
+ const pill = document.createElement("span");
6384
+ pill.className = "inline-flex max-w-full items-center rounded-md border border-border/60 bg-muted/40 px-2 py-0.5 text-xs text-foreground";
6385
+ const label = document.createElement("span");
6386
+ label.className = "truncate";
6387
+ label.textContent = hash2 ? `#${value}` : value;
6388
+ pill.appendChild(label);
6389
+ return pill;
6390
+ }
6391
+ function createFrontmatterSummary(entries, onShowDetails) {
6392
+ const summary = document.createElement("div");
6393
+ summary.dataset.frontmatterSummary = "true";
6394
+ summary.className = "divide-y divide-border/60";
6395
+ const groups = [
6396
+ { label: "Tags", entries: entries.filter(([key]) => TAG_CATEGORY_SUMMARY_KEYS[key.trim().toLowerCase()] === true), hash: true },
6397
+ { label: "Aliases", entries: entries.filter(([key]) => isAliasProperty(key)), hash: false }
6398
+ ];
6399
+ for (const group of groups) {
6400
+ const values2 = group.entries.flatMap(([, value]) => parseTagList(value));
6401
+ if (values2.length === 0) continue;
6402
+ const row = document.createElement("div");
6403
+ row.className = "flex flex-wrap items-center gap-1.5 px-3 py-2";
6404
+ const label = document.createElement("span");
6405
+ label.className = "mr-1 w-14 shrink-0 text-[11px] font-medium text-muted-foreground";
6406
+ label.textContent = group.label;
6407
+ row.appendChild(label);
6408
+ for (const value of values2) row.appendChild(createSummaryPill(value, group.hash));
6409
+ summary.appendChild(row);
6410
+ }
6411
+ const hidden = entries.filter(([key]) => TAG_CATEGORY_SUMMARY_KEYS[key.trim().toLowerCase()] !== true && !isAliasProperty(key));
6412
+ const metricCount = hidden.filter(([key]) => {
6413
+ const k = key.trim().toLowerCase();
6414
+ return k.startsWith("metrics.") || k === "metrics";
6415
+ }).length;
6416
+ const summaryCount = hidden.filter(([key]) => {
6417
+ const k = key.trim().toLowerCase();
6418
+ return k.startsWith("summaries.") || k === "summaries";
6419
+ }).length;
6420
+ const details = document.createElement("button");
6421
+ details.type = "button";
6422
+ details.setAttribute("aria-label", "Show property details");
6423
+ details.setAttribute("aria-expanded", "false");
6424
+ details.className = "flex w-full flex-wrap items-center gap-x-2 gap-y-1 px-3 py-2 text-left text-[11px] text-muted-foreground transition-colors hover:bg-muted/20 hover:text-foreground cursor-pointer";
6425
+ const count2 = document.createElement("span");
6426
+ count2.textContent = `${hidden.length} more ${hidden.length === 1 ? "property" : "properties"}`;
6427
+ details.appendChild(count2);
6428
+ if (metricCount > 0) {
6429
+ const metrics = document.createElement("span");
6430
+ metrics.textContent = `\xB7 ${metricCount} custom ${metricCount === 1 ? "metric" : "metrics"}`;
6431
+ details.appendChild(metrics);
6432
+ }
6433
+ if (summaryCount > 0) {
6434
+ const summaries = document.createElement("span");
6435
+ summaries.textContent = `\xB7 ${summaryCount} ${summaryCount === 1 ? "summary" : "summaries"}`;
6436
+ details.appendChild(summaries);
6437
+ }
6438
+ const disclosure = document.createElement("span");
6439
+ disclosure.className = "ml-auto";
6440
+ disclosure.setAttribute("aria-hidden", "true");
6441
+ disclosure.textContent = "\u25BE";
6442
+ details.appendChild(disclosure);
6443
+ details.addEventListener("click", onShowDetails);
6444
+ summary.appendChild(details);
6445
+ return summary;
6446
+ }
6211
6447
  var DefaultFrontmatterWidget = class extends view.WidgetType {
6212
6448
  sectionId;
6213
6449
  rawContent;
6214
6450
  readOnly;
6215
- constructor(sectionId, rawContent, readOnly = false) {
6451
+ detailed;
6452
+ constructor(sectionId, rawContent, readOnly = false, detailed = false) {
6216
6453
  super();
6217
6454
  this.sectionId = sectionId;
6218
6455
  this.rawContent = rawContent ?? "";
6219
6456
  this.readOnly = readOnly;
6457
+ this.detailed = detailed;
6220
6458
  }
6221
6459
  eq(other) {
6222
- return this.sectionId === other.sectionId && this.rawContent === other.rawContent && this.readOnly === other.readOnly;
6460
+ return this.sectionId === other.sectionId && this.rawContent === other.rawContent && this.readOnly === other.readOnly && this.detailed === other.detailed;
6223
6461
  }
6224
6462
  ignoreEvent() {
6225
6463
  return true;
@@ -6229,10 +6467,25 @@ var DefaultFrontmatterWidget = class extends view.WidgetType {
6229
6467
  widgetCleanup.set(root, []);
6230
6468
  root.className = "cm-frontmatter-preview py-2 font-sans select-none";
6231
6469
  const heading = document.createElement("div");
6232
- heading.className = "text-xs font-medium text-muted-foreground mb-1.5 px-0.5";
6233
- heading.textContent = "Properties";
6470
+ heading.className = "mb-1.5 flex items-center px-0.5 text-xs font-medium text-muted-foreground";
6471
+ const headingLabel = document.createElement("span");
6472
+ headingLabel.textContent = "Properties";
6473
+ heading.appendChild(headingLabel);
6474
+ if (this.detailed) {
6475
+ const summaryButton = document.createElement("button");
6476
+ summaryButton.type = "button";
6477
+ summaryButton.setAttribute("aria-label", "Show property summary");
6478
+ summaryButton.setAttribute("aria-expanded", "true");
6479
+ summaryButton.className = "ml-auto px-1 py-0.5 text-[11px] font-normal hover:text-foreground cursor-pointer";
6480
+ summaryButton.textContent = "Hide details \u25B4";
6481
+ summaryButton.addEventListener("click", () => {
6482
+ view.dispatch({ effects: setFrontmatterDetailed.of(false) });
6483
+ });
6484
+ heading.appendChild(summaryButton);
6485
+ }
6234
6486
  root.appendChild(heading);
6235
6487
  const box = document.createElement("div");
6488
+ box.dataset.frontmatterProperties = "true";
6236
6489
  box.className = "rounded-lg border border-border/80 bg-background/50 text-xs shadow-xs";
6237
6490
  root.appendChild(box);
6238
6491
  const body = this.rawContent.replace(/^---\r?\n?/, "").replace(/\r?\n?---\r?$/, "");
@@ -6240,329 +6493,335 @@ var DefaultFrontmatterWidget = class extends view.WidgetType {
6240
6493
  const entries = Object.entries(meta);
6241
6494
  const docKind = detectDefinitionKind(meta);
6242
6495
  const referenceProvider = view.state.facet(frontmatterReferenceIndex)?.() ?? null;
6243
- const base = docKind === "effort" ? readBaseAttributes(body) : {};
6244
- box.appendChild(createColumnHeader());
6245
- if (entries.length === 0) {
6246
- const emptyRow = document.createElement("div");
6247
- emptyRow.className = "px-3 py-2 text-xs text-muted-foreground/60 italic";
6248
- emptyRow.textContent = "No properties";
6249
- box.appendChild(emptyRow);
6250
- }
6251
- for (const [key, value] of entries) {
6252
- const isTags = isTagProperty(key);
6253
- const valueKind = referenceValueKind(key, docKind);
6254
- const isReferenceList = (isReferenceKey(key, docKind) || key === "aliases") && Array.isArray(value);
6255
- const nested = value === "" && ownsNestedBlock(body, key);
6256
- const row = document.createElement("div");
6257
- row.className = "flex items-center min-h-[34px] border-b border-dashed border-border last:border-b-0 group hover:bg-muted/10 transition-colors max-[479px]:flex-col max-[479px]:items-stretch";
6258
- row.dataset.frontmatterRow = key;
6259
- box.appendChild(row);
6260
- const keyCol = document.createElement("div");
6261
- keyCol.className = "flex items-center gap-2 px-3 py-1.5 w-36 shrink-0 border-r border-dashed border-border text-muted-foreground max-[479px]:w-auto max-[479px]:border-r-0 max-[479px]:border-b";
6262
- row.appendChild(keyCol);
6263
- keyCol.appendChild(isTags || isReferenceList ? createTagIcon() : createListIcon());
6264
- if (this.readOnly || nested) {
6265
- const keyLabel = document.createElement("span");
6266
- keyLabel.className = "truncate font-medium text-foreground/90";
6267
- keyLabel.textContent = key;
6268
- keyCol.appendChild(keyLabel);
6269
- } else {
6270
- const keyInput = document.createElement("input");
6271
- keyInput.className = "w-full bg-transparent text-xs text-foreground/90 font-medium outline-none truncate hover:bg-muted/30 focus:bg-background focus:ring-1 focus:ring-ring rounded px-1 -mx-1";
6272
- keyInput.value = key;
6273
- keyInput.spellcheck = false;
6274
- keyInput.setAttribute("aria-label", `Property key: ${key}`);
6275
- keyInput.dataset.frontmatterField = "key";
6276
- keyInput.dataset.frontmatterProperty = key;
6277
- keyInput.setAttribute(
6278
- "aria-describedby",
6279
- appendFieldHelper(
6280
- keyCol,
6281
- "Property name. Press Enter to apply. Arrow keys browse suggested property names."
6282
- ).id
6283
- );
6284
- let currentKey = key;
6285
- const commitKey = () => {
6286
- const nextKey = keyInput.value.trim();
6287
- if (!nextKey || nextKey === currentKey) {
6288
- keyInput.value = currentKey;
6289
- return;
6290
- }
6291
- if (!/^[A-Za-z_][\w.-]*$/.test(nextKey) || nextKey in meta) {
6292
- keyInput.value = currentKey;
6293
- return;
6294
- }
6295
- renamePropertyInView(view, this.sectionId, currentKey, nextKey, value);
6296
- delete meta[currentKey];
6297
- meta[nextKey] = value;
6298
- currentKey = nextKey;
6299
- };
6300
- const commitKeyIfAttached = () => queueMicrotask(() => {
6301
- if (keyInput.isConnected) commitKey();
6302
- });
6303
- keyInput.addEventListener("change", commitKeyIfAttached);
6304
- keyCol.appendChild(keyInput);
6305
- attachSuggestions(keyInput, view, root, "key", key, entries.map(([name]) => name), commitKey);
6306
- keyInput.addEventListener("blur", commitKeyIfAttached);
6307
- keyInput.addEventListener("keydown", (e) => {
6308
- if (e.defaultPrevented) return;
6309
- if (e.key === "Enter") {
6310
- e.preventDefault();
6311
- keyInput.blur();
6312
- }
6313
- });
6314
- const focus = pendingFocus.get(view);
6315
- if (focus?.kind === "key" && focus.key === key) {
6316
- setTimeout(() => {
6317
- if (!keyInput.isConnected) return;
6318
- keyInput.focus();
6319
- keyInput.select();
6320
- }, 0);
6321
- pendingFocus.delete(view);
6322
- }
6323
- }
6324
- const valCol = document.createElement("div");
6325
- valCol.className = "flex items-center flex-wrap gap-1.5 px-3 py-1.5 flex-1 min-w-0";
6326
- row.appendChild(valCol);
6327
- const commitList = (next) => {
6328
- meta[key] = next;
6329
- patchPropertyInView(view, this.sectionId, [key], next.length > 0 ? next : "");
6330
- };
6331
- const refMatch = (token) => {
6332
- if (!referenceProvider) return null;
6333
- return valueKind === "effort" ? referenceProvider.resolveEffort(token) : valueKind === "equipment" ? referenceProvider.resolveEquipment(token) : null;
6334
- };
6335
- if (isTags || isReferenceList) {
6336
- const chips = parseTagList(value);
6337
- const chipIsReference = valueKind !== "tags";
6338
- for (let idx = 0; idx < chips.length; idx++) {
6339
- const chip = chips[idx];
6340
- const match = chipIsReference ? refMatch(chip) : null;
6341
- const pill = document.createElement("span");
6342
- pill.className = match ? "inline-flex items-center gap-1 px-2 py-0.5 rounded-md text-xs font-medium bg-muted/60 text-foreground border border-border/60" : chipIsReference ? "inline-flex items-center gap-1 px-2 py-0.5 rounded-md text-xs font-medium bg-destructive/10 text-destructive border border-destructive/30" : "inline-flex items-center gap-1 px-2 py-0.5 rounded-md text-xs font-medium bg-amber-500/10 text-amber-800 dark:text-amber-300 border border-amber-500/20 dark:border-amber-500/30";
6343
- const pillText = document.createElement("span");
6344
- pillText.textContent = match ? match.label : chip;
6345
- pill.appendChild(pillText);
6346
- if (match && referenceProvider) {
6347
- const openLink = document.createElement("a");
6348
- openLink.href = referenceProvider.openHref(valueKind, match.slug);
6349
- openLink.title = `Open ${match.label} page`;
6350
- openLink.setAttribute("aria-label", `Open ${match.label} page`);
6351
- openLink.className = "inline-flex items-center justify-center text-muted-foreground/60 hover:text-foreground cursor-pointer";
6352
- openLink.appendChild(createExternalIcon());
6353
- pill.appendChild(openLink);
6354
- }
6355
- if (!this.readOnly) {
6356
- const rmBtn = document.createElement("button");
6357
- rmBtn.type = "button";
6358
- rmBtn.setAttribute("aria-label", isTags ? `Remove tag ${chip}` : `Remove ${chip}`);
6359
- rmBtn.className = "inline-flex items-center justify-center text-muted-foreground/60 hover:text-foreground dark:hover:text-foreground cursor-pointer";
6360
- rmBtn.appendChild(createCloseIcon("sm"));
6361
- rmBtn.addEventListener("click", (e) => {
6362
- e.preventDefault();
6363
- e.stopPropagation();
6364
- commitList(chips.filter((_, i) => i !== idx));
6365
- });
6366
- pill.appendChild(rmBtn);
6367
- }
6368
- valCol.appendChild(pill);
6369
- }
6370
- if (!this.readOnly) {
6371
- const chipInput = document.createElement("input");
6372
- chipInput.className = "flex-none w-44 max-w-full rounded-md border border-field-border bg-background/60 px-2 py-1 text-xs text-foreground outline-none placeholder:text-field-placeholder focus:border-ring focus:ring-1 focus:ring-ring";
6373
- chipInput.placeholder = chips.length === 0 ? isReferenceList ? `Add ${valueKind === "effort" ? "effort" : "equipment"}\u2026` : "Add tags\u2026" : isTags ? "Add tag\u2026" : "Add\u2026";
6374
- chipInput.setAttribute("aria-label", `Value for ${key}`);
6375
- chipInput.dataset.frontmatterField = "value";
6376
- chipInput.dataset.frontmatterProperty = key;
6377
- chipInput.setAttribute(
6496
+ const base = docKind === "effort" ? readBaseAttributes(body, meta) : {};
6497
+ if (!this.detailed) {
6498
+ box.appendChild(createFrontmatterSummary(entries, () => {
6499
+ view.dispatch({ effects: setFrontmatterDetailed.of(true) });
6500
+ }));
6501
+ } else {
6502
+ box.appendChild(createColumnHeader());
6503
+ if (entries.length === 0) {
6504
+ const emptyRow = document.createElement("div");
6505
+ emptyRow.className = "px-3 py-2 text-xs text-muted-foreground/60 italic";
6506
+ emptyRow.textContent = "No properties";
6507
+ box.appendChild(emptyRow);
6508
+ }
6509
+ for (const [key, value] of entries) {
6510
+ const isTags = isTagProperty(key);
6511
+ const valueKind = referenceValueKind(key, docKind);
6512
+ const isReferenceList = (isReferenceKey(key, docKind) || key === "aliases") && Array.isArray(value);
6513
+ const nested = value === "" && ownsNestedBlock(body, key);
6514
+ const row = document.createElement("div");
6515
+ row.className = "flex items-center min-h-[34px] border-b border-dashed border-border last:border-b-0 group hover:bg-muted/10 transition-colors max-[479px]:flex-col max-[479px]:items-stretch";
6516
+ row.dataset.frontmatterRow = key;
6517
+ box.appendChild(row);
6518
+ const keyCol = document.createElement("div");
6519
+ keyCol.className = "flex items-center gap-2 px-3 py-1.5 w-36 shrink-0 border-r border-dashed border-border text-muted-foreground max-[479px]:w-auto max-[479px]:border-r-0 max-[479px]:border-b";
6520
+ row.appendChild(keyCol);
6521
+ keyCol.appendChild(isTags || isReferenceList ? createTagIcon() : createListIcon());
6522
+ if (this.readOnly || nested) {
6523
+ const keyLabel = document.createElement("span");
6524
+ keyLabel.className = "truncate font-medium text-foreground/90";
6525
+ keyLabel.textContent = key;
6526
+ keyCol.appendChild(keyLabel);
6527
+ } else {
6528
+ const keyInput = document.createElement("input");
6529
+ keyInput.className = "w-full bg-transparent text-xs text-foreground/90 font-medium outline-none truncate hover:bg-muted/30 focus:bg-background focus:ring-1 focus:ring-ring rounded px-1 -mx-1";
6530
+ keyInput.value = key;
6531
+ keyInput.spellcheck = false;
6532
+ keyInput.setAttribute("aria-label", `Property key: ${key}`);
6533
+ keyInput.dataset.frontmatterField = "key";
6534
+ keyInput.dataset.frontmatterProperty = key;
6535
+ keyInput.setAttribute(
6378
6536
  "aria-describedby",
6379
6537
  appendFieldHelper(
6380
- valCol,
6381
- "Press Enter or comma to add a value. Arrow keys browse the suggestions."
6538
+ keyCol,
6539
+ "Property name. Press Enter to apply. Arrow keys browse suggested property names."
6382
6540
  ).id
6383
6541
  );
6384
- const commitNewChip = () => {
6385
- const val = chipInput.value.trim().replace(/^,|,$/g, "");
6386
- if (val && !chips.some((chip) => chip.toLowerCase() === val.toLowerCase())) {
6387
- chipInput.value = "";
6388
- pendingFocus.set(view, { kind: "tag", key });
6389
- commitList([...chips, val]);
6542
+ let currentKey = key;
6543
+ const commitKey = () => {
6544
+ const nextKey = keyInput.value.trim();
6545
+ if (!nextKey || nextKey === currentKey) {
6546
+ keyInput.value = currentKey;
6547
+ return;
6548
+ }
6549
+ if (!/^[A-Za-z_][\w.-]*$/.test(nextKey) || nextKey in meta) {
6550
+ keyInput.value = currentKey;
6551
+ return;
6390
6552
  }
6553
+ renamePropertyInView(view, this.sectionId, currentKey, nextKey, value);
6554
+ delete meta[currentKey];
6555
+ meta[nextKey] = value;
6556
+ currentKey = nextKey;
6391
6557
  };
6392
- valCol.appendChild(chipInput);
6393
- attachSuggestions(chipInput, view, root, "value", key, chips, commitNewChip, valueKind);
6394
- chipInput.addEventListener("keydown", (e) => {
6558
+ const commitKeyIfAttached = () => queueMicrotask(() => {
6559
+ if (keyInput.isConnected) commitKey();
6560
+ });
6561
+ keyInput.addEventListener("change", commitKeyIfAttached);
6562
+ keyCol.appendChild(keyInput);
6563
+ attachSuggestions(keyInput, view, root, "key", key, entries.map(([name]) => name), commitKey);
6564
+ keyInput.addEventListener("blur", commitKeyIfAttached);
6565
+ keyInput.addEventListener("keydown", (e) => {
6395
6566
  if (e.defaultPrevented) return;
6396
- if (e.key === "Enter" || e.key === ",") {
6397
- e.preventDefault();
6398
- commitNewChip();
6399
- } else if (e.key === "Backspace" && !chipInput.value && chips.length > 0) {
6567
+ if (e.key === "Enter") {
6400
6568
  e.preventDefault();
6401
- pendingFocus.set(view, { kind: "tag", key });
6402
- commitList(chips.slice(0, -1));
6569
+ keyInput.blur();
6403
6570
  }
6404
6571
  });
6405
- chipInput.addEventListener("blur", () => queueMicrotask(() => {
6406
- if (chipInput.isConnected && chipInput.value.trim()) commitNewChip();
6407
- }));
6408
6572
  const focus = pendingFocus.get(view);
6409
- if (focus?.kind === "tag" && focus.key === key) {
6573
+ if (focus?.kind === "key" && focus.key === key) {
6410
6574
  setTimeout(() => {
6411
- if (chipInput.isConnected) chipInput.focus();
6575
+ if (!keyInput.isConnected) return;
6576
+ keyInput.focus();
6577
+ keyInput.select();
6412
6578
  }, 0);
6413
6579
  pendingFocus.delete(view);
6414
6580
  }
6415
- if (chipIsReference && referenceProvider) {
6416
- const unknown = chips.filter((chip) => !refMatch(chip));
6417
- if (unknown.length > 0) {
6418
- const diag = document.createElement("div");
6419
- diag.setAttribute("data-testid", "editor-ref-unknown");
6420
- diag.className = "w-full basis-full flex flex-wrap items-center gap-2 text-[10px] text-destructive";
6421
- const label = document.createElement("span");
6422
- label.textContent = `Unknown ${valueKind === "effort" ? "effort" : "equipment"}: ${unknown.join(", ")}`;
6423
- diag.appendChild(label);
6424
- const selectBtn = document.createElement("button");
6425
- selectBtn.type = "button";
6426
- selectBtn.textContent = "Select existing";
6427
- selectBtn.className = "underline hover:text-foreground cursor-pointer";
6428
- selectBtn.addEventListener("click", (e) => {
6429
- e.preventDefault();
6430
- chipInput.focus();
6431
- });
6432
- diag.appendChild(selectBtn);
6433
- const createLink = document.createElement("a");
6434
- createLink.href = referenceProvider.createHref(valueKind);
6435
- createLink.textContent = "Create page";
6436
- createLink.className = "underline hover:text-foreground";
6437
- diag.appendChild(createLink);
6438
- valCol.appendChild(diag);
6439
- }
6440
- }
6441
6581
  }
6442
- } else if (nested) {
6443
- const valSpan = document.createElement("span");
6444
- valSpan.className = "text-xs text-muted-foreground/60 italic py-0.5 truncate";
6445
- valSpan.textContent = "Nested block \u2014 edit in YAML";
6446
- valCol.appendChild(valSpan);
6447
- } else {
6448
- const strVal = Array.isArray(value) ? value.join(", ") : String(value ?? "");
6449
- if (this.readOnly) {
6450
- const valSpan = document.createElement("span");
6451
- valSpan.className = "text-xs text-foreground py-0.5 truncate";
6452
- valSpan.textContent = strVal;
6453
- valCol.appendChild(valSpan);
6454
- } else {
6455
- const valInput = document.createElement("input");
6456
- valInput.className = "w-full rounded-md border border-field-border bg-background/60 px-2 py-1 text-xs text-foreground outline-none placeholder:text-field-placeholder focus:border-ring focus:ring-1 focus:ring-ring transition-colors";
6457
- valInput.value = strVal;
6458
- valInput.spellcheck = false;
6459
- valInput.setAttribute("aria-label", `Value for ${key}`);
6460
- valInput.dataset.frontmatterField = "value";
6461
- valInput.dataset.frontmatterProperty = key;
6462
- valInput.setAttribute(
6463
- "aria-describedby",
6464
- appendFieldHelper(valCol, "Property value. Press Enter to apply.").id
6465
- );
6466
- let committedVal = strVal;
6467
- const commitVal = () => {
6468
- const cur = valInput.value.trim();
6469
- if (cur !== committedVal) {
6470
- committedVal = cur;
6471
- const num = Number(cur);
6472
- const next = cur !== "" && !isNaN(num) ? num : cur;
6473
- meta[key] = next;
6474
- patchPropertyInView(view, this.sectionId, [key], next);
6475
- }
6476
- };
6477
- const commitValIfAttached = () => queueMicrotask(() => {
6478
- if (valInput.isConnected) commitVal();
6479
- });
6480
- valInput.addEventListener("change", commitValIfAttached);
6481
- valCol.appendChild(valInput);
6482
- attachSuggestions(
6483
- valInput,
6484
- view,
6485
- root,
6486
- "value",
6487
- key,
6488
- [strVal],
6489
- commitVal,
6490
- key.trim().toLowerCase() === "metrics.effort" ? "effort" : "tags"
6491
- );
6492
- valInput.addEventListener("blur", commitValIfAttached);
6493
- valInput.addEventListener("keydown", (e) => {
6494
- if (e.defaultPrevented) return;
6495
- if (e.key === "Enter") {
6496
- e.preventDefault();
6497
- valInput.blur();
6498
- }
6499
- });
6500
- if (key.trim().toLowerCase() === "metrics.effort" && referenceProvider) {
6501
- const match = referenceProvider.resolveEffort(strVal);
6502
- if (match) {
6503
- const pill = document.createElement("span");
6504
- pill.className = "inline-flex items-center gap-1 px-2 py-0.5 rounded-md text-xs font-medium bg-muted/60 text-foreground border border-border/60 shrink-0";
6505
- const pillText = document.createElement("span");
6506
- pillText.textContent = match.label;
6507
- pill.appendChild(pillText);
6582
+ const valCol = document.createElement("div");
6583
+ valCol.className = "flex items-center flex-wrap gap-1.5 px-3 py-1.5 flex-1 min-w-0";
6584
+ row.appendChild(valCol);
6585
+ const commitList = (next) => {
6586
+ meta[key] = next;
6587
+ patchPropertyInView(view, this.sectionId, [key], next.length > 0 ? next : "");
6588
+ };
6589
+ const refMatch = (token) => {
6590
+ if (!referenceProvider) return null;
6591
+ return valueKind === "effort" ? referenceProvider.resolveEffort(token) : valueKind === "equipment" ? referenceProvider.resolveEquipment(token) : null;
6592
+ };
6593
+ if (isTags || isReferenceList) {
6594
+ const chips = parseTagList(value);
6595
+ const chipIsReference = valueKind !== "tags";
6596
+ for (let idx = 0; idx < chips.length; idx++) {
6597
+ const chip = chips[idx];
6598
+ const match = chipIsReference ? refMatch(chip) : null;
6599
+ const pill = document.createElement("span");
6600
+ pill.className = match ? "inline-flex items-center gap-1 px-2 py-0.5 rounded-md text-xs font-medium bg-muted/60 text-foreground border border-border/60" : chipIsReference ? "inline-flex items-center gap-1 px-2 py-0.5 rounded-md text-xs font-medium bg-destructive/10 text-destructive border border-destructive/30" : "inline-flex items-center gap-1 px-2 py-0.5 rounded-md text-xs font-medium bg-amber-500/10 text-amber-800 dark:text-amber-300 border border-amber-500/20 dark:border-amber-500/30";
6601
+ const pillText = document.createElement("span");
6602
+ pillText.textContent = match ? match.label : chip;
6603
+ pill.appendChild(pillText);
6604
+ if (match && referenceProvider) {
6508
6605
  const openLink = document.createElement("a");
6509
- openLink.href = referenceProvider.openHref("effort", match.slug);
6606
+ openLink.href = referenceProvider.openHref(valueKind, match.slug);
6510
6607
  openLink.title = `Open ${match.label} page`;
6511
6608
  openLink.setAttribute("aria-label", `Open ${match.label} page`);
6512
6609
  openLink.className = "inline-flex items-center justify-center text-muted-foreground/60 hover:text-foreground cursor-pointer";
6513
6610
  openLink.appendChild(createExternalIcon());
6514
6611
  pill.appendChild(openLink);
6515
- valCol.insertBefore(pill, valInput);
6516
- valInput.className = "flex-1 min-w-[70px] rounded-md border border-field-border bg-background/60 px-2 py-1 text-xs text-foreground outline-none placeholder:text-field-placeholder focus:border-ring focus:ring-1 focus:ring-ring transition-colors";
6517
6612
  }
6518
- const diag = renderEffortValueDiagnostics(
6519
- referenceProvider,
6520
- strVal,
6521
- (slug) => {
6522
- meta[key] = slug;
6523
- patchPropertyInView(view, this.sectionId, [key], slug);
6613
+ if (!this.readOnly) {
6614
+ const rmBtn = document.createElement("button");
6615
+ rmBtn.type = "button";
6616
+ rmBtn.setAttribute("aria-label", isTags ? `Remove tag ${chip}` : `Remove ${chip}`);
6617
+ rmBtn.className = "inline-flex items-center justify-center text-muted-foreground/60 hover:text-foreground dark:hover:text-foreground cursor-pointer";
6618
+ rmBtn.appendChild(createCloseIcon("sm"));
6619
+ rmBtn.addEventListener("click", (e) => {
6620
+ e.preventDefault();
6621
+ e.stopPropagation();
6622
+ commitList(chips.filter((_, i) => i !== idx));
6623
+ });
6624
+ pill.appendChild(rmBtn);
6625
+ }
6626
+ valCol.appendChild(pill);
6627
+ }
6628
+ if (!this.readOnly) {
6629
+ const chipInput = document.createElement("input");
6630
+ chipInput.className = "flex-none w-44 max-w-full rounded-md border border-field-border bg-background/60 px-2 py-1 text-xs text-foreground outline-none placeholder:text-field-placeholder focus:border-ring focus:ring-1 focus:ring-ring";
6631
+ chipInput.placeholder = chips.length === 0 ? isReferenceList ? `Add ${valueKind === "effort" ? "effort" : "equipment"}\u2026` : "Add tags\u2026" : isTags ? "Add tag\u2026" : "Add\u2026";
6632
+ chipInput.setAttribute("aria-label", `Value for ${key}`);
6633
+ chipInput.dataset.frontmatterField = "value";
6634
+ chipInput.dataset.frontmatterProperty = key;
6635
+ chipInput.setAttribute(
6636
+ "aria-describedby",
6637
+ appendFieldHelper(
6638
+ valCol,
6639
+ "Press Enter or comma to add a value. Arrow keys browse the suggestions."
6640
+ ).id
6641
+ );
6642
+ const commitNewChip = () => {
6643
+ const val = chipInput.value.trim().replace(/^,|,$/g, "");
6644
+ if (val && !chips.some((chip) => chip.toLowerCase() === val.toLowerCase())) {
6645
+ chipInput.value = "";
6646
+ pendingFocus.set(view, { kind: "tag", key });
6647
+ commitList([...chips, val]);
6648
+ }
6649
+ };
6650
+ valCol.appendChild(chipInput);
6651
+ attachSuggestions(chipInput, view, root, "value", key, chips, commitNewChip, valueKind);
6652
+ chipInput.addEventListener("keydown", (e) => {
6653
+ if (e.defaultPrevented) return;
6654
+ if (e.key === "Enter" || e.key === ",") {
6655
+ e.preventDefault();
6656
+ commitNewChip();
6657
+ } else if (e.key === "Backspace" && !chipInput.value && chips.length > 0) {
6658
+ e.preventDefault();
6659
+ pendingFocus.set(view, { kind: "tag", key });
6660
+ commitList(chips.slice(0, -1));
6524
6661
  }
6662
+ });
6663
+ chipInput.addEventListener("blur", () => queueMicrotask(() => {
6664
+ if (chipInput.isConnected && chipInput.value.trim()) commitNewChip();
6665
+ }));
6666
+ const focus = pendingFocus.get(view);
6667
+ if (focus?.kind === "tag" && focus.key === key) {
6668
+ setTimeout(() => {
6669
+ if (chipInput.isConnected) chipInput.focus();
6670
+ }, 0);
6671
+ pendingFocus.delete(view);
6672
+ }
6673
+ if (chipIsReference && referenceProvider) {
6674
+ const unknown = chips.filter((chip) => !refMatch(chip));
6675
+ if (unknown.length > 0) {
6676
+ const diag = document.createElement("div");
6677
+ diag.setAttribute("data-testid", "editor-ref-unknown");
6678
+ diag.className = "w-full basis-full flex flex-wrap items-center gap-2 text-[10px] text-destructive";
6679
+ const label = document.createElement("span");
6680
+ label.textContent = `Unknown ${valueKind === "effort" ? "effort" : "equipment"}: ${unknown.join(", ")}`;
6681
+ diag.appendChild(label);
6682
+ const selectBtn = document.createElement("button");
6683
+ selectBtn.type = "button";
6684
+ selectBtn.textContent = "Select existing";
6685
+ selectBtn.className = "underline hover:text-foreground cursor-pointer";
6686
+ selectBtn.addEventListener("click", (e) => {
6687
+ e.preventDefault();
6688
+ chipInput.focus();
6689
+ });
6690
+ diag.appendChild(selectBtn);
6691
+ const createLink = document.createElement("a");
6692
+ createLink.href = referenceProvider.createHref(valueKind);
6693
+ createLink.textContent = "Create page";
6694
+ createLink.className = "underline hover:text-foreground";
6695
+ diag.appendChild(createLink);
6696
+ valCol.appendChild(diag);
6697
+ }
6698
+ }
6699
+ }
6700
+ } else if (nested) {
6701
+ const valSpan = document.createElement("span");
6702
+ valSpan.className = "text-xs text-muted-foreground/60 italic py-0.5 truncate";
6703
+ valSpan.textContent = "Nested block \u2014 edit in YAML";
6704
+ valCol.appendChild(valSpan);
6705
+ } else {
6706
+ const strVal = Array.isArray(value) ? value.join(", ") : String(value ?? "");
6707
+ if (this.readOnly) {
6708
+ const valSpan = document.createElement("span");
6709
+ valSpan.className = "text-xs text-foreground py-0.5 truncate";
6710
+ valSpan.textContent = strVal;
6711
+ valCol.appendChild(valSpan);
6712
+ } else {
6713
+ const valInput = document.createElement("input");
6714
+ valInput.className = "w-full rounded-md border border-field-border bg-background/60 px-2 py-1 text-xs text-foreground outline-none placeholder:text-field-placeholder focus:border-ring focus:ring-1 focus:ring-ring transition-colors";
6715
+ valInput.value = strVal;
6716
+ valInput.spellcheck = false;
6717
+ valInput.setAttribute("aria-label", `Value for ${key}`);
6718
+ valInput.dataset.frontmatterField = "value";
6719
+ valInput.dataset.frontmatterProperty = key;
6720
+ valInput.setAttribute(
6721
+ "aria-describedby",
6722
+ appendFieldHelper(valCol, "Property value. Press Enter to apply.").id
6723
+ );
6724
+ let committedVal = strVal;
6725
+ const commitVal = () => {
6726
+ const cur = valInput.value.trim();
6727
+ if (cur !== committedVal) {
6728
+ committedVal = cur;
6729
+ const num = Number(cur);
6730
+ const next = cur !== "" && !isNaN(num) ? num : cur;
6731
+ meta[key] = next;
6732
+ patchPropertyInView(view, this.sectionId, [key], next);
6733
+ }
6734
+ };
6735
+ const commitValIfAttached = () => queueMicrotask(() => {
6736
+ if (valInput.isConnected) commitVal();
6737
+ });
6738
+ valInput.addEventListener("change", commitValIfAttached);
6739
+ valCol.appendChild(valInput);
6740
+ attachSuggestions(
6741
+ valInput,
6742
+ view,
6743
+ root,
6744
+ "value",
6745
+ key,
6746
+ [strVal],
6747
+ commitVal,
6748
+ key.trim().toLowerCase() === "metrics.effort" ? "effort" : "tags"
6525
6749
  );
6526
- if (diag) valCol.appendChild(diag);
6750
+ valInput.addEventListener("blur", commitValIfAttached);
6751
+ valInput.addEventListener("keydown", (e) => {
6752
+ if (e.defaultPrevented) return;
6753
+ if (e.key === "Enter") {
6754
+ e.preventDefault();
6755
+ valInput.blur();
6756
+ }
6757
+ });
6758
+ if (key.trim().toLowerCase() === "metrics.effort" && referenceProvider) {
6759
+ const match = referenceProvider.resolveEffort(strVal);
6760
+ if (match) {
6761
+ const pill = document.createElement("span");
6762
+ pill.className = "inline-flex items-center gap-1 px-2 py-0.5 rounded-md text-xs font-medium bg-muted/60 text-foreground border border-border/60 shrink-0";
6763
+ const pillText = document.createElement("span");
6764
+ pillText.textContent = match.label;
6765
+ pill.appendChild(pillText);
6766
+ const openLink = document.createElement("a");
6767
+ openLink.href = referenceProvider.openHref("effort", match.slug);
6768
+ openLink.title = `Open ${match.label} page`;
6769
+ openLink.setAttribute("aria-label", `Open ${match.label} page`);
6770
+ openLink.className = "inline-flex items-center justify-center text-muted-foreground/60 hover:text-foreground cursor-pointer";
6771
+ openLink.appendChild(createExternalIcon());
6772
+ pill.appendChild(openLink);
6773
+ valCol.insertBefore(pill, valInput);
6774
+ valInput.className = "flex-1 min-w-[70px] rounded-md border border-field-border bg-background/60 px-2 py-1 text-xs text-foreground outline-none placeholder:text-field-placeholder focus:border-ring focus:ring-1 focus:ring-ring transition-colors";
6775
+ }
6776
+ const diag = renderEffortValueDiagnostics(
6777
+ referenceProvider,
6778
+ strVal,
6779
+ (slug) => {
6780
+ meta[key] = slug;
6781
+ patchPropertyInView(view, this.sectionId, [key], slug);
6782
+ }
6783
+ );
6784
+ if (diag) valCol.appendChild(diag);
6785
+ }
6786
+ }
6787
+ if (/^https?:\/\/\S+$/i.test(strVal)) {
6788
+ const openLink = document.createElement("a");
6789
+ openLink.href = strVal;
6790
+ openLink.target = "_blank";
6791
+ openLink.rel = "noopener noreferrer";
6792
+ openLink.title = strVal;
6793
+ openLink.setAttribute("aria-label", `Open ${key} in new tab`);
6794
+ openLink.className = "shrink-0 inline-flex items-center justify-center text-muted-foreground/60 hover:text-foreground transition-colors cursor-pointer";
6795
+ openLink.appendChild(createExternalIcon());
6796
+ valCol.appendChild(openLink);
6527
6797
  }
6528
6798
  }
6529
- if (/^https?:\/\/\S+$/i.test(strVal)) {
6530
- const openLink = document.createElement("a");
6531
- openLink.href = strVal;
6532
- openLink.target = "_blank";
6533
- openLink.rel = "noopener noreferrer";
6534
- openLink.title = strVal;
6535
- openLink.setAttribute("aria-label", `Open ${key} in new tab`);
6536
- openLink.className = "shrink-0 inline-flex items-center justify-center text-muted-foreground/60 hover:text-foreground transition-colors cursor-pointer";
6537
- openLink.appendChild(createExternalIcon());
6538
- valCol.appendChild(openLink);
6799
+ if (!this.readOnly && !nested) {
6800
+ const actionsCol = document.createElement("div");
6801
+ actionsCol.className = "flex items-center pr-2";
6802
+ const delBtn = document.createElement("button");
6803
+ delBtn.type = "button";
6804
+ delBtn.setAttribute("aria-label", `Remove property ${key}`);
6805
+ delBtn.className = "opacity-0 group-hover:opacity-100 focus-visible:opacity-100 text-muted-foreground/70 hover:text-destructive p-1 rounded transition-opacity cursor-pointer";
6806
+ delBtn.appendChild(createCloseIcon("md"));
6807
+ delBtn.addEventListener("click", (e) => {
6808
+ e.preventDefault();
6809
+ e.stopPropagation();
6810
+ delete meta[key];
6811
+ patchPropertyInView(view, this.sectionId, [key], null);
6812
+ });
6813
+ actionsCol.appendChild(delBtn);
6814
+ row.appendChild(actionsCol);
6539
6815
  }
6540
6816
  }
6541
- if (!this.readOnly && !nested) {
6542
- const actionsCol = document.createElement("div");
6543
- actionsCol.className = "flex items-center pr-2";
6544
- const delBtn = document.createElement("button");
6545
- delBtn.type = "button";
6546
- delBtn.setAttribute("aria-label", `Remove property ${key}`);
6547
- delBtn.className = "opacity-0 group-hover:opacity-100 focus-visible:opacity-100 text-muted-foreground/70 hover:text-destructive p-1 rounded transition-opacity cursor-pointer";
6548
- delBtn.appendChild(createCloseIcon("md"));
6549
- delBtn.addEventListener("click", (e) => {
6550
- e.preventDefault();
6551
- e.stopPropagation();
6552
- delete meta[key];
6553
- patchPropertyInView(view, this.sectionId, [key], null);
6554
- });
6555
- actionsCol.appendChild(delBtn);
6556
- row.appendChild(actionsCol);
6817
+ if (docKind === "effort" && !this.readOnly) {
6818
+ box.appendChild(this.buildDefinitionRows(view, base));
6557
6819
  }
6558
6820
  }
6559
- if (docKind === "effort" && !this.readOnly) {
6560
- box.appendChild(this.buildDefinitionRows(view, base));
6561
- }
6562
6821
  if (!this.readOnly) {
6563
6822
  const existingTagKey = entries.find(([key]) => key.trim().toLowerCase() === "tags")?.[0] ?? entries.find(([key]) => isTagProperty(key))?.[0];
6564
6823
  const actionsRow = document.createElement("div");
6565
- actionsRow.className = "mt-2 flex items-center gap-3";
6824
+ actionsRow.className = "mt-2 flex flex-wrap items-center gap-x-3 gap-y-1";
6566
6825
  actionsRow.dataset.frontmatterActions = "true";
6567
6826
  const addBtn = document.createElement("button");
6568
6827
  addBtn.type = "button";
@@ -6582,7 +6841,12 @@ var DefaultFrontmatterWidget = class extends view.WidgetType {
6582
6841
  newKey = `property_${count2}`;
6583
6842
  }
6584
6843
  pendingFocus.set(view, { kind: "key", key: newKey });
6585
- patchPropertyInView(view, this.sectionId, [newKey], "");
6844
+ patchPropertyInView(view, this.sectionId, [newKey], "", !this.detailed);
6845
+ const keyField = view.dom.querySelector(
6846
+ `[data-frontmatter-row="${newKey}"] [data-frontmatter-field="key"]`
6847
+ );
6848
+ keyField?.focus();
6849
+ keyField?.select();
6586
6850
  });
6587
6851
  actionsRow.appendChild(addBtn);
6588
6852
  {
@@ -6598,20 +6862,57 @@ var DefaultFrontmatterWidget = class extends view.WidgetType {
6598
6862
  addMetricBtn.addEventListener("click", (e) => {
6599
6863
  e.preventDefault();
6600
6864
  e.stopPropagation();
6601
- const existing = root.querySelector("[data-frontmatter-metric-draft] input");
6865
+ if (!this.detailed) {
6866
+ view.dispatch({ effects: setFrontmatterDetailed.of(true) });
6867
+ }
6868
+ const activeRoot = view.dom.querySelector(".cm-frontmatter-preview") ?? root;
6869
+ const activeBox = activeRoot.querySelector("[data-frontmatter-properties]") ?? box;
6870
+ const existing = activeRoot.querySelector("[data-frontmatter-metric-draft] input");
6602
6871
  if (existing) {
6603
6872
  existing.focus();
6604
6873
  return;
6605
6874
  }
6606
- const draftRow = createMetricDraftRow(view, root, referenceProvider, (path, value) => {
6875
+ const draftRow = createMetricDraftRow(view, activeRoot, referenceProvider, (path, value) => {
6607
6876
  patchPropertyInView(view, this.sectionId, path, value);
6608
6877
  });
6609
- box.appendChild(draftRow);
6878
+ activeBox.appendChild(draftRow);
6610
6879
  draftRow.querySelector("input")?.focus();
6611
6880
  view.requestMeasure();
6612
6881
  });
6613
6882
  actionsRow.appendChild(addMetricBtn);
6614
6883
  }
6884
+ {
6885
+ const addSummaryBtn = document.createElement("button");
6886
+ addSummaryBtn.type = "button";
6887
+ addSummaryBtn.setAttribute("aria-label", "Add summary");
6888
+ addSummaryBtn.setAttribute("data-testid", "editor-add-summary-note");
6889
+ addSummaryBtn.className = "inline-flex items-center gap-1.5 text-xs font-medium text-muted-foreground hover:text-foreground px-1 py-1 cursor-pointer transition-colors select-none";
6890
+ addSummaryBtn.appendChild(createPlusIcon());
6891
+ const addSummaryLabel = document.createElement("span");
6892
+ addSummaryLabel.textContent = "Add summary";
6893
+ addSummaryBtn.appendChild(addSummaryLabel);
6894
+ addSummaryBtn.addEventListener("click", (e) => {
6895
+ e.preventDefault();
6896
+ e.stopPropagation();
6897
+ if (!this.detailed) {
6898
+ view.dispatch({ effects: setFrontmatterDetailed.of(true) });
6899
+ }
6900
+ const activeRoot = view.dom.querySelector(".cm-frontmatter-preview") ?? root;
6901
+ const activeBox = activeRoot.querySelector("[data-frontmatter-properties]") ?? box;
6902
+ const existing = activeRoot.querySelector("[data-frontmatter-summary-draft] input");
6903
+ if (existing) {
6904
+ existing.focus();
6905
+ return;
6906
+ }
6907
+ const draftRow = createSummaryDraftRow(view, (path, value) => {
6908
+ patchPropertyInView(view, this.sectionId, path, value);
6909
+ });
6910
+ activeBox.appendChild(draftRow);
6911
+ draftRow.querySelector("input")?.focus();
6912
+ view.requestMeasure();
6913
+ });
6914
+ actionsRow.appendChild(addSummaryBtn);
6915
+ }
6615
6916
  {
6616
6917
  const addTagBtn = document.createElement("button");
6617
6918
  addTagBtn.type = "button";
@@ -6624,23 +6925,28 @@ var DefaultFrontmatterWidget = class extends view.WidgetType {
6624
6925
  addTagBtn.addEventListener("click", (e) => {
6625
6926
  e.preventDefault();
6626
6927
  e.stopPropagation();
6928
+ if (!this.detailed) {
6929
+ view.dispatch({ effects: setFrontmatterDetailed.of(true) });
6930
+ }
6931
+ const activeRoot = view.dom.querySelector(".cm-frontmatter-preview") ?? root;
6932
+ const activeBox = activeRoot.querySelector("[data-frontmatter-properties]") ?? box;
6627
6933
  if (existingTagKey) {
6628
- const input = Array.from(root.querySelectorAll(
6934
+ const input = Array.from(activeRoot.querySelectorAll(
6629
6935
  '[data-frontmatter-field="value"]'
6630
6936
  )).find((element) => element.dataset.frontmatterProperty === existingTagKey);
6631
6937
  input?.focus();
6632
6938
  return;
6633
6939
  }
6634
- const existingDraft = root.querySelector("[data-frontmatter-tag-draft] input");
6940
+ const existingDraft = activeRoot.querySelector("[data-frontmatter-tag-draft] input");
6635
6941
  if (existingDraft) {
6636
6942
  existingDraft.focus();
6637
6943
  return;
6638
6944
  }
6639
- const draftRow = createTagDraftRow(view, root, "category", (val) => {
6945
+ const draftRow = createTagDraftRow(view, activeRoot, "category", (val) => {
6640
6946
  pendingFocus.set(view, { kind: "tag", key: "category" });
6641
6947
  patchPropertyInView(view, this.sectionId, ["category"], [val]);
6642
6948
  });
6643
- box.appendChild(draftRow);
6949
+ activeBox.appendChild(draftRow);
6644
6950
  draftRow.querySelector("input")?.focus();
6645
6951
  view.requestMeasure();
6646
6952
  });
@@ -6661,7 +6967,11 @@ var DefaultFrontmatterWidget = class extends view.WidgetType {
6661
6967
  const { sections: currentSections } = view.state.field(sectionField);
6662
6968
  const section = currentSections.find((s) => s.id === this.sectionId && s.type === "frontmatter") || currentSections.find((s) => s.type === "frontmatter");
6663
6969
  if (!section) return;
6664
- view.dispatch({ selection: { anchor: section.contentFrom ?? section.from }, scrollIntoView: true });
6970
+ view.dispatch({
6971
+ selection: { anchor: section.from },
6972
+ effects: setFrontmatterSource.of(true),
6973
+ scrollIntoView: true
6974
+ });
6665
6975
  view.focus();
6666
6976
  });
6667
6977
  actionsRow.appendChild(editYamlBtn);
@@ -6686,10 +6996,12 @@ var DefaultFrontmatterWidget = class extends view.WidgetType {
6686
6996
  const commitField = (field, raw) => {
6687
6997
  if (field === "met") {
6688
6998
  const num = Number(raw);
6689
- patchPropertyInView(view, this.sectionId, ["baseAttributes", "met"], raw !== "" && !Number.isNaN(num) ? num : null);
6999
+ const path2 = base.hasBaseBlock ? ["baseAttributes", "met"] : ["metrics.met"];
7000
+ patchPropertyInView(view, this.sectionId, path2, raw !== "" && !Number.isNaN(num) ? num : null);
6690
7001
  return;
6691
7002
  }
6692
- patchPropertyInView(view, this.sectionId, ["baseAttributes", field], raw === "" ? null : raw);
7003
+ const path = base.hasBaseBlock ? ["baseAttributes", field] : [field];
7004
+ patchPropertyInView(view, this.sectionId, path, raw === "" ? null : raw);
6693
7005
  };
6694
7006
  const fieldRow = (label, child) => {
6695
7007
  const row = document.createElement("label");
@@ -6752,8 +7064,13 @@ var DefaultFrontmatterWidget = class extends view.WidgetType {
6752
7064
  }
6753
7065
  };
6754
7066
  var EmptyFrontmatterWidget = class extends view.WidgetType {
6755
- eq(_other) {
6756
- return true;
7067
+ constructor(detailed = false) {
7068
+ super();
7069
+ this.detailed = detailed;
7070
+ }
7071
+ detailed;
7072
+ eq(other) {
7073
+ return this.detailed === other.detailed;
6757
7074
  }
6758
7075
  ignoreEvent() {
6759
7076
  return true;
@@ -6766,7 +7083,7 @@ var EmptyFrontmatterWidget = class extends view.WidgetType {
6766
7083
  heading.textContent = "Properties";
6767
7084
  root.appendChild(heading);
6768
7085
  const actionsRow = document.createElement("div");
6769
- actionsRow.className = "flex items-center gap-3";
7086
+ actionsRow.className = "flex flex-wrap items-center gap-x-3 gap-y-1";
6770
7087
  actionsRow.dataset.frontmatterActions = "true";
6771
7088
  const addBtn = document.createElement("button");
6772
7089
  addBtn.type = "button";
@@ -6780,9 +7097,71 @@ var EmptyFrontmatterWidget = class extends view.WidgetType {
6780
7097
  e.preventDefault();
6781
7098
  e.stopPropagation();
6782
7099
  pendingFocus.set(view, { kind: "key", key: "property" });
6783
- patchPropertyInView(view, "new", ["property"], "");
7100
+ patchPropertyInView(view, "new", ["property"], "", true);
7101
+ const keyField = view.dom.querySelector(
7102
+ '[data-frontmatter-row="property"] [data-frontmatter-field="key"]'
7103
+ );
7104
+ keyField?.focus();
7105
+ keyField?.select();
6784
7106
  });
6785
7107
  actionsRow.appendChild(addBtn);
7108
+ const addMetricBtn = document.createElement("button");
7109
+ addMetricBtn.type = "button";
7110
+ addMetricBtn.setAttribute("aria-label", "Add metric");
7111
+ addMetricBtn.setAttribute("data-testid", "editor-add-metric-note");
7112
+ addMetricBtn.className = "inline-flex items-center gap-1.5 text-xs font-medium text-muted-foreground hover:text-foreground px-1 py-1 cursor-pointer transition-colors select-none";
7113
+ addMetricBtn.appendChild(createPlusIcon());
7114
+ const addMetricLabel = document.createElement("span");
7115
+ addMetricLabel.textContent = "Add metric";
7116
+ addMetricBtn.appendChild(addMetricLabel);
7117
+ addMetricBtn.addEventListener("click", (e) => {
7118
+ e.preventDefault();
7119
+ e.stopPropagation();
7120
+ if (!this.detailed) view.dispatch({ effects: setFrontmatterDetailed.of(true) });
7121
+ const activeRoot = view.dom.querySelector(".cm-frontmatter-preview") ?? root;
7122
+ const existing = activeRoot.querySelector("[data-frontmatter-metric-draft] input");
7123
+ if (existing) {
7124
+ existing.focus();
7125
+ return;
7126
+ }
7127
+ const referenceProvider = view.state.facet(frontmatterReferenceIndex)?.() ?? null;
7128
+ const draftRow = createMetricDraftRow(view, activeRoot, referenceProvider, (path, value) => {
7129
+ patchPropertyInView(view, "new", path, value, true);
7130
+ });
7131
+ activeRoot.appendChild(draftRow);
7132
+ ensureColumnHeader(activeRoot, draftRow);
7133
+ draftRow.querySelector("input")?.focus();
7134
+ view.requestMeasure();
7135
+ });
7136
+ actionsRow.appendChild(addMetricBtn);
7137
+ const addSummaryBtn = document.createElement("button");
7138
+ addSummaryBtn.type = "button";
7139
+ addSummaryBtn.setAttribute("aria-label", "Add summary");
7140
+ addSummaryBtn.setAttribute("data-testid", "editor-add-summary-note");
7141
+ addSummaryBtn.className = "inline-flex items-center gap-1.5 text-xs font-medium text-muted-foreground hover:text-foreground px-1 py-1 cursor-pointer transition-colors select-none";
7142
+ addSummaryBtn.appendChild(createPlusIcon());
7143
+ const addSummaryLabel = document.createElement("span");
7144
+ addSummaryLabel.textContent = "Add summary";
7145
+ addSummaryBtn.appendChild(addSummaryLabel);
7146
+ addSummaryBtn.addEventListener("click", (e) => {
7147
+ e.preventDefault();
7148
+ e.stopPropagation();
7149
+ if (!this.detailed) view.dispatch({ effects: setFrontmatterDetailed.of(true) });
7150
+ const activeRoot = view.dom.querySelector(".cm-frontmatter-preview") ?? root;
7151
+ const existing = activeRoot.querySelector("[data-frontmatter-summary-draft] input");
7152
+ if (existing) {
7153
+ existing.focus();
7154
+ return;
7155
+ }
7156
+ const draftRow = createSummaryDraftRow(view, (path, value) => {
7157
+ patchPropertyInView(view, "new", path, value, true);
7158
+ });
7159
+ activeRoot.appendChild(draftRow);
7160
+ ensureColumnHeader(activeRoot, draftRow);
7161
+ draftRow.querySelector("input")?.focus();
7162
+ view.requestMeasure();
7163
+ });
7164
+ actionsRow.appendChild(addSummaryBtn);
6786
7165
  const addTagBtn = document.createElement("button");
6787
7166
  addTagBtn.type = "button";
6788
7167
  addTagBtn.setAttribute("aria-label", "Add tag");
@@ -6794,17 +7173,19 @@ var EmptyFrontmatterWidget = class extends view.WidgetType {
6794
7173
  addTagBtn.addEventListener("click", (e) => {
6795
7174
  e.preventDefault();
6796
7175
  e.stopPropagation();
6797
- const existingDraft = root.querySelector("[data-frontmatter-tag-draft] input");
7176
+ if (!this.detailed) view.dispatch({ effects: setFrontmatterDetailed.of(true) });
7177
+ const activeRoot = view.dom.querySelector(".cm-frontmatter-preview") ?? root;
7178
+ const existingDraft = activeRoot.querySelector("[data-frontmatter-tag-draft] input");
6798
7179
  if (existingDraft) {
6799
7180
  existingDraft.focus();
6800
7181
  return;
6801
7182
  }
6802
- const draftRow = createTagDraftRow(view, root, "category", (val) => {
7183
+ const draftRow = createTagDraftRow(view, activeRoot, "category", (val) => {
6803
7184
  pendingFocus.set(view, { kind: "tag", key: "category" });
6804
- patchPropertyInView(view, "new", ["category"], [val]);
7185
+ patchPropertyInView(view, "new", ["category"], [val], true);
6805
7186
  });
6806
- root.appendChild(draftRow);
6807
- ensureColumnHeader(root, draftRow);
7187
+ activeRoot.appendChild(draftRow);
7188
+ ensureColumnHeader(activeRoot, draftRow);
6808
7189
  draftRow.querySelector("input")?.focus();
6809
7190
  view.requestMeasure();
6810
7191
  });
@@ -6826,14 +7207,14 @@ function buildFrontmatterDecos(state$1) {
6826
7207
  builder.add(section.from, to, view.Decoration.replace({ block: true }));
6827
7208
  continue;
6828
7209
  }
6829
- const selected = !state$1.readOnly && state$1.selection.ranges.some((range) => range.from < section.to && range.to > section.from);
7210
+ const selected = !state$1.readOnly && (state$1.field(frontmatterSource) || state$1.selection.ranges.some((range) => range.from < section.to && range.to > section.from));
6830
7211
  if (selected) continue;
6831
7212
  const rawContent = state$1.doc.sliceString(section.from, section.to);
6832
7213
  builder.add(
6833
7214
  section.from,
6834
7215
  section.to,
6835
7216
  view.Decoration.replace({
6836
- widget: new DefaultFrontmatterWidget(section.id, rawContent, state$1.readOnly),
7217
+ widget: new DefaultFrontmatterWidget(section.id, rawContent, state$1.readOnly, state$1.field(frontmatterDetailed)),
6837
7218
  block: true
6838
7219
  })
6839
7220
  );
@@ -6843,7 +7224,7 @@ function buildFrontmatterDecos(state$1) {
6843
7224
  0,
6844
7225
  0,
6845
7226
  view.Decoration.widget({
6846
- widget: new EmptyFrontmatterWidget(),
7227
+ widget: new EmptyFrontmatterWidget(state$1.field(frontmatterDetailed)),
6847
7228
  side: -1,
6848
7229
  block: true
6849
7230
  })
@@ -6856,7 +7237,7 @@ var frontmatterPreviewField = state.StateField.define({
6856
7237
  return buildFrontmatterDecos(state);
6857
7238
  },
6858
7239
  update(deco, tr) {
6859
- if (tr.docChanged || tr.reconfigured || tr.selection || tr.effects.some((e) => e.is(referenceIndexReload))) {
7240
+ if (tr.docChanged || tr.reconfigured || tr.selection || tr.effects.some((e) => e.is(referenceIndexReload) || e.is(setFrontmatterDetailed) || e.is(setFrontmatterSource))) {
6860
7241
  return buildFrontmatterDecos(tr.state);
6861
7242
  }
6862
7243
  return deco;
@@ -6868,7 +7249,7 @@ var frontmatterPreviewField = state.StateField.define({
6868
7249
  )
6869
7250
  ]
6870
7251
  });
6871
- var frontmatterPreview = [frontmatterPreviewField];
7252
+ var frontmatterPreview = [frontmatterDetailed, frontmatterSource, frontmatterPreviewField];
6872
7253
  var HEADING_RE = /^(#{1,6}) /;
6873
7254
  var BOLD_RE = /\*\*([^*\n]+)\*\*/g;
6874
7255
  var ITALIC_STAR_RE = /(?<!\*)\*([^*\n]+)\*(?!\*)/g;
@@ -13659,13 +14040,13 @@ function editorPreset(optionsOrDialect = "markdown") {
13659
14040
  }
13660
14041
 
13661
14042
  // src/version.ts
13662
- var VERSION = "0.45.2167";
13663
- var GIT_SHA = "d9859fd";
13664
- var BUILD_TIME = "2026-10-26T20:26:47.975Z";
14043
+ var VERSION = "0.47.2169";
14044
+ var GIT_SHA = "388d659";
14045
+ var BUILD_TIME = "2026-10-26T23:06:14.030Z";
13665
14046
  var SEMVER = {
13666
- version: "0.45.2167",
13667
- gitSha: "d9859fd",
13668
- buildTime: "2026-10-26T20:26:47.975Z"
14047
+ version: "0.47.2169",
14048
+ gitSha: "388d659",
14049
+ buildTime: "2026-10-26T23:06:14.030Z"
13669
14050
  };
13670
14051
 
13671
14052
  Object.defineProperty(exports, "CONTENT_GROUPING_DIMENSIONS", {
@@ -13769,9 +14150,11 @@ exports.forceSectionParse = forceSectionParse;
13769
14150
  exports.formatClockTime = formatClockTime;
13770
14151
  exports.formatMMSS = formatMMSS;
13771
14152
  exports.formatTimestamp = formatTimestamp;
14153
+ exports.frontmatterDetailed = frontmatterDetailed;
13772
14154
  exports.frontmatterPreview = frontmatterPreview;
13773
14155
  exports.frontmatterPreviewField = frontmatterPreviewField;
13774
14156
  exports.frontmatterReferenceIndex = frontmatterReferenceIndex;
14157
+ exports.frontmatterSource = frontmatterSource;
13775
14158
  exports.frontmatterSuggestions = frontmatterSuggestions;
13776
14159
  exports.frontmatterVisibility = frontmatterVisibility;
13777
14160
  exports.getClauseMeta = getClauseMeta;
@@ -13824,6 +14207,8 @@ exports.sectionRestore = sectionRestore;
13824
14207
  exports.sectionSaved = sectionSaved;
13825
14208
  exports.sessionQueryInsert = sessionQueryInsert;
13826
14209
  exports.sessionQueryWql = sessionQueryWql;
14210
+ exports.setFrontmatterDetailed = setFrontmatterDetailed;
14211
+ exports.setFrontmatterSource = setFrontmatterSource;
13827
14212
  exports.setGutterHighlights = setGutterHighlights;
13828
14213
  exports.setSuggestionBinding = setSuggestionBinding;
13829
14214
  exports.smartIncrement = smartIncrement;