@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/{chunk-QCWASNQO.mjs → chunk-WI2IFNAU.mjs} +708 -327
- package/dist/chunk-WI2IFNAU.mjs.map +1 -0
- package/dist/extensions/index.cjs +709 -324
- package/dist/extensions/index.cjs.map +1 -1
- package/dist/extensions/index.d.cts +1 -1
- package/dist/extensions/index.d.ts +1 -1
- package/dist/extensions/index.mjs +1 -1
- package/dist/{index-BPh_dUN3.d.cts → index--_wwHFvD.d.cts} +7 -2
- package/dist/{index-BPh_dUN3.d.ts → index--_wwHFvD.d.ts} +7 -2
- package/dist/index.cjs +715 -330
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +9 -9
- package/dist/index.d.ts +9 -9
- package/dist/index.mjs +8 -8
- package/dist/index.mjs.map +1 -1
- package/dist/styles.css +4 -4
- package/package.json +4 -4
- package/dist/chunk-QCWASNQO.mjs.map +0 -1
|
@@ -1319,9 +1319,51 @@ var referenceIndexReload = state.StateEffect.define();
|
|
|
1319
1319
|
var frontmatterVisibility = state.Facet.define({
|
|
1320
1320
|
combine: (values2) => values2[0] ?? true
|
|
1321
1321
|
});
|
|
1322
|
+
var setFrontmatterDetailed = state.StateEffect.define();
|
|
1323
|
+
var frontmatterDetailed = state.StateField.define({
|
|
1324
|
+
create: () => false,
|
|
1325
|
+
update(value, tr) {
|
|
1326
|
+
for (const effect of tr.effects) {
|
|
1327
|
+
if (effect.is(setFrontmatterDetailed)) return effect.value;
|
|
1328
|
+
}
|
|
1329
|
+
return value;
|
|
1330
|
+
}
|
|
1331
|
+
});
|
|
1332
|
+
var setFrontmatterSource = state.StateEffect.define();
|
|
1333
|
+
var frontmatterSource = state.StateField.define({
|
|
1334
|
+
create: () => false,
|
|
1335
|
+
update(value, tr) {
|
|
1336
|
+
let source = value;
|
|
1337
|
+
for (const effect of tr.effects) {
|
|
1338
|
+
if (effect.is(setFrontmatterSource)) source = effect.value;
|
|
1339
|
+
}
|
|
1340
|
+
if (source && tr.selection) {
|
|
1341
|
+
const section = tr.state.field(sectionField).sections.find((item) => item.type === "frontmatter");
|
|
1342
|
+
if (!section || !tr.state.selection.ranges.some((range) => range.from <= section.to && range.to >= section.from)) {
|
|
1343
|
+
source = false;
|
|
1344
|
+
}
|
|
1345
|
+
}
|
|
1346
|
+
return source;
|
|
1347
|
+
}
|
|
1348
|
+
});
|
|
1322
1349
|
var pendingFocus = /* @__PURE__ */ new WeakMap();
|
|
1323
1350
|
var widgetCleanup = /* @__PURE__ */ new WeakMap();
|
|
1324
1351
|
var helperId = 0;
|
|
1352
|
+
var TAG_CATEGORY_SUMMARY_KEYS = {
|
|
1353
|
+
tag: true,
|
|
1354
|
+
tags: true,
|
|
1355
|
+
category: true,
|
|
1356
|
+
domain: true,
|
|
1357
|
+
format: true,
|
|
1358
|
+
equipment: true,
|
|
1359
|
+
effort: true,
|
|
1360
|
+
quality: true,
|
|
1361
|
+
intent: true,
|
|
1362
|
+
discipline: true,
|
|
1363
|
+
intensity: true,
|
|
1364
|
+
intensitytier: true,
|
|
1365
|
+
sound: true
|
|
1366
|
+
};
|
|
1325
1367
|
function isTagProperty(key) {
|
|
1326
1368
|
return ["tag", "tags", "category"].includes(key.trim().toLowerCase());
|
|
1327
1369
|
}
|
|
@@ -1438,12 +1480,18 @@ function parseTagList(val) {
|
|
|
1438
1480
|
}
|
|
1439
1481
|
return [];
|
|
1440
1482
|
}
|
|
1441
|
-
function patchPropertyInView(view, _sectionId, path, value) {
|
|
1483
|
+
function patchPropertyInView(view, _sectionId, path, value, expandDetailed = false) {
|
|
1442
1484
|
if (view.state.readOnly) return;
|
|
1443
1485
|
const wholeDoc = view.state.doc.toString();
|
|
1444
1486
|
const next = wodWikiCore.patchFrontmatterProperty(wholeDoc, path, value);
|
|
1445
|
-
if (next === wholeDoc)
|
|
1446
|
-
|
|
1487
|
+
if (next === wholeDoc) {
|
|
1488
|
+
if (expandDetailed) view.dispatch({ effects: setFrontmatterDetailed.of(true) });
|
|
1489
|
+
return;
|
|
1490
|
+
}
|
|
1491
|
+
view.dispatch({
|
|
1492
|
+
changes: { from: 0, to: wholeDoc.length, insert: next },
|
|
1493
|
+
...expandDetailed ? { effects: setFrontmatterDetailed.of(true) } : {}
|
|
1494
|
+
});
|
|
1447
1495
|
}
|
|
1448
1496
|
function renamePropertyInView(view, _sectionId, fromKey, toKey, value) {
|
|
1449
1497
|
if (view.state.readOnly) return;
|
|
@@ -1463,12 +1511,22 @@ function detectDefinitionKind(meta) {
|
|
|
1463
1511
|
if (meta.registrySource !== void 0 || meta.baseAttributes !== void 0) return "effort";
|
|
1464
1512
|
return null;
|
|
1465
1513
|
}
|
|
1466
|
-
function readBaseAttributes(body) {
|
|
1514
|
+
function readBaseAttributes(body, meta = {}) {
|
|
1467
1515
|
const out = {};
|
|
1516
|
+
if (meta["metrics.met"] !== void 0 && !Array.isArray(meta["metrics.met"])) {
|
|
1517
|
+
out.met = String(meta["metrics.met"]);
|
|
1518
|
+
}
|
|
1519
|
+
if (meta["discipline"] !== void 0 && !Array.isArray(meta["discipline"])) {
|
|
1520
|
+
out.discipline = String(meta["discipline"]);
|
|
1521
|
+
}
|
|
1522
|
+
if (meta["intensityTier"] !== void 0 && !Array.isArray(meta["intensityTier"])) {
|
|
1523
|
+
out.intensityTier = String(meta["intensityTier"]);
|
|
1524
|
+
}
|
|
1468
1525
|
let inBase = false;
|
|
1469
1526
|
for (const line of body.split(/\r?\n/)) {
|
|
1470
1527
|
if (/^baseAttributes:\s*(#.*)?$/.test(line)) {
|
|
1471
1528
|
inBase = true;
|
|
1529
|
+
out.hasBaseBlock = true;
|
|
1472
1530
|
continue;
|
|
1473
1531
|
}
|
|
1474
1532
|
if (!inBase) continue;
|
|
@@ -1598,7 +1656,8 @@ function renderEffortValueDiagnostics(provider, token, onPick) {
|
|
|
1598
1656
|
var METRIC_DRAFT_KINDS = {
|
|
1599
1657
|
effort: { label: "Effort", path: "metrics.effort", kind: "effort" },
|
|
1600
1658
|
equipment: { label: "Equipment", path: "metrics.equipment", kind: "equipment" },
|
|
1601
|
-
rpe: { label: "RPE", path: "metrics.rpe", kind: "number" }
|
|
1659
|
+
rpe: { label: "RPE", path: "metrics.rpe", kind: "number" },
|
|
1660
|
+
custom: { label: "Custom / Equation", path: "metrics.", kind: "custom" }
|
|
1602
1661
|
};
|
|
1603
1662
|
function createMetricDraftRow(view, root, referenceProvider, commit) {
|
|
1604
1663
|
const row = document.createElement("div");
|
|
@@ -1617,6 +1676,7 @@ function createMetricDraftRow(view, root, referenceProvider, commit) {
|
|
|
1617
1676
|
row.appendChild(select);
|
|
1618
1677
|
const valCol = document.createElement("div");
|
|
1619
1678
|
valCol.className = "flex items-center flex-wrap gap-1.5 px-3 py-1.5 flex-1 min-w-0";
|
|
1679
|
+
let customKeyInput = null;
|
|
1620
1680
|
const input = document.createElement("input");
|
|
1621
1681
|
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";
|
|
1622
1682
|
input.placeholder = "Value\u2026";
|
|
@@ -1639,6 +1699,10 @@ function createMetricDraftRow(view, root, referenceProvider, commit) {
|
|
|
1639
1699
|
else if (spec.kind === "number") {
|
|
1640
1700
|
const num = Number(raw);
|
|
1641
1701
|
if (!Number.isNaN(num)) commit([spec.path], num);
|
|
1702
|
+
} else if (spec.kind === "custom") {
|
|
1703
|
+
const keyName = (customKeyInput?.value.trim() || "custom").replace(/^metrics\./i, "");
|
|
1704
|
+
const num = Number(raw);
|
|
1705
|
+
commit([`metrics.${keyName}`], !Number.isNaN(num) && raw !== "" ? num : raw);
|
|
1642
1706
|
} else commit([spec.path], raw);
|
|
1643
1707
|
}
|
|
1644
1708
|
row.remove();
|
|
@@ -1646,6 +1710,31 @@ function createMetricDraftRow(view, root, referenceProvider, commit) {
|
|
|
1646
1710
|
};
|
|
1647
1711
|
const attachFor = () => {
|
|
1648
1712
|
const spec = METRIC_DRAFT_KINDS[select.value];
|
|
1713
|
+
if (spec?.kind === "custom") {
|
|
1714
|
+
if (!customKeyInput) {
|
|
1715
|
+
customKeyInput = document.createElement("input");
|
|
1716
|
+
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";
|
|
1717
|
+
customKeyInput.placeholder = "Metric key\u2026";
|
|
1718
|
+
customKeyInput.setAttribute("aria-label", "Metric key");
|
|
1719
|
+
customKeyInput.dataset.frontmatterField = "key";
|
|
1720
|
+
customKeyInput.addEventListener("keydown", (e) => {
|
|
1721
|
+
if (e.key === "Escape") {
|
|
1722
|
+
e.preventDefault();
|
|
1723
|
+
settle(false);
|
|
1724
|
+
} else if (e.key === "Enter") {
|
|
1725
|
+
e.preventDefault();
|
|
1726
|
+
if (input.value.trim()) settle(true);
|
|
1727
|
+
else input.focus();
|
|
1728
|
+
}
|
|
1729
|
+
});
|
|
1730
|
+
valCol.insertBefore(customKeyInput, input);
|
|
1731
|
+
}
|
|
1732
|
+
input.placeholder = "Value or equation\u2026";
|
|
1733
|
+
} else if (customKeyInput) {
|
|
1734
|
+
customKeyInput.remove();
|
|
1735
|
+
customKeyInput = null;
|
|
1736
|
+
input.placeholder = "Value\u2026";
|
|
1737
|
+
}
|
|
1649
1738
|
if (spec && (spec.kind === "effort" || spec.kind === "equipment") && referenceProvider) {
|
|
1650
1739
|
attachSuggestions(input, view, root, "value", spec.path, [], () => settle(true), spec.kind);
|
|
1651
1740
|
}
|
|
@@ -1666,6 +1755,7 @@ function createMetricDraftRow(view, root, referenceProvider, commit) {
|
|
|
1666
1755
|
});
|
|
1667
1756
|
input.addEventListener("blur", () => queueMicrotask(() => {
|
|
1668
1757
|
if (!input.isConnected) return;
|
|
1758
|
+
if (row.contains(document.activeElement)) return;
|
|
1669
1759
|
settle(input.value.trim().length > 0);
|
|
1670
1760
|
}));
|
|
1671
1761
|
select.addEventListener("keydown", (e) => {
|
|
@@ -1676,6 +1766,82 @@ function createMetricDraftRow(view, root, referenceProvider, commit) {
|
|
|
1676
1766
|
});
|
|
1677
1767
|
return row;
|
|
1678
1768
|
}
|
|
1769
|
+
function createSummaryDraftRow(view, commit) {
|
|
1770
|
+
const row = document.createElement("div");
|
|
1771
|
+
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";
|
|
1772
|
+
row.setAttribute("data-frontmatter-summary-draft", "true");
|
|
1773
|
+
row.dataset.frontmatterRow = "summary";
|
|
1774
|
+
const keyCol = document.createElement("div");
|
|
1775
|
+
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";
|
|
1776
|
+
keyCol.appendChild(createListIcon());
|
|
1777
|
+
const keyInput = document.createElement("input");
|
|
1778
|
+
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";
|
|
1779
|
+
keyInput.placeholder = "Summary key\u2026";
|
|
1780
|
+
keyInput.setAttribute("aria-label", "Summary key");
|
|
1781
|
+
keyInput.dataset.frontmatterField = "key";
|
|
1782
|
+
keyInput.setAttribute(
|
|
1783
|
+
"aria-describedby",
|
|
1784
|
+
appendFieldHelper(keyCol, "Summary metric name. Press Enter to move to the equation.").id
|
|
1785
|
+
);
|
|
1786
|
+
keyCol.appendChild(keyInput);
|
|
1787
|
+
row.appendChild(keyCol);
|
|
1788
|
+
const valCol = document.createElement("div");
|
|
1789
|
+
valCol.className = "flex items-center flex-wrap gap-1.5 px-3 py-1.5 flex-1 min-w-0";
|
|
1790
|
+
const valInput = document.createElement("input");
|
|
1791
|
+
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";
|
|
1792
|
+
valInput.placeholder = "Equation (e.g. sum(work))\u2026";
|
|
1793
|
+
valInput.setAttribute("aria-label", "Summary equation");
|
|
1794
|
+
valInput.dataset.frontmatterField = "value";
|
|
1795
|
+
valInput.setAttribute(
|
|
1796
|
+
"aria-describedby",
|
|
1797
|
+
appendFieldHelper(valCol, "Summary equation. Press Enter to apply.").id
|
|
1798
|
+
);
|
|
1799
|
+
valCol.appendChild(valInput);
|
|
1800
|
+
row.appendChild(valCol);
|
|
1801
|
+
let settled = false;
|
|
1802
|
+
const settle = (doCommit) => {
|
|
1803
|
+
if (settled) return;
|
|
1804
|
+
settled = true;
|
|
1805
|
+
const rawKey = (keyInput.value.trim() || "summary").replace(/^summaries\./i, "");
|
|
1806
|
+
const rawExpr = valInput.value.trim();
|
|
1807
|
+
if (doCommit && rawExpr) {
|
|
1808
|
+
commit([`summaries.${rawKey}`], rawExpr);
|
|
1809
|
+
}
|
|
1810
|
+
row.remove();
|
|
1811
|
+
view.requestMeasure();
|
|
1812
|
+
};
|
|
1813
|
+
keyInput.addEventListener("keydown", (e) => {
|
|
1814
|
+
if (e.key === "Escape") {
|
|
1815
|
+
e.preventDefault();
|
|
1816
|
+
settle(false);
|
|
1817
|
+
return;
|
|
1818
|
+
}
|
|
1819
|
+
if (e.key === "Enter") {
|
|
1820
|
+
e.preventDefault();
|
|
1821
|
+
if (valInput.value.trim()) settle(true);
|
|
1822
|
+
else valInput.focus();
|
|
1823
|
+
}
|
|
1824
|
+
});
|
|
1825
|
+
valInput.addEventListener("keydown", (e) => {
|
|
1826
|
+
if (e.key === "Escape") {
|
|
1827
|
+
e.preventDefault();
|
|
1828
|
+
settle(false);
|
|
1829
|
+
return;
|
|
1830
|
+
}
|
|
1831
|
+
if (e.key === "Enter") {
|
|
1832
|
+
e.preventDefault();
|
|
1833
|
+
settle(true);
|
|
1834
|
+
}
|
|
1835
|
+
});
|
|
1836
|
+
const onBlur = () => queueMicrotask(() => {
|
|
1837
|
+
if (!row.isConnected) return;
|
|
1838
|
+
if (row.contains(document.activeElement)) return;
|
|
1839
|
+
settle(valInput.value.trim().length > 0);
|
|
1840
|
+
});
|
|
1841
|
+
keyInput.addEventListener("blur", onBlur);
|
|
1842
|
+
valInput.addEventListener("blur", onBlur);
|
|
1843
|
+
return row;
|
|
1844
|
+
}
|
|
1679
1845
|
function createTagIcon() {
|
|
1680
1846
|
const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
|
1681
1847
|
svg.setAttribute("viewBox", "0 0 24 24");
|
|
@@ -1784,18 +1950,88 @@ function createCloseIcon(size = "md") {
|
|
|
1784
1950
|
svg.appendChild(l2);
|
|
1785
1951
|
return svg;
|
|
1786
1952
|
}
|
|
1953
|
+
function isAliasProperty(key) {
|
|
1954
|
+
return ["alias", "aliases"].includes(key.trim().toLowerCase());
|
|
1955
|
+
}
|
|
1956
|
+
function createSummaryPill(value, hash2) {
|
|
1957
|
+
const pill = document.createElement("span");
|
|
1958
|
+
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";
|
|
1959
|
+
const label = document.createElement("span");
|
|
1960
|
+
label.className = "truncate";
|
|
1961
|
+
label.textContent = hash2 ? `#${value}` : value;
|
|
1962
|
+
pill.appendChild(label);
|
|
1963
|
+
return pill;
|
|
1964
|
+
}
|
|
1965
|
+
function createFrontmatterSummary(entries, onShowDetails) {
|
|
1966
|
+
const summary = document.createElement("div");
|
|
1967
|
+
summary.dataset.frontmatterSummary = "true";
|
|
1968
|
+
summary.className = "divide-y divide-border/60";
|
|
1969
|
+
const groups = [
|
|
1970
|
+
{ label: "Tags", entries: entries.filter(([key]) => TAG_CATEGORY_SUMMARY_KEYS[key.trim().toLowerCase()] === true), hash: true },
|
|
1971
|
+
{ label: "Aliases", entries: entries.filter(([key]) => isAliasProperty(key)), hash: false }
|
|
1972
|
+
];
|
|
1973
|
+
for (const group of groups) {
|
|
1974
|
+
const values2 = group.entries.flatMap(([, value]) => parseTagList(value));
|
|
1975
|
+
if (values2.length === 0) continue;
|
|
1976
|
+
const row = document.createElement("div");
|
|
1977
|
+
row.className = "flex flex-wrap items-center gap-1.5 px-3 py-2";
|
|
1978
|
+
const label = document.createElement("span");
|
|
1979
|
+
label.className = "mr-1 w-14 shrink-0 text-[11px] font-medium text-muted-foreground";
|
|
1980
|
+
label.textContent = group.label;
|
|
1981
|
+
row.appendChild(label);
|
|
1982
|
+
for (const value of values2) row.appendChild(createSummaryPill(value, group.hash));
|
|
1983
|
+
summary.appendChild(row);
|
|
1984
|
+
}
|
|
1985
|
+
const hidden = entries.filter(([key]) => TAG_CATEGORY_SUMMARY_KEYS[key.trim().toLowerCase()] !== true && !isAliasProperty(key));
|
|
1986
|
+
const metricCount = hidden.filter(([key]) => {
|
|
1987
|
+
const k = key.trim().toLowerCase();
|
|
1988
|
+
return k.startsWith("metrics.") || k === "metrics";
|
|
1989
|
+
}).length;
|
|
1990
|
+
const summaryCount = hidden.filter(([key]) => {
|
|
1991
|
+
const k = key.trim().toLowerCase();
|
|
1992
|
+
return k.startsWith("summaries.") || k === "summaries";
|
|
1993
|
+
}).length;
|
|
1994
|
+
const details = document.createElement("button");
|
|
1995
|
+
details.type = "button";
|
|
1996
|
+
details.setAttribute("aria-label", "Show property details");
|
|
1997
|
+
details.setAttribute("aria-expanded", "false");
|
|
1998
|
+
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";
|
|
1999
|
+
const count2 = document.createElement("span");
|
|
2000
|
+
count2.textContent = `${hidden.length} more ${hidden.length === 1 ? "property" : "properties"}`;
|
|
2001
|
+
details.appendChild(count2);
|
|
2002
|
+
if (metricCount > 0) {
|
|
2003
|
+
const metrics = document.createElement("span");
|
|
2004
|
+
metrics.textContent = `\xB7 ${metricCount} custom ${metricCount === 1 ? "metric" : "metrics"}`;
|
|
2005
|
+
details.appendChild(metrics);
|
|
2006
|
+
}
|
|
2007
|
+
if (summaryCount > 0) {
|
|
2008
|
+
const summaries = document.createElement("span");
|
|
2009
|
+
summaries.textContent = `\xB7 ${summaryCount} ${summaryCount === 1 ? "summary" : "summaries"}`;
|
|
2010
|
+
details.appendChild(summaries);
|
|
2011
|
+
}
|
|
2012
|
+
const disclosure = document.createElement("span");
|
|
2013
|
+
disclosure.className = "ml-auto";
|
|
2014
|
+
disclosure.setAttribute("aria-hidden", "true");
|
|
2015
|
+
disclosure.textContent = "\u25BE";
|
|
2016
|
+
details.appendChild(disclosure);
|
|
2017
|
+
details.addEventListener("click", onShowDetails);
|
|
2018
|
+
summary.appendChild(details);
|
|
2019
|
+
return summary;
|
|
2020
|
+
}
|
|
1787
2021
|
var DefaultFrontmatterWidget = class extends view.WidgetType {
|
|
1788
2022
|
sectionId;
|
|
1789
2023
|
rawContent;
|
|
1790
2024
|
readOnly;
|
|
1791
|
-
|
|
2025
|
+
detailed;
|
|
2026
|
+
constructor(sectionId, rawContent, readOnly = false, detailed = false) {
|
|
1792
2027
|
super();
|
|
1793
2028
|
this.sectionId = sectionId;
|
|
1794
2029
|
this.rawContent = rawContent ?? "";
|
|
1795
2030
|
this.readOnly = readOnly;
|
|
2031
|
+
this.detailed = detailed;
|
|
1796
2032
|
}
|
|
1797
2033
|
eq(other) {
|
|
1798
|
-
return this.sectionId === other.sectionId && this.rawContent === other.rawContent && this.readOnly === other.readOnly;
|
|
2034
|
+
return this.sectionId === other.sectionId && this.rawContent === other.rawContent && this.readOnly === other.readOnly && this.detailed === other.detailed;
|
|
1799
2035
|
}
|
|
1800
2036
|
ignoreEvent() {
|
|
1801
2037
|
return true;
|
|
@@ -1805,10 +2041,25 @@ var DefaultFrontmatterWidget = class extends view.WidgetType {
|
|
|
1805
2041
|
widgetCleanup.set(root, []);
|
|
1806
2042
|
root.className = "cm-frontmatter-preview py-2 font-sans select-none";
|
|
1807
2043
|
const heading = document.createElement("div");
|
|
1808
|
-
heading.className = "text-xs font-medium text-muted-foreground
|
|
1809
|
-
|
|
2044
|
+
heading.className = "mb-1.5 flex items-center px-0.5 text-xs font-medium text-muted-foreground";
|
|
2045
|
+
const headingLabel = document.createElement("span");
|
|
2046
|
+
headingLabel.textContent = "Properties";
|
|
2047
|
+
heading.appendChild(headingLabel);
|
|
2048
|
+
if (this.detailed) {
|
|
2049
|
+
const summaryButton = document.createElement("button");
|
|
2050
|
+
summaryButton.type = "button";
|
|
2051
|
+
summaryButton.setAttribute("aria-label", "Show property summary");
|
|
2052
|
+
summaryButton.setAttribute("aria-expanded", "true");
|
|
2053
|
+
summaryButton.className = "ml-auto px-1 py-0.5 text-[11px] font-normal hover:text-foreground cursor-pointer";
|
|
2054
|
+
summaryButton.textContent = "Hide details \u25B4";
|
|
2055
|
+
summaryButton.addEventListener("click", () => {
|
|
2056
|
+
view.dispatch({ effects: setFrontmatterDetailed.of(false) });
|
|
2057
|
+
});
|
|
2058
|
+
heading.appendChild(summaryButton);
|
|
2059
|
+
}
|
|
1810
2060
|
root.appendChild(heading);
|
|
1811
2061
|
const box = document.createElement("div");
|
|
2062
|
+
box.dataset.frontmatterProperties = "true";
|
|
1812
2063
|
box.className = "rounded-lg border border-border/80 bg-background/50 text-xs shadow-xs";
|
|
1813
2064
|
root.appendChild(box);
|
|
1814
2065
|
const body = this.rawContent.replace(/^---\r?\n?/, "").replace(/\r?\n?---\r?$/, "");
|
|
@@ -1816,329 +2067,335 @@ var DefaultFrontmatterWidget = class extends view.WidgetType {
|
|
|
1816
2067
|
const entries = Object.entries(meta);
|
|
1817
2068
|
const docKind = detectDefinitionKind(meta);
|
|
1818
2069
|
const referenceProvider = view.state.facet(frontmatterReferenceIndex)?.() ?? null;
|
|
1819
|
-
const base = docKind === "effort" ? readBaseAttributes(body) : {};
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
box.appendChild(
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
const
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
const
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
keyCol.appendChild(
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
);
|
|
1860
|
-
let currentKey = key;
|
|
1861
|
-
const commitKey = () => {
|
|
1862
|
-
const nextKey = keyInput.value.trim();
|
|
1863
|
-
if (!nextKey || nextKey === currentKey) {
|
|
1864
|
-
keyInput.value = currentKey;
|
|
1865
|
-
return;
|
|
1866
|
-
}
|
|
1867
|
-
if (!/^[A-Za-z_][\w.-]*$/.test(nextKey) || nextKey in meta) {
|
|
1868
|
-
keyInput.value = currentKey;
|
|
1869
|
-
return;
|
|
1870
|
-
}
|
|
1871
|
-
renamePropertyInView(view, this.sectionId, currentKey, nextKey, value);
|
|
1872
|
-
delete meta[currentKey];
|
|
1873
|
-
meta[nextKey] = value;
|
|
1874
|
-
currentKey = nextKey;
|
|
1875
|
-
};
|
|
1876
|
-
const commitKeyIfAttached = () => queueMicrotask(() => {
|
|
1877
|
-
if (keyInput.isConnected) commitKey();
|
|
1878
|
-
});
|
|
1879
|
-
keyInput.addEventListener("change", commitKeyIfAttached);
|
|
1880
|
-
keyCol.appendChild(keyInput);
|
|
1881
|
-
attachSuggestions(keyInput, view, root, "key", key, entries.map(([name]) => name), commitKey);
|
|
1882
|
-
keyInput.addEventListener("blur", commitKeyIfAttached);
|
|
1883
|
-
keyInput.addEventListener("keydown", (e) => {
|
|
1884
|
-
if (e.defaultPrevented) return;
|
|
1885
|
-
if (e.key === "Enter") {
|
|
1886
|
-
e.preventDefault();
|
|
1887
|
-
keyInput.blur();
|
|
1888
|
-
}
|
|
1889
|
-
});
|
|
1890
|
-
const focus = pendingFocus.get(view);
|
|
1891
|
-
if (focus?.kind === "key" && focus.key === key) {
|
|
1892
|
-
setTimeout(() => {
|
|
1893
|
-
if (!keyInput.isConnected) return;
|
|
1894
|
-
keyInput.focus();
|
|
1895
|
-
keyInput.select();
|
|
1896
|
-
}, 0);
|
|
1897
|
-
pendingFocus.delete(view);
|
|
1898
|
-
}
|
|
1899
|
-
}
|
|
1900
|
-
const valCol = document.createElement("div");
|
|
1901
|
-
valCol.className = "flex items-center flex-wrap gap-1.5 px-3 py-1.5 flex-1 min-w-0";
|
|
1902
|
-
row.appendChild(valCol);
|
|
1903
|
-
const commitList = (next) => {
|
|
1904
|
-
meta[key] = next;
|
|
1905
|
-
patchPropertyInView(view, this.sectionId, [key], next.length > 0 ? next : "");
|
|
1906
|
-
};
|
|
1907
|
-
const refMatch = (token) => {
|
|
1908
|
-
if (!referenceProvider) return null;
|
|
1909
|
-
return valueKind === "effort" ? referenceProvider.resolveEffort(token) : valueKind === "equipment" ? referenceProvider.resolveEquipment(token) : null;
|
|
1910
|
-
};
|
|
1911
|
-
if (isTags || isReferenceList) {
|
|
1912
|
-
const chips = parseTagList(value);
|
|
1913
|
-
const chipIsReference = valueKind !== "tags";
|
|
1914
|
-
for (let idx = 0; idx < chips.length; idx++) {
|
|
1915
|
-
const chip = chips[idx];
|
|
1916
|
-
const match = chipIsReference ? refMatch(chip) : null;
|
|
1917
|
-
const pill = document.createElement("span");
|
|
1918
|
-
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";
|
|
1919
|
-
const pillText = document.createElement("span");
|
|
1920
|
-
pillText.textContent = match ? match.label : chip;
|
|
1921
|
-
pill.appendChild(pillText);
|
|
1922
|
-
if (match && referenceProvider) {
|
|
1923
|
-
const openLink = document.createElement("a");
|
|
1924
|
-
openLink.href = referenceProvider.openHref(valueKind, match.slug);
|
|
1925
|
-
openLink.title = `Open ${match.label} page`;
|
|
1926
|
-
openLink.setAttribute("aria-label", `Open ${match.label} page`);
|
|
1927
|
-
openLink.className = "inline-flex items-center justify-center text-muted-foreground/60 hover:text-foreground cursor-pointer";
|
|
1928
|
-
openLink.appendChild(createExternalIcon());
|
|
1929
|
-
pill.appendChild(openLink);
|
|
1930
|
-
}
|
|
1931
|
-
if (!this.readOnly) {
|
|
1932
|
-
const rmBtn = document.createElement("button");
|
|
1933
|
-
rmBtn.type = "button";
|
|
1934
|
-
rmBtn.setAttribute("aria-label", isTags ? `Remove tag ${chip}` : `Remove ${chip}`);
|
|
1935
|
-
rmBtn.className = "inline-flex items-center justify-center text-muted-foreground/60 hover:text-foreground dark:hover:text-foreground cursor-pointer";
|
|
1936
|
-
rmBtn.appendChild(createCloseIcon("sm"));
|
|
1937
|
-
rmBtn.addEventListener("click", (e) => {
|
|
1938
|
-
e.preventDefault();
|
|
1939
|
-
e.stopPropagation();
|
|
1940
|
-
commitList(chips.filter((_, i) => i !== idx));
|
|
1941
|
-
});
|
|
1942
|
-
pill.appendChild(rmBtn);
|
|
1943
|
-
}
|
|
1944
|
-
valCol.appendChild(pill);
|
|
1945
|
-
}
|
|
1946
|
-
if (!this.readOnly) {
|
|
1947
|
-
const chipInput = document.createElement("input");
|
|
1948
|
-
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";
|
|
1949
|
-
chipInput.placeholder = chips.length === 0 ? isReferenceList ? `Add ${valueKind === "effort" ? "effort" : "equipment"}\u2026` : "Add tags\u2026" : isTags ? "Add tag\u2026" : "Add\u2026";
|
|
1950
|
-
chipInput.setAttribute("aria-label", `Value for ${key}`);
|
|
1951
|
-
chipInput.dataset.frontmatterField = "value";
|
|
1952
|
-
chipInput.dataset.frontmatterProperty = key;
|
|
1953
|
-
chipInput.setAttribute(
|
|
2070
|
+
const base = docKind === "effort" ? readBaseAttributes(body, meta) : {};
|
|
2071
|
+
if (!this.detailed) {
|
|
2072
|
+
box.appendChild(createFrontmatterSummary(entries, () => {
|
|
2073
|
+
view.dispatch({ effects: setFrontmatterDetailed.of(true) });
|
|
2074
|
+
}));
|
|
2075
|
+
} else {
|
|
2076
|
+
box.appendChild(createColumnHeader());
|
|
2077
|
+
if (entries.length === 0) {
|
|
2078
|
+
const emptyRow = document.createElement("div");
|
|
2079
|
+
emptyRow.className = "px-3 py-2 text-xs text-muted-foreground/60 italic";
|
|
2080
|
+
emptyRow.textContent = "No properties";
|
|
2081
|
+
box.appendChild(emptyRow);
|
|
2082
|
+
}
|
|
2083
|
+
for (const [key, value] of entries) {
|
|
2084
|
+
const isTags = isTagProperty(key);
|
|
2085
|
+
const valueKind = referenceValueKind(key, docKind);
|
|
2086
|
+
const isReferenceList = (isReferenceKey(key, docKind) || key === "aliases") && Array.isArray(value);
|
|
2087
|
+
const nested = value === "" && ownsNestedBlock(body, key);
|
|
2088
|
+
const row = document.createElement("div");
|
|
2089
|
+
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";
|
|
2090
|
+
row.dataset.frontmatterRow = key;
|
|
2091
|
+
box.appendChild(row);
|
|
2092
|
+
const keyCol = document.createElement("div");
|
|
2093
|
+
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";
|
|
2094
|
+
row.appendChild(keyCol);
|
|
2095
|
+
keyCol.appendChild(isTags || isReferenceList ? createTagIcon() : createListIcon());
|
|
2096
|
+
if (this.readOnly || nested) {
|
|
2097
|
+
const keyLabel = document.createElement("span");
|
|
2098
|
+
keyLabel.className = "truncate font-medium text-foreground/90";
|
|
2099
|
+
keyLabel.textContent = key;
|
|
2100
|
+
keyCol.appendChild(keyLabel);
|
|
2101
|
+
} else {
|
|
2102
|
+
const keyInput = document.createElement("input");
|
|
2103
|
+
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";
|
|
2104
|
+
keyInput.value = key;
|
|
2105
|
+
keyInput.spellcheck = false;
|
|
2106
|
+
keyInput.setAttribute("aria-label", `Property key: ${key}`);
|
|
2107
|
+
keyInput.dataset.frontmatterField = "key";
|
|
2108
|
+
keyInput.dataset.frontmatterProperty = key;
|
|
2109
|
+
keyInput.setAttribute(
|
|
1954
2110
|
"aria-describedby",
|
|
1955
2111
|
appendFieldHelper(
|
|
1956
|
-
|
|
1957
|
-
"Press Enter
|
|
2112
|
+
keyCol,
|
|
2113
|
+
"Property name. Press Enter to apply. Arrow keys browse suggested property names."
|
|
1958
2114
|
).id
|
|
1959
2115
|
);
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
2116
|
+
let currentKey = key;
|
|
2117
|
+
const commitKey = () => {
|
|
2118
|
+
const nextKey = keyInput.value.trim();
|
|
2119
|
+
if (!nextKey || nextKey === currentKey) {
|
|
2120
|
+
keyInput.value = currentKey;
|
|
2121
|
+
return;
|
|
2122
|
+
}
|
|
2123
|
+
if (!/^[A-Za-z_][\w.-]*$/.test(nextKey) || nextKey in meta) {
|
|
2124
|
+
keyInput.value = currentKey;
|
|
2125
|
+
return;
|
|
1966
2126
|
}
|
|
2127
|
+
renamePropertyInView(view, this.sectionId, currentKey, nextKey, value);
|
|
2128
|
+
delete meta[currentKey];
|
|
2129
|
+
meta[nextKey] = value;
|
|
2130
|
+
currentKey = nextKey;
|
|
1967
2131
|
};
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
2132
|
+
const commitKeyIfAttached = () => queueMicrotask(() => {
|
|
2133
|
+
if (keyInput.isConnected) commitKey();
|
|
2134
|
+
});
|
|
2135
|
+
keyInput.addEventListener("change", commitKeyIfAttached);
|
|
2136
|
+
keyCol.appendChild(keyInput);
|
|
2137
|
+
attachSuggestions(keyInput, view, root, "key", key, entries.map(([name]) => name), commitKey);
|
|
2138
|
+
keyInput.addEventListener("blur", commitKeyIfAttached);
|
|
2139
|
+
keyInput.addEventListener("keydown", (e) => {
|
|
1971
2140
|
if (e.defaultPrevented) return;
|
|
1972
|
-
if (e.key === "Enter"
|
|
1973
|
-
e.preventDefault();
|
|
1974
|
-
commitNewChip();
|
|
1975
|
-
} else if (e.key === "Backspace" && !chipInput.value && chips.length > 0) {
|
|
2141
|
+
if (e.key === "Enter") {
|
|
1976
2142
|
e.preventDefault();
|
|
1977
|
-
|
|
1978
|
-
commitList(chips.slice(0, -1));
|
|
2143
|
+
keyInput.blur();
|
|
1979
2144
|
}
|
|
1980
2145
|
});
|
|
1981
|
-
chipInput.addEventListener("blur", () => queueMicrotask(() => {
|
|
1982
|
-
if (chipInput.isConnected && chipInput.value.trim()) commitNewChip();
|
|
1983
|
-
}));
|
|
1984
2146
|
const focus = pendingFocus.get(view);
|
|
1985
|
-
if (focus?.kind === "
|
|
2147
|
+
if (focus?.kind === "key" && focus.key === key) {
|
|
1986
2148
|
setTimeout(() => {
|
|
1987
|
-
if (
|
|
2149
|
+
if (!keyInput.isConnected) return;
|
|
2150
|
+
keyInput.focus();
|
|
2151
|
+
keyInput.select();
|
|
1988
2152
|
}, 0);
|
|
1989
2153
|
pendingFocus.delete(view);
|
|
1990
2154
|
}
|
|
1991
|
-
if (chipIsReference && referenceProvider) {
|
|
1992
|
-
const unknown = chips.filter((chip) => !refMatch(chip));
|
|
1993
|
-
if (unknown.length > 0) {
|
|
1994
|
-
const diag = document.createElement("div");
|
|
1995
|
-
diag.setAttribute("data-testid", "editor-ref-unknown");
|
|
1996
|
-
diag.className = "w-full basis-full flex flex-wrap items-center gap-2 text-[10px] text-destructive";
|
|
1997
|
-
const label = document.createElement("span");
|
|
1998
|
-
label.textContent = `Unknown ${valueKind === "effort" ? "effort" : "equipment"}: ${unknown.join(", ")}`;
|
|
1999
|
-
diag.appendChild(label);
|
|
2000
|
-
const selectBtn = document.createElement("button");
|
|
2001
|
-
selectBtn.type = "button";
|
|
2002
|
-
selectBtn.textContent = "Select existing";
|
|
2003
|
-
selectBtn.className = "underline hover:text-foreground cursor-pointer";
|
|
2004
|
-
selectBtn.addEventListener("click", (e) => {
|
|
2005
|
-
e.preventDefault();
|
|
2006
|
-
chipInput.focus();
|
|
2007
|
-
});
|
|
2008
|
-
diag.appendChild(selectBtn);
|
|
2009
|
-
const createLink = document.createElement("a");
|
|
2010
|
-
createLink.href = referenceProvider.createHref(valueKind);
|
|
2011
|
-
createLink.textContent = "Create page";
|
|
2012
|
-
createLink.className = "underline hover:text-foreground";
|
|
2013
|
-
diag.appendChild(createLink);
|
|
2014
|
-
valCol.appendChild(diag);
|
|
2015
|
-
}
|
|
2016
|
-
}
|
|
2017
2155
|
}
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
const
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
);
|
|
2042
|
-
let committedVal = strVal;
|
|
2043
|
-
const commitVal = () => {
|
|
2044
|
-
const cur = valInput.value.trim();
|
|
2045
|
-
if (cur !== committedVal) {
|
|
2046
|
-
committedVal = cur;
|
|
2047
|
-
const num = Number(cur);
|
|
2048
|
-
const next = cur !== "" && !isNaN(num) ? num : cur;
|
|
2049
|
-
meta[key] = next;
|
|
2050
|
-
patchPropertyInView(view, this.sectionId, [key], next);
|
|
2051
|
-
}
|
|
2052
|
-
};
|
|
2053
|
-
const commitValIfAttached = () => queueMicrotask(() => {
|
|
2054
|
-
if (valInput.isConnected) commitVal();
|
|
2055
|
-
});
|
|
2056
|
-
valInput.addEventListener("change", commitValIfAttached);
|
|
2057
|
-
valCol.appendChild(valInput);
|
|
2058
|
-
attachSuggestions(
|
|
2059
|
-
valInput,
|
|
2060
|
-
view,
|
|
2061
|
-
root,
|
|
2062
|
-
"value",
|
|
2063
|
-
key,
|
|
2064
|
-
[strVal],
|
|
2065
|
-
commitVal,
|
|
2066
|
-
key.trim().toLowerCase() === "metrics.effort" ? "effort" : "tags"
|
|
2067
|
-
);
|
|
2068
|
-
valInput.addEventListener("blur", commitValIfAttached);
|
|
2069
|
-
valInput.addEventListener("keydown", (e) => {
|
|
2070
|
-
if (e.defaultPrevented) return;
|
|
2071
|
-
if (e.key === "Enter") {
|
|
2072
|
-
e.preventDefault();
|
|
2073
|
-
valInput.blur();
|
|
2074
|
-
}
|
|
2075
|
-
});
|
|
2076
|
-
if (key.trim().toLowerCase() === "metrics.effort" && referenceProvider) {
|
|
2077
|
-
const match = referenceProvider.resolveEffort(strVal);
|
|
2078
|
-
if (match) {
|
|
2079
|
-
const pill = document.createElement("span");
|
|
2080
|
-
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";
|
|
2081
|
-
const pillText = document.createElement("span");
|
|
2082
|
-
pillText.textContent = match.label;
|
|
2083
|
-
pill.appendChild(pillText);
|
|
2156
|
+
const valCol = document.createElement("div");
|
|
2157
|
+
valCol.className = "flex items-center flex-wrap gap-1.5 px-3 py-1.5 flex-1 min-w-0";
|
|
2158
|
+
row.appendChild(valCol);
|
|
2159
|
+
const commitList = (next) => {
|
|
2160
|
+
meta[key] = next;
|
|
2161
|
+
patchPropertyInView(view, this.sectionId, [key], next.length > 0 ? next : "");
|
|
2162
|
+
};
|
|
2163
|
+
const refMatch = (token) => {
|
|
2164
|
+
if (!referenceProvider) return null;
|
|
2165
|
+
return valueKind === "effort" ? referenceProvider.resolveEffort(token) : valueKind === "equipment" ? referenceProvider.resolveEquipment(token) : null;
|
|
2166
|
+
};
|
|
2167
|
+
if (isTags || isReferenceList) {
|
|
2168
|
+
const chips = parseTagList(value);
|
|
2169
|
+
const chipIsReference = valueKind !== "tags";
|
|
2170
|
+
for (let idx = 0; idx < chips.length; idx++) {
|
|
2171
|
+
const chip = chips[idx];
|
|
2172
|
+
const match = chipIsReference ? refMatch(chip) : null;
|
|
2173
|
+
const pill = document.createElement("span");
|
|
2174
|
+
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";
|
|
2175
|
+
const pillText = document.createElement("span");
|
|
2176
|
+
pillText.textContent = match ? match.label : chip;
|
|
2177
|
+
pill.appendChild(pillText);
|
|
2178
|
+
if (match && referenceProvider) {
|
|
2084
2179
|
const openLink = document.createElement("a");
|
|
2085
|
-
openLink.href = referenceProvider.openHref(
|
|
2180
|
+
openLink.href = referenceProvider.openHref(valueKind, match.slug);
|
|
2086
2181
|
openLink.title = `Open ${match.label} page`;
|
|
2087
2182
|
openLink.setAttribute("aria-label", `Open ${match.label} page`);
|
|
2088
2183
|
openLink.className = "inline-flex items-center justify-center text-muted-foreground/60 hover:text-foreground cursor-pointer";
|
|
2089
2184
|
openLink.appendChild(createExternalIcon());
|
|
2090
2185
|
pill.appendChild(openLink);
|
|
2091
|
-
valCol.insertBefore(pill, valInput);
|
|
2092
|
-
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";
|
|
2093
2186
|
}
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
(
|
|
2098
|
-
|
|
2099
|
-
|
|
2187
|
+
if (!this.readOnly) {
|
|
2188
|
+
const rmBtn = document.createElement("button");
|
|
2189
|
+
rmBtn.type = "button";
|
|
2190
|
+
rmBtn.setAttribute("aria-label", isTags ? `Remove tag ${chip}` : `Remove ${chip}`);
|
|
2191
|
+
rmBtn.className = "inline-flex items-center justify-center text-muted-foreground/60 hover:text-foreground dark:hover:text-foreground cursor-pointer";
|
|
2192
|
+
rmBtn.appendChild(createCloseIcon("sm"));
|
|
2193
|
+
rmBtn.addEventListener("click", (e) => {
|
|
2194
|
+
e.preventDefault();
|
|
2195
|
+
e.stopPropagation();
|
|
2196
|
+
commitList(chips.filter((_, i) => i !== idx));
|
|
2197
|
+
});
|
|
2198
|
+
pill.appendChild(rmBtn);
|
|
2199
|
+
}
|
|
2200
|
+
valCol.appendChild(pill);
|
|
2201
|
+
}
|
|
2202
|
+
if (!this.readOnly) {
|
|
2203
|
+
const chipInput = document.createElement("input");
|
|
2204
|
+
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";
|
|
2205
|
+
chipInput.placeholder = chips.length === 0 ? isReferenceList ? `Add ${valueKind === "effort" ? "effort" : "equipment"}\u2026` : "Add tags\u2026" : isTags ? "Add tag\u2026" : "Add\u2026";
|
|
2206
|
+
chipInput.setAttribute("aria-label", `Value for ${key}`);
|
|
2207
|
+
chipInput.dataset.frontmatterField = "value";
|
|
2208
|
+
chipInput.dataset.frontmatterProperty = key;
|
|
2209
|
+
chipInput.setAttribute(
|
|
2210
|
+
"aria-describedby",
|
|
2211
|
+
appendFieldHelper(
|
|
2212
|
+
valCol,
|
|
2213
|
+
"Press Enter or comma to add a value. Arrow keys browse the suggestions."
|
|
2214
|
+
).id
|
|
2215
|
+
);
|
|
2216
|
+
const commitNewChip = () => {
|
|
2217
|
+
const val = chipInput.value.trim().replace(/^,|,$/g, "");
|
|
2218
|
+
if (val && !chips.some((chip) => chip.toLowerCase() === val.toLowerCase())) {
|
|
2219
|
+
chipInput.value = "";
|
|
2220
|
+
pendingFocus.set(view, { kind: "tag", key });
|
|
2221
|
+
commitList([...chips, val]);
|
|
2222
|
+
}
|
|
2223
|
+
};
|
|
2224
|
+
valCol.appendChild(chipInput);
|
|
2225
|
+
attachSuggestions(chipInput, view, root, "value", key, chips, commitNewChip, valueKind);
|
|
2226
|
+
chipInput.addEventListener("keydown", (e) => {
|
|
2227
|
+
if (e.defaultPrevented) return;
|
|
2228
|
+
if (e.key === "Enter" || e.key === ",") {
|
|
2229
|
+
e.preventDefault();
|
|
2230
|
+
commitNewChip();
|
|
2231
|
+
} else if (e.key === "Backspace" && !chipInput.value && chips.length > 0) {
|
|
2232
|
+
e.preventDefault();
|
|
2233
|
+
pendingFocus.set(view, { kind: "tag", key });
|
|
2234
|
+
commitList(chips.slice(0, -1));
|
|
2100
2235
|
}
|
|
2236
|
+
});
|
|
2237
|
+
chipInput.addEventListener("blur", () => queueMicrotask(() => {
|
|
2238
|
+
if (chipInput.isConnected && chipInput.value.trim()) commitNewChip();
|
|
2239
|
+
}));
|
|
2240
|
+
const focus = pendingFocus.get(view);
|
|
2241
|
+
if (focus?.kind === "tag" && focus.key === key) {
|
|
2242
|
+
setTimeout(() => {
|
|
2243
|
+
if (chipInput.isConnected) chipInput.focus();
|
|
2244
|
+
}, 0);
|
|
2245
|
+
pendingFocus.delete(view);
|
|
2246
|
+
}
|
|
2247
|
+
if (chipIsReference && referenceProvider) {
|
|
2248
|
+
const unknown = chips.filter((chip) => !refMatch(chip));
|
|
2249
|
+
if (unknown.length > 0) {
|
|
2250
|
+
const diag = document.createElement("div");
|
|
2251
|
+
diag.setAttribute("data-testid", "editor-ref-unknown");
|
|
2252
|
+
diag.className = "w-full basis-full flex flex-wrap items-center gap-2 text-[10px] text-destructive";
|
|
2253
|
+
const label = document.createElement("span");
|
|
2254
|
+
label.textContent = `Unknown ${valueKind === "effort" ? "effort" : "equipment"}: ${unknown.join(", ")}`;
|
|
2255
|
+
diag.appendChild(label);
|
|
2256
|
+
const selectBtn = document.createElement("button");
|
|
2257
|
+
selectBtn.type = "button";
|
|
2258
|
+
selectBtn.textContent = "Select existing";
|
|
2259
|
+
selectBtn.className = "underline hover:text-foreground cursor-pointer";
|
|
2260
|
+
selectBtn.addEventListener("click", (e) => {
|
|
2261
|
+
e.preventDefault();
|
|
2262
|
+
chipInput.focus();
|
|
2263
|
+
});
|
|
2264
|
+
diag.appendChild(selectBtn);
|
|
2265
|
+
const createLink = document.createElement("a");
|
|
2266
|
+
createLink.href = referenceProvider.createHref(valueKind);
|
|
2267
|
+
createLink.textContent = "Create page";
|
|
2268
|
+
createLink.className = "underline hover:text-foreground";
|
|
2269
|
+
diag.appendChild(createLink);
|
|
2270
|
+
valCol.appendChild(diag);
|
|
2271
|
+
}
|
|
2272
|
+
}
|
|
2273
|
+
}
|
|
2274
|
+
} else if (nested) {
|
|
2275
|
+
const valSpan = document.createElement("span");
|
|
2276
|
+
valSpan.className = "text-xs text-muted-foreground/60 italic py-0.5 truncate";
|
|
2277
|
+
valSpan.textContent = "Nested block \u2014 edit in YAML";
|
|
2278
|
+
valCol.appendChild(valSpan);
|
|
2279
|
+
} else {
|
|
2280
|
+
const strVal = Array.isArray(value) ? value.join(", ") : String(value ?? "");
|
|
2281
|
+
if (this.readOnly) {
|
|
2282
|
+
const valSpan = document.createElement("span");
|
|
2283
|
+
valSpan.className = "text-xs text-foreground py-0.5 truncate";
|
|
2284
|
+
valSpan.textContent = strVal;
|
|
2285
|
+
valCol.appendChild(valSpan);
|
|
2286
|
+
} else {
|
|
2287
|
+
const valInput = document.createElement("input");
|
|
2288
|
+
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";
|
|
2289
|
+
valInput.value = strVal;
|
|
2290
|
+
valInput.spellcheck = false;
|
|
2291
|
+
valInput.setAttribute("aria-label", `Value for ${key}`);
|
|
2292
|
+
valInput.dataset.frontmatterField = "value";
|
|
2293
|
+
valInput.dataset.frontmatterProperty = key;
|
|
2294
|
+
valInput.setAttribute(
|
|
2295
|
+
"aria-describedby",
|
|
2296
|
+
appendFieldHelper(valCol, "Property value. Press Enter to apply.").id
|
|
2297
|
+
);
|
|
2298
|
+
let committedVal = strVal;
|
|
2299
|
+
const commitVal = () => {
|
|
2300
|
+
const cur = valInput.value.trim();
|
|
2301
|
+
if (cur !== committedVal) {
|
|
2302
|
+
committedVal = cur;
|
|
2303
|
+
const num = Number(cur);
|
|
2304
|
+
const next = cur !== "" && !isNaN(num) ? num : cur;
|
|
2305
|
+
meta[key] = next;
|
|
2306
|
+
patchPropertyInView(view, this.sectionId, [key], next);
|
|
2307
|
+
}
|
|
2308
|
+
};
|
|
2309
|
+
const commitValIfAttached = () => queueMicrotask(() => {
|
|
2310
|
+
if (valInput.isConnected) commitVal();
|
|
2311
|
+
});
|
|
2312
|
+
valInput.addEventListener("change", commitValIfAttached);
|
|
2313
|
+
valCol.appendChild(valInput);
|
|
2314
|
+
attachSuggestions(
|
|
2315
|
+
valInput,
|
|
2316
|
+
view,
|
|
2317
|
+
root,
|
|
2318
|
+
"value",
|
|
2319
|
+
key,
|
|
2320
|
+
[strVal],
|
|
2321
|
+
commitVal,
|
|
2322
|
+
key.trim().toLowerCase() === "metrics.effort" ? "effort" : "tags"
|
|
2101
2323
|
);
|
|
2102
|
-
|
|
2324
|
+
valInput.addEventListener("blur", commitValIfAttached);
|
|
2325
|
+
valInput.addEventListener("keydown", (e) => {
|
|
2326
|
+
if (e.defaultPrevented) return;
|
|
2327
|
+
if (e.key === "Enter") {
|
|
2328
|
+
e.preventDefault();
|
|
2329
|
+
valInput.blur();
|
|
2330
|
+
}
|
|
2331
|
+
});
|
|
2332
|
+
if (key.trim().toLowerCase() === "metrics.effort" && referenceProvider) {
|
|
2333
|
+
const match = referenceProvider.resolveEffort(strVal);
|
|
2334
|
+
if (match) {
|
|
2335
|
+
const pill = document.createElement("span");
|
|
2336
|
+
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";
|
|
2337
|
+
const pillText = document.createElement("span");
|
|
2338
|
+
pillText.textContent = match.label;
|
|
2339
|
+
pill.appendChild(pillText);
|
|
2340
|
+
const openLink = document.createElement("a");
|
|
2341
|
+
openLink.href = referenceProvider.openHref("effort", match.slug);
|
|
2342
|
+
openLink.title = `Open ${match.label} page`;
|
|
2343
|
+
openLink.setAttribute("aria-label", `Open ${match.label} page`);
|
|
2344
|
+
openLink.className = "inline-flex items-center justify-center text-muted-foreground/60 hover:text-foreground cursor-pointer";
|
|
2345
|
+
openLink.appendChild(createExternalIcon());
|
|
2346
|
+
pill.appendChild(openLink);
|
|
2347
|
+
valCol.insertBefore(pill, valInput);
|
|
2348
|
+
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";
|
|
2349
|
+
}
|
|
2350
|
+
const diag = renderEffortValueDiagnostics(
|
|
2351
|
+
referenceProvider,
|
|
2352
|
+
strVal,
|
|
2353
|
+
(slug) => {
|
|
2354
|
+
meta[key] = slug;
|
|
2355
|
+
patchPropertyInView(view, this.sectionId, [key], slug);
|
|
2356
|
+
}
|
|
2357
|
+
);
|
|
2358
|
+
if (diag) valCol.appendChild(diag);
|
|
2359
|
+
}
|
|
2360
|
+
}
|
|
2361
|
+
if (/^https?:\/\/\S+$/i.test(strVal)) {
|
|
2362
|
+
const openLink = document.createElement("a");
|
|
2363
|
+
openLink.href = strVal;
|
|
2364
|
+
openLink.target = "_blank";
|
|
2365
|
+
openLink.rel = "noopener noreferrer";
|
|
2366
|
+
openLink.title = strVal;
|
|
2367
|
+
openLink.setAttribute("aria-label", `Open ${key} in new tab`);
|
|
2368
|
+
openLink.className = "shrink-0 inline-flex items-center justify-center text-muted-foreground/60 hover:text-foreground transition-colors cursor-pointer";
|
|
2369
|
+
openLink.appendChild(createExternalIcon());
|
|
2370
|
+
valCol.appendChild(openLink);
|
|
2103
2371
|
}
|
|
2104
2372
|
}
|
|
2105
|
-
if (
|
|
2106
|
-
const
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2373
|
+
if (!this.readOnly && !nested) {
|
|
2374
|
+
const actionsCol = document.createElement("div");
|
|
2375
|
+
actionsCol.className = "flex items-center pr-2";
|
|
2376
|
+
const delBtn = document.createElement("button");
|
|
2377
|
+
delBtn.type = "button";
|
|
2378
|
+
delBtn.setAttribute("aria-label", `Remove property ${key}`);
|
|
2379
|
+
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";
|
|
2380
|
+
delBtn.appendChild(createCloseIcon("md"));
|
|
2381
|
+
delBtn.addEventListener("click", (e) => {
|
|
2382
|
+
e.preventDefault();
|
|
2383
|
+
e.stopPropagation();
|
|
2384
|
+
delete meta[key];
|
|
2385
|
+
patchPropertyInView(view, this.sectionId, [key], null);
|
|
2386
|
+
});
|
|
2387
|
+
actionsCol.appendChild(delBtn);
|
|
2388
|
+
row.appendChild(actionsCol);
|
|
2115
2389
|
}
|
|
2116
2390
|
}
|
|
2117
|
-
if (!this.readOnly
|
|
2118
|
-
|
|
2119
|
-
actionsCol.className = "flex items-center pr-2";
|
|
2120
|
-
const delBtn = document.createElement("button");
|
|
2121
|
-
delBtn.type = "button";
|
|
2122
|
-
delBtn.setAttribute("aria-label", `Remove property ${key}`);
|
|
2123
|
-
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";
|
|
2124
|
-
delBtn.appendChild(createCloseIcon("md"));
|
|
2125
|
-
delBtn.addEventListener("click", (e) => {
|
|
2126
|
-
e.preventDefault();
|
|
2127
|
-
e.stopPropagation();
|
|
2128
|
-
delete meta[key];
|
|
2129
|
-
patchPropertyInView(view, this.sectionId, [key], null);
|
|
2130
|
-
});
|
|
2131
|
-
actionsCol.appendChild(delBtn);
|
|
2132
|
-
row.appendChild(actionsCol);
|
|
2391
|
+
if (docKind === "effort" && !this.readOnly) {
|
|
2392
|
+
box.appendChild(this.buildDefinitionRows(view, base));
|
|
2133
2393
|
}
|
|
2134
2394
|
}
|
|
2135
|
-
if (docKind === "effort" && !this.readOnly) {
|
|
2136
|
-
box.appendChild(this.buildDefinitionRows(view, base));
|
|
2137
|
-
}
|
|
2138
2395
|
if (!this.readOnly) {
|
|
2139
2396
|
const existingTagKey = entries.find(([key]) => key.trim().toLowerCase() === "tags")?.[0] ?? entries.find(([key]) => isTagProperty(key))?.[0];
|
|
2140
2397
|
const actionsRow = document.createElement("div");
|
|
2141
|
-
actionsRow.className = "mt-2 flex items-center gap-3";
|
|
2398
|
+
actionsRow.className = "mt-2 flex flex-wrap items-center gap-x-3 gap-y-1";
|
|
2142
2399
|
actionsRow.dataset.frontmatterActions = "true";
|
|
2143
2400
|
const addBtn = document.createElement("button");
|
|
2144
2401
|
addBtn.type = "button";
|
|
@@ -2158,7 +2415,12 @@ var DefaultFrontmatterWidget = class extends view.WidgetType {
|
|
|
2158
2415
|
newKey = `property_${count2}`;
|
|
2159
2416
|
}
|
|
2160
2417
|
pendingFocus.set(view, { kind: "key", key: newKey });
|
|
2161
|
-
patchPropertyInView(view, this.sectionId, [newKey], "");
|
|
2418
|
+
patchPropertyInView(view, this.sectionId, [newKey], "", !this.detailed);
|
|
2419
|
+
const keyField = view.dom.querySelector(
|
|
2420
|
+
`[data-frontmatter-row="${newKey}"] [data-frontmatter-field="key"]`
|
|
2421
|
+
);
|
|
2422
|
+
keyField?.focus();
|
|
2423
|
+
keyField?.select();
|
|
2162
2424
|
});
|
|
2163
2425
|
actionsRow.appendChild(addBtn);
|
|
2164
2426
|
{
|
|
@@ -2174,20 +2436,57 @@ var DefaultFrontmatterWidget = class extends view.WidgetType {
|
|
|
2174
2436
|
addMetricBtn.addEventListener("click", (e) => {
|
|
2175
2437
|
e.preventDefault();
|
|
2176
2438
|
e.stopPropagation();
|
|
2177
|
-
|
|
2439
|
+
if (!this.detailed) {
|
|
2440
|
+
view.dispatch({ effects: setFrontmatterDetailed.of(true) });
|
|
2441
|
+
}
|
|
2442
|
+
const activeRoot = view.dom.querySelector(".cm-frontmatter-preview") ?? root;
|
|
2443
|
+
const activeBox = activeRoot.querySelector("[data-frontmatter-properties]") ?? box;
|
|
2444
|
+
const existing = activeRoot.querySelector("[data-frontmatter-metric-draft] input");
|
|
2178
2445
|
if (existing) {
|
|
2179
2446
|
existing.focus();
|
|
2180
2447
|
return;
|
|
2181
2448
|
}
|
|
2182
|
-
const draftRow = createMetricDraftRow(view,
|
|
2449
|
+
const draftRow = createMetricDraftRow(view, activeRoot, referenceProvider, (path, value) => {
|
|
2183
2450
|
patchPropertyInView(view, this.sectionId, path, value);
|
|
2184
2451
|
});
|
|
2185
|
-
|
|
2452
|
+
activeBox.appendChild(draftRow);
|
|
2186
2453
|
draftRow.querySelector("input")?.focus();
|
|
2187
2454
|
view.requestMeasure();
|
|
2188
2455
|
});
|
|
2189
2456
|
actionsRow.appendChild(addMetricBtn);
|
|
2190
2457
|
}
|
|
2458
|
+
{
|
|
2459
|
+
const addSummaryBtn = document.createElement("button");
|
|
2460
|
+
addSummaryBtn.type = "button";
|
|
2461
|
+
addSummaryBtn.setAttribute("aria-label", "Add summary");
|
|
2462
|
+
addSummaryBtn.setAttribute("data-testid", "editor-add-summary-note");
|
|
2463
|
+
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";
|
|
2464
|
+
addSummaryBtn.appendChild(createPlusIcon());
|
|
2465
|
+
const addSummaryLabel = document.createElement("span");
|
|
2466
|
+
addSummaryLabel.textContent = "Add summary";
|
|
2467
|
+
addSummaryBtn.appendChild(addSummaryLabel);
|
|
2468
|
+
addSummaryBtn.addEventListener("click", (e) => {
|
|
2469
|
+
e.preventDefault();
|
|
2470
|
+
e.stopPropagation();
|
|
2471
|
+
if (!this.detailed) {
|
|
2472
|
+
view.dispatch({ effects: setFrontmatterDetailed.of(true) });
|
|
2473
|
+
}
|
|
2474
|
+
const activeRoot = view.dom.querySelector(".cm-frontmatter-preview") ?? root;
|
|
2475
|
+
const activeBox = activeRoot.querySelector("[data-frontmatter-properties]") ?? box;
|
|
2476
|
+
const existing = activeRoot.querySelector("[data-frontmatter-summary-draft] input");
|
|
2477
|
+
if (existing) {
|
|
2478
|
+
existing.focus();
|
|
2479
|
+
return;
|
|
2480
|
+
}
|
|
2481
|
+
const draftRow = createSummaryDraftRow(view, (path, value) => {
|
|
2482
|
+
patchPropertyInView(view, this.sectionId, path, value);
|
|
2483
|
+
});
|
|
2484
|
+
activeBox.appendChild(draftRow);
|
|
2485
|
+
draftRow.querySelector("input")?.focus();
|
|
2486
|
+
view.requestMeasure();
|
|
2487
|
+
});
|
|
2488
|
+
actionsRow.appendChild(addSummaryBtn);
|
|
2489
|
+
}
|
|
2191
2490
|
{
|
|
2192
2491
|
const addTagBtn = document.createElement("button");
|
|
2193
2492
|
addTagBtn.type = "button";
|
|
@@ -2200,23 +2499,28 @@ var DefaultFrontmatterWidget = class extends view.WidgetType {
|
|
|
2200
2499
|
addTagBtn.addEventListener("click", (e) => {
|
|
2201
2500
|
e.preventDefault();
|
|
2202
2501
|
e.stopPropagation();
|
|
2502
|
+
if (!this.detailed) {
|
|
2503
|
+
view.dispatch({ effects: setFrontmatterDetailed.of(true) });
|
|
2504
|
+
}
|
|
2505
|
+
const activeRoot = view.dom.querySelector(".cm-frontmatter-preview") ?? root;
|
|
2506
|
+
const activeBox = activeRoot.querySelector("[data-frontmatter-properties]") ?? box;
|
|
2203
2507
|
if (existingTagKey) {
|
|
2204
|
-
const input = Array.from(
|
|
2508
|
+
const input = Array.from(activeRoot.querySelectorAll(
|
|
2205
2509
|
'[data-frontmatter-field="value"]'
|
|
2206
2510
|
)).find((element) => element.dataset.frontmatterProperty === existingTagKey);
|
|
2207
2511
|
input?.focus();
|
|
2208
2512
|
return;
|
|
2209
2513
|
}
|
|
2210
|
-
const existingDraft =
|
|
2514
|
+
const existingDraft = activeRoot.querySelector("[data-frontmatter-tag-draft] input");
|
|
2211
2515
|
if (existingDraft) {
|
|
2212
2516
|
existingDraft.focus();
|
|
2213
2517
|
return;
|
|
2214
2518
|
}
|
|
2215
|
-
const draftRow = createTagDraftRow(view,
|
|
2519
|
+
const draftRow = createTagDraftRow(view, activeRoot, "category", (val) => {
|
|
2216
2520
|
pendingFocus.set(view, { kind: "tag", key: "category" });
|
|
2217
2521
|
patchPropertyInView(view, this.sectionId, ["category"], [val]);
|
|
2218
2522
|
});
|
|
2219
|
-
|
|
2523
|
+
activeBox.appendChild(draftRow);
|
|
2220
2524
|
draftRow.querySelector("input")?.focus();
|
|
2221
2525
|
view.requestMeasure();
|
|
2222
2526
|
});
|
|
@@ -2237,7 +2541,11 @@ var DefaultFrontmatterWidget = class extends view.WidgetType {
|
|
|
2237
2541
|
const { sections: currentSections } = view.state.field(sectionField);
|
|
2238
2542
|
const section = currentSections.find((s) => s.id === this.sectionId && s.type === "frontmatter") || currentSections.find((s) => s.type === "frontmatter");
|
|
2239
2543
|
if (!section) return;
|
|
2240
|
-
view.dispatch({
|
|
2544
|
+
view.dispatch({
|
|
2545
|
+
selection: { anchor: section.from },
|
|
2546
|
+
effects: setFrontmatterSource.of(true),
|
|
2547
|
+
scrollIntoView: true
|
|
2548
|
+
});
|
|
2241
2549
|
view.focus();
|
|
2242
2550
|
});
|
|
2243
2551
|
actionsRow.appendChild(editYamlBtn);
|
|
@@ -2262,10 +2570,12 @@ var DefaultFrontmatterWidget = class extends view.WidgetType {
|
|
|
2262
2570
|
const commitField = (field, raw) => {
|
|
2263
2571
|
if (field === "met") {
|
|
2264
2572
|
const num = Number(raw);
|
|
2265
|
-
|
|
2573
|
+
const path2 = base.hasBaseBlock ? ["baseAttributes", "met"] : ["metrics.met"];
|
|
2574
|
+
patchPropertyInView(view, this.sectionId, path2, raw !== "" && !Number.isNaN(num) ? num : null);
|
|
2266
2575
|
return;
|
|
2267
2576
|
}
|
|
2268
|
-
|
|
2577
|
+
const path = base.hasBaseBlock ? ["baseAttributes", field] : [field];
|
|
2578
|
+
patchPropertyInView(view, this.sectionId, path, raw === "" ? null : raw);
|
|
2269
2579
|
};
|
|
2270
2580
|
const fieldRow = (label, child) => {
|
|
2271
2581
|
const row = document.createElement("label");
|
|
@@ -2328,8 +2638,13 @@ var DefaultFrontmatterWidget = class extends view.WidgetType {
|
|
|
2328
2638
|
}
|
|
2329
2639
|
};
|
|
2330
2640
|
var EmptyFrontmatterWidget = class extends view.WidgetType {
|
|
2331
|
-
|
|
2332
|
-
|
|
2641
|
+
constructor(detailed = false) {
|
|
2642
|
+
super();
|
|
2643
|
+
this.detailed = detailed;
|
|
2644
|
+
}
|
|
2645
|
+
detailed;
|
|
2646
|
+
eq(other) {
|
|
2647
|
+
return this.detailed === other.detailed;
|
|
2333
2648
|
}
|
|
2334
2649
|
ignoreEvent() {
|
|
2335
2650
|
return true;
|
|
@@ -2342,7 +2657,7 @@ var EmptyFrontmatterWidget = class extends view.WidgetType {
|
|
|
2342
2657
|
heading.textContent = "Properties";
|
|
2343
2658
|
root.appendChild(heading);
|
|
2344
2659
|
const actionsRow = document.createElement("div");
|
|
2345
|
-
actionsRow.className = "flex items-center gap-3";
|
|
2660
|
+
actionsRow.className = "flex flex-wrap items-center gap-x-3 gap-y-1";
|
|
2346
2661
|
actionsRow.dataset.frontmatterActions = "true";
|
|
2347
2662
|
const addBtn = document.createElement("button");
|
|
2348
2663
|
addBtn.type = "button";
|
|
@@ -2356,9 +2671,71 @@ var EmptyFrontmatterWidget = class extends view.WidgetType {
|
|
|
2356
2671
|
e.preventDefault();
|
|
2357
2672
|
e.stopPropagation();
|
|
2358
2673
|
pendingFocus.set(view, { kind: "key", key: "property" });
|
|
2359
|
-
patchPropertyInView(view, "new", ["property"], "");
|
|
2674
|
+
patchPropertyInView(view, "new", ["property"], "", true);
|
|
2675
|
+
const keyField = view.dom.querySelector(
|
|
2676
|
+
'[data-frontmatter-row="property"] [data-frontmatter-field="key"]'
|
|
2677
|
+
);
|
|
2678
|
+
keyField?.focus();
|
|
2679
|
+
keyField?.select();
|
|
2360
2680
|
});
|
|
2361
2681
|
actionsRow.appendChild(addBtn);
|
|
2682
|
+
const addMetricBtn = document.createElement("button");
|
|
2683
|
+
addMetricBtn.type = "button";
|
|
2684
|
+
addMetricBtn.setAttribute("aria-label", "Add metric");
|
|
2685
|
+
addMetricBtn.setAttribute("data-testid", "editor-add-metric-note");
|
|
2686
|
+
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";
|
|
2687
|
+
addMetricBtn.appendChild(createPlusIcon());
|
|
2688
|
+
const addMetricLabel = document.createElement("span");
|
|
2689
|
+
addMetricLabel.textContent = "Add metric";
|
|
2690
|
+
addMetricBtn.appendChild(addMetricLabel);
|
|
2691
|
+
addMetricBtn.addEventListener("click", (e) => {
|
|
2692
|
+
e.preventDefault();
|
|
2693
|
+
e.stopPropagation();
|
|
2694
|
+
if (!this.detailed) view.dispatch({ effects: setFrontmatterDetailed.of(true) });
|
|
2695
|
+
const activeRoot = view.dom.querySelector(".cm-frontmatter-preview") ?? root;
|
|
2696
|
+
const existing = activeRoot.querySelector("[data-frontmatter-metric-draft] input");
|
|
2697
|
+
if (existing) {
|
|
2698
|
+
existing.focus();
|
|
2699
|
+
return;
|
|
2700
|
+
}
|
|
2701
|
+
const referenceProvider = view.state.facet(frontmatterReferenceIndex)?.() ?? null;
|
|
2702
|
+
const draftRow = createMetricDraftRow(view, activeRoot, referenceProvider, (path, value) => {
|
|
2703
|
+
patchPropertyInView(view, "new", path, value, true);
|
|
2704
|
+
});
|
|
2705
|
+
activeRoot.appendChild(draftRow);
|
|
2706
|
+
ensureColumnHeader(activeRoot, draftRow);
|
|
2707
|
+
draftRow.querySelector("input")?.focus();
|
|
2708
|
+
view.requestMeasure();
|
|
2709
|
+
});
|
|
2710
|
+
actionsRow.appendChild(addMetricBtn);
|
|
2711
|
+
const addSummaryBtn = document.createElement("button");
|
|
2712
|
+
addSummaryBtn.type = "button";
|
|
2713
|
+
addSummaryBtn.setAttribute("aria-label", "Add summary");
|
|
2714
|
+
addSummaryBtn.setAttribute("data-testid", "editor-add-summary-note");
|
|
2715
|
+
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";
|
|
2716
|
+
addSummaryBtn.appendChild(createPlusIcon());
|
|
2717
|
+
const addSummaryLabel = document.createElement("span");
|
|
2718
|
+
addSummaryLabel.textContent = "Add summary";
|
|
2719
|
+
addSummaryBtn.appendChild(addSummaryLabel);
|
|
2720
|
+
addSummaryBtn.addEventListener("click", (e) => {
|
|
2721
|
+
e.preventDefault();
|
|
2722
|
+
e.stopPropagation();
|
|
2723
|
+
if (!this.detailed) view.dispatch({ effects: setFrontmatterDetailed.of(true) });
|
|
2724
|
+
const activeRoot = view.dom.querySelector(".cm-frontmatter-preview") ?? root;
|
|
2725
|
+
const existing = activeRoot.querySelector("[data-frontmatter-summary-draft] input");
|
|
2726
|
+
if (existing) {
|
|
2727
|
+
existing.focus();
|
|
2728
|
+
return;
|
|
2729
|
+
}
|
|
2730
|
+
const draftRow = createSummaryDraftRow(view, (path, value) => {
|
|
2731
|
+
patchPropertyInView(view, "new", path, value, true);
|
|
2732
|
+
});
|
|
2733
|
+
activeRoot.appendChild(draftRow);
|
|
2734
|
+
ensureColumnHeader(activeRoot, draftRow);
|
|
2735
|
+
draftRow.querySelector("input")?.focus();
|
|
2736
|
+
view.requestMeasure();
|
|
2737
|
+
});
|
|
2738
|
+
actionsRow.appendChild(addSummaryBtn);
|
|
2362
2739
|
const addTagBtn = document.createElement("button");
|
|
2363
2740
|
addTagBtn.type = "button";
|
|
2364
2741
|
addTagBtn.setAttribute("aria-label", "Add tag");
|
|
@@ -2370,17 +2747,19 @@ var EmptyFrontmatterWidget = class extends view.WidgetType {
|
|
|
2370
2747
|
addTagBtn.addEventListener("click", (e) => {
|
|
2371
2748
|
e.preventDefault();
|
|
2372
2749
|
e.stopPropagation();
|
|
2373
|
-
|
|
2750
|
+
if (!this.detailed) view.dispatch({ effects: setFrontmatterDetailed.of(true) });
|
|
2751
|
+
const activeRoot = view.dom.querySelector(".cm-frontmatter-preview") ?? root;
|
|
2752
|
+
const existingDraft = activeRoot.querySelector("[data-frontmatter-tag-draft] input");
|
|
2374
2753
|
if (existingDraft) {
|
|
2375
2754
|
existingDraft.focus();
|
|
2376
2755
|
return;
|
|
2377
2756
|
}
|
|
2378
|
-
const draftRow = createTagDraftRow(view,
|
|
2757
|
+
const draftRow = createTagDraftRow(view, activeRoot, "category", (val) => {
|
|
2379
2758
|
pendingFocus.set(view, { kind: "tag", key: "category" });
|
|
2380
|
-
patchPropertyInView(view, "new", ["category"], [val]);
|
|
2759
|
+
patchPropertyInView(view, "new", ["category"], [val], true);
|
|
2381
2760
|
});
|
|
2382
|
-
|
|
2383
|
-
ensureColumnHeader(
|
|
2761
|
+
activeRoot.appendChild(draftRow);
|
|
2762
|
+
ensureColumnHeader(activeRoot, draftRow);
|
|
2384
2763
|
draftRow.querySelector("input")?.focus();
|
|
2385
2764
|
view.requestMeasure();
|
|
2386
2765
|
});
|
|
@@ -2402,14 +2781,14 @@ function buildFrontmatterDecos(state$1) {
|
|
|
2402
2781
|
builder.add(section.from, to, view.Decoration.replace({ block: true }));
|
|
2403
2782
|
continue;
|
|
2404
2783
|
}
|
|
2405
|
-
const selected = !state$1.readOnly && state$1.selection.ranges.some((range) => range.from < section.to && range.to > section.from);
|
|
2784
|
+
const selected = !state$1.readOnly && (state$1.field(frontmatterSource) || state$1.selection.ranges.some((range) => range.from < section.to && range.to > section.from));
|
|
2406
2785
|
if (selected) continue;
|
|
2407
2786
|
const rawContent = state$1.doc.sliceString(section.from, section.to);
|
|
2408
2787
|
builder.add(
|
|
2409
2788
|
section.from,
|
|
2410
2789
|
section.to,
|
|
2411
2790
|
view.Decoration.replace({
|
|
2412
|
-
widget: new DefaultFrontmatterWidget(section.id, rawContent, state$1.readOnly),
|
|
2791
|
+
widget: new DefaultFrontmatterWidget(section.id, rawContent, state$1.readOnly, state$1.field(frontmatterDetailed)),
|
|
2413
2792
|
block: true
|
|
2414
2793
|
})
|
|
2415
2794
|
);
|
|
@@ -2419,7 +2798,7 @@ function buildFrontmatterDecos(state$1) {
|
|
|
2419
2798
|
0,
|
|
2420
2799
|
0,
|
|
2421
2800
|
view.Decoration.widget({
|
|
2422
|
-
widget: new EmptyFrontmatterWidget(),
|
|
2801
|
+
widget: new EmptyFrontmatterWidget(state$1.field(frontmatterDetailed)),
|
|
2423
2802
|
side: -1,
|
|
2424
2803
|
block: true
|
|
2425
2804
|
})
|
|
@@ -2432,7 +2811,7 @@ var frontmatterPreviewField = state.StateField.define({
|
|
|
2432
2811
|
return buildFrontmatterDecos(state);
|
|
2433
2812
|
},
|
|
2434
2813
|
update(deco, tr) {
|
|
2435
|
-
if (tr.docChanged || tr.reconfigured || tr.selection || tr.effects.some((e) => e.is(referenceIndexReload))) {
|
|
2814
|
+
if (tr.docChanged || tr.reconfigured || tr.selection || tr.effects.some((e) => e.is(referenceIndexReload) || e.is(setFrontmatterDetailed) || e.is(setFrontmatterSource))) {
|
|
2436
2815
|
return buildFrontmatterDecos(tr.state);
|
|
2437
2816
|
}
|
|
2438
2817
|
return deco;
|
|
@@ -2444,7 +2823,7 @@ var frontmatterPreviewField = state.StateField.define({
|
|
|
2444
2823
|
)
|
|
2445
2824
|
]
|
|
2446
2825
|
});
|
|
2447
|
-
var frontmatterPreview = [frontmatterPreviewField];
|
|
2826
|
+
var frontmatterPreview = [frontmatterDetailed, frontmatterSource, frontmatterPreviewField];
|
|
2448
2827
|
var HEADING_RE = /^(#{1,6}) /;
|
|
2449
2828
|
var BOLD_RE = /\*\*([^*\n]+)\*\*/g;
|
|
2450
2829
|
var ITALIC_STAR_RE = /(?<!\*)\*([^*\n]+)\*(?!\*)/g;
|
|
@@ -4990,7 +5369,9 @@ var CLAUSE_META = {
|
|
|
4990
5369
|
note: { label: "Note", inputType: "freetext", placeholder: "note id\u2026", placeholderText: "note: [id]", icon: "\u{1F4D3}", description: "Scope to one note", prefix: "note:" },
|
|
4991
5370
|
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:" },
|
|
4992
5371
|
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:" },
|
|
4993
|
-
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" }
|
|
5372
|
+
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" },
|
|
5373
|
+
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:" },
|
|
5374
|
+
subscribed: { label: "Subscribed", inputType: "select", placeholder: "true, false", placeholderText: "subscribed: [true|false]", icon: "\u2B50", description: "Filter by notebook subscription state", prefix: "subscribed:" }
|
|
4994
5375
|
};
|
|
4995
5376
|
var CUSTOM_FALLBACK_ICON = "\u{1F9E9}";
|
|
4996
5377
|
function getClauseMeta(type) {
|
|
@@ -12832,9 +13213,11 @@ exports.fenceCompletion = fenceCompletion;
|
|
|
12832
13213
|
exports.findQueryBlockSection = findQueryBlockSection;
|
|
12833
13214
|
exports.findWorkoutErrors = findWorkoutErrors;
|
|
12834
13215
|
exports.forceSectionParse = forceSectionParse;
|
|
13216
|
+
exports.frontmatterDetailed = frontmatterDetailed;
|
|
12835
13217
|
exports.frontmatterPreview = frontmatterPreview;
|
|
12836
13218
|
exports.frontmatterPreviewField = frontmatterPreviewField;
|
|
12837
13219
|
exports.frontmatterReferenceIndex = frontmatterReferenceIndex;
|
|
13220
|
+
exports.frontmatterSource = frontmatterSource;
|
|
12838
13221
|
exports.frontmatterSuggestions = frontmatterSuggestions;
|
|
12839
13222
|
exports.frontmatterVisibility = frontmatterVisibility;
|
|
12840
13223
|
exports.getCursorFocusState = getCursorFocusState;
|
|
@@ -12869,6 +13252,8 @@ exports.sectionRestore = sectionRestore;
|
|
|
12869
13252
|
exports.sectionSaved = sectionSaved;
|
|
12870
13253
|
exports.sessionQueryInsert = sessionQueryInsert;
|
|
12871
13254
|
exports.sessionQueryWql = sessionQueryWql;
|
|
13255
|
+
exports.setFrontmatterDetailed = setFrontmatterDetailed;
|
|
13256
|
+
exports.setFrontmatterSource = setFrontmatterSource;
|
|
12872
13257
|
exports.setGutterHighlights = setGutterHighlights;
|
|
12873
13258
|
exports.smartIncrement = smartIncrement;
|
|
12874
13259
|
exports.toggleWidgetEdit = toggleWidgetEdit;
|