@ai-matrx/design-system 0.20.2 → 0.20.6

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.
@@ -2825,7 +2825,7 @@ var useContentTransferCapabilities = () => React14.useContext(CapabilityContext)
2825
2825
  var SurfaceContext = React14.createContext(null);
2826
2826
  function MenuPresentation({ label, hint, icon: Icon3 }) {
2827
2827
  return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(import_jsx_runtime24.Fragment, { children: [
2828
- Icon3 ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(Icon3, { className: "matrx-alchemy-menu-icon" }) : null,
2828
+ Icon3 ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(Icon3, { className: "matrx-alchemy-menu-icon" }) : /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(PaletteGlyph, { file: true }),
2829
2829
  /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("span", { children: [
2830
2830
  label,
2831
2831
  hint ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("small", { className: "matrx-alchemy-menu-hint", children: hint }) : null
@@ -2847,8 +2847,11 @@ function DestinationPresentation({ action }) {
2847
2847
  };
2848
2848
  const path2 = paths[action.id.replace(/^matrx:/, "")];
2849
2849
  return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(import_jsx_runtime24.Fragment, { children: [
2850
- !action.icon ? path2 ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("svg", { className: "matrx-alchemy-palette-glyph", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("path", { d: path2 }) }) : /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(PaletteGlyph, { file: true }) : null,
2851
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(MenuPresentation, { ...action })
2850
+ action.icon ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(action.icon, { className: "matrx-alchemy-menu-icon" }) : path2 ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("svg", { className: "matrx-alchemy-palette-glyph", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("path", { d: path2 }) }) : /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(PaletteGlyph, { file: true }),
2851
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("span", { children: [
2852
+ action.label,
2853
+ action.hint ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("small", { className: "matrx-alchemy-menu-hint", children: action.hint }) : null
2854
+ ] })
2852
2855
  ] });
2853
2856
  }
2854
2857
  function ReceiptLink({ target }) {
@@ -2858,6 +2861,11 @@ function ReceiptLink({ target }) {
2858
2861
  ] }) : null;
2859
2862
  }
2860
2863
  function PaletteGlyph({ format, ai = false, file = false, kind }) {
2864
+ if (kind === "link") return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("svg", { className: "matrx-alchemy-palette-glyph", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("path", { d: "M10 13a5 5 0 0 0 7 0l3-3a5 5 0 0 0-7-7l-2 2M14 11a5 5 0 0 0-7 0l-3 3a5 5 0 0 0 7 7l2-2" }) });
2865
+ if (kind === "surface") return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("svg", { className: "matrx-alchemy-palette-glyph", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [
2866
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("rect", { x: "3", y: "3", width: "18", height: "18", rx: "2" }),
2867
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("path", { d: "M3 9h18M9 9v12" })
2868
+ ] });
2861
2869
  if (kind === "ai-copy") return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("svg", { className: "matrx-alchemy-palette-glyph", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [
2862
2870
  /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("rect", { x: "5", y: "5", width: "11", height: "14", rx: "2" }),
2863
2871
  /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("path", { d: "M9 3h6a2 2 0 0 1 2 2v10" }),
@@ -3338,7 +3346,7 @@ function ContentTransferMenuSession({ references = [], referenceActions = [], so
3338
3346
  const presentation = options.action;
3339
3347
  const Icon3 = presentation?.icon;
3340
3348
  const fallbackLabel = options.file && !/^download\b/i.test(label2) ? `Download ${label2}` : label2;
3341
- return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("button", { type: "button", className: "matrx-alchemy-palette-tile", "aria-label": presentation?.ariaLabel ?? (presentation ? fallbackLabel : options.ariaLabel ?? `${resolvedMode === "copy" ? "Copy" : "Download"} ${label2}`), onClick: () => {
3349
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("button", { type: "button", className: "matrx-alchemy-palette-tile", title: id === "table-data" ? "JSON using column display names" : id === "table-schema" ? "Table schema and rows" : label2, "aria-label": presentation?.ariaLabel ?? (presentation ? fallbackLabel : options.ariaLabel ?? `${resolvedMode === "copy" ? "Copy" : "Download"} ${label2}`), onClick: () => {
3342
3350
  closePalette();
3343
3351
  onClick();
3344
3352
  }, children: [
@@ -3400,7 +3408,7 @@ function ContentTransferMenuSession({ references = [], referenceActions = [], so
3400
3408
  /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(PaletteGlyph, { kind: "ai-copy" }),
3401
3409
  /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("span", { children: [
3402
3410
  "Copy with AI context",
3403
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("small", { className: "matrx-alchemy-menu-hint", children: "Content, source and capture details" })
3411
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("small", { className: "matrx-alchemy-menu-hint", children: "Include source context" })
3404
3412
  ] })
3405
3413
  ] }) : null, items: quickVariants("ai"), onRun: (variant) => {
3406
3414
  closePalette();
@@ -3413,32 +3421,44 @@ function ContentTransferMenuSession({ references = [], referenceActions = [], so
3413
3421
  /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(PaletteGlyph, { kind: "sliders" }),
3414
3422
  /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("span", { children: [
3415
3423
  "Prepare for AI",
3416
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("small", { className: "matrx-alchemy-menu-hint", children: "Review and edit before copying" })
3424
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("small", { className: "matrx-alchemy-menu-hint", children: "Trim, edit or summarize" })
3417
3425
  ] })
3418
3426
  ] }) })
3419
3427
  ] }) : null,
3420
3428
  prepareAvailable && (nearestWorkspaceKey || variants.length) ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(PaletteRows, { title: "Other sources", items: [], extra: /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(import_jsx_runtime24.Fragment, { children: [
3421
- nearestWorkspaceKey && nearestSurface ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("button", { type: "button", className: "matrx-alchemy-palette-row", onClick: () => {
3429
+ nearestWorkspaceKey && nearestSurface ? /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("button", { type: "button", className: "matrx-alchemy-palette-row", onClick: () => {
3422
3430
  closePalette();
3423
3431
  void openWorkspace(nearestWorkspaceKey, { source: nearestSurface, label: nearestSurface.surfaceName });
3424
- }, children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { children: "Prepare this surface" }) }) : null,
3432
+ }, children: [
3433
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(PaletteGlyph, { kind: "surface" }),
3434
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { children: "Prepare this page" })
3435
+ ] }) : null,
3425
3436
  variants.map((variant) => /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("button", { type: "button", className: "matrx-alchemy-palette-row", "aria-label": variant.ariaLabel, onClick: () => {
3426
3437
  closePalette();
3427
3438
  void openWorkspace(variantWorkspaceKeys.get(variant.id), variant);
3428
3439
  }, children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(MenuPresentation, { ...variant, label: `Prepare ${variant.label}` }) }, variant.id))
3429
3440
  ] }) }) : null,
3430
3441
  references.length || referenceActions.length ? /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("section", { className: "matrx-alchemy-palette-section", children: [
3431
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("h3", { children: "Live references" }),
3432
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("p", { className: "matrx-alchemy-menu-hint", children: "Pointers to saved data, independent of filters and edits." }),
3433
- references.map((reference) => /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("button", { type: "button", className: "matrx-alchemy-palette-row", onClick: () => void copyReference(reference), children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { children: reference.label }) }, reference.id)),
3434
- referenceActions.map((action) => /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("button", { type: "button", className: "matrx-alchemy-palette-row", onClick: () => void openReference(action), children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { children: action.label }) }, action.id))
3442
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("h3", { children: "References" }),
3443
+ referenceActions.map((action) => /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("button", { type: "button", className: "matrx-alchemy-palette-row", onClick: () => void openReference(action), children: [
3444
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(PaletteGlyph, { kind: "link" }),
3445
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { children: action.label }),
3446
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("svg", { className: "matrx-alchemy-disclosure", viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: "1.5", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("path", { d: "m6 4 4 4-4 4" }) })
3447
+ ] }, action.id)),
3448
+ references.map((reference) => /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("button", { type: "button", className: "matrx-alchemy-palette-row", title: "Copies a reference to saved data", onClick: () => void copyReference(reference), children: [
3449
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(PaletteGlyph, { kind: "link" }),
3450
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { children: reference.label })
3451
+ ] }, reference.id))
3435
3452
  ] }) : null,
3436
3453
  ["save", "chat", "email", "other"].map((group) => {
3437
3454
  const entries = ordinaryActions.filter((action) => (action.group ?? "other") === group);
3438
3455
  return entries.length ? /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("details", { className: "matrx-alchemy-palette-destinations", children: [
3439
3456
  /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("summary", { children: [
3440
3457
  group === "save" ? "Save to Matrx" : group === "chat" ? "Continue with AI" : group === "email" ? "Email to me" : "Actions",
3441
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { children: entries.length })
3458
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("span", { className: "matrx-alchemy-disclosure-meta", children: [
3459
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { children: entries.length }),
3460
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("svg", { className: "matrx-alchemy-disclosure", viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: "1.5", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("path", { d: "m6 4 4 4-4 4" }) })
3461
+ ] })
3442
3462
  ] }),
3443
3463
  /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { children: entries.map((action) => /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("button", { type: "button", className: "matrx-alchemy-palette-row", "aria-label": action.ariaLabel, onClick: () => {
3444
3464
  closePalette();
@@ -6850,6 +6870,17 @@ function tokenFromColumnName(name) {
6850
6870
  return name.endsWith("_id") ? name.slice(0, -3) : null;
6851
6871
  }
6852
6872
 
6873
+ // src/data-table/display-value.ts
6874
+ function formatTableDisplayValue(value) {
6875
+ if (typeof value === "number") return value.toLocaleString(void 0, { maximumFractionDigits: 12 });
6876
+ if (value instanceof Date) return value.toLocaleString();
6877
+ if (typeof value === "string" && /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}/.test(value) && !Number.isNaN(Date.parse(value))) {
6878
+ return new Date(value).toLocaleString();
6879
+ }
6880
+ if (typeof value === "boolean") return value ? "Yes" : "No";
6881
+ return stringifyCellValue(value) || "\u2014";
6882
+ }
6883
+
6853
6884
  // src/data-table/DataRowInspector.tsx
6854
6885
  var import_jsx_runtime33 = require("react/jsx-runtime");
6855
6886
  var noFieldToken = () => null;
@@ -6922,7 +6953,7 @@ function DataRowInspector({
6922
6953
  key
6923
6954
  );
6924
6955
  }
6925
- const formatted = formatValue(value);
6956
+ const formatted = value !== null && typeof value === "object" && !(value instanceof Date) ? formatValue(value) : formatTableDisplayValue(value);
6926
6957
  const multiline = formatted.includes("\n") || formatted.length > 80;
6927
6958
  return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(
6928
6959
  "div",
@@ -7051,6 +7082,22 @@ function TabButton({
7051
7082
  );
7052
7083
  }
7053
7084
 
7085
+ // src/data-table/row-title.ts
7086
+ function defaultRowTitle(row, columns) {
7087
+ const values = columns.map((column) => ({
7088
+ id: columnId(column),
7089
+ value: getCellValue(row, column)
7090
+ })).filter(
7091
+ (entry) => typeof entry.value === "string" && entry.value.trim().length > 0
7092
+ );
7093
+ const named = values.find(
7094
+ ({ id, value: value2 }) => inferTableSearchRole(id) === "name" && !isUuidValue(value2)
7095
+ );
7096
+ const identity = values.find(({ id }) => id === "id" || id === "uuid") ?? values.find(({ id }) => inferTableSearchRole(id) === "id");
7097
+ const value = (named ?? identity ?? values[0])?.value.trim();
7098
+ return value ? isUuidValue(value) ? value.slice(0, 8) : value : "Details";
7099
+ }
7100
+
7054
7101
  // src/data-table/DirtySavePill.tsx
7055
7102
  var import_jsx_runtime35 = require("react/jsx-runtime");
7056
7103
  function DirtySavePill({
@@ -8544,25 +8591,6 @@ function MatrxDataTableCore({
8544
8591
  ...appendScrollPolicy?.mode === "scroll" && appendScrollPolicy.intentTimeoutMs !== void 0 ? { intentTimeoutMs: appendScrollPolicy.intentTimeoutMs } : {}
8545
8592
  });
8546
8593
  const rootRef = (0, import_react17.useRef)(null);
8547
- const setDiagnosticBoundaryRef = (0, import_react17.useCallback)((node) => {
8548
- rootRef.current = node;
8549
- if (!node) return;
8550
- node.style.setProperty("outline", "2px solid #2563eb", "important");
8551
- node.style.setProperty("outline-offset", "-2px", "important");
8552
- node.style.setProperty("isolation", "isolate", "important");
8553
- const position = window.getComputedStyle(node).position;
8554
- if (position === "" || position === "static") {
8555
- node.style.setProperty("position", "relative", "important");
8556
- }
8557
- const boundary = node.querySelector(":scope > [data-matrx-table-boundary]");
8558
- if (!boundary) return;
8559
- boundary.style.setProperty("position", "absolute", "important");
8560
- boundary.style.setProperty("inset", "0", "important");
8561
- boundary.style.setProperty("pointer-events", "none", "important");
8562
- boundary.style.setProperty("z-index", "2147483647", "important");
8563
- boundary.style.setProperty("box-sizing", "border-box", "important");
8564
- boundary.style.setProperty("border", "2px solid #2563eb", "important");
8565
- }, []);
8566
8594
  useClippedContentGuard(rootRef, {
8567
8595
  label: typeof copy === "object" && copy.listLabel ? `Table: ${copy.listLabel}` : "MatrxDataTable"
8568
8596
  });
@@ -9603,7 +9631,7 @@ function MatrxDataTableCore({
9603
9631
  /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)(
9604
9632
  "div",
9605
9633
  {
9606
- ref: setDiagnosticBoundaryRef,
9634
+ ref: rootRef,
9607
9635
  "data-matrx-table": true,
9608
9636
  "data-matrx-table-density": resolvedDensity,
9609
9637
  "data-url-state-table": urlState?.id,
@@ -9612,7 +9640,6 @@ function MatrxDataTableCore({
9612
9640
  className
9613
9641
  ),
9614
9642
  children: [
9615
- /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("span", { "aria-hidden": "true", "data-matrx-table-boundary": true }),
9616
9643
  notice,
9617
9644
  savedViewError ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("p", { role: "alert", className: "shrink-0 text-xs text-destructive", children: savedViewError }) : null,
9618
9645
  toolbarActionError ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("p", { role: "alert", className: "shrink-0 text-xs text-destructive", children: toolbarActionError }) : null,
@@ -10691,7 +10718,7 @@ function MatrxDataTableCore({
10691
10718
  {
10692
10719
  title: resolveStringTitle(
10693
10720
  detail?.title?.(selectedRow),
10694
- defaultRowTitle(selectedRow, visibleColumns)
10721
+ defaultRowTitle(selectedRow, columns)
10695
10722
  ),
10696
10723
  titleNode: detail?.title?.(selectedRow),
10697
10724
  description: detail?.description?.(selectedRow),
@@ -10758,7 +10785,7 @@ function MatrxDataTableCore({
10758
10785
  {
10759
10786
  isOpen: true,
10760
10787
  onClose: closeWindow,
10761
- title: windowConfig?.title?.(windowRow) ?? defaultRowTitle(windowRow, visibleColumns),
10788
+ title: windowConfig?.title?.(windowRow) ?? defaultRowTitle(windowRow, columns),
10762
10789
  row: windowRow,
10763
10790
  width: windowConfig?.width,
10764
10791
  height: windowConfig?.height,
@@ -11090,7 +11117,8 @@ function normalizeColumnWidthsForColumns(widths, columns) {
11090
11117
  }
11091
11118
  function columnWidthVar(width, resizedWidth, id, resizable) {
11092
11119
  if (width === void 0 && resizedWidth === void 0 && !resizable) return void 0;
11093
- const fallback = resizedWidth === void 0 ? width === void 0 ? "auto" : typeof width === "number" ? `${width}px` : width : `${resizedWidth}px`;
11120
+ const inferredWidth = resizable && inferTableSearchRole(id) === "name" ? "12rem" : "auto";
11121
+ const fallback = resizedWidth === void 0 ? width === void 0 ? inferredWidth : typeof width === "number" ? `${width}px` : width : `${resizedWidth}px`;
11094
11122
  return {
11095
11123
  "--matrx-col-w": resizable ? `var(${columnResizeCssVar(id)}, ${fallback})` : fallback
11096
11124
  };
@@ -11140,19 +11168,12 @@ function renderCell(row, col, index) {
11140
11168
  if (typeof raw === "boolean") {
11141
11169
  return /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("span", { className: raw ? "rounded bg-emerald-500/10 px-1.5 py-0.5 text-xs text-emerald-600" : "rounded bg-slate-500/10 px-1.5 py-0.5 text-xs text-muted-foreground", children: raw ? "Yes" : "No" });
11142
11170
  }
11143
- const formatted = typeof raw === "number" ? raw.toLocaleString(void 0, { maximumFractionDigits: 12 }) : raw instanceof Date ? raw.toLocaleString() : typeof raw === "string" && /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}/.test(raw) && !Number.isNaN(Date.parse(raw)) ? new Date(raw).toLocaleString() : stringifyCellValue(raw) || "\u2014";
11171
+ const formatted = formatTableDisplayValue(raw);
11144
11172
  return /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)(Tooltip, { children: [
11145
11173
  /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("span", { className: "block text-sm text-foreground", style: { display: "-webkit-box", WebkitLineClamp: 2, WebkitBoxOrient: "vertical", overflow: "hidden", overflowWrap: "anywhere" }, children: formatted }) }),
11146
11174
  /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(TooltipContent, { className: "max-w-md whitespace-pre-wrap break-words max-h-80 overflow-auto", children: typeof raw === "object" && raw !== null ? JSON.stringify(raw, null, 2) : formatted })
11147
11175
  ] });
11148
11176
  }
11149
- function defaultRowTitle(row, columns) {
11150
- for (const col of columns) {
11151
- const v = getCellValue(row, col);
11152
- if (typeof v === "string" && v.trim()) return v;
11153
- }
11154
- return "Details";
11155
- }
11156
11177
  function resolveStringTitle(title, fallback) {
11157
11178
  if (typeof title === "string" && title.trim()) return title;
11158
11179
  if (typeof title === "number") return String(title);