@bitcobblers/wod-wiki-ui 0.44.2155 → 0.44.2157

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.
@@ -1035,6 +1035,143 @@ var embedPreviewField = state.StateField.define({
1035
1035
  var embedPreviewDecorations = [
1036
1036
  embedPreviewField
1037
1037
  ];
1038
+
1039
+ // src/extensions/frontmatter-suggestions.ts
1040
+ var POPOVER_MIN_WIDTH = 192;
1041
+ var POPOVER_MAX_WIDTH = 320;
1042
+ var POPOVER_MAX_HEIGHT = 256;
1043
+ function computeInlinePanelBox(field, host, viewportHeight) {
1044
+ const offsetLeft = Math.max(0, Math.min(field.left - host.left, Math.max(0, host.width - POPOVER_MIN_WIDTH)));
1045
+ const room = Math.max(host.width - offsetLeft, POPOVER_MIN_WIDTH);
1046
+ const width = Math.min(Math.max(field.width, POPOVER_MIN_WIDTH), POPOVER_MAX_WIDTH, room);
1047
+ const belowField = Math.max(0, viewportHeight - field.top - 64);
1048
+ return {
1049
+ width,
1050
+ offsetLeft,
1051
+ maxHeight: Math.max(96, Math.min(POPOVER_MAX_HEIGHT, belowField))
1052
+ };
1053
+ }
1054
+ var panelId = 0;
1055
+ var SuggestionPanel = class {
1056
+ element;
1057
+ inputElement;
1058
+ onPick;
1059
+ view;
1060
+ host;
1061
+ listId;
1062
+ currentChoices = [];
1063
+ currentIndex = -1;
1064
+ isOpen = false;
1065
+ constructor(input, options) {
1066
+ this.inputElement = input;
1067
+ this.onPick = options.onPick;
1068
+ this.view = options.view;
1069
+ this.host = options.host;
1070
+ this.listId = `frontmatter-suggestions-${++panelId}`;
1071
+ const list = document.createElement("div");
1072
+ list.id = this.listId;
1073
+ list.setAttribute("role", "listbox");
1074
+ list.setAttribute("aria-label", options.label);
1075
+ list.tabIndex = -1;
1076
+ list.className = "mt-1 overflow-auto rounded-md border border-border bg-popover text-popover-foreground shadow-md text-xs";
1077
+ list.hidden = true;
1078
+ this.element = list;
1079
+ this.attach();
1080
+ input.setAttribute("role", "combobox");
1081
+ input.setAttribute("aria-haspopup", "listbox");
1082
+ input.setAttribute("aria-autocomplete", "list");
1083
+ input.setAttribute("aria-controls", this.listId);
1084
+ input.setAttribute("aria-expanded", "false");
1085
+ }
1086
+ get choices() {
1087
+ return this.currentChoices;
1088
+ }
1089
+ get activeIndex() {
1090
+ return this.currentIndex;
1091
+ }
1092
+ setChoices(choices) {
1093
+ this.currentChoices = choices;
1094
+ this.currentIndex = -1;
1095
+ this.inputElement.removeAttribute("aria-activedescendant");
1096
+ this.element.replaceChildren();
1097
+ choices.forEach((choice, index) => {
1098
+ const option = document.createElement("div");
1099
+ option.id = `${this.listId}-${index}`;
1100
+ option.setAttribute("role", "option");
1101
+ option.setAttribute("aria-selected", "false");
1102
+ option.className = "cursor-pointer px-2 py-1.5 hover:bg-muted";
1103
+ option.textContent = choice.label;
1104
+ option.addEventListener("mousedown", (event) => event.preventDefault());
1105
+ option.addEventListener("click", () => this.pick(choice));
1106
+ this.element.appendChild(option);
1107
+ });
1108
+ }
1109
+ open() {
1110
+ if (this.isOpen) return;
1111
+ this.isOpen = true;
1112
+ this.element.hidden = false;
1113
+ this.attach();
1114
+ this.position();
1115
+ this.inputElement.setAttribute("aria-expanded", "true");
1116
+ this.view.requestMeasure();
1117
+ }
1118
+ close() {
1119
+ if (!this.isOpen) return;
1120
+ this.isOpen = false;
1121
+ this.element.hidden = true;
1122
+ this.currentIndex = -1;
1123
+ this.inputElement.setAttribute("aria-expanded", "false");
1124
+ this.inputElement.removeAttribute("aria-activedescendant");
1125
+ this.view.requestMeasure();
1126
+ }
1127
+ destroy() {
1128
+ this.close();
1129
+ }
1130
+ activate(index) {
1131
+ this.currentIndex = index;
1132
+ Array.from(this.element.children).forEach((option2, i) => {
1133
+ option2.setAttribute("aria-selected", String(i === index));
1134
+ option2.classList.toggle("bg-muted", i === index);
1135
+ });
1136
+ const option = this.element.children[index];
1137
+ if (!option) return;
1138
+ this.inputElement.setAttribute("aria-activedescendant", option.id);
1139
+ if (option instanceof HTMLElement) option.scrollIntoView?.({ block: "nearest" });
1140
+ }
1141
+ /** Move the highlighted option; the first/last option wraps around. */
1142
+ moveActive(delta) {
1143
+ if (this.currentChoices.length === 0) return;
1144
+ const next = this.currentIndex < 0 ? delta > 0 ? 0 : this.currentChoices.length - 1 : (this.currentIndex + delta + this.currentChoices.length) % this.currentChoices.length;
1145
+ this.activate(next);
1146
+ }
1147
+ pickActive() {
1148
+ const choice = this.currentChoices[this.currentIndex];
1149
+ if (choice) this.pick(choice);
1150
+ }
1151
+ pick(choice) {
1152
+ this.inputElement.value = choice.value;
1153
+ this.close();
1154
+ this.onPick(choice);
1155
+ }
1156
+ /** Join the DOM under its host row (idempotent). */
1157
+ attach() {
1158
+ if (this.element.parentElement) return;
1159
+ this.host.after(this.element);
1160
+ }
1161
+ /** Anchor the panel to the field inside its row. */
1162
+ position() {
1163
+ const box = computeInlinePanelBox(
1164
+ this.inputElement.getBoundingClientRect(),
1165
+ this.host.getBoundingClientRect(),
1166
+ window.innerHeight
1167
+ );
1168
+ this.element.style.width = `${box.width}px`;
1169
+ this.element.style.marginLeft = `${box.offsetLeft}px`;
1170
+ this.element.style.maxHeight = `${box.maxHeight}px`;
1171
+ }
1172
+ };
1173
+
1174
+ // src/extensions/frontmatter-preview.ts
1038
1175
  var frontmatterSuggestions = state.Facet.define({ combine: (providers) => providers[0] ?? null });
1039
1176
  var frontmatterReferenceIndex = state.Facet.define({ combine: (providers) => providers[0] ?? null });
1040
1177
  var referenceIndexReload = state.StateEffect.define();
@@ -1043,52 +1180,52 @@ var frontmatterVisibility = state.Facet.define({
1043
1180
  });
1044
1181
  var pendingFocus = /* @__PURE__ */ new WeakMap();
1045
1182
  var widgetCleanup = /* @__PURE__ */ new WeakMap();
1046
- var suggestionId = 0;
1183
+ var helperId = 0;
1047
1184
  function isTagProperty(key) {
1048
1185
  return ["tag", "tags", "category"].includes(key.trim().toLowerCase());
1049
1186
  }
1187
+ function appendFieldHelper(parent, text) {
1188
+ const helper = document.createElement("span");
1189
+ helper.id = `frontmatter-hint-${++helperId}`;
1190
+ helper.className = "sr-only";
1191
+ helper.textContent = text;
1192
+ parent.appendChild(helper);
1193
+ return helper;
1194
+ }
1195
+ function createColumnHeader() {
1196
+ const header = document.createElement("div");
1197
+ header.className = "flex items-center border-b border-border text-[11px] font-semibold uppercase tracking-wide text-muted-foreground";
1198
+ const key = document.createElement("div");
1199
+ key.dataset.frontmatterColumn = "key";
1200
+ key.className = "px-3 py-1.5 w-36 shrink-0 border-r border-border max-[479px]:w-auto max-[479px]:border-r-0 max-[479px]:border-b";
1201
+ key.textContent = "Key";
1202
+ const value = document.createElement("div");
1203
+ value.dataset.frontmatterColumn = "value";
1204
+ value.className = "px-3 py-1.5 flex-1 min-w-0";
1205
+ value.textContent = "Value";
1206
+ const actions = document.createElement("div");
1207
+ actions.className = "w-9 shrink-0";
1208
+ header.append(key, value, actions);
1209
+ return header;
1210
+ }
1211
+ function ensureColumnHeader(box, before) {
1212
+ if (box.querySelector("[data-frontmatter-column]")) return;
1213
+ box.insertBefore(createColumnHeader(), before);
1214
+ }
1050
1215
  function attachSuggestions(input, view, root, kind, key, excluded, commit, valueKind = "tags") {
1051
1216
  const provider = view.state.facet(frontmatterSuggestions);
1052
1217
  if (!provider || !input.parentElement) return;
1053
- const parent = input.parentElement;
1054
- parent.classList.add("relative");
1055
- const list = document.createElement("div");
1056
- list.id = `frontmatter-suggestions-${++suggestionId}`;
1057
- list.setAttribute("role", "listbox");
1058
- list.className = "absolute left-0 top-full z-50 max-h-48 overflow-auto rounded-md border border-border bg-background shadow-md min-w-full text-xs";
1059
- list.hidden = true;
1060
- parent.appendChild(list);
1061
- input.setAttribute("role", "combobox");
1062
- input.setAttribute("aria-autocomplete", "list");
1063
- input.setAttribute("aria-controls", list.id);
1064
- input.setAttribute("aria-expanded", "false");
1218
+ const host = input.closest("[data-frontmatter-row]") ?? input.parentElement;
1219
+ const panel = new SuggestionPanel(input, {
1220
+ label: kind === "key" ? "Suggested properties" : suggestionListLabel(valueKind),
1221
+ onPick: () => commit(),
1222
+ view,
1223
+ host
1224
+ });
1065
1225
  let generation = 0;
1066
- let choices = [];
1067
- let active = -1;
1068
1226
  const close = () => {
1069
1227
  generation++;
1070
- choices = [];
1071
- active = -1;
1072
- list.hidden = true;
1073
- input.setAttribute("aria-expanded", "false");
1074
- input.removeAttribute("aria-activedescendant");
1075
- };
1076
- const select = (choice) => {
1077
- input.value = choice.value;
1078
- close();
1079
- commit();
1080
- };
1081
- const activate = (index) => {
1082
- active = index;
1083
- Array.from(list.children).forEach((option2, i) => {
1084
- option2.setAttribute("aria-selected", String(i === active));
1085
- option2.classList.toggle("bg-muted", i === active);
1086
- });
1087
- const option = list.children[active];
1088
- if (option) {
1089
- input.setAttribute("aria-activedescendant", option.id);
1090
- if (option instanceof HTMLElement) option.scrollIntoView?.({ block: "nearest" });
1091
- }
1228
+ panel.close();
1092
1229
  };
1093
1230
  const refresh = async (showAll = false) => {
1094
1231
  close();
@@ -1111,25 +1248,11 @@ function attachSuggestions(input, view, root, kind, key, excluded, commit, value
1111
1248
  candidates = catalog.tags.filter((tag) => isTagProperty(key) || tag.type?.toLowerCase() === key.trim().toLowerCase()).map((tag) => ({ value: tag.label, label: tag.label, haystack: tag.label.toLowerCase() }));
1112
1249
  }
1113
1250
  const selected = new Set(excluded.map((label) => label.toLowerCase()));
1114
- choices = candidates.filter(
1251
+ const choices = candidates.filter(
1115
1252
  (candidate) => (query === "" || candidate.haystack.includes(query)) && !selected.has(candidate.value.toLowerCase())
1116
- );
1117
- active = -1;
1118
- input.removeAttribute("aria-activedescendant");
1119
- list.replaceChildren();
1120
- choices.forEach((choice, index) => {
1121
- const option = document.createElement("div");
1122
- option.id = `${list.id}-${index}`;
1123
- option.setAttribute("role", "option");
1124
- option.setAttribute("aria-selected", "false");
1125
- option.className = "cursor-pointer px-2 py-1.5 hover:bg-muted";
1126
- option.textContent = choice.label;
1127
- option.addEventListener("mousedown", (event) => event.preventDefault());
1128
- option.addEventListener("click", () => select(choice));
1129
- list.appendChild(option);
1130
- });
1131
- list.hidden = choices.length === 0;
1132
- input.setAttribute("aria-expanded", String(choices.length > 0));
1253
+ ).map((candidate) => ({ value: candidate.value, label: candidate.label }));
1254
+ panel.setChoices(choices);
1255
+ if (choices.length > 0) panel.open();
1133
1256
  } catch {
1134
1257
  if (request === generation) close();
1135
1258
  }
@@ -1144,16 +1267,22 @@ function attachSuggestions(input, view, root, kind, key, excluded, commit, value
1144
1267
  } else if (event.key === "Escape") {
1145
1268
  event.preventDefault();
1146
1269
  close();
1147
- } else if ((event.key === "ArrowDown" || event.key === "ArrowUp") && choices.length) {
1270
+ } else if (event.key === "ArrowDown" || event.key === "ArrowUp") {
1271
+ if (panel.choices.length === 0) return;
1148
1272
  event.preventDefault();
1149
- const direction = event.key === "ArrowDown" ? 1 : -1;
1150
- activate(active < 0 ? direction === 1 ? 0 : choices.length - 1 : (active + direction + choices.length) % choices.length);
1151
- } else if ((event.key === "Enter" || event.key === "Tab") && active >= 0) {
1273
+ panel.moveActive(event.key === "ArrowDown" ? 1 : -1);
1274
+ } else if (event.key === "Enter" || event.key === "Tab") {
1275
+ if (panel.activeIndex < 0) return;
1152
1276
  event.preventDefault();
1153
- select(choices[active]);
1277
+ panel.pickActive();
1154
1278
  }
1155
1279
  });
1156
- widgetCleanup.get(root)?.push(close);
1280
+ widgetCleanup.get(root)?.push(() => panel.destroy());
1281
+ }
1282
+ function suggestionListLabel(valueKind) {
1283
+ if (valueKind === "effort") return "Suggested efforts";
1284
+ if (valueKind === "equipment") return "Suggested equipment";
1285
+ return "Suggested tags";
1157
1286
  }
1158
1287
  function parseTagList(val) {
1159
1288
  if (Array.isArray(val)) {
@@ -1231,10 +1360,11 @@ function isReferenceKey(key, docKind) {
1231
1360
  }
1232
1361
  function createTagDraftRow(view, root, key, onCommit) {
1233
1362
  const row = document.createElement("div");
1234
- row.className = "flex items-center min-h-[34px] border-b border-dashed border-border/60 last:border-b-0 group hover:bg-muted/10 transition-colors max-[479px]:flex-col max-[479px]:items-stretch";
1363
+ 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";
1235
1364
  row.setAttribute("data-frontmatter-tag-draft", "true");
1365
+ row.dataset.frontmatterRow = key;
1236
1366
  const keyCol = document.createElement("div");
1237
- keyCol.className = "flex items-center gap-2 px-3 py-1.5 w-36 shrink-0 border-r border-dashed border-border/60 text-muted-foreground max-[479px]:w-auto max-[479px]:border-r-0 max-[479px]:border-b";
1367
+ 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";
1238
1368
  keyCol.appendChild(createTagIcon());
1239
1369
  const keyLabel = document.createElement("span");
1240
1370
  keyLabel.className = "truncate font-medium text-foreground/90";
@@ -1244,9 +1374,15 @@ function createTagDraftRow(view, root, key, onCommit) {
1244
1374
  const valCol = document.createElement("div");
1245
1375
  valCol.className = "flex items-center flex-wrap gap-1.5 px-3 py-1.5 flex-1 min-w-0";
1246
1376
  const input = document.createElement("input");
1247
- input.className = "flex-1 min-w-[70px] bg-transparent text-xs text-foreground outline-none px-1 py-0.5 placeholder:text-muted-foreground/40";
1377
+ 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";
1248
1378
  input.placeholder = "Add tag\u2026";
1249
- input.setAttribute("aria-label", `Add tag to ${key}`);
1379
+ input.setAttribute("aria-label", `Value for ${key}`);
1380
+ input.dataset.frontmatterField = "value";
1381
+ input.dataset.frontmatterProperty = key;
1382
+ input.setAttribute(
1383
+ "aria-describedby",
1384
+ appendFieldHelper(valCol, "Press Enter to add the value.").id
1385
+ );
1250
1386
  valCol.appendChild(input);
1251
1387
  row.appendChild(valCol);
1252
1388
  let settled = false;
@@ -1325,8 +1461,9 @@ var METRIC_DRAFT_KINDS = {
1325
1461
  };
1326
1462
  function createMetricDraftRow(view, root, referenceProvider, commit) {
1327
1463
  const row = document.createElement("div");
1328
- row.className = "flex items-center min-h-[34px] border-b border-dashed border-border/60 last:border-b-0 group hover:bg-muted/10 transition-colors max-[479px]:flex-col max-[479px]:items-stretch";
1464
+ 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";
1329
1465
  row.setAttribute("data-frontmatter-metric-draft", "true");
1466
+ row.dataset.frontmatterRow = "metric";
1330
1467
  const select = document.createElement("select");
1331
1468
  select.setAttribute("aria-label", "Metric kind");
1332
1469
  select.className = "mx-3 my-1.5 h-7 w-28 shrink-0 rounded-md border border-input bg-background px-1.5 text-[11px] text-muted-foreground outline-none";
@@ -1340,9 +1477,14 @@ function createMetricDraftRow(view, root, referenceProvider, commit) {
1340
1477
  const valCol = document.createElement("div");
1341
1478
  valCol.className = "flex items-center flex-wrap gap-1.5 px-3 py-1.5 flex-1 min-w-0";
1342
1479
  const input = document.createElement("input");
1343
- input.className = "flex-1 min-w-[70px] bg-transparent text-xs text-foreground outline-none px-1 py-0.5 placeholder:text-muted-foreground/40";
1480
+ 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";
1344
1481
  input.placeholder = "Value\u2026";
1345
1482
  input.setAttribute("aria-label", "Metric value");
1483
+ input.dataset.frontmatterField = "value";
1484
+ input.setAttribute(
1485
+ "aria-describedby",
1486
+ appendFieldHelper(valCol, "Press Enter to apply the metric value.").id
1487
+ );
1346
1488
  valCol.appendChild(input);
1347
1489
  row.appendChild(valCol);
1348
1490
  let settled = false;
@@ -1534,6 +1676,7 @@ var DefaultFrontmatterWidget = class extends view.WidgetType {
1534
1676
  const docKind = detectDefinitionKind(meta);
1535
1677
  const referenceProvider = view.state.facet(frontmatterReferenceIndex)?.() ?? null;
1536
1678
  const base = docKind === "effort" ? readBaseAttributes(body) : {};
1679
+ box.appendChild(createColumnHeader());
1537
1680
  if (entries.length === 0) {
1538
1681
  const emptyRow = document.createElement("div");
1539
1682
  emptyRow.className = "px-3 py-2 text-xs text-muted-foreground/60 italic";
@@ -1546,10 +1689,12 @@ var DefaultFrontmatterWidget = class extends view.WidgetType {
1546
1689
  const isReferenceList = (isReferenceKey(key, docKind) || key === "aliases") && Array.isArray(value);
1547
1690
  const nested = value === "" && ownsNestedBlock(body, key);
1548
1691
  const row = document.createElement("div");
1549
- row.className = "flex items-center min-h-[34px] border-b border-dashed border-border/60 last:border-b-0 group hover:bg-muted/10 transition-colors max-[479px]:flex-col max-[479px]:items-stretch";
1692
+ 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";
1693
+ row.dataset.frontmatterRow = key;
1550
1694
  box.appendChild(row);
1551
1695
  const keyCol = document.createElement("div");
1552
- keyCol.className = "flex items-center gap-2 px-3 py-1.5 w-36 shrink-0 border-r border-dashed border-border/60 text-muted-foreground max-[479px]:w-auto max-[479px]:border-r-0 max-[479px]:border-b";
1696
+ 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";
1697
+ row.appendChild(keyCol);
1553
1698
  keyCol.appendChild(isTags || isReferenceList ? createTagIcon() : createListIcon());
1554
1699
  if (this.readOnly || nested) {
1555
1700
  const keyLabel = document.createElement("span");
@@ -1561,7 +1706,16 @@ var DefaultFrontmatterWidget = class extends view.WidgetType {
1561
1706
  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";
1562
1707
  keyInput.value = key;
1563
1708
  keyInput.spellcheck = false;
1564
- keyInput.setAttribute("aria-label", `Property name ${key}`);
1709
+ keyInput.setAttribute("aria-label", `Property key: ${key}`);
1710
+ keyInput.dataset.frontmatterField = "key";
1711
+ keyInput.dataset.frontmatterProperty = key;
1712
+ keyInput.setAttribute(
1713
+ "aria-describedby",
1714
+ appendFieldHelper(
1715
+ keyCol,
1716
+ "Property name. Press Enter to apply. Arrow keys browse suggested property names."
1717
+ ).id
1718
+ );
1565
1719
  let currentKey = key;
1566
1720
  const commitKey = () => {
1567
1721
  const nextKey = keyInput.value.trim();
@@ -1602,9 +1756,9 @@ var DefaultFrontmatterWidget = class extends view.WidgetType {
1602
1756
  pendingFocus.delete(view);
1603
1757
  }
1604
1758
  }
1605
- row.appendChild(keyCol);
1606
1759
  const valCol = document.createElement("div");
1607
1760
  valCol.className = "flex items-center flex-wrap gap-1.5 px-3 py-1.5 flex-1 min-w-0";
1761
+ row.appendChild(valCol);
1608
1762
  const commitList = (next) => {
1609
1763
  meta[key] = next;
1610
1764
  patchPropertyInView(view, this.sectionId, [key], next.length > 0 ? next : "");
@@ -1650,9 +1804,18 @@ var DefaultFrontmatterWidget = class extends view.WidgetType {
1650
1804
  }
1651
1805
  if (!this.readOnly) {
1652
1806
  const chipInput = document.createElement("input");
1653
- chipInput.className = "flex-1 min-w-[70px] bg-transparent text-xs text-foreground outline-none px-1 py-0.5 placeholder:text-muted-foreground/40";
1807
+ 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";
1654
1808
  chipInput.placeholder = chips.length === 0 ? isReferenceList ? `Add ${valueKind === "effort" ? "effort" : "equipment"}\u2026` : "Add tags\u2026" : isTags ? "Add tag\u2026" : "Add\u2026";
1655
- chipInput.setAttribute("aria-label", isTags ? `Add tag to ${key}` : `Add to ${key}`);
1809
+ chipInput.setAttribute("aria-label", `Value for ${key}`);
1810
+ chipInput.dataset.frontmatterField = "value";
1811
+ chipInput.dataset.frontmatterProperty = key;
1812
+ chipInput.setAttribute(
1813
+ "aria-describedby",
1814
+ appendFieldHelper(
1815
+ valCol,
1816
+ "Press Enter or comma to add a value. Arrow keys browse the suggestions."
1817
+ ).id
1818
+ );
1656
1819
  const commitNewChip = () => {
1657
1820
  const val = chipInput.value.trim().replace(/^,|,$/g, "");
1658
1821
  if (val && !chips.some((chip) => chip.toLowerCase() === val.toLowerCase())) {
@@ -1725,10 +1888,16 @@ var DefaultFrontmatterWidget = class extends view.WidgetType {
1725
1888
  valCol.appendChild(valSpan);
1726
1889
  } else {
1727
1890
  const valInput = document.createElement("input");
1728
- valInput.className = "w-full bg-transparent text-xs text-foreground outline-none py-0.5 hover:bg-muted/20 focus:bg-background focus:ring-1 focus:ring-ring rounded px-1 -mx-1 transition-colors";
1891
+ 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";
1729
1892
  valInput.value = strVal;
1730
1893
  valInput.spellcheck = false;
1731
1894
  valInput.setAttribute("aria-label", `Value for ${key}`);
1895
+ valInput.dataset.frontmatterField = "value";
1896
+ valInput.dataset.frontmatterProperty = key;
1897
+ valInput.setAttribute(
1898
+ "aria-describedby",
1899
+ appendFieldHelper(valCol, "Property value. Press Enter to apply.").id
1900
+ );
1732
1901
  let committedVal = strVal;
1733
1902
  const commitVal = () => {
1734
1903
  const cur = valInput.value.trim();
@@ -1779,7 +1948,7 @@ var DefaultFrontmatterWidget = class extends view.WidgetType {
1779
1948
  openLink.appendChild(createExternalIcon());
1780
1949
  pill.appendChild(openLink);
1781
1950
  valCol.insertBefore(pill, valInput);
1782
- valInput.className = "flex-1 min-w-[70px] bg-transparent text-xs text-foreground outline-none py-0.5 hover:bg-muted/20 focus:bg-background focus:ring-1 focus:ring-ring rounded px-1 -mx-1 transition-colors";
1951
+ 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";
1783
1952
  }
1784
1953
  const diag = renderEffortValueDiagnostics(
1785
1954
  referenceProvider,
@@ -1804,14 +1973,13 @@ var DefaultFrontmatterWidget = class extends view.WidgetType {
1804
1973
  valCol.appendChild(openLink);
1805
1974
  }
1806
1975
  }
1807
- row.appendChild(valCol);
1808
1976
  if (!this.readOnly && !nested) {
1809
1977
  const actionsCol = document.createElement("div");
1810
1978
  actionsCol.className = "flex items-center pr-2";
1811
1979
  const delBtn = document.createElement("button");
1812
1980
  delBtn.type = "button";
1813
1981
  delBtn.setAttribute("aria-label", `Remove property ${key}`);
1814
- delBtn.className = "opacity-0 group-hover:opacity-100 text-muted-foreground/40 hover:text-destructive p-1 rounded transition-opacity cursor-pointer";
1982
+ 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";
1815
1983
  delBtn.appendChild(createCloseIcon("md"));
1816
1984
  delBtn.addEventListener("click", (e) => {
1817
1985
  e.preventDefault();
@@ -1891,7 +2059,9 @@ var DefaultFrontmatterWidget = class extends view.WidgetType {
1891
2059
  e.preventDefault();
1892
2060
  e.stopPropagation();
1893
2061
  if (existingTagKey) {
1894
- const input = Array.from(root.querySelectorAll("input")).find((element) => element.getAttribute("aria-label") === `Add tag to ${existingTagKey}`);
2062
+ const input = Array.from(root.querySelectorAll(
2063
+ '[data-frontmatter-field="value"]'
2064
+ )).find((element) => element.dataset.frontmatterProperty === existingTagKey);
1895
2065
  input?.focus();
1896
2066
  return;
1897
2067
  }
@@ -2067,6 +2237,7 @@ var EmptyFrontmatterWidget = class extends view.WidgetType {
2067
2237
  patchPropertyInView(view, "new", ["tags"], [val]);
2068
2238
  });
2069
2239
  root.appendChild(draftRow);
2240
+ ensureColumnHeader(root, draftRow);
2070
2241
  draftRow.querySelector("input")?.focus();
2071
2242
  view.requestMeasure();
2072
2243
  });