@bitcobblers/wod-wiki-ui 0.45.2167 → 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.
- 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/package.json +4 -4
- package/dist/chunk-QCWASNQO.mjs.map +0 -1
|
@@ -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)
|
|
1427
|
-
|
|
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
|
-
|
|
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
|
|
1790
|
-
|
|
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
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
box.appendChild(
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
const
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
const
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
keyCol.appendChild(
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
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
|
-
|
|
1938
|
-
"Press Enter
|
|
2093
|
+
keyCol,
|
|
2094
|
+
"Property name. Press Enter to apply. Arrow keys browse suggested property names."
|
|
1939
2095
|
).id
|
|
1940
2096
|
);
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
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
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
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"
|
|
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
|
-
|
|
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 === "
|
|
2128
|
+
if (focus?.kind === "key" && focus.key === key) {
|
|
1967
2129
|
setTimeout(() => {
|
|
1968
|
-
if (
|
|
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
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
const
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
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(
|
|
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
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
(
|
|
2079
|
-
|
|
2080
|
-
|
|
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
|
-
|
|
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 (
|
|
2087
|
-
const
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
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
|
|
2099
|
-
|
|
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
|
-
|
|
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,
|
|
2430
|
+
const draftRow = createMetricDraftRow(view, activeRoot, referenceProvider, (path, value) => {
|
|
2164
2431
|
patchPropertyInView(view, this.sectionId, path, value);
|
|
2165
2432
|
});
|
|
2166
|
-
|
|
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(
|
|
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 =
|
|
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,
|
|
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
|
-
|
|
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({
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2313
|
-
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
2364
|
-
ensureColumnHeader(
|
|
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-
|
|
12972
|
-
//# sourceMappingURL=chunk-
|
|
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
|