@bitcobblers/wod-wiki-ui 0.45.2166 → 0.46.2168

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.
@@ -1300,9 +1300,51 @@ var referenceIndexReload = StateEffect.define();
1300
1300
  var frontmatterVisibility = Facet.define({
1301
1301
  combine: (values2) => values2[0] ?? true
1302
1302
  });
1303
+ var setFrontmatterDetailed = StateEffect.define();
1304
+ var frontmatterDetailed = StateField.define({
1305
+ create: () => false,
1306
+ update(value, tr) {
1307
+ for (const effect of tr.effects) {
1308
+ if (effect.is(setFrontmatterDetailed)) return effect.value;
1309
+ }
1310
+ return value;
1311
+ }
1312
+ });
1313
+ var setFrontmatterSource = StateEffect.define();
1314
+ var frontmatterSource = StateField.define({
1315
+ create: () => false,
1316
+ update(value, tr) {
1317
+ let source = value;
1318
+ for (const effect of tr.effects) {
1319
+ if (effect.is(setFrontmatterSource)) source = effect.value;
1320
+ }
1321
+ if (source && tr.selection) {
1322
+ const section = tr.state.field(sectionField).sections.find((item) => item.type === "frontmatter");
1323
+ if (!section || !tr.state.selection.ranges.some((range) => range.from <= section.to && range.to >= section.from)) {
1324
+ source = false;
1325
+ }
1326
+ }
1327
+ return source;
1328
+ }
1329
+ });
1303
1330
  var pendingFocus = /* @__PURE__ */ new WeakMap();
1304
1331
  var widgetCleanup = /* @__PURE__ */ new WeakMap();
1305
1332
  var helperId = 0;
1333
+ var TAG_CATEGORY_SUMMARY_KEYS = {
1334
+ tag: true,
1335
+ tags: true,
1336
+ category: true,
1337
+ domain: true,
1338
+ format: true,
1339
+ equipment: true,
1340
+ effort: true,
1341
+ quality: true,
1342
+ intent: true,
1343
+ discipline: true,
1344
+ intensity: true,
1345
+ intensitytier: true,
1346
+ sound: true
1347
+ };
1306
1348
  function isTagProperty(key) {
1307
1349
  return ["tag", "tags", "category"].includes(key.trim().toLowerCase());
1308
1350
  }
@@ -1419,12 +1461,18 @@ function parseTagList(val) {
1419
1461
  }
1420
1462
  return [];
1421
1463
  }
1422
- function patchPropertyInView(view, _sectionId, path, value) {
1464
+ function patchPropertyInView(view, _sectionId, path, value, expandDetailed = false) {
1423
1465
  if (view.state.readOnly) return;
1424
1466
  const wholeDoc = view.state.doc.toString();
1425
1467
  const next = patchFrontmatterProperty(wholeDoc, path, value);
1426
- if (next === wholeDoc) return;
1427
- view.dispatch({ changes: { from: 0, to: wholeDoc.length, insert: next } });
1468
+ if (next === wholeDoc) {
1469
+ if (expandDetailed) view.dispatch({ effects: setFrontmatterDetailed.of(true) });
1470
+ return;
1471
+ }
1472
+ view.dispatch({
1473
+ changes: { from: 0, to: wholeDoc.length, insert: next },
1474
+ ...expandDetailed ? { effects: setFrontmatterDetailed.of(true) } : {}
1475
+ });
1428
1476
  }
1429
1477
  function renamePropertyInView(view, _sectionId, fromKey, toKey, value) {
1430
1478
  if (view.state.readOnly) return;
@@ -1444,12 +1492,22 @@ function detectDefinitionKind(meta) {
1444
1492
  if (meta.registrySource !== void 0 || meta.baseAttributes !== void 0) return "effort";
1445
1493
  return null;
1446
1494
  }
1447
- function readBaseAttributes(body) {
1495
+ function readBaseAttributes(body, meta = {}) {
1448
1496
  const out = {};
1497
+ if (meta["metrics.met"] !== void 0 && !Array.isArray(meta["metrics.met"])) {
1498
+ out.met = String(meta["metrics.met"]);
1499
+ }
1500
+ if (meta["discipline"] !== void 0 && !Array.isArray(meta["discipline"])) {
1501
+ out.discipline = String(meta["discipline"]);
1502
+ }
1503
+ if (meta["intensityTier"] !== void 0 && !Array.isArray(meta["intensityTier"])) {
1504
+ out.intensityTier = String(meta["intensityTier"]);
1505
+ }
1449
1506
  let inBase = false;
1450
1507
  for (const line of body.split(/\r?\n/)) {
1451
1508
  if (/^baseAttributes:\s*(#.*)?$/.test(line)) {
1452
1509
  inBase = true;
1510
+ out.hasBaseBlock = true;
1453
1511
  continue;
1454
1512
  }
1455
1513
  if (!inBase) continue;
@@ -1579,7 +1637,8 @@ function renderEffortValueDiagnostics(provider, token, onPick) {
1579
1637
  var METRIC_DRAFT_KINDS = {
1580
1638
  effort: { label: "Effort", path: "metrics.effort", kind: "effort" },
1581
1639
  equipment: { label: "Equipment", path: "metrics.equipment", kind: "equipment" },
1582
- rpe: { label: "RPE", path: "metrics.rpe", kind: "number" }
1640
+ rpe: { label: "RPE", path: "metrics.rpe", kind: "number" },
1641
+ custom: { label: "Custom / Equation", path: "metrics.", kind: "custom" }
1583
1642
  };
1584
1643
  function createMetricDraftRow(view, root, referenceProvider, commit) {
1585
1644
  const row = document.createElement("div");
@@ -1598,6 +1657,7 @@ function createMetricDraftRow(view, root, referenceProvider, commit) {
1598
1657
  row.appendChild(select);
1599
1658
  const valCol = document.createElement("div");
1600
1659
  valCol.className = "flex items-center flex-wrap gap-1.5 px-3 py-1.5 flex-1 min-w-0";
1660
+ let customKeyInput = null;
1601
1661
  const input = document.createElement("input");
1602
1662
  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";
1603
1663
  input.placeholder = "Value\u2026";
@@ -1620,6 +1680,10 @@ function createMetricDraftRow(view, root, referenceProvider, commit) {
1620
1680
  else if (spec.kind === "number") {
1621
1681
  const num = Number(raw);
1622
1682
  if (!Number.isNaN(num)) commit([spec.path], num);
1683
+ } else if (spec.kind === "custom") {
1684
+ const keyName = (customKeyInput?.value.trim() || "custom").replace(/^metrics\./i, "");
1685
+ const num = Number(raw);
1686
+ commit([`metrics.${keyName}`], !Number.isNaN(num) && raw !== "" ? num : raw);
1623
1687
  } else commit([spec.path], raw);
1624
1688
  }
1625
1689
  row.remove();
@@ -1627,6 +1691,31 @@ function createMetricDraftRow(view, root, referenceProvider, commit) {
1627
1691
  };
1628
1692
  const attachFor = () => {
1629
1693
  const spec = METRIC_DRAFT_KINDS[select.value];
1694
+ if (spec?.kind === "custom") {
1695
+ if (!customKeyInput) {
1696
+ customKeyInput = document.createElement("input");
1697
+ 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";
1698
+ customKeyInput.placeholder = "Metric key\u2026";
1699
+ customKeyInput.setAttribute("aria-label", "Metric key");
1700
+ customKeyInput.dataset.frontmatterField = "key";
1701
+ customKeyInput.addEventListener("keydown", (e) => {
1702
+ if (e.key === "Escape") {
1703
+ e.preventDefault();
1704
+ settle(false);
1705
+ } else if (e.key === "Enter") {
1706
+ e.preventDefault();
1707
+ if (input.value.trim()) settle(true);
1708
+ else input.focus();
1709
+ }
1710
+ });
1711
+ valCol.insertBefore(customKeyInput, input);
1712
+ }
1713
+ input.placeholder = "Value or equation\u2026";
1714
+ } else if (customKeyInput) {
1715
+ customKeyInput.remove();
1716
+ customKeyInput = null;
1717
+ input.placeholder = "Value\u2026";
1718
+ }
1630
1719
  if (spec && (spec.kind === "effort" || spec.kind === "equipment") && referenceProvider) {
1631
1720
  attachSuggestions(input, view, root, "value", spec.path, [], () => settle(true), spec.kind);
1632
1721
  }
@@ -1647,6 +1736,7 @@ function createMetricDraftRow(view, root, referenceProvider, commit) {
1647
1736
  });
1648
1737
  input.addEventListener("blur", () => queueMicrotask(() => {
1649
1738
  if (!input.isConnected) return;
1739
+ if (row.contains(document.activeElement)) return;
1650
1740
  settle(input.value.trim().length > 0);
1651
1741
  }));
1652
1742
  select.addEventListener("keydown", (e) => {
@@ -1657,6 +1747,82 @@ function createMetricDraftRow(view, root, referenceProvider, commit) {
1657
1747
  });
1658
1748
  return row;
1659
1749
  }
1750
+ function createSummaryDraftRow(view, commit) {
1751
+ const row = document.createElement("div");
1752
+ 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";
1753
+ row.setAttribute("data-frontmatter-summary-draft", "true");
1754
+ row.dataset.frontmatterRow = "summary";
1755
+ const keyCol = document.createElement("div");
1756
+ 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";
1757
+ keyCol.appendChild(createListIcon());
1758
+ const keyInput = document.createElement("input");
1759
+ 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";
1760
+ keyInput.placeholder = "Summary key\u2026";
1761
+ keyInput.setAttribute("aria-label", "Summary key");
1762
+ keyInput.dataset.frontmatterField = "key";
1763
+ keyInput.setAttribute(
1764
+ "aria-describedby",
1765
+ appendFieldHelper(keyCol, "Summary metric name. Press Enter to move to the equation.").id
1766
+ );
1767
+ keyCol.appendChild(keyInput);
1768
+ row.appendChild(keyCol);
1769
+ const valCol = document.createElement("div");
1770
+ valCol.className = "flex items-center flex-wrap gap-1.5 px-3 py-1.5 flex-1 min-w-0";
1771
+ const valInput = document.createElement("input");
1772
+ 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";
1773
+ valInput.placeholder = "Equation (e.g. sum(work))\u2026";
1774
+ valInput.setAttribute("aria-label", "Summary equation");
1775
+ valInput.dataset.frontmatterField = "value";
1776
+ valInput.setAttribute(
1777
+ "aria-describedby",
1778
+ appendFieldHelper(valCol, "Summary equation. Press Enter to apply.").id
1779
+ );
1780
+ valCol.appendChild(valInput);
1781
+ row.appendChild(valCol);
1782
+ let settled = false;
1783
+ const settle = (doCommit) => {
1784
+ if (settled) return;
1785
+ settled = true;
1786
+ const rawKey = (keyInput.value.trim() || "summary").replace(/^summaries\./i, "");
1787
+ const rawExpr = valInput.value.trim();
1788
+ if (doCommit && rawExpr) {
1789
+ commit([`summaries.${rawKey}`], rawExpr);
1790
+ }
1791
+ row.remove();
1792
+ view.requestMeasure();
1793
+ };
1794
+ keyInput.addEventListener("keydown", (e) => {
1795
+ if (e.key === "Escape") {
1796
+ e.preventDefault();
1797
+ settle(false);
1798
+ return;
1799
+ }
1800
+ if (e.key === "Enter") {
1801
+ e.preventDefault();
1802
+ if (valInput.value.trim()) settle(true);
1803
+ else valInput.focus();
1804
+ }
1805
+ });
1806
+ valInput.addEventListener("keydown", (e) => {
1807
+ if (e.key === "Escape") {
1808
+ e.preventDefault();
1809
+ settle(false);
1810
+ return;
1811
+ }
1812
+ if (e.key === "Enter") {
1813
+ e.preventDefault();
1814
+ settle(true);
1815
+ }
1816
+ });
1817
+ const onBlur = () => queueMicrotask(() => {
1818
+ if (!row.isConnected) return;
1819
+ if (row.contains(document.activeElement)) return;
1820
+ settle(valInput.value.trim().length > 0);
1821
+ });
1822
+ keyInput.addEventListener("blur", onBlur);
1823
+ valInput.addEventListener("blur", onBlur);
1824
+ return row;
1825
+ }
1660
1826
  function createTagIcon() {
1661
1827
  const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
1662
1828
  svg.setAttribute("viewBox", "0 0 24 24");
@@ -1765,18 +1931,88 @@ function createCloseIcon(size = "md") {
1765
1931
  svg.appendChild(l2);
1766
1932
  return svg;
1767
1933
  }
1934
+ function isAliasProperty(key) {
1935
+ return ["alias", "aliases"].includes(key.trim().toLowerCase());
1936
+ }
1937
+ function createSummaryPill(value, hash2) {
1938
+ const pill = document.createElement("span");
1939
+ 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";
1940
+ const label = document.createElement("span");
1941
+ label.className = "truncate";
1942
+ label.textContent = hash2 ? `#${value}` : value;
1943
+ pill.appendChild(label);
1944
+ return pill;
1945
+ }
1946
+ function createFrontmatterSummary(entries, onShowDetails) {
1947
+ const summary = document.createElement("div");
1948
+ summary.dataset.frontmatterSummary = "true";
1949
+ summary.className = "divide-y divide-border/60";
1950
+ const groups = [
1951
+ { label: "Tags", entries: entries.filter(([key]) => TAG_CATEGORY_SUMMARY_KEYS[key.trim().toLowerCase()] === true), hash: true },
1952
+ { label: "Aliases", entries: entries.filter(([key]) => isAliasProperty(key)), hash: false }
1953
+ ];
1954
+ for (const group of groups) {
1955
+ const values2 = group.entries.flatMap(([, value]) => parseTagList(value));
1956
+ if (values2.length === 0) continue;
1957
+ const row = document.createElement("div");
1958
+ row.className = "flex flex-wrap items-center gap-1.5 px-3 py-2";
1959
+ const label = document.createElement("span");
1960
+ label.className = "mr-1 w-14 shrink-0 text-[11px] font-medium text-muted-foreground";
1961
+ label.textContent = group.label;
1962
+ row.appendChild(label);
1963
+ for (const value of values2) row.appendChild(createSummaryPill(value, group.hash));
1964
+ summary.appendChild(row);
1965
+ }
1966
+ const hidden = entries.filter(([key]) => TAG_CATEGORY_SUMMARY_KEYS[key.trim().toLowerCase()] !== true && !isAliasProperty(key));
1967
+ const metricCount = hidden.filter(([key]) => {
1968
+ const k = key.trim().toLowerCase();
1969
+ return k.startsWith("metrics.") || k === "metrics";
1970
+ }).length;
1971
+ const summaryCount = hidden.filter(([key]) => {
1972
+ const k = key.trim().toLowerCase();
1973
+ return k.startsWith("summaries.") || k === "summaries";
1974
+ }).length;
1975
+ const details = document.createElement("button");
1976
+ details.type = "button";
1977
+ details.setAttribute("aria-label", "Show property details");
1978
+ details.setAttribute("aria-expanded", "false");
1979
+ 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";
1980
+ const count2 = document.createElement("span");
1981
+ count2.textContent = `${hidden.length} more ${hidden.length === 1 ? "property" : "properties"}`;
1982
+ details.appendChild(count2);
1983
+ if (metricCount > 0) {
1984
+ const metrics = document.createElement("span");
1985
+ metrics.textContent = `\xB7 ${metricCount} custom ${metricCount === 1 ? "metric" : "metrics"}`;
1986
+ details.appendChild(metrics);
1987
+ }
1988
+ if (summaryCount > 0) {
1989
+ const summaries = document.createElement("span");
1990
+ summaries.textContent = `\xB7 ${summaryCount} ${summaryCount === 1 ? "summary" : "summaries"}`;
1991
+ details.appendChild(summaries);
1992
+ }
1993
+ const disclosure = document.createElement("span");
1994
+ disclosure.className = "ml-auto";
1995
+ disclosure.setAttribute("aria-hidden", "true");
1996
+ disclosure.textContent = "\u25BE";
1997
+ details.appendChild(disclosure);
1998
+ details.addEventListener("click", onShowDetails);
1999
+ summary.appendChild(details);
2000
+ return summary;
2001
+ }
1768
2002
  var DefaultFrontmatterWidget = class extends WidgetType {
1769
2003
  sectionId;
1770
2004
  rawContent;
1771
2005
  readOnly;
1772
- constructor(sectionId, rawContent, readOnly = false) {
2006
+ detailed;
2007
+ constructor(sectionId, rawContent, readOnly = false, detailed = false) {
1773
2008
  super();
1774
2009
  this.sectionId = sectionId;
1775
2010
  this.rawContent = rawContent ?? "";
1776
2011
  this.readOnly = readOnly;
2012
+ this.detailed = detailed;
1777
2013
  }
1778
2014
  eq(other) {
1779
- return this.sectionId === other.sectionId && this.rawContent === other.rawContent && this.readOnly === other.readOnly;
2015
+ return this.sectionId === other.sectionId && this.rawContent === other.rawContent && this.readOnly === other.readOnly && this.detailed === other.detailed;
1780
2016
  }
1781
2017
  ignoreEvent() {
1782
2018
  return true;
@@ -1786,10 +2022,25 @@ var DefaultFrontmatterWidget = class extends WidgetType {
1786
2022
  widgetCleanup.set(root, []);
1787
2023
  root.className = "cm-frontmatter-preview py-2 font-sans select-none";
1788
2024
  const heading = document.createElement("div");
1789
- heading.className = "text-xs font-medium text-muted-foreground mb-1.5 px-0.5";
1790
- heading.textContent = "Properties";
2025
+ heading.className = "mb-1.5 flex items-center px-0.5 text-xs font-medium text-muted-foreground";
2026
+ const headingLabel = document.createElement("span");
2027
+ headingLabel.textContent = "Properties";
2028
+ heading.appendChild(headingLabel);
2029
+ if (this.detailed) {
2030
+ const summaryButton = document.createElement("button");
2031
+ summaryButton.type = "button";
2032
+ summaryButton.setAttribute("aria-label", "Show property summary");
2033
+ summaryButton.setAttribute("aria-expanded", "true");
2034
+ summaryButton.className = "ml-auto px-1 py-0.5 text-[11px] font-normal hover:text-foreground cursor-pointer";
2035
+ summaryButton.textContent = "Hide details \u25B4";
2036
+ summaryButton.addEventListener("click", () => {
2037
+ view.dispatch({ effects: setFrontmatterDetailed.of(false) });
2038
+ });
2039
+ heading.appendChild(summaryButton);
2040
+ }
1791
2041
  root.appendChild(heading);
1792
2042
  const box = document.createElement("div");
2043
+ box.dataset.frontmatterProperties = "true";
1793
2044
  box.className = "rounded-lg border border-border/80 bg-background/50 text-xs shadow-xs";
1794
2045
  root.appendChild(box);
1795
2046
  const body = this.rawContent.replace(/^---\r?\n?/, "").replace(/\r?\n?---\r?$/, "");
@@ -1797,329 +2048,335 @@ var DefaultFrontmatterWidget = class extends WidgetType {
1797
2048
  const entries = Object.entries(meta);
1798
2049
  const docKind = detectDefinitionKind(meta);
1799
2050
  const referenceProvider = view.state.facet(frontmatterReferenceIndex)?.() ?? null;
1800
- const base = docKind === "effort" ? readBaseAttributes(body) : {};
1801
- box.appendChild(createColumnHeader());
1802
- if (entries.length === 0) {
1803
- const emptyRow = document.createElement("div");
1804
- emptyRow.className = "px-3 py-2 text-xs text-muted-foreground/60 italic";
1805
- emptyRow.textContent = "No properties";
1806
- box.appendChild(emptyRow);
1807
- }
1808
- for (const [key, value] of entries) {
1809
- const isTags = isTagProperty(key);
1810
- const valueKind = referenceValueKind(key, docKind);
1811
- const isReferenceList = (isReferenceKey(key, docKind) || key === "aliases") && Array.isArray(value);
1812
- const nested = value === "" && ownsNestedBlock(body, key);
1813
- const row = document.createElement("div");
1814
- 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";
1815
- row.dataset.frontmatterRow = key;
1816
- box.appendChild(row);
1817
- const keyCol = document.createElement("div");
1818
- 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";
1819
- row.appendChild(keyCol);
1820
- keyCol.appendChild(isTags || isReferenceList ? createTagIcon() : createListIcon());
1821
- if (this.readOnly || nested) {
1822
- const keyLabel = document.createElement("span");
1823
- keyLabel.className = "truncate font-medium text-foreground/90";
1824
- keyLabel.textContent = key;
1825
- keyCol.appendChild(keyLabel);
1826
- } else {
1827
- const keyInput = document.createElement("input");
1828
- 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";
1829
- keyInput.value = key;
1830
- keyInput.spellcheck = false;
1831
- keyInput.setAttribute("aria-label", `Property key: ${key}`);
1832
- keyInput.dataset.frontmatterField = "key";
1833
- keyInput.dataset.frontmatterProperty = key;
1834
- keyInput.setAttribute(
1835
- "aria-describedby",
1836
- appendFieldHelper(
1837
- keyCol,
1838
- "Property name. Press Enter to apply. Arrow keys browse suggested property names."
1839
- ).id
1840
- );
1841
- let currentKey = key;
1842
- const commitKey = () => {
1843
- const nextKey = keyInput.value.trim();
1844
- if (!nextKey || nextKey === currentKey) {
1845
- keyInput.value = currentKey;
1846
- return;
1847
- }
1848
- if (!/^[A-Za-z_][\w.-]*$/.test(nextKey) || nextKey in meta) {
1849
- keyInput.value = currentKey;
1850
- return;
1851
- }
1852
- renamePropertyInView(view, this.sectionId, currentKey, nextKey, value);
1853
- delete meta[currentKey];
1854
- meta[nextKey] = value;
1855
- currentKey = nextKey;
1856
- };
1857
- const commitKeyIfAttached = () => queueMicrotask(() => {
1858
- if (keyInput.isConnected) commitKey();
1859
- });
1860
- keyInput.addEventListener("change", commitKeyIfAttached);
1861
- keyCol.appendChild(keyInput);
1862
- attachSuggestions(keyInput, view, root, "key", key, entries.map(([name]) => name), commitKey);
1863
- keyInput.addEventListener("blur", commitKeyIfAttached);
1864
- keyInput.addEventListener("keydown", (e) => {
1865
- if (e.defaultPrevented) return;
1866
- if (e.key === "Enter") {
1867
- e.preventDefault();
1868
- keyInput.blur();
1869
- }
1870
- });
1871
- const focus = pendingFocus.get(view);
1872
- if (focus?.kind === "key" && focus.key === key) {
1873
- setTimeout(() => {
1874
- if (!keyInput.isConnected) return;
1875
- keyInput.focus();
1876
- keyInput.select();
1877
- }, 0);
1878
- pendingFocus.delete(view);
1879
- }
1880
- }
1881
- const valCol = document.createElement("div");
1882
- valCol.className = "flex items-center flex-wrap gap-1.5 px-3 py-1.5 flex-1 min-w-0";
1883
- row.appendChild(valCol);
1884
- const commitList = (next) => {
1885
- meta[key] = next;
1886
- patchPropertyInView(view, this.sectionId, [key], next.length > 0 ? next : "");
1887
- };
1888
- const refMatch = (token) => {
1889
- if (!referenceProvider) return null;
1890
- return valueKind === "effort" ? referenceProvider.resolveEffort(token) : valueKind === "equipment" ? referenceProvider.resolveEquipment(token) : null;
1891
- };
1892
- if (isTags || isReferenceList) {
1893
- const chips = parseTagList(value);
1894
- const chipIsReference = valueKind !== "tags";
1895
- for (let idx = 0; idx < chips.length; idx++) {
1896
- const chip = chips[idx];
1897
- const match = chipIsReference ? refMatch(chip) : null;
1898
- const pill = document.createElement("span");
1899
- 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";
1900
- const pillText = document.createElement("span");
1901
- pillText.textContent = match ? match.label : chip;
1902
- pill.appendChild(pillText);
1903
- if (match && referenceProvider) {
1904
- const openLink = document.createElement("a");
1905
- openLink.href = referenceProvider.openHref(valueKind, match.slug);
1906
- openLink.title = `Open ${match.label} page`;
1907
- openLink.setAttribute("aria-label", `Open ${match.label} page`);
1908
- openLink.className = "inline-flex items-center justify-center text-muted-foreground/60 hover:text-foreground cursor-pointer";
1909
- openLink.appendChild(createExternalIcon());
1910
- pill.appendChild(openLink);
1911
- }
1912
- if (!this.readOnly) {
1913
- const rmBtn = document.createElement("button");
1914
- rmBtn.type = "button";
1915
- rmBtn.setAttribute("aria-label", isTags ? `Remove tag ${chip}` : `Remove ${chip}`);
1916
- rmBtn.className = "inline-flex items-center justify-center text-muted-foreground/60 hover:text-foreground dark:hover:text-foreground cursor-pointer";
1917
- rmBtn.appendChild(createCloseIcon("sm"));
1918
- rmBtn.addEventListener("click", (e) => {
1919
- e.preventDefault();
1920
- e.stopPropagation();
1921
- commitList(chips.filter((_, i) => i !== idx));
1922
- });
1923
- pill.appendChild(rmBtn);
1924
- }
1925
- valCol.appendChild(pill);
1926
- }
1927
- if (!this.readOnly) {
1928
- const chipInput = document.createElement("input");
1929
- 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";
1930
- chipInput.placeholder = chips.length === 0 ? isReferenceList ? `Add ${valueKind === "effort" ? "effort" : "equipment"}\u2026` : "Add tags\u2026" : isTags ? "Add tag\u2026" : "Add\u2026";
1931
- chipInput.setAttribute("aria-label", `Value for ${key}`);
1932
- chipInput.dataset.frontmatterField = "value";
1933
- chipInput.dataset.frontmatterProperty = key;
1934
- chipInput.setAttribute(
2051
+ const base = docKind === "effort" ? readBaseAttributes(body, meta) : {};
2052
+ if (!this.detailed) {
2053
+ box.appendChild(createFrontmatterSummary(entries, () => {
2054
+ view.dispatch({ effects: setFrontmatterDetailed.of(true) });
2055
+ }));
2056
+ } else {
2057
+ box.appendChild(createColumnHeader());
2058
+ if (entries.length === 0) {
2059
+ const emptyRow = document.createElement("div");
2060
+ emptyRow.className = "px-3 py-2 text-xs text-muted-foreground/60 italic";
2061
+ emptyRow.textContent = "No properties";
2062
+ box.appendChild(emptyRow);
2063
+ }
2064
+ for (const [key, value] of entries) {
2065
+ const isTags = isTagProperty(key);
2066
+ const valueKind = referenceValueKind(key, docKind);
2067
+ const isReferenceList = (isReferenceKey(key, docKind) || key === "aliases") && Array.isArray(value);
2068
+ const nested = value === "" && ownsNestedBlock(body, key);
2069
+ const row = document.createElement("div");
2070
+ 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";
2071
+ row.dataset.frontmatterRow = key;
2072
+ box.appendChild(row);
2073
+ const keyCol = document.createElement("div");
2074
+ 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";
2075
+ row.appendChild(keyCol);
2076
+ keyCol.appendChild(isTags || isReferenceList ? createTagIcon() : createListIcon());
2077
+ if (this.readOnly || nested) {
2078
+ const keyLabel = document.createElement("span");
2079
+ keyLabel.className = "truncate font-medium text-foreground/90";
2080
+ keyLabel.textContent = key;
2081
+ keyCol.appendChild(keyLabel);
2082
+ } else {
2083
+ const keyInput = document.createElement("input");
2084
+ 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";
2085
+ keyInput.value = key;
2086
+ keyInput.spellcheck = false;
2087
+ keyInput.setAttribute("aria-label", `Property key: ${key}`);
2088
+ keyInput.dataset.frontmatterField = "key";
2089
+ keyInput.dataset.frontmatterProperty = key;
2090
+ keyInput.setAttribute(
1935
2091
  "aria-describedby",
1936
2092
  appendFieldHelper(
1937
- valCol,
1938
- "Press Enter or comma to add a value. Arrow keys browse the suggestions."
2093
+ keyCol,
2094
+ "Property name. Press Enter to apply. Arrow keys browse suggested property names."
1939
2095
  ).id
1940
2096
  );
1941
- const commitNewChip = () => {
1942
- const val = chipInput.value.trim().replace(/^,|,$/g, "");
1943
- if (val && !chips.some((chip) => chip.toLowerCase() === val.toLowerCase())) {
1944
- chipInput.value = "";
1945
- pendingFocus.set(view, { kind: "tag", key });
1946
- commitList([...chips, val]);
2097
+ let currentKey = key;
2098
+ const commitKey = () => {
2099
+ const nextKey = keyInput.value.trim();
2100
+ if (!nextKey || nextKey === currentKey) {
2101
+ keyInput.value = currentKey;
2102
+ return;
2103
+ }
2104
+ if (!/^[A-Za-z_][\w.-]*$/.test(nextKey) || nextKey in meta) {
2105
+ keyInput.value = currentKey;
2106
+ return;
1947
2107
  }
2108
+ renamePropertyInView(view, this.sectionId, currentKey, nextKey, value);
2109
+ delete meta[currentKey];
2110
+ meta[nextKey] = value;
2111
+ currentKey = nextKey;
1948
2112
  };
1949
- valCol.appendChild(chipInput);
1950
- attachSuggestions(chipInput, view, root, "value", key, chips, commitNewChip, valueKind);
1951
- chipInput.addEventListener("keydown", (e) => {
2113
+ const commitKeyIfAttached = () => queueMicrotask(() => {
2114
+ if (keyInput.isConnected) commitKey();
2115
+ });
2116
+ keyInput.addEventListener("change", commitKeyIfAttached);
2117
+ keyCol.appendChild(keyInput);
2118
+ attachSuggestions(keyInput, view, root, "key", key, entries.map(([name]) => name), commitKey);
2119
+ keyInput.addEventListener("blur", commitKeyIfAttached);
2120
+ keyInput.addEventListener("keydown", (e) => {
1952
2121
  if (e.defaultPrevented) return;
1953
- if (e.key === "Enter" || e.key === ",") {
1954
- e.preventDefault();
1955
- commitNewChip();
1956
- } else if (e.key === "Backspace" && !chipInput.value && chips.length > 0) {
2122
+ if (e.key === "Enter") {
1957
2123
  e.preventDefault();
1958
- pendingFocus.set(view, { kind: "tag", key });
1959
- commitList(chips.slice(0, -1));
2124
+ keyInput.blur();
1960
2125
  }
1961
2126
  });
1962
- chipInput.addEventListener("blur", () => queueMicrotask(() => {
1963
- if (chipInput.isConnected && chipInput.value.trim()) commitNewChip();
1964
- }));
1965
2127
  const focus = pendingFocus.get(view);
1966
- if (focus?.kind === "tag" && focus.key === key) {
2128
+ if (focus?.kind === "key" && focus.key === key) {
1967
2129
  setTimeout(() => {
1968
- if (chipInput.isConnected) chipInput.focus();
2130
+ if (!keyInput.isConnected) return;
2131
+ keyInput.focus();
2132
+ keyInput.select();
1969
2133
  }, 0);
1970
2134
  pendingFocus.delete(view);
1971
2135
  }
1972
- if (chipIsReference && referenceProvider) {
1973
- const unknown = chips.filter((chip) => !refMatch(chip));
1974
- if (unknown.length > 0) {
1975
- const diag = document.createElement("div");
1976
- diag.setAttribute("data-testid", "editor-ref-unknown");
1977
- diag.className = "w-full basis-full flex flex-wrap items-center gap-2 text-[10px] text-destructive";
1978
- const label = document.createElement("span");
1979
- label.textContent = `Unknown ${valueKind === "effort" ? "effort" : "equipment"}: ${unknown.join(", ")}`;
1980
- diag.appendChild(label);
1981
- const selectBtn = document.createElement("button");
1982
- selectBtn.type = "button";
1983
- selectBtn.textContent = "Select existing";
1984
- selectBtn.className = "underline hover:text-foreground cursor-pointer";
1985
- selectBtn.addEventListener("click", (e) => {
1986
- e.preventDefault();
1987
- chipInput.focus();
1988
- });
1989
- diag.appendChild(selectBtn);
1990
- const createLink = document.createElement("a");
1991
- createLink.href = referenceProvider.createHref(valueKind);
1992
- createLink.textContent = "Create page";
1993
- createLink.className = "underline hover:text-foreground";
1994
- diag.appendChild(createLink);
1995
- valCol.appendChild(diag);
1996
- }
1997
- }
1998
2136
  }
1999
- } else if (nested) {
2000
- const valSpan = document.createElement("span");
2001
- valSpan.className = "text-xs text-muted-foreground/60 italic py-0.5 truncate";
2002
- valSpan.textContent = "Nested block \u2014 edit in YAML";
2003
- valCol.appendChild(valSpan);
2004
- } else {
2005
- const strVal = Array.isArray(value) ? value.join(", ") : String(value ?? "");
2006
- if (this.readOnly) {
2007
- const valSpan = document.createElement("span");
2008
- valSpan.className = "text-xs text-foreground py-0.5 truncate";
2009
- valSpan.textContent = strVal;
2010
- valCol.appendChild(valSpan);
2011
- } else {
2012
- const valInput = document.createElement("input");
2013
- 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";
2014
- valInput.value = strVal;
2015
- valInput.spellcheck = false;
2016
- valInput.setAttribute("aria-label", `Value for ${key}`);
2017
- valInput.dataset.frontmatterField = "value";
2018
- valInput.dataset.frontmatterProperty = key;
2019
- valInput.setAttribute(
2020
- "aria-describedby",
2021
- appendFieldHelper(valCol, "Property value. Press Enter to apply.").id
2022
- );
2023
- let committedVal = strVal;
2024
- const commitVal = () => {
2025
- const cur = valInput.value.trim();
2026
- if (cur !== committedVal) {
2027
- committedVal = cur;
2028
- const num = Number(cur);
2029
- const next = cur !== "" && !isNaN(num) ? num : cur;
2030
- meta[key] = next;
2031
- patchPropertyInView(view, this.sectionId, [key], next);
2032
- }
2033
- };
2034
- const commitValIfAttached = () => queueMicrotask(() => {
2035
- if (valInput.isConnected) commitVal();
2036
- });
2037
- valInput.addEventListener("change", commitValIfAttached);
2038
- valCol.appendChild(valInput);
2039
- attachSuggestions(
2040
- valInput,
2041
- view,
2042
- root,
2043
- "value",
2044
- key,
2045
- [strVal],
2046
- commitVal,
2047
- key.trim().toLowerCase() === "metrics.effort" ? "effort" : "tags"
2048
- );
2049
- valInput.addEventListener("blur", commitValIfAttached);
2050
- valInput.addEventListener("keydown", (e) => {
2051
- if (e.defaultPrevented) return;
2052
- if (e.key === "Enter") {
2053
- e.preventDefault();
2054
- valInput.blur();
2055
- }
2056
- });
2057
- if (key.trim().toLowerCase() === "metrics.effort" && referenceProvider) {
2058
- const match = referenceProvider.resolveEffort(strVal);
2059
- if (match) {
2060
- const pill = document.createElement("span");
2061
- 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";
2062
- const pillText = document.createElement("span");
2063
- pillText.textContent = match.label;
2064
- pill.appendChild(pillText);
2137
+ const valCol = document.createElement("div");
2138
+ valCol.className = "flex items-center flex-wrap gap-1.5 px-3 py-1.5 flex-1 min-w-0";
2139
+ row.appendChild(valCol);
2140
+ const commitList = (next) => {
2141
+ meta[key] = next;
2142
+ patchPropertyInView(view, this.sectionId, [key], next.length > 0 ? next : "");
2143
+ };
2144
+ const refMatch = (token) => {
2145
+ if (!referenceProvider) return null;
2146
+ return valueKind === "effort" ? referenceProvider.resolveEffort(token) : valueKind === "equipment" ? referenceProvider.resolveEquipment(token) : null;
2147
+ };
2148
+ if (isTags || isReferenceList) {
2149
+ const chips = parseTagList(value);
2150
+ const chipIsReference = valueKind !== "tags";
2151
+ for (let idx = 0; idx < chips.length; idx++) {
2152
+ const chip = chips[idx];
2153
+ const match = chipIsReference ? refMatch(chip) : null;
2154
+ const pill = document.createElement("span");
2155
+ 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";
2156
+ const pillText = document.createElement("span");
2157
+ pillText.textContent = match ? match.label : chip;
2158
+ pill.appendChild(pillText);
2159
+ if (match && referenceProvider) {
2065
2160
  const openLink = document.createElement("a");
2066
- openLink.href = referenceProvider.openHref("effort", match.slug);
2161
+ openLink.href = referenceProvider.openHref(valueKind, match.slug);
2067
2162
  openLink.title = `Open ${match.label} page`;
2068
2163
  openLink.setAttribute("aria-label", `Open ${match.label} page`);
2069
2164
  openLink.className = "inline-flex items-center justify-center text-muted-foreground/60 hover:text-foreground cursor-pointer";
2070
2165
  openLink.appendChild(createExternalIcon());
2071
2166
  pill.appendChild(openLink);
2072
- valCol.insertBefore(pill, valInput);
2073
- 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";
2074
2167
  }
2075
- const diag = renderEffortValueDiagnostics(
2076
- referenceProvider,
2077
- strVal,
2078
- (slug) => {
2079
- meta[key] = slug;
2080
- patchPropertyInView(view, this.sectionId, [key], slug);
2168
+ if (!this.readOnly) {
2169
+ const rmBtn = document.createElement("button");
2170
+ rmBtn.type = "button";
2171
+ rmBtn.setAttribute("aria-label", isTags ? `Remove tag ${chip}` : `Remove ${chip}`);
2172
+ rmBtn.className = "inline-flex items-center justify-center text-muted-foreground/60 hover:text-foreground dark:hover:text-foreground cursor-pointer";
2173
+ rmBtn.appendChild(createCloseIcon("sm"));
2174
+ rmBtn.addEventListener("click", (e) => {
2175
+ e.preventDefault();
2176
+ e.stopPropagation();
2177
+ commitList(chips.filter((_, i) => i !== idx));
2178
+ });
2179
+ pill.appendChild(rmBtn);
2180
+ }
2181
+ valCol.appendChild(pill);
2182
+ }
2183
+ if (!this.readOnly) {
2184
+ const chipInput = document.createElement("input");
2185
+ 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";
2186
+ chipInput.placeholder = chips.length === 0 ? isReferenceList ? `Add ${valueKind === "effort" ? "effort" : "equipment"}\u2026` : "Add tags\u2026" : isTags ? "Add tag\u2026" : "Add\u2026";
2187
+ chipInput.setAttribute("aria-label", `Value for ${key}`);
2188
+ chipInput.dataset.frontmatterField = "value";
2189
+ chipInput.dataset.frontmatterProperty = key;
2190
+ chipInput.setAttribute(
2191
+ "aria-describedby",
2192
+ appendFieldHelper(
2193
+ valCol,
2194
+ "Press Enter or comma to add a value. Arrow keys browse the suggestions."
2195
+ ).id
2196
+ );
2197
+ const commitNewChip = () => {
2198
+ const val = chipInput.value.trim().replace(/^,|,$/g, "");
2199
+ if (val && !chips.some((chip) => chip.toLowerCase() === val.toLowerCase())) {
2200
+ chipInput.value = "";
2201
+ pendingFocus.set(view, { kind: "tag", key });
2202
+ commitList([...chips, val]);
2203
+ }
2204
+ };
2205
+ valCol.appendChild(chipInput);
2206
+ attachSuggestions(chipInput, view, root, "value", key, chips, commitNewChip, valueKind);
2207
+ chipInput.addEventListener("keydown", (e) => {
2208
+ if (e.defaultPrevented) return;
2209
+ if (e.key === "Enter" || e.key === ",") {
2210
+ e.preventDefault();
2211
+ commitNewChip();
2212
+ } else if (e.key === "Backspace" && !chipInput.value && chips.length > 0) {
2213
+ e.preventDefault();
2214
+ pendingFocus.set(view, { kind: "tag", key });
2215
+ commitList(chips.slice(0, -1));
2081
2216
  }
2217
+ });
2218
+ chipInput.addEventListener("blur", () => queueMicrotask(() => {
2219
+ if (chipInput.isConnected && chipInput.value.trim()) commitNewChip();
2220
+ }));
2221
+ const focus = pendingFocus.get(view);
2222
+ if (focus?.kind === "tag" && focus.key === key) {
2223
+ setTimeout(() => {
2224
+ if (chipInput.isConnected) chipInput.focus();
2225
+ }, 0);
2226
+ pendingFocus.delete(view);
2227
+ }
2228
+ if (chipIsReference && referenceProvider) {
2229
+ const unknown = chips.filter((chip) => !refMatch(chip));
2230
+ if (unknown.length > 0) {
2231
+ const diag = document.createElement("div");
2232
+ diag.setAttribute("data-testid", "editor-ref-unknown");
2233
+ diag.className = "w-full basis-full flex flex-wrap items-center gap-2 text-[10px] text-destructive";
2234
+ const label = document.createElement("span");
2235
+ label.textContent = `Unknown ${valueKind === "effort" ? "effort" : "equipment"}: ${unknown.join(", ")}`;
2236
+ diag.appendChild(label);
2237
+ const selectBtn = document.createElement("button");
2238
+ selectBtn.type = "button";
2239
+ selectBtn.textContent = "Select existing";
2240
+ selectBtn.className = "underline hover:text-foreground cursor-pointer";
2241
+ selectBtn.addEventListener("click", (e) => {
2242
+ e.preventDefault();
2243
+ chipInput.focus();
2244
+ });
2245
+ diag.appendChild(selectBtn);
2246
+ const createLink = document.createElement("a");
2247
+ createLink.href = referenceProvider.createHref(valueKind);
2248
+ createLink.textContent = "Create page";
2249
+ createLink.className = "underline hover:text-foreground";
2250
+ diag.appendChild(createLink);
2251
+ valCol.appendChild(diag);
2252
+ }
2253
+ }
2254
+ }
2255
+ } else if (nested) {
2256
+ const valSpan = document.createElement("span");
2257
+ valSpan.className = "text-xs text-muted-foreground/60 italic py-0.5 truncate";
2258
+ valSpan.textContent = "Nested block \u2014 edit in YAML";
2259
+ valCol.appendChild(valSpan);
2260
+ } else {
2261
+ const strVal = Array.isArray(value) ? value.join(", ") : String(value ?? "");
2262
+ if (this.readOnly) {
2263
+ const valSpan = document.createElement("span");
2264
+ valSpan.className = "text-xs text-foreground py-0.5 truncate";
2265
+ valSpan.textContent = strVal;
2266
+ valCol.appendChild(valSpan);
2267
+ } else {
2268
+ const valInput = document.createElement("input");
2269
+ 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";
2270
+ valInput.value = strVal;
2271
+ valInput.spellcheck = false;
2272
+ valInput.setAttribute("aria-label", `Value for ${key}`);
2273
+ valInput.dataset.frontmatterField = "value";
2274
+ valInput.dataset.frontmatterProperty = key;
2275
+ valInput.setAttribute(
2276
+ "aria-describedby",
2277
+ appendFieldHelper(valCol, "Property value. Press Enter to apply.").id
2278
+ );
2279
+ let committedVal = strVal;
2280
+ const commitVal = () => {
2281
+ const cur = valInput.value.trim();
2282
+ if (cur !== committedVal) {
2283
+ committedVal = cur;
2284
+ const num = Number(cur);
2285
+ const next = cur !== "" && !isNaN(num) ? num : cur;
2286
+ meta[key] = next;
2287
+ patchPropertyInView(view, this.sectionId, [key], next);
2288
+ }
2289
+ };
2290
+ const commitValIfAttached = () => queueMicrotask(() => {
2291
+ if (valInput.isConnected) commitVal();
2292
+ });
2293
+ valInput.addEventListener("change", commitValIfAttached);
2294
+ valCol.appendChild(valInput);
2295
+ attachSuggestions(
2296
+ valInput,
2297
+ view,
2298
+ root,
2299
+ "value",
2300
+ key,
2301
+ [strVal],
2302
+ commitVal,
2303
+ key.trim().toLowerCase() === "metrics.effort" ? "effort" : "tags"
2082
2304
  );
2083
- if (diag) valCol.appendChild(diag);
2305
+ valInput.addEventListener("blur", commitValIfAttached);
2306
+ valInput.addEventListener("keydown", (e) => {
2307
+ if (e.defaultPrevented) return;
2308
+ if (e.key === "Enter") {
2309
+ e.preventDefault();
2310
+ valInput.blur();
2311
+ }
2312
+ });
2313
+ if (key.trim().toLowerCase() === "metrics.effort" && referenceProvider) {
2314
+ const match = referenceProvider.resolveEffort(strVal);
2315
+ if (match) {
2316
+ const pill = document.createElement("span");
2317
+ 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";
2318
+ const pillText = document.createElement("span");
2319
+ pillText.textContent = match.label;
2320
+ pill.appendChild(pillText);
2321
+ const openLink = document.createElement("a");
2322
+ openLink.href = referenceProvider.openHref("effort", match.slug);
2323
+ openLink.title = `Open ${match.label} page`;
2324
+ openLink.setAttribute("aria-label", `Open ${match.label} page`);
2325
+ openLink.className = "inline-flex items-center justify-center text-muted-foreground/60 hover:text-foreground cursor-pointer";
2326
+ openLink.appendChild(createExternalIcon());
2327
+ pill.appendChild(openLink);
2328
+ valCol.insertBefore(pill, valInput);
2329
+ 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";
2330
+ }
2331
+ const diag = renderEffortValueDiagnostics(
2332
+ referenceProvider,
2333
+ strVal,
2334
+ (slug) => {
2335
+ meta[key] = slug;
2336
+ patchPropertyInView(view, this.sectionId, [key], slug);
2337
+ }
2338
+ );
2339
+ if (diag) valCol.appendChild(diag);
2340
+ }
2341
+ }
2342
+ if (/^https?:\/\/\S+$/i.test(strVal)) {
2343
+ const openLink = document.createElement("a");
2344
+ openLink.href = strVal;
2345
+ openLink.target = "_blank";
2346
+ openLink.rel = "noopener noreferrer";
2347
+ openLink.title = strVal;
2348
+ openLink.setAttribute("aria-label", `Open ${key} in new tab`);
2349
+ openLink.className = "shrink-0 inline-flex items-center justify-center text-muted-foreground/60 hover:text-foreground transition-colors cursor-pointer";
2350
+ openLink.appendChild(createExternalIcon());
2351
+ valCol.appendChild(openLink);
2084
2352
  }
2085
2353
  }
2086
- if (/^https?:\/\/\S+$/i.test(strVal)) {
2087
- const openLink = document.createElement("a");
2088
- openLink.href = strVal;
2089
- openLink.target = "_blank";
2090
- openLink.rel = "noopener noreferrer";
2091
- openLink.title = strVal;
2092
- openLink.setAttribute("aria-label", `Open ${key} in new tab`);
2093
- openLink.className = "shrink-0 inline-flex items-center justify-center text-muted-foreground/60 hover:text-foreground transition-colors cursor-pointer";
2094
- openLink.appendChild(createExternalIcon());
2095
- valCol.appendChild(openLink);
2354
+ if (!this.readOnly && !nested) {
2355
+ const actionsCol = document.createElement("div");
2356
+ actionsCol.className = "flex items-center pr-2";
2357
+ const delBtn = document.createElement("button");
2358
+ delBtn.type = "button";
2359
+ delBtn.setAttribute("aria-label", `Remove property ${key}`);
2360
+ 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";
2361
+ delBtn.appendChild(createCloseIcon("md"));
2362
+ delBtn.addEventListener("click", (e) => {
2363
+ e.preventDefault();
2364
+ e.stopPropagation();
2365
+ delete meta[key];
2366
+ patchPropertyInView(view, this.sectionId, [key], null);
2367
+ });
2368
+ actionsCol.appendChild(delBtn);
2369
+ row.appendChild(actionsCol);
2096
2370
  }
2097
2371
  }
2098
- if (!this.readOnly && !nested) {
2099
- const actionsCol = document.createElement("div");
2100
- actionsCol.className = "flex items-center pr-2";
2101
- const delBtn = document.createElement("button");
2102
- delBtn.type = "button";
2103
- delBtn.setAttribute("aria-label", `Remove property ${key}`);
2104
- 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";
2105
- delBtn.appendChild(createCloseIcon("md"));
2106
- delBtn.addEventListener("click", (e) => {
2107
- e.preventDefault();
2108
- e.stopPropagation();
2109
- delete meta[key];
2110
- patchPropertyInView(view, this.sectionId, [key], null);
2111
- });
2112
- actionsCol.appendChild(delBtn);
2113
- row.appendChild(actionsCol);
2372
+ if (docKind === "effort" && !this.readOnly) {
2373
+ box.appendChild(this.buildDefinitionRows(view, base));
2114
2374
  }
2115
2375
  }
2116
- if (docKind === "effort" && !this.readOnly) {
2117
- box.appendChild(this.buildDefinitionRows(view, base));
2118
- }
2119
2376
  if (!this.readOnly) {
2120
2377
  const existingTagKey = entries.find(([key]) => key.trim().toLowerCase() === "tags")?.[0] ?? entries.find(([key]) => isTagProperty(key))?.[0];
2121
2378
  const actionsRow = document.createElement("div");
2122
- actionsRow.className = "mt-2 flex items-center gap-3";
2379
+ actionsRow.className = "mt-2 flex flex-wrap items-center gap-x-3 gap-y-1";
2123
2380
  actionsRow.dataset.frontmatterActions = "true";
2124
2381
  const addBtn = document.createElement("button");
2125
2382
  addBtn.type = "button";
@@ -2139,7 +2396,12 @@ var DefaultFrontmatterWidget = class extends WidgetType {
2139
2396
  newKey = `property_${count2}`;
2140
2397
  }
2141
2398
  pendingFocus.set(view, { kind: "key", key: newKey });
2142
- patchPropertyInView(view, this.sectionId, [newKey], "");
2399
+ patchPropertyInView(view, this.sectionId, [newKey], "", !this.detailed);
2400
+ const keyField = view.dom.querySelector(
2401
+ `[data-frontmatter-row="${newKey}"] [data-frontmatter-field="key"]`
2402
+ );
2403
+ keyField?.focus();
2404
+ keyField?.select();
2143
2405
  });
2144
2406
  actionsRow.appendChild(addBtn);
2145
2407
  {
@@ -2155,20 +2417,57 @@ var DefaultFrontmatterWidget = class extends WidgetType {
2155
2417
  addMetricBtn.addEventListener("click", (e) => {
2156
2418
  e.preventDefault();
2157
2419
  e.stopPropagation();
2158
- const existing = root.querySelector("[data-frontmatter-metric-draft] input");
2420
+ if (!this.detailed) {
2421
+ view.dispatch({ effects: setFrontmatterDetailed.of(true) });
2422
+ }
2423
+ const activeRoot = view.dom.querySelector(".cm-frontmatter-preview") ?? root;
2424
+ const activeBox = activeRoot.querySelector("[data-frontmatter-properties]") ?? box;
2425
+ const existing = activeRoot.querySelector("[data-frontmatter-metric-draft] input");
2159
2426
  if (existing) {
2160
2427
  existing.focus();
2161
2428
  return;
2162
2429
  }
2163
- const draftRow = createMetricDraftRow(view, root, referenceProvider, (path, value) => {
2430
+ const draftRow = createMetricDraftRow(view, activeRoot, referenceProvider, (path, value) => {
2164
2431
  patchPropertyInView(view, this.sectionId, path, value);
2165
2432
  });
2166
- box.appendChild(draftRow);
2433
+ activeBox.appendChild(draftRow);
2167
2434
  draftRow.querySelector("input")?.focus();
2168
2435
  view.requestMeasure();
2169
2436
  });
2170
2437
  actionsRow.appendChild(addMetricBtn);
2171
2438
  }
2439
+ {
2440
+ const addSummaryBtn = document.createElement("button");
2441
+ addSummaryBtn.type = "button";
2442
+ addSummaryBtn.setAttribute("aria-label", "Add summary");
2443
+ addSummaryBtn.setAttribute("data-testid", "editor-add-summary-note");
2444
+ 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";
2445
+ addSummaryBtn.appendChild(createPlusIcon());
2446
+ const addSummaryLabel = document.createElement("span");
2447
+ addSummaryLabel.textContent = "Add summary";
2448
+ addSummaryBtn.appendChild(addSummaryLabel);
2449
+ addSummaryBtn.addEventListener("click", (e) => {
2450
+ e.preventDefault();
2451
+ e.stopPropagation();
2452
+ if (!this.detailed) {
2453
+ view.dispatch({ effects: setFrontmatterDetailed.of(true) });
2454
+ }
2455
+ const activeRoot = view.dom.querySelector(".cm-frontmatter-preview") ?? root;
2456
+ const activeBox = activeRoot.querySelector("[data-frontmatter-properties]") ?? box;
2457
+ const existing = activeRoot.querySelector("[data-frontmatter-summary-draft] input");
2458
+ if (existing) {
2459
+ existing.focus();
2460
+ return;
2461
+ }
2462
+ const draftRow = createSummaryDraftRow(view, (path, value) => {
2463
+ patchPropertyInView(view, this.sectionId, path, value);
2464
+ });
2465
+ activeBox.appendChild(draftRow);
2466
+ draftRow.querySelector("input")?.focus();
2467
+ view.requestMeasure();
2468
+ });
2469
+ actionsRow.appendChild(addSummaryBtn);
2470
+ }
2172
2471
  {
2173
2472
  const addTagBtn = document.createElement("button");
2174
2473
  addTagBtn.type = "button";
@@ -2181,23 +2480,28 @@ var DefaultFrontmatterWidget = class extends WidgetType {
2181
2480
  addTagBtn.addEventListener("click", (e) => {
2182
2481
  e.preventDefault();
2183
2482
  e.stopPropagation();
2483
+ if (!this.detailed) {
2484
+ view.dispatch({ effects: setFrontmatterDetailed.of(true) });
2485
+ }
2486
+ const activeRoot = view.dom.querySelector(".cm-frontmatter-preview") ?? root;
2487
+ const activeBox = activeRoot.querySelector("[data-frontmatter-properties]") ?? box;
2184
2488
  if (existingTagKey) {
2185
- const input = Array.from(root.querySelectorAll(
2489
+ const input = Array.from(activeRoot.querySelectorAll(
2186
2490
  '[data-frontmatter-field="value"]'
2187
2491
  )).find((element) => element.dataset.frontmatterProperty === existingTagKey);
2188
2492
  input?.focus();
2189
2493
  return;
2190
2494
  }
2191
- const existingDraft = root.querySelector("[data-frontmatter-tag-draft] input");
2495
+ const existingDraft = activeRoot.querySelector("[data-frontmatter-tag-draft] input");
2192
2496
  if (existingDraft) {
2193
2497
  existingDraft.focus();
2194
2498
  return;
2195
2499
  }
2196
- const draftRow = createTagDraftRow(view, root, "category", (val) => {
2500
+ const draftRow = createTagDraftRow(view, activeRoot, "category", (val) => {
2197
2501
  pendingFocus.set(view, { kind: "tag", key: "category" });
2198
2502
  patchPropertyInView(view, this.sectionId, ["category"], [val]);
2199
2503
  });
2200
- box.appendChild(draftRow);
2504
+ activeBox.appendChild(draftRow);
2201
2505
  draftRow.querySelector("input")?.focus();
2202
2506
  view.requestMeasure();
2203
2507
  });
@@ -2218,7 +2522,11 @@ var DefaultFrontmatterWidget = class extends WidgetType {
2218
2522
  const { sections: currentSections } = view.state.field(sectionField);
2219
2523
  const section = currentSections.find((s) => s.id === this.sectionId && s.type === "frontmatter") || currentSections.find((s) => s.type === "frontmatter");
2220
2524
  if (!section) return;
2221
- view.dispatch({ selection: { anchor: section.contentFrom ?? section.from }, scrollIntoView: true });
2525
+ view.dispatch({
2526
+ selection: { anchor: section.from },
2527
+ effects: setFrontmatterSource.of(true),
2528
+ scrollIntoView: true
2529
+ });
2222
2530
  view.focus();
2223
2531
  });
2224
2532
  actionsRow.appendChild(editYamlBtn);
@@ -2243,10 +2551,12 @@ var DefaultFrontmatterWidget = class extends WidgetType {
2243
2551
  const commitField = (field, raw) => {
2244
2552
  if (field === "met") {
2245
2553
  const num = Number(raw);
2246
- patchPropertyInView(view, this.sectionId, ["baseAttributes", "met"], raw !== "" && !Number.isNaN(num) ? num : null);
2554
+ const path2 = base.hasBaseBlock ? ["baseAttributes", "met"] : ["metrics.met"];
2555
+ patchPropertyInView(view, this.sectionId, path2, raw !== "" && !Number.isNaN(num) ? num : null);
2247
2556
  return;
2248
2557
  }
2249
- patchPropertyInView(view, this.sectionId, ["baseAttributes", field], raw === "" ? null : raw);
2558
+ const path = base.hasBaseBlock ? ["baseAttributes", field] : [field];
2559
+ patchPropertyInView(view, this.sectionId, path, raw === "" ? null : raw);
2250
2560
  };
2251
2561
  const fieldRow = (label, child) => {
2252
2562
  const row = document.createElement("label");
@@ -2309,8 +2619,13 @@ var DefaultFrontmatterWidget = class extends WidgetType {
2309
2619
  }
2310
2620
  };
2311
2621
  var EmptyFrontmatterWidget = class extends WidgetType {
2312
- eq(_other) {
2313
- return true;
2622
+ constructor(detailed = false) {
2623
+ super();
2624
+ this.detailed = detailed;
2625
+ }
2626
+ detailed;
2627
+ eq(other) {
2628
+ return this.detailed === other.detailed;
2314
2629
  }
2315
2630
  ignoreEvent() {
2316
2631
  return true;
@@ -2323,7 +2638,7 @@ var EmptyFrontmatterWidget = class extends WidgetType {
2323
2638
  heading.textContent = "Properties";
2324
2639
  root.appendChild(heading);
2325
2640
  const actionsRow = document.createElement("div");
2326
- actionsRow.className = "flex items-center gap-3";
2641
+ actionsRow.className = "flex flex-wrap items-center gap-x-3 gap-y-1";
2327
2642
  actionsRow.dataset.frontmatterActions = "true";
2328
2643
  const addBtn = document.createElement("button");
2329
2644
  addBtn.type = "button";
@@ -2337,9 +2652,71 @@ var EmptyFrontmatterWidget = class extends WidgetType {
2337
2652
  e.preventDefault();
2338
2653
  e.stopPropagation();
2339
2654
  pendingFocus.set(view, { kind: "key", key: "property" });
2340
- patchPropertyInView(view, "new", ["property"], "");
2655
+ patchPropertyInView(view, "new", ["property"], "", true);
2656
+ const keyField = view.dom.querySelector(
2657
+ '[data-frontmatter-row="property"] [data-frontmatter-field="key"]'
2658
+ );
2659
+ keyField?.focus();
2660
+ keyField?.select();
2341
2661
  });
2342
2662
  actionsRow.appendChild(addBtn);
2663
+ const addMetricBtn = document.createElement("button");
2664
+ addMetricBtn.type = "button";
2665
+ addMetricBtn.setAttribute("aria-label", "Add metric");
2666
+ addMetricBtn.setAttribute("data-testid", "editor-add-metric-note");
2667
+ 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";
2668
+ addMetricBtn.appendChild(createPlusIcon());
2669
+ const addMetricLabel = document.createElement("span");
2670
+ addMetricLabel.textContent = "Add metric";
2671
+ addMetricBtn.appendChild(addMetricLabel);
2672
+ addMetricBtn.addEventListener("click", (e) => {
2673
+ e.preventDefault();
2674
+ e.stopPropagation();
2675
+ if (!this.detailed) view.dispatch({ effects: setFrontmatterDetailed.of(true) });
2676
+ const activeRoot = view.dom.querySelector(".cm-frontmatter-preview") ?? root;
2677
+ const existing = activeRoot.querySelector("[data-frontmatter-metric-draft] input");
2678
+ if (existing) {
2679
+ existing.focus();
2680
+ return;
2681
+ }
2682
+ const referenceProvider = view.state.facet(frontmatterReferenceIndex)?.() ?? null;
2683
+ const draftRow = createMetricDraftRow(view, activeRoot, referenceProvider, (path, value) => {
2684
+ patchPropertyInView(view, "new", path, value, true);
2685
+ });
2686
+ activeRoot.appendChild(draftRow);
2687
+ ensureColumnHeader(activeRoot, draftRow);
2688
+ draftRow.querySelector("input")?.focus();
2689
+ view.requestMeasure();
2690
+ });
2691
+ actionsRow.appendChild(addMetricBtn);
2692
+ const addSummaryBtn = document.createElement("button");
2693
+ addSummaryBtn.type = "button";
2694
+ addSummaryBtn.setAttribute("aria-label", "Add summary");
2695
+ addSummaryBtn.setAttribute("data-testid", "editor-add-summary-note");
2696
+ 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";
2697
+ addSummaryBtn.appendChild(createPlusIcon());
2698
+ const addSummaryLabel = document.createElement("span");
2699
+ addSummaryLabel.textContent = "Add summary";
2700
+ addSummaryBtn.appendChild(addSummaryLabel);
2701
+ addSummaryBtn.addEventListener("click", (e) => {
2702
+ e.preventDefault();
2703
+ e.stopPropagation();
2704
+ if (!this.detailed) view.dispatch({ effects: setFrontmatterDetailed.of(true) });
2705
+ const activeRoot = view.dom.querySelector(".cm-frontmatter-preview") ?? root;
2706
+ const existing = activeRoot.querySelector("[data-frontmatter-summary-draft] input");
2707
+ if (existing) {
2708
+ existing.focus();
2709
+ return;
2710
+ }
2711
+ const draftRow = createSummaryDraftRow(view, (path, value) => {
2712
+ patchPropertyInView(view, "new", path, value, true);
2713
+ });
2714
+ activeRoot.appendChild(draftRow);
2715
+ ensureColumnHeader(activeRoot, draftRow);
2716
+ draftRow.querySelector("input")?.focus();
2717
+ view.requestMeasure();
2718
+ });
2719
+ actionsRow.appendChild(addSummaryBtn);
2343
2720
  const addTagBtn = document.createElement("button");
2344
2721
  addTagBtn.type = "button";
2345
2722
  addTagBtn.setAttribute("aria-label", "Add tag");
@@ -2351,17 +2728,19 @@ var EmptyFrontmatterWidget = class extends WidgetType {
2351
2728
  addTagBtn.addEventListener("click", (e) => {
2352
2729
  e.preventDefault();
2353
2730
  e.stopPropagation();
2354
- const existingDraft = root.querySelector("[data-frontmatter-tag-draft] input");
2731
+ if (!this.detailed) view.dispatch({ effects: setFrontmatterDetailed.of(true) });
2732
+ const activeRoot = view.dom.querySelector(".cm-frontmatter-preview") ?? root;
2733
+ const existingDraft = activeRoot.querySelector("[data-frontmatter-tag-draft] input");
2355
2734
  if (existingDraft) {
2356
2735
  existingDraft.focus();
2357
2736
  return;
2358
2737
  }
2359
- const draftRow = createTagDraftRow(view, root, "category", (val) => {
2738
+ const draftRow = createTagDraftRow(view, activeRoot, "category", (val) => {
2360
2739
  pendingFocus.set(view, { kind: "tag", key: "category" });
2361
- patchPropertyInView(view, "new", ["category"], [val]);
2740
+ patchPropertyInView(view, "new", ["category"], [val], true);
2362
2741
  });
2363
- root.appendChild(draftRow);
2364
- ensureColumnHeader(root, draftRow);
2742
+ activeRoot.appendChild(draftRow);
2743
+ ensureColumnHeader(activeRoot, draftRow);
2365
2744
  draftRow.querySelector("input")?.focus();
2366
2745
  view.requestMeasure();
2367
2746
  });
@@ -2383,14 +2762,14 @@ function buildFrontmatterDecos(state) {
2383
2762
  builder.add(section.from, to, Decoration.replace({ block: true }));
2384
2763
  continue;
2385
2764
  }
2386
- const selected = !state.readOnly && state.selection.ranges.some((range) => range.from < section.to && range.to > section.from);
2765
+ const selected = !state.readOnly && (state.field(frontmatterSource) || state.selection.ranges.some((range) => range.from < section.to && range.to > section.from));
2387
2766
  if (selected) continue;
2388
2767
  const rawContent = state.doc.sliceString(section.from, section.to);
2389
2768
  builder.add(
2390
2769
  section.from,
2391
2770
  section.to,
2392
2771
  Decoration.replace({
2393
- widget: new DefaultFrontmatterWidget(section.id, rawContent, state.readOnly),
2772
+ widget: new DefaultFrontmatterWidget(section.id, rawContent, state.readOnly, state.field(frontmatterDetailed)),
2394
2773
  block: true
2395
2774
  })
2396
2775
  );
@@ -2400,7 +2779,7 @@ function buildFrontmatterDecos(state) {
2400
2779
  0,
2401
2780
  0,
2402
2781
  Decoration.widget({
2403
- widget: new EmptyFrontmatterWidget(),
2782
+ widget: new EmptyFrontmatterWidget(state.field(frontmatterDetailed)),
2404
2783
  side: -1,
2405
2784
  block: true
2406
2785
  })
@@ -2413,7 +2792,7 @@ var frontmatterPreviewField = StateField.define({
2413
2792
  return buildFrontmatterDecos(state);
2414
2793
  },
2415
2794
  update(deco, tr) {
2416
- if (tr.docChanged || tr.reconfigured || tr.selection || tr.effects.some((e) => e.is(referenceIndexReload))) {
2795
+ if (tr.docChanged || tr.reconfigured || tr.selection || tr.effects.some((e) => e.is(referenceIndexReload) || e.is(setFrontmatterDetailed) || e.is(setFrontmatterSource))) {
2417
2796
  return buildFrontmatterDecos(tr.state);
2418
2797
  }
2419
2798
  return deco;
@@ -2425,7 +2804,7 @@ var frontmatterPreviewField = StateField.define({
2425
2804
  )
2426
2805
  ]
2427
2806
  });
2428
- var frontmatterPreview = [frontmatterPreviewField];
2807
+ var frontmatterPreview = [frontmatterDetailed, frontmatterSource, frontmatterPreviewField];
2429
2808
  var HEADING_RE = /^(#{1,6}) /;
2430
2809
  var BOLD_RE = /\*\*([^*\n]+)\*\*/g;
2431
2810
  var ITALIC_STAR_RE = /(?<!\*)\*([^*\n]+)\*(?!\*)/g;
@@ -4994,7 +5373,9 @@ var CLAUSE_META = {
4994
5373
  note: { label: "Note", inputType: "freetext", placeholder: "note id\u2026", placeholderText: "note: [id]", icon: "\u{1F4D3}", description: "Scope to one note", prefix: "note:" },
4995
5374
  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:" },
4996
5375
  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:" },
4997
- 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" }
5376
+ 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" },
5377
+ 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:" },
5378
+ subscribed: { label: "Subscribed", inputType: "select", placeholder: "true, false", placeholderText: "subscribed: [true|false]", icon: "\u2B50", description: "Filter by notebook subscription state", prefix: "subscribed:" }
4998
5379
  };
4999
5380
  var CUSTOM_FALLBACK_ICON = "\u{1F9E9}";
5000
5381
  function getClauseMeta(type) {
@@ -12967,6 +13348,6 @@ function editorPreset(optionsOrDialect = "markdown") {
12967
13348
  return extensions;
12968
13349
  }
12969
13350
 
12970
- export { AGG_OPTIONS, CANONICAL_BLOCK_TYPES, CLAUSE_META, ComposerRegistry, DEFAULT_DIAGNOSTICS_DEBOUNCE_MS, DEFAULT_OUTPUT_FILTERS, DEFAULT_PRIMARY_FILTER, DefaultFrontmatterWidget, EditorDialog, GROUPBY_OPTIONS, GoalRings, KIND_OPTIONS, METRIC_OPTIONS, MULTI_VALUE_TYPES, MetricBadge, OUTPUT_TYPE_CLASS, OUTPUT_TYPE_DOT, OutputFilterPills, OutputStatementsTable, PLANE_OPTIONS, ProposedMetricBadge, QueryBlockView, QueryValue, ROLLUP_OPTIONS, RowsResultsChrome, RowsTable, SERIES_COLORS, SOURCE_OPTIONS, SUGGESTION_BINDINGS, StackedBar, TARGET_OPTIONS, TIME_OPTIONS, TONE_CLASS, TokenSlotPill, TopList, UNIT_OPTIONS, WHERE_AGGREGATORS, WHERE_METRICS, WidgetChart, WidgetProblemBadge, WqlBars, WqlComposer, WqlDiagnosticsStrip, WqlEmptyState, WqlQueryInspectorModal, WqlTable, WqlTimeseries, ZoneDistribution, activeCursorSection, addRuntimePanel, autocompleteTooltipTheme, blockTypesFromBlocks, canonicalSuggestionItems, cn, compactNumber, composerRegistry, configureOverlayActions, cursorFocusExtension, customCompletionSources, dateRangeSlot, dispatchAddRuntimePanel, dispatchExpandRuntimePanel, dispatchGutterHighlights, dispatchRemoveRuntimePanel, editQueryClause, editingWidgetsField, editorPreset, editorTheme, embedPreviewDecorations, expandRuntimePanel, extractBlockQueries, extractMetricsByCategory, fenceCompletion, findQueryBlockSection, findWorkoutErrors, forceSectionParse, formatClockTime, formatMMSS, formatTimestamp, frontmatterPreview, frontmatterPreviewField, frontmatterReferenceIndex, frontmatterSuggestions, frontmatterVisibility, getClauseMeta, getCursorFocusState, getOverlayActions, getSuggestionBinding, gutterHighlightsField, gutterUnified, handleFenceAutoWrap, hashCode, inlineButtonDecoration, invalidateSuggestions, lineIdsExtension, linkOpen, linkPreview, loadSuggestions, markdownSyntaxHiding, markdownTablePreview, matchFilterTypeahead, mergeSeries, mergeSuggestionItems, mergeTagSuggestions, navigationFacet, nestedCompletion, nestedCompletionOptions, normalizeOutputFilter, notebookSlugsFromBlocks, patchBlockQuery, presentBadges, previewDecorations, queryBlockPreview, readSessionRpe, referenceIndexReload, registeredSectionWidgets, removeRuntimePanel, resolveCodeLanguage, resolveQueryDraft, runtimePanelField, saveBlockQuerySource, sectionAtPos, sectionField, sectionGeometry, sectionIdentity, sectionIdentityHistory, sectionMove, sectionRestore, sectionSaved, sessionQueryInsert, sessionQueryWql, setGutterHighlights, setSuggestionBinding, smartIncrement, summarizeAggregate, summarizeFind, summarizePipeline, tagsFromStaticBlocks, toChartResult, toggleWidgetEdit, tooltipTimestamp, urlAtPos, useChartShape, useComposerSlots, useSuggestions, useVisualViewportRect, useWqlStageCounts, widgetBlockPreview, wodAutoWrap, wodAutocompletion, wodCompletionSource, wodEditorKeymap, wodLinter, wodOverlayPanel, workoutLintSource, wqlToPills, wrapInTimeFence };
12971
- //# sourceMappingURL=chunk-QCWASNQO.mjs.map
12972
- //# sourceMappingURL=chunk-QCWASNQO.mjs.map
13351
+ export { AGG_OPTIONS, CANONICAL_BLOCK_TYPES, CLAUSE_META, ComposerRegistry, DEFAULT_DIAGNOSTICS_DEBOUNCE_MS, DEFAULT_OUTPUT_FILTERS, DEFAULT_PRIMARY_FILTER, DefaultFrontmatterWidget, EditorDialog, GROUPBY_OPTIONS, GoalRings, KIND_OPTIONS, METRIC_OPTIONS, MULTI_VALUE_TYPES, MetricBadge, OUTPUT_TYPE_CLASS, OUTPUT_TYPE_DOT, OutputFilterPills, OutputStatementsTable, PLANE_OPTIONS, ProposedMetricBadge, QueryBlockView, QueryValue, ROLLUP_OPTIONS, RowsResultsChrome, RowsTable, SERIES_COLORS, SOURCE_OPTIONS, SUGGESTION_BINDINGS, StackedBar, TARGET_OPTIONS, TIME_OPTIONS, TONE_CLASS, TokenSlotPill, TopList, UNIT_OPTIONS, WHERE_AGGREGATORS, WHERE_METRICS, WidgetChart, WidgetProblemBadge, WqlBars, WqlComposer, WqlDiagnosticsStrip, WqlEmptyState, WqlQueryInspectorModal, WqlTable, WqlTimeseries, ZoneDistribution, activeCursorSection, addRuntimePanel, autocompleteTooltipTheme, blockTypesFromBlocks, canonicalSuggestionItems, cn, compactNumber, composerRegistry, configureOverlayActions, cursorFocusExtension, customCompletionSources, dateRangeSlot, dispatchAddRuntimePanel, dispatchExpandRuntimePanel, dispatchGutterHighlights, dispatchRemoveRuntimePanel, editQueryClause, editingWidgetsField, editorPreset, editorTheme, embedPreviewDecorations, expandRuntimePanel, extractBlockQueries, extractMetricsByCategory, fenceCompletion, findQueryBlockSection, findWorkoutErrors, forceSectionParse, formatClockTime, formatMMSS, formatTimestamp, frontmatterDetailed, frontmatterPreview, frontmatterPreviewField, frontmatterReferenceIndex, frontmatterSource, frontmatterSuggestions, frontmatterVisibility, getClauseMeta, getCursorFocusState, getOverlayActions, getSuggestionBinding, gutterHighlightsField, gutterUnified, handleFenceAutoWrap, hashCode, inlineButtonDecoration, invalidateSuggestions, lineIdsExtension, linkOpen, linkPreview, loadSuggestions, markdownSyntaxHiding, markdownTablePreview, matchFilterTypeahead, mergeSeries, mergeSuggestionItems, mergeTagSuggestions, navigationFacet, nestedCompletion, nestedCompletionOptions, normalizeOutputFilter, notebookSlugsFromBlocks, patchBlockQuery, presentBadges, previewDecorations, queryBlockPreview, readSessionRpe, referenceIndexReload, registeredSectionWidgets, removeRuntimePanel, resolveCodeLanguage, resolveQueryDraft, runtimePanelField, saveBlockQuerySource, sectionAtPos, sectionField, sectionGeometry, sectionIdentity, sectionIdentityHistory, sectionMove, sectionRestore, sectionSaved, sessionQueryInsert, sessionQueryWql, setFrontmatterDetailed, setFrontmatterSource, setGutterHighlights, setSuggestionBinding, smartIncrement, summarizeAggregate, summarizeFind, summarizePipeline, tagsFromStaticBlocks, toChartResult, toggleWidgetEdit, tooltipTimestamp, urlAtPos, useChartShape, useComposerSlots, useSuggestions, useVisualViewportRect, useWqlStageCounts, widgetBlockPreview, wodAutoWrap, wodAutocompletion, wodCompletionSource, wodEditorKeymap, wodLinter, wodOverlayPanel, workoutLintSource, wqlToPills, wrapInTimeFence };
13352
+ //# sourceMappingURL=chunk-WI2IFNAU.mjs.map
13353
+ //# sourceMappingURL=chunk-WI2IFNAU.mjs.map