@almadar/ui 5.133.0 → 5.135.0

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.
@@ -1645,6 +1645,87 @@ var init_Dialog = __esm({
1645
1645
  }
1646
1646
  });
1647
1647
 
1648
+ // lib/format.ts
1649
+ function humanizeFieldName(name) {
1650
+ return name.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1 $2").replace(/[_-]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase()).replace(/\b([A-Z])([A-Z]+)\b/g, (_, first, rest) => first + rest.toLowerCase()).trim();
1651
+ }
1652
+ function humanizeEnumValue(value) {
1653
+ return /^[a-z0-9]+(?:[_-][a-z0-9]+)+$/.test(value) ? humanizeFieldName(value) : value;
1654
+ }
1655
+ function formatDate(value) {
1656
+ if (!value) return "";
1657
+ const d = new Date(String(value));
1658
+ if (isNaN(d.getTime())) return String(value);
1659
+ return d.toLocaleDateString(void 0, { year: "numeric", month: "short", day: "numeric" });
1660
+ }
1661
+ function formatTime(value) {
1662
+ if (!value) return "";
1663
+ const d = new Date(String(value));
1664
+ if (isNaN(d.getTime())) return String(value);
1665
+ return d.toLocaleTimeString(void 0, { hour: "numeric", minute: "2-digit" });
1666
+ }
1667
+ function formatDateTime(value) {
1668
+ if (!value) return "";
1669
+ const d = new Date(String(value));
1670
+ if (isNaN(d.getTime())) return String(value);
1671
+ return `${formatDate(value)} ${formatTime(value)}`;
1672
+ }
1673
+ function asYesNo(value) {
1674
+ return value === false || value === 0 || String(value) === "false" ? "No" : "Yes";
1675
+ }
1676
+ function formatValue(value, format) {
1677
+ if (value === void 0 || value === null) return "";
1678
+ if (typeof value === "boolean") return asYesNo(value);
1679
+ switch (format) {
1680
+ case "date":
1681
+ return formatDate(value);
1682
+ case "time":
1683
+ return formatTime(value);
1684
+ case "datetime":
1685
+ return formatDateTime(value);
1686
+ case "currency":
1687
+ return typeof value === "number" ? `$${value.toFixed(2)}` : String(value);
1688
+ case "number":
1689
+ return typeof value === "number" ? value.toLocaleString() : String(value);
1690
+ case "percent":
1691
+ return typeof value === "number" ? `${Math.round(value)}%` : String(value);
1692
+ case "boolean":
1693
+ return asYesNo(value);
1694
+ default:
1695
+ return String(value);
1696
+ }
1697
+ }
1698
+ function compareCellValues(a, b) {
1699
+ const aEmpty = a === null || a === void 0 || a === "";
1700
+ const bEmpty = b === null || b === void 0 || b === "";
1701
+ if (aEmpty || bEmpty) return aEmpty && bEmpty ? 0 : aEmpty ? 1 : -1;
1702
+ if (typeof a === "number" && typeof b === "number") return a - b;
1703
+ if (typeof a === "boolean" && typeof b === "boolean") return Number(a) - Number(b);
1704
+ const aNum = Number(a);
1705
+ const bNum = Number(b);
1706
+ if (Number.isFinite(aNum) && Number.isFinite(bNum)) return aNum - bNum;
1707
+ const aTime = dateLikeTime(a);
1708
+ const bTime = dateLikeTime(b);
1709
+ if (aTime !== null && bTime !== null) return aTime - bTime;
1710
+ return String(a).localeCompare(String(b));
1711
+ }
1712
+ function dateLikeTime(value) {
1713
+ if (value instanceof Date) return value.getTime();
1714
+ const text = String(value);
1715
+ if (!/\d/.test(text) || !/[-/:T]/.test(text)) return null;
1716
+ const time = Date.parse(text);
1717
+ return Number.isNaN(time) ? null : time;
1718
+ }
1719
+ function sortRows(rows, field, direction = "asc") {
1720
+ if (!field) return rows;
1721
+ const dir = direction === "desc" ? -1 : 1;
1722
+ return [...rows].sort((a, b) => dir * compareCellValues(a?.[field], b?.[field]));
1723
+ }
1724
+ var init_format = __esm({
1725
+ "lib/format.ts"() {
1726
+ }
1727
+ });
1728
+
1648
1729
  // components/core/atoms/Typography.tsx
1649
1730
  var Typography_exports = {};
1650
1731
  __export(Typography_exports, {
@@ -1654,6 +1735,7 @@ var variantStyles2, colorStyles, weightStyles, defaultElements, typographySizeSt
1654
1735
  var init_Typography = __esm({
1655
1736
  "components/core/atoms/Typography.tsx"() {
1656
1737
  init_cn();
1738
+ init_format();
1657
1739
  variantStyles2 = {
1658
1740
  h1: "text-4xl font-bold tracking-tight text-foreground",
1659
1741
  h2: "text-3xl font-bold tracking-tight text-foreground",
@@ -1741,11 +1823,16 @@ var init_Typography = __esm({
1741
1823
  id,
1742
1824
  className,
1743
1825
  style,
1826
+ format,
1744
1827
  content,
1745
1828
  children
1746
1829
  }) => {
1747
1830
  const variant = variantProp ?? (level ? `h${level}` : "body1");
1748
1831
  const Component = as || defaultElements[variant];
1832
+ let body = children ?? content;
1833
+ if (format !== void 0 && format !== "none" && (typeof body === "string" || typeof body === "number" || body instanceof Date)) {
1834
+ body = formatValue(body, format);
1835
+ }
1749
1836
  return React82__default.createElement(
1750
1837
  Component,
1751
1838
  {
@@ -1762,7 +1849,7 @@ var init_Typography = __esm({
1762
1849
  ),
1763
1850
  style
1764
1851
  },
1765
- children ?? content
1852
+ body
1766
1853
  );
1767
1854
  };
1768
1855
  Typography.displayName = "Typography";
@@ -7814,7 +7901,7 @@ var init_ControlButton = __esm({
7814
7901
  ControlButton.displayName = "ControlButton";
7815
7902
  }
7816
7903
  });
7817
- function formatTime(seconds, format) {
7904
+ function formatTime2(seconds, format) {
7818
7905
  const clamped = Math.max(0, Math.floor(seconds));
7819
7906
  if (format === "ss") {
7820
7907
  return `${clamped}s`;
@@ -7851,7 +7938,7 @@ function TimerDisplay({
7851
7938
  ),
7852
7939
  children: [
7853
7940
  iconAsset && /* @__PURE__ */ jsx(GameIcon, { assetUrl: iconAsset, icon: "image", size: 16, className: "w-4 h-4 object-contain flex-shrink-0" }),
7854
- formatTime(seconds, format)
7941
+ formatTime2(seconds, format)
7855
7942
  ]
7856
7943
  }
7857
7944
  );
@@ -9066,6 +9153,15 @@ function createWebPainter(ctx, onAssetLoad) {
9066
9153
  ctx.lineWidth = lineWidth;
9067
9154
  ctx.stroke();
9068
9155
  },
9156
+ fillPath(d, color) {
9157
+ ctx.fillStyle = color;
9158
+ ctx.fill(new Path2D(d));
9159
+ },
9160
+ strokePath(d, color, lineWidth = 1) {
9161
+ ctx.strokeStyle = color;
9162
+ ctx.lineWidth = lineWidth;
9163
+ ctx.stroke(new Path2D(d));
9164
+ },
9069
9165
  text(str, x, y, style) {
9070
9166
  if (style.font) ctx.font = style.font;
9071
9167
  ctx.fillStyle = style.color;
@@ -9241,6 +9337,16 @@ var init_DrawShape = __esm({
9241
9337
  if (node.stroke) painter.strokePoly(pts, node.stroke, node.strokeWidth ?? 1, true);
9242
9338
  break;
9243
9339
  }
9340
+ case "path": {
9341
+ if (!node.d) break;
9342
+ const base = dctx.projector.project(node.position);
9343
+ const tw = dctx.projector.tileWidth;
9344
+ painter.translate(base.x, base.y);
9345
+ painter.scale(tw, tw);
9346
+ if (node.fill) painter.fillPath(node.d, node.fill);
9347
+ if (node.stroke) painter.strokePath(node.d, node.stroke, (node.strokeWidth ?? 1) / tw);
9348
+ break;
9349
+ }
9244
9350
  }
9245
9351
  painter.restore();
9246
9352
  };
@@ -9332,6 +9438,19 @@ function paintDrawable(painter, node, dctx) {
9332
9438
  case "draw-text":
9333
9439
  paintText(painter, node, dctx);
9334
9440
  break;
9441
+ case "draw-group": {
9442
+ if (!isValidScenePos(node.position)) break;
9443
+ if (!Array.isArray(node.items)) break;
9444
+ const p = dctx.projector.project(node.position);
9445
+ painter.save();
9446
+ painter.translate(p.x, p.y);
9447
+ if (node.scale !== void 0) painter.scale(node.scale, node.scale);
9448
+ if (node.rotate !== void 0) painter.rotate(node.rotate);
9449
+ if (node.opacity !== void 0 && node.opacity !== 1) painter.setAlpha(node.opacity);
9450
+ for (const item of node.items) paintDrawable(painter, item, dctx);
9451
+ painter.restore();
9452
+ break;
9453
+ }
9335
9454
  case "draw-sprite-layer":
9336
9455
  paintSpriteLayer(painter, node, dctx);
9337
9456
  break;
@@ -9345,6 +9464,7 @@ function paintDrawable(painter, node, dctx) {
9345
9464
  }
9346
9465
  var init_paintDispatch = __esm({
9347
9466
  "lib/drawable/paintDispatch.ts"() {
9467
+ init_contract();
9348
9468
  init_DrawSprite();
9349
9469
  init_DrawShape();
9350
9470
  init_DrawText();
@@ -9362,6 +9482,7 @@ function collectDrawnItems(nodes) {
9362
9482
  case "draw-sprite":
9363
9483
  case "draw-shape":
9364
9484
  case "draw-text":
9485
+ case "draw-group":
9365
9486
  if (isValidScenePos(n.position)) out.push({ pos: n.position, id: n.id });
9366
9487
  break;
9367
9488
  case "draw-sprite-layer":
@@ -18979,9 +19100,6 @@ function normalizeFields(fields) {
18979
19100
  if (!fields) return [];
18980
19101
  return fields.map((f3) => typeof f3 === "string" ? f3 : f3.key ?? f3.name ?? "");
18981
19102
  }
18982
- function fieldLabel(key) {
18983
- return key.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/[_-]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
18984
- }
18985
19103
  function asBooleanValue(value) {
18986
19104
  if (typeof value === "boolean") return value;
18987
19105
  if (value === "true") return true;
@@ -18992,12 +19110,6 @@ function isDateField(key) {
18992
19110
  const lower = key.toLowerCase();
18993
19111
  return lower.includes("date") || lower.includes("time") || lower.endsWith("at") || lower.endsWith("_at");
18994
19112
  }
18995
- function formatDate(value) {
18996
- if (!value) return "";
18997
- const d = new Date(String(value));
18998
- if (isNaN(d.getTime())) return String(value);
18999
- return d.toLocaleDateString(void 0, { year: "numeric", month: "short", day: "numeric" });
19000
- }
19001
19113
  function statusVariant(value) {
19002
19114
  const v = value.toLowerCase();
19003
19115
  if (["active", "completed", "done", "approved", "published", "resolved", "open"].includes(v)) return "success";
@@ -19006,11 +19118,12 @@ function statusVariant(value) {
19006
19118
  if (["new", "created", "scheduled", "queued"].includes(v)) return "info";
19007
19119
  return "default";
19008
19120
  }
19009
- var STATUS_FIELDS, gapStyles3, alignStyles2, CardGrid;
19121
+ var fieldLabel, STATUS_FIELDS, gapStyles3, alignStyles2, CardGrid;
19010
19122
  var init_CardGrid = __esm({
19011
19123
  "components/core/organisms/CardGrid.tsx"() {
19012
19124
  "use client";
19013
19125
  init_cn();
19126
+ init_format();
19014
19127
  init_getNestedValue();
19015
19128
  init_useEventBus();
19016
19129
  init_atoms();
@@ -19019,6 +19132,7 @@ var init_CardGrid = __esm({
19019
19132
  init_Typography();
19020
19133
  init_Stack();
19021
19134
  init_Pagination();
19135
+ fieldLabel = humanizeFieldName;
19022
19136
  STATUS_FIELDS = /* @__PURE__ */ new Set(["status", "state", "priority", "type", "category", "role", "level", "tier"]);
19023
19137
  gapStyles3 = {
19024
19138
  none: "gap-0",
@@ -19526,7 +19640,7 @@ var init_CaseStudyOrganism = __esm({
19526
19640
  CaseStudyOrganism.displayName = "CaseStudyOrganism";
19527
19641
  }
19528
19642
  });
19529
- var CHART_COLORS, seriesColor, monthFormatter, formatTimeLabel, BarChart, PieChart, LineChart, ScatterChart, LOOK_FROM_CHART_TYPE, Chart;
19643
+ var CHART_COLORS, seriesColor, barColor, monthFormatter, formatTimeLabel, BarChart, PieChart, LineChart, ScatterChart, LOOK_FROM_CHART_TYPE, Chart;
19530
19644
  var init_Chart = __esm({
19531
19645
  "components/core/molecules/Chart.tsx"() {
19532
19646
  "use client";
@@ -19546,6 +19660,7 @@ var init_Chart = __esm({
19546
19660
  "var(--color-accent)"
19547
19661
  ];
19548
19662
  seriesColor = (series, idx) => series.color ?? CHART_COLORS[idx % CHART_COLORS.length];
19663
+ barColor = (series, sIdx, catIdx, seriesCount) => series.color ?? CHART_COLORS[(seriesCount === 1 ? catIdx : sIdx) % CHART_COLORS.length];
19549
19664
  monthFormatter = new Intl.DateTimeFormat(void 0, {
19550
19665
  month: "short",
19551
19666
  year: "2-digit"
@@ -19618,7 +19733,7 @@ var init_Chart = __esm({
19618
19733
  children: series.map((s, sIdx) => {
19619
19734
  const value = valueAt(s, label);
19620
19735
  const ratio = stack === "normalize" ? total === 0 ? 0 : value / total * 100 : value / maxValue * 100;
19621
- const color = seriesColor(s, sIdx);
19736
+ const color = barColor(s, sIdx, catIdx, series.length);
19622
19737
  return /* @__PURE__ */ jsx(
19623
19738
  Box,
19624
19739
  {
@@ -19673,7 +19788,7 @@ var init_Chart = __esm({
19673
19788
  /* @__PURE__ */ jsx(HStack, { gap: histogram ? "none" : "xs", align: "end", justify: histogram ? void 0 : "center", className: "w-full flex-1 min-h-0", children: series.map((s, sIdx) => {
19674
19789
  const value = valueAt(s, label);
19675
19790
  const barHeight = value / maxValue * 100;
19676
- const color = seriesColor(s, sIdx);
19791
+ const color = barColor(s, sIdx, catIdx, series.length);
19677
19792
  return /* @__PURE__ */ jsx(
19678
19793
  Box,
19679
19794
  {
@@ -19724,7 +19839,7 @@ var init_Chart = __esm({
19724
19839
  /* @__PURE__ */ jsx(VStack, { gap: "none", className: "w-full flex-1 min-h-0", justify: "end", children: series.map((s, sIdx) => {
19725
19840
  const value = valueAt(s, label);
19726
19841
  const ratio = stack === "normalize" ? total === 0 ? 0 : value / total * 100 : value / maxValue * 100;
19727
- const color = seriesColor(s, sIdx);
19842
+ const color = barColor(s, sIdx, catIdx, series.length);
19728
19843
  return /* @__PURE__ */ jsx(
19729
19844
  Box,
19730
19845
  {
@@ -22572,9 +22687,6 @@ var init_useDataDnd = __esm({
22572
22687
  function renderIconInput(icon, props) {
22573
22688
  return typeof icon === "string" ? /* @__PURE__ */ jsx(Icon, { name: icon, ...props }) : /* @__PURE__ */ jsx(Icon, { icon, ...props });
22574
22689
  }
22575
- function fieldLabel2(key) {
22576
- return key.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/[_-]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
22577
- }
22578
22690
  function statusVariant2(value) {
22579
22691
  const v = value.toLowerCase();
22580
22692
  if (["active", "completed", "done", "approved", "published", "resolved", "open", "online"].includes(v)) return "success";
@@ -22593,29 +22705,6 @@ function resolveBadgeVariant(field, value) {
22593
22705
  }
22594
22706
  return statusVariant2(value);
22595
22707
  }
22596
- function formatDate2(value) {
22597
- if (!value) return "";
22598
- const d = new Date(String(value));
22599
- if (isNaN(d.getTime())) return String(value);
22600
- return d.toLocaleDateString(void 0, { year: "numeric", month: "short", day: "numeric" });
22601
- }
22602
- function formatValue(value, format) {
22603
- if (value === void 0 || value === null) return "";
22604
- switch (format) {
22605
- case "date":
22606
- return formatDate2(value);
22607
- case "currency":
22608
- return typeof value === "number" ? `$${value.toFixed(2)}` : String(value);
22609
- case "number":
22610
- return typeof value === "number" ? value.toLocaleString() : String(value);
22611
- case "percent":
22612
- return typeof value === "number" ? `${Math.round(value)}%` : String(value);
22613
- case "boolean":
22614
- return value ? "Yes" : "No";
22615
- default:
22616
- return String(value);
22617
- }
22618
- }
22619
22708
  function DataGrid({
22620
22709
  entity,
22621
22710
  fields,
@@ -22902,7 +22991,7 @@ function DataGrid({
22902
22991
  if (val === void 0 || val === null || val === "") return null;
22903
22992
  return /* @__PURE__ */ jsxs(HStack, { gap: "xs", className: "items-center", children: [
22904
22993
  field.icon && renderIconInput(field.icon, { size: "xs" }),
22905
- /* @__PURE__ */ jsx(Badge, { variant: resolveBadgeVariant(field, String(val)), children: formatValue(val, field.format) })
22994
+ /* @__PURE__ */ jsx(Badge, { variant: resolveBadgeVariant(field, String(val)), children: humanizeEnumValue(formatValue(val, field.format)) })
22906
22995
  ] }, field.name);
22907
22996
  }) })
22908
22997
  ] }),
@@ -23012,11 +23101,12 @@ function DataGrid({
23012
23101
  ] })
23013
23102
  );
23014
23103
  }
23015
- var dataGridLog, BADGE_VARIANTS, gapStyles5, lookStyles5;
23104
+ var dataGridLog, fieldLabel2, BADGE_VARIANTS, gapStyles5, lookStyles5;
23016
23105
  var init_DataGrid = __esm({
23017
23106
  "components/core/molecules/DataGrid.tsx"() {
23018
23107
  "use client";
23019
23108
  init_cn();
23109
+ init_format();
23020
23110
  init_getNestedValue();
23021
23111
  init_useEventBus();
23022
23112
  init_Box();
@@ -23029,6 +23119,7 @@ var init_DataGrid = __esm({
23029
23119
  init_Menu();
23030
23120
  init_useDataDnd();
23031
23121
  dataGridLog = createLogger("almadar:ui:data-grid");
23122
+ fieldLabel2 = humanizeFieldName;
23032
23123
  BADGE_VARIANTS = /* @__PURE__ */ new Set([
23033
23124
  "default",
23034
23125
  "primary",
@@ -23060,9 +23151,6 @@ var init_DataGrid = __esm({
23060
23151
  function renderIconInput2(icon, props) {
23061
23152
  return typeof icon === "string" ? /* @__PURE__ */ jsx(Icon, { name: icon, ...props }) : /* @__PURE__ */ jsx(Icon, { icon, ...props });
23062
23153
  }
23063
- function fieldLabel3(key) {
23064
- return key.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/[_-]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
23065
- }
23066
23154
  function statusVariant3(value) {
23067
23155
  const v = value.toLowerCase();
23068
23156
  if (["active", "completed", "done", "approved", "published", "resolved", "open", "online"].includes(v)) return "success";
@@ -23071,28 +23159,12 @@ function statusVariant3(value) {
23071
23159
  if (["new", "created", "scheduled", "queued", "info"].includes(v)) return "info";
23072
23160
  return "default";
23073
23161
  }
23074
- function formatDate3(value) {
23075
- if (!value) return "";
23076
- const d = new Date(String(value));
23077
- if (isNaN(d.getTime())) return String(value);
23078
- return d.toLocaleDateString(void 0, { year: "numeric", month: "short", day: "numeric" });
23079
- }
23080
23162
  function formatValue2(value, format, boolLabels) {
23081
- if (value === void 0 || value === null) return "";
23082
- switch (format) {
23083
- case "date":
23084
- return formatDate3(value);
23085
- case "currency":
23086
- return typeof value === "number" ? `$${value.toFixed(2)}` : String(value);
23087
- case "number":
23088
- return typeof value === "number" ? value.toLocaleString() : String(value);
23089
- case "percent":
23090
- return typeof value === "number" ? `${Math.round(value)}%` : String(value);
23091
- case "boolean":
23092
- return value ? boolLabels?.yes ?? "Yes" : boolLabels?.no ?? "No";
23093
- default:
23094
- return String(value);
23163
+ if (value !== void 0 && value !== null && (format === "boolean" || typeof value === "boolean")) {
23164
+ const isNo = value === false || value === 0 || String(value) === "false";
23165
+ return isNo ? boolLabels?.no ?? "No" : boolLabels?.yes ?? "Yes";
23095
23166
  }
23167
+ return formatValue(value, format);
23096
23168
  }
23097
23169
  function groupData(items, field) {
23098
23170
  const groups = /* @__PURE__ */ new Map();
@@ -23115,7 +23187,9 @@ function DataList({
23115
23187
  variant = "default",
23116
23188
  groupBy,
23117
23189
  senderField,
23190
+ senderLabelField,
23118
23191
  currentUser,
23192
+ emptyMessage,
23119
23193
  className,
23120
23194
  isLoading = false,
23121
23195
  error = null,
@@ -23134,6 +23208,8 @@ function DataList({
23134
23208
  hasMore,
23135
23209
  children,
23136
23210
  pageSize = 5,
23211
+ sortBy,
23212
+ sortDirection,
23137
23213
  renderItem: schemaRenderItem,
23138
23214
  dragGroup,
23139
23215
  accepts,
@@ -23162,7 +23238,11 @@ function DataList({
23162
23238
  dndItemIdField,
23163
23239
  dndRoot
23164
23240
  });
23165
- const allData = dnd.orderedItems;
23241
+ const orderedData = dnd.orderedItems;
23242
+ const allData = React82__default.useMemo(
23243
+ () => sortRows(orderedData, sortBy, sortDirection),
23244
+ [orderedData, sortBy, sortDirection]
23245
+ );
23166
23246
  const data = pageSize > 0 ? allData.slice(0, visibleCount) : allData;
23167
23247
  const hasMoreLocal = pageSize > 0 && visibleCount < allData.length;
23168
23248
  const hasRenderProp = typeof children === "function";
@@ -23199,7 +23279,7 @@ function DataList({
23199
23279
  };
23200
23280
  eventBus.emit(`UI:${action.event}`, payload);
23201
23281
  };
23202
- const renderItemActions = (itemData) => {
23282
+ const renderItemActions = (itemData, onPrimary = false) => {
23203
23283
  if (!itemActions || itemActions.length === 0) return null;
23204
23284
  const inline = maxInlineActions != null ? itemActions.slice(0, maxInlineActions) : itemActions;
23205
23285
  const overflow = maxInlineActions != null ? itemActions.slice(maxInlineActions) : [];
@@ -23212,7 +23292,12 @@ function DataList({
23212
23292
  onClick: handleActionClick(action, itemData),
23213
23293
  "data-testid": `action-${action.event}`,
23214
23294
  "data-row-id": String(itemData.id),
23215
- className: cn(action.variant === "danger" && "text-error hover:bg-error/10"),
23295
+ className: cn(
23296
+ action.variant === "danger" && "text-error hover:bg-error/10",
23297
+ // Must sit on the Button itself: the variant's own text colour
23298
+ // beats an inherited one from the row wrapper.
23299
+ onPrimary && "!text-primary-foreground hover:bg-primary-foreground/15"
23300
+ ),
23216
23301
  children: [
23217
23302
  action.icon && renderIconInput2(action.icon, { size: "xs", className: "mr-1" }),
23218
23303
  action.label
@@ -23254,7 +23339,7 @@ function DataList({
23254
23339
  return /* @__PURE__ */ jsx(Box, { className: "text-center py-8", children: /* @__PURE__ */ jsx(Typography, { variant: "body", color: "error", children: error.message }) });
23255
23340
  }
23256
23341
  if (data.length === 0) {
23257
- const emptyNode = /* @__PURE__ */ jsx(Box, { className: "text-center py-12", children: /* @__PURE__ */ jsx(Typography, { variant: "body", color: "secondary", children: t("empty.noItems") }) });
23342
+ const emptyNode = /* @__PURE__ */ jsx(Box, { className: "text-center py-12", children: /* @__PURE__ */ jsx(Typography, { variant: "body", color: "secondary", children: emptyMessage || t("empty.noItems") }) });
23258
23343
  return dnd.enabled ? /* @__PURE__ */ jsx(Fragment, { children: dnd.wrapContainer(emptyNode) }) : emptyNode;
23259
23344
  }
23260
23345
  const gapClass = {
@@ -23270,51 +23355,93 @@ function DataList({
23270
23355
  const items2 = [...data];
23271
23356
  const groups2 = groupBy ? groupData(items2, groupBy) : [{ label: "", items: items2 }];
23272
23357
  const contentField = titleField?.name ?? fieldDefs[0]?.name ?? "";
23273
- return /* @__PURE__ */ jsx(VStack, { gap: "sm", className: cn("py-2", className), children: groups2.map((group, gi) => /* @__PURE__ */ jsxs(React82__default.Fragment, { children: [
23274
- group.label && /* @__PURE__ */ jsx(Divider, { label: group.label, className: "my-2" }),
23275
- group.items.map((itemData, index) => {
23276
- const id = itemData.id || `${gi}-${index}`;
23277
- const sender = senderField ? String(getNestedValue(itemData, senderField) ?? "") : "";
23278
- const isSent = Boolean(currentUser && sender === currentUser);
23279
- const content = getNestedValue(itemData, contentField);
23280
- const timestampField = fieldDefs.find((f3) => f3.format === "date");
23281
- const timestamp = timestampField ? getNestedValue(itemData, timestampField.name) : null;
23282
- return /* @__PURE__ */ jsx(
23283
- Box,
23284
- {
23285
- className: cn(
23286
- "flex px-4",
23287
- isSent ? "justify-end" : "justify-start"
23288
- ),
23289
- children: /* @__PURE__ */ jsxs(
23290
- Box,
23291
- {
23292
- className: cn(
23293
- "max-w-[75%] px-4 py-2",
23294
- isSent ? "bg-primary text-primary-foreground rounded-2xl rounded-br-sm" : "bg-muted text-foreground rounded-2xl rounded-bl-sm"
23295
- ),
23296
- children: [
23297
- !isSent && senderField && /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "font-semibold mb-0.5", children: sender }),
23298
- /* @__PURE__ */ jsx(Typography, { variant: "body", className: cn(isSent && "text-primary-foreground"), children: content !== void 0 && content !== null ? String(content) : "" }),
23299
- timestamp != null ? /* @__PURE__ */ jsx(
23300
- Typography,
23301
- {
23302
- variant: "caption",
23303
- className: cn(
23304
- "mt-1 text-xs",
23305
- isSent ? "opacity-70" : "text-muted-foreground"
23306
- ),
23307
- children: formatDate3(timestamp)
23308
- }
23309
- ) : null
23310
- ]
23311
- }
23312
- )
23313
- },
23314
- id
23315
- );
23316
- })
23317
- ] }, gi)) });
23358
+ const senderLabel = (itemData, raw) => {
23359
+ if (!senderLabelField) return raw;
23360
+ const v = getNestedValue(itemData, senderLabelField);
23361
+ return v === void 0 || v === null || v === "" ? raw : String(v);
23362
+ };
23363
+ return /* @__PURE__ */ jsxs(VStack, { gap: "sm", className: cn("py-2", className), children: [
23364
+ groups2.map((group, gi) => /* @__PURE__ */ jsxs(React82__default.Fragment, { children: [
23365
+ group.label && /* @__PURE__ */ jsx(Divider, { label: group.label, className: "my-2" }),
23366
+ group.items.map((itemData, index) => {
23367
+ const id = itemData.id || `${gi}-${index}`;
23368
+ const sender = senderField ? String(getNestedValue(itemData, senderField) ?? "") : "";
23369
+ const isSent = Boolean(currentUser && sender === currentUser);
23370
+ const content = getNestedValue(itemData, contentField);
23371
+ const timestampField = fieldDefs.find((f3) => f3.format === "date");
23372
+ const timestamp = timestampField ? getNestedValue(itemData, timestampField.name) : null;
23373
+ const metaFields = fieldDefs.filter(
23374
+ (f3) => f3.name !== contentField && f3.name !== timestampField?.name
23375
+ );
23376
+ return /* @__PURE__ */ jsx(
23377
+ Box,
23378
+ {
23379
+ "data-entity-row": true,
23380
+ "data-entity-id": id,
23381
+ onClick: itemClickEvent ? handleRowClick(itemData) : void 0,
23382
+ className: cn(
23383
+ "flex px-4 group/rowactions",
23384
+ itemClickEvent && "cursor-pointer",
23385
+ isSent ? "justify-end" : "justify-start"
23386
+ ),
23387
+ children: /* @__PURE__ */ jsxs(
23388
+ Box,
23389
+ {
23390
+ className: cn(
23391
+ "max-w-[75%] px-4 py-2",
23392
+ isSent ? "bg-primary text-primary-foreground rounded-2xl rounded-br-sm" : "bg-muted text-foreground rounded-2xl rounded-bl-sm"
23393
+ ),
23394
+ children: [
23395
+ !isSent && senderField && /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "font-semibold mb-0.5", children: senderLabel(itemData, sender) }),
23396
+ /* @__PURE__ */ jsx(Typography, { variant: "body", className: cn(isSent && "text-primary-foreground"), children: content !== void 0 && content !== null ? String(content) : "" }),
23397
+ metaFields.length > 0 && /* @__PURE__ */ jsx(HStack, { gap: "xs", className: "mt-1 flex-wrap", children: metaFields.map((f3) => {
23398
+ const v = getNestedValue(itemData, f3.name);
23399
+ if (v === void 0 || v === null || v === "") return null;
23400
+ return f3.variant === "badge" ? /* @__PURE__ */ jsx(Badge, { variant: statusVariant3(String(v)), children: String(v) }, f3.name) : /* @__PURE__ */ jsx(
23401
+ Typography,
23402
+ {
23403
+ variant: "caption",
23404
+ className: cn("text-xs", isSent ? "opacity-70" : "text-muted-foreground"),
23405
+ children: formatValue2(v, f3.format)
23406
+ },
23407
+ f3.name
23408
+ );
23409
+ }) }),
23410
+ /* @__PURE__ */ jsxs(HStack, { gap: "xs", className: "mt-1 items-center justify-between", children: [
23411
+ timestamp != null ? /* @__PURE__ */ jsx(
23412
+ Typography,
23413
+ {
23414
+ variant: "caption",
23415
+ className: cn("text-xs", isSent ? "opacity-70" : "text-muted-foreground"),
23416
+ children: formatDate(timestamp)
23417
+ }
23418
+ ) : /* @__PURE__ */ jsx("span", {}),
23419
+ renderItemActions(itemData, isSent)
23420
+ ] })
23421
+ ]
23422
+ }
23423
+ )
23424
+ },
23425
+ id
23426
+ );
23427
+ })
23428
+ ] }, gi)),
23429
+ hasMoreLocal && /* @__PURE__ */ jsx(Box, { className: "flex justify-center py-3", children: /* @__PURE__ */ jsxs(
23430
+ Button,
23431
+ {
23432
+ variant: "ghost",
23433
+ size: "sm",
23434
+ onClick: () => setVisibleCount((prev) => prev + (pageSize || 5)),
23435
+ children: [
23436
+ /* @__PURE__ */ jsx(Icon, { name: "chevron-down", size: "xs", className: "mr-1" }),
23437
+ t("common.showMore"),
23438
+ " (",
23439
+ t("common.remaining", { count: allData.length - visibleCount }),
23440
+ ")"
23441
+ ]
23442
+ }
23443
+ ) })
23444
+ ] });
23318
23445
  }
23319
23446
  const items = [...data];
23320
23447
  const groups = groupBy ? groupData(items, groupBy) : [{ label: "", items }];
@@ -23463,11 +23590,12 @@ function DataList({
23463
23590
  )
23464
23591
  );
23465
23592
  }
23466
- var dataListLog, listLookStyles;
23593
+ var dataListLog, fieldLabel3, listLookStyles;
23467
23594
  var init_DataList = __esm({
23468
23595
  "components/core/molecules/DataList.tsx"() {
23469
23596
  "use client";
23470
23597
  init_cn();
23598
+ init_format();
23471
23599
  init_getNestedValue();
23472
23600
  init_useEventBus();
23473
23601
  init_Box();
@@ -23482,6 +23610,7 @@ var init_DataList = __esm({
23482
23610
  init_Menu();
23483
23611
  init_useDataDnd();
23484
23612
  dataListLog = createLogger("almadar:ui:data-list");
23613
+ fieldLabel3 = humanizeFieldName;
23485
23614
  listLookStyles = {
23486
23615
  dense: "[&_[data-entity-row]>div]:!py-1 [&_[data-entity-row]>div]:!px-3",
23487
23616
  spacious: "[&_[data-entity-row]>div]:!py-5 [&_[data-entity-row]>div]:!px-8",
@@ -27753,7 +27882,7 @@ function renderIconInput3(icon, props) {
27753
27882
  return typeof icon === "string" ? /* @__PURE__ */ jsx(Icon, { name: icon, ...props }) : /* @__PURE__ */ jsx(Icon, { icon, ...props });
27754
27883
  }
27755
27884
  function columnLabel(col) {
27756
- return col.header ?? col.label ?? col.key.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/[_-]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
27885
+ return col.header ?? col.label ?? humanizeFieldName(col.key);
27757
27886
  }
27758
27887
  function asFieldValue(v) {
27759
27888
  if (v === void 0 || v === null) return v;
@@ -27772,25 +27901,6 @@ function statusVariant4(value) {
27772
27901
  if (["new", "created", "scheduled", "queued", "info"].includes(v)) return "info";
27773
27902
  return "default";
27774
27903
  }
27775
- function formatCell(value, format) {
27776
- if (value === void 0 || value === null) return "";
27777
- switch (format) {
27778
- case "date": {
27779
- const d = new Date(String(value));
27780
- return isNaN(d.getTime()) ? String(value) : d.toLocaleDateString(void 0, { year: "numeric", month: "short", day: "numeric" });
27781
- }
27782
- case "currency":
27783
- return typeof value === "number" ? `$${value.toFixed(2)}` : String(value);
27784
- case "number":
27785
- return typeof value === "number" ? value.toLocaleString() : String(value);
27786
- case "percent":
27787
- return typeof value === "number" ? `${Math.round(value)}%` : String(value);
27788
- case "boolean":
27789
- return value ? "Yes" : "No";
27790
- default:
27791
- return String(value);
27792
- }
27793
- }
27794
27904
  function groupData2(items, field) {
27795
27905
  const groups = /* @__PURE__ */ new Map();
27796
27906
  for (const item of items) {
@@ -27836,7 +27946,8 @@ function TableView({
27836
27946
  const { t } = useTranslate();
27837
27947
  const [visibleCount, setVisibleCount] = React82__default.useState(pageSize > 0 ? pageSize : Infinity);
27838
27948
  const [localSelected, setLocalSelected] = React82__default.useState(/* @__PURE__ */ new Set());
27839
- const colDefs = columns ?? fields ?? [];
27949
+ const colDefs = (Array.isArray(columns) ? columns : void 0) ?? (Array.isArray(fields) ? fields : void 0) ?? [];
27950
+ const actionDefs = Array.isArray(itemActions) ? itemActions : [];
27840
27951
  const allDataRaw = Array.isArray(entity) ? entity : entity ? [entity] : [];
27841
27952
  const dnd = useDataDnd({
27842
27953
  items: allDataRaw,
@@ -27856,6 +27967,23 @@ function TableView({
27856
27967
  const hasRenderProp = typeof children === "function";
27857
27968
  const idField = dndItemIdField ?? "id";
27858
27969
  const isCoarsePointer = useMediaQuery("(pointer: coarse)");
27970
+ React82__default.useEffect(() => {
27971
+ tableViewLog.debug("render", {
27972
+ rowCount: data.length,
27973
+ colCount: colDefs.length,
27974
+ look,
27975
+ isLoading: Boolean(isLoading),
27976
+ hasError: Boolean(error),
27977
+ dnd: dnd.enabled
27978
+ });
27979
+ if (data.length > 0 && !hasRenderProp && colDefs.length === 0) {
27980
+ tableViewLog.warn("columns-unresolved", {
27981
+ rowCount: data.length,
27982
+ columnsIsArray: Array.isArray(columns),
27983
+ fieldsIsArray: Array.isArray(fields)
27984
+ });
27985
+ }
27986
+ }, [data, colDefs, look, isLoading, error, dnd.enabled, hasRenderProp, columns, fields]);
27859
27987
  const selected = selectedIds ? new Set(selectedIds) : localSelected;
27860
27988
  const emitSelection = (next) => {
27861
27989
  if (!selectedIds) setLocalSelected(next);
@@ -27899,21 +28027,12 @@ function TableView({
27899
28027
  }),
27900
28028
  [colDefs, data]
27901
28029
  );
27902
- if (isLoading) {
27903
- return /* @__PURE__ */ jsx(Box, { className: "text-center py-8", children: /* @__PURE__ */ jsx(Typography, { variant: "body", color: "secondary", children: t("loading.items") }) });
27904
- }
27905
- if (error) {
27906
- return /* @__PURE__ */ jsx(Box, { className: "text-center py-8", children: /* @__PURE__ */ jsx(Typography, { variant: "body", color: "error", children: error.message }) });
27907
- }
27908
- if (data.length === 0) {
27909
- const emptyNode = /* @__PURE__ */ jsx(Box, { className: "text-center py-12", children: /* @__PURE__ */ jsx(Typography, { variant: "body", color: "secondary", children: emptyMessage || t("empty.noItems") }) });
27910
- return dnd.enabled ? /* @__PURE__ */ jsx(Fragment, { children: dnd.wrapContainer(emptyNode) }) : emptyNode;
27911
- }
28030
+ const statusNode = isLoading ? /* @__PURE__ */ jsx(Box, { className: "text-center py-8", children: /* @__PURE__ */ jsx(Typography, { variant: "body", color: "secondary", children: t("loading.items") }) }) : error ? /* @__PURE__ */ jsx(Box, { className: "text-center py-8", children: /* @__PURE__ */ jsx(Typography, { variant: "body", color: "error", children: error.message }) }) : data.length === 0 ? /* @__PURE__ */ jsx(Box, { className: "text-center py-12", children: /* @__PURE__ */ jsx(Typography, { variant: "body", color: "secondary", children: emptyMessage || t("empty.noItems") }) }) : null;
27912
28031
  const lk = LOOKS[look];
27913
- const hasActions = Boolean(itemActions && itemActions.length > 0);
28032
+ const hasActions = actionDefs.length > 0;
27914
28033
  const effectiveMaxInline = isCoarsePointer ? 0 : maxInlineActions;
27915
- const inlineActionCount = hasActions ? effectiveMaxInline != null ? Math.min(itemActions.length, effectiveMaxInline) : itemActions.length : 0;
27916
- const hasOverflowActions = hasActions && effectiveMaxInline != null && itemActions.length > effectiveMaxInline;
28034
+ const inlineActionCount = hasActions ? effectiveMaxInline != null ? Math.min(actionDefs.length, effectiveMaxInline) : actionDefs.length : 0;
28035
+ const hasOverflowActions = hasActions && effectiveMaxInline != null && actionDefs.length > effectiveMaxInline;
27917
28036
  const actionsTrack = hasActions ? `${inlineActionCount * 6 + (hasOverflowActions ? 3 : 0)}rem` : null;
27918
28037
  const gridTemplateColumns = [
27919
28038
  selectable ? "auto" : null,
@@ -28001,11 +28120,11 @@ function TableView({
28001
28120
  col.className
28002
28121
  );
28003
28122
  if (col.format === "badge" && raw != null && raw !== "") {
28004
- return /* @__PURE__ */ jsx(Box, { role: "cell", className: cellBase, children: /* @__PURE__ */ jsx(Badge, { variant: statusVariant4(String(raw)), size: "sm", className: "whitespace-nowrap", children: String(raw) }) }, col.key);
28123
+ return /* @__PURE__ */ jsx(Box, { role: "cell", className: cellBase, children: /* @__PURE__ */ jsx(Badge, { variant: statusVariant4(String(raw)), size: "sm", className: "whitespace-nowrap", children: humanizeEnumValue(String(raw)) }) }, col.key);
28005
28124
  }
28006
28125
  return /* @__PURE__ */ jsx(Box, { role: "cell", className: cellBase, children: /* @__PURE__ */ jsx("span", { className: "truncate text-foreground", children: formatCell(raw, col.format) }) }, col.key);
28007
28126
  }),
28008
- itemActions && itemActions.length > 0 && /* @__PURE__ */ jsxs(
28127
+ hasActions && /* @__PURE__ */ jsxs(
28009
28128
  HStack,
28010
28129
  {
28011
28130
  gap: "xs",
@@ -28017,7 +28136,7 @@ function TableView({
28017
28136
  lk.striped && index % 2 === 1 ? "bg-[var(--color-surface-subtle)]" : "bg-[var(--color-card)] group-hover:bg-[var(--color-surface-subtle)]"
28018
28137
  ),
28019
28138
  children: [
28020
- (effectiveMaxInline != null ? itemActions.slice(0, effectiveMaxInline) : itemActions).map((action, i) => /* @__PURE__ */ jsxs(
28139
+ (effectiveMaxInline != null ? actionDefs.slice(0, effectiveMaxInline) : actionDefs).map((action, i) => /* @__PURE__ */ jsxs(
28021
28140
  Button,
28022
28141
  {
28023
28142
  variant: action.variant === "primary" ? "primary" : "ghost",
@@ -28033,12 +28152,12 @@ function TableView({
28033
28152
  },
28034
28153
  i
28035
28154
  )),
28036
- effectiveMaxInline != null && itemActions.length > effectiveMaxInline && /* @__PURE__ */ jsx(
28155
+ effectiveMaxInline != null && actionDefs.length > effectiveMaxInline && /* @__PURE__ */ jsx(
28037
28156
  Menu,
28038
28157
  {
28039
28158
  position: "bottom-end",
28040
28159
  trigger: /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", "aria-label": t("common.actions"), "data-testid": "action-overflow", children: /* @__PURE__ */ jsx(Icon, { name: "more-horizontal", size: "xs" }) }),
28041
- items: itemActions.slice(effectiveMaxInline).map((action) => ({
28160
+ items: actionDefs.slice(effectiveMaxInline).map((action) => ({
28042
28161
  label: action.label,
28043
28162
  icon: action.icon,
28044
28163
  event: action.event,
@@ -28065,15 +28184,16 @@ function TableView({
28065
28184
  group.label && /* @__PURE__ */ jsx(Divider, { label: group.label, className: gi > 0 ? "mt-3" : "mt-0" }),
28066
28185
  group.items.map((row) => renderRow(row, runningIndex++))
28067
28186
  ] }, gi)) });
28187
+ const showHeader = colDefs.length > 0 || hasRenderProp;
28068
28188
  return /* @__PURE__ */ jsxs(
28069
28189
  Box,
28070
28190
  {
28071
28191
  role: "table",
28072
28192
  className: cn("w-full text-sm", className),
28073
28193
  children: [
28074
- header,
28075
- dnd.wrapContainer(body),
28076
- hasMore && /* @__PURE__ */ jsx(Box, { className: "flex justify-center py-3", children: /* @__PURE__ */ jsxs(Button, { variant: "ghost", size: "sm", onClick: () => setVisibleCount((p) => p + (pageSize || 5)), children: [
28194
+ showHeader && header,
28195
+ dnd.wrapContainer(statusNode ? /* @__PURE__ */ jsx(Box, { role: "rowgroup", children: statusNode }) : body),
28196
+ !statusNode && hasMore && /* @__PURE__ */ jsx(Box, { className: "flex justify-center py-3", children: /* @__PURE__ */ jsxs(Button, { variant: "ghost", size: "sm", onClick: () => setVisibleCount((p) => p + (pageSize || 5)), children: [
28077
28197
  /* @__PURE__ */ jsx(Icon, { name: "chevron-down", size: "xs", className: "mr-1" }),
28078
28198
  t("common.showMore"),
28079
28199
  " (",
@@ -28084,11 +28204,12 @@ function TableView({
28084
28204
  }
28085
28205
  );
28086
28206
  }
28087
- var MAX_MEASURED_COL_CH, BADGE_CHROME_CH, alignClass, weightClass, LOOKS;
28207
+ var tableViewLog, formatCell, MAX_MEASURED_COL_CH, BADGE_CHROME_CH, alignClass, weightClass, LOOKS;
28088
28208
  var init_TableView = __esm({
28089
28209
  "components/core/molecules/TableView.tsx"() {
28090
28210
  "use client";
28091
28211
  init_cn();
28212
+ init_format();
28092
28213
  init_getNestedValue();
28093
28214
  init_useEventBus();
28094
28215
  init_useMediaQuery();
@@ -28102,7 +28223,8 @@ var init_TableView = __esm({
28102
28223
  init_Divider();
28103
28224
  init_Menu();
28104
28225
  init_useDataDnd();
28105
- createLogger("almadar:ui:table-view");
28226
+ tableViewLog = createLogger("almadar:ui:table-view");
28227
+ formatCell = (value, format) => formatValue(value, format);
28106
28228
  MAX_MEASURED_COL_CH = 32;
28107
28229
  BADGE_CHROME_CH = 4;
28108
28230
  alignClass = {
@@ -35348,6 +35470,7 @@ var init_GraphCanvas = __esm({
35348
35470
  title,
35349
35471
  nodes: propNodes = NO_NODES,
35350
35472
  edges: propEdges = NO_EDGES,
35473
+ proposedEdges = NO_EDGES,
35351
35474
  similarity: propSimilarity = NO_SIM,
35352
35475
  height = 400,
35353
35476
  showLabels = true,
@@ -35355,6 +35478,7 @@ var init_GraphCanvas = __esm({
35355
35478
  draggable = true,
35356
35479
  actions,
35357
35480
  onNodeClick,
35481
+ onMarkClick,
35358
35482
  onNodeDoubleClick,
35359
35483
  onBadgeClick,
35360
35484
  nodeClickEvent,
@@ -35383,6 +35507,18 @@ var init_GraphCanvas = __esm({
35383
35507
  const laidOutRef = useRef(false);
35384
35508
  const [, forceUpdate] = useState(0);
35385
35509
  const [logicalW, setLogicalW] = useState(800);
35510
+ const hasProposed = useMemo(() => propNodes.some((n) => n.mark?.kind === "proposed"), [propNodes]);
35511
+ const [pulseTick, setPulseTick] = useState(0);
35512
+ useEffect(() => {
35513
+ if (!hasProposed) return;
35514
+ let raf = 0;
35515
+ const loop = () => {
35516
+ setPulseTick((t2) => (t2 + 1) % 1e6);
35517
+ raf = requestAnimationFrame(loop);
35518
+ };
35519
+ raf = requestAnimationFrame(loop);
35520
+ return () => cancelAnimationFrame(raf);
35521
+ }, [hasProposed]);
35386
35522
  useEffect(() => {
35387
35523
  const canvas = canvasRef.current;
35388
35524
  if (!canvas || typeof ResizeObserver === "undefined") return;
@@ -35684,18 +35820,38 @@ var init_GraphCanvas = __esm({
35684
35820
  }
35685
35821
  }
35686
35822
  ctx.globalAlpha = 1;
35823
+ ctx.setLineDash([4, 4]);
35824
+ for (const edge of proposedEdges) {
35825
+ const source = nodes.find((n) => n.id === edge.source);
35826
+ const target = nodes.find((n) => n.id === edge.target);
35827
+ if (!source || !target) continue;
35828
+ ctx.globalAlpha = 0.4;
35829
+ ctx.beginPath();
35830
+ ctx.moveTo(source.x, source.y);
35831
+ ctx.lineTo(target.x, target.y);
35832
+ ctx.strokeStyle = edge.color || mutedColor;
35833
+ ctx.lineWidth = 1;
35834
+ ctx.stroke();
35835
+ }
35836
+ ctx.setLineDash([]);
35837
+ ctx.globalAlpha = 1;
35687
35838
  for (const node of nodes) {
35688
35839
  const size = node.size || 8;
35689
35840
  const color = resolveColor3(node.color || getGroupColor(node.group, groups), canvas);
35690
35841
  const isHovered = hoveredNode === node.id;
35691
35842
  const isSelected = selectedNodeId !== void 0 && node.id === selectedNodeId;
35692
- ctx.globalAlpha = hoveredNode && !neighbors.has(node.id) ? 0.2 : 1;
35843
+ const mark = node.mark;
35844
+ ctx.globalAlpha = hoveredNode && !neighbors.has(node.id) ? 0.2 : mark?.kind === "proposed" ? 0.55 : 1;
35693
35845
  const radius = isSelected ? size + 4 : isHovered ? size + 2 : size;
35694
35846
  ctx.beginPath();
35695
35847
  ctx.arc(node.x, node.y, radius, 0, Math.PI * 2);
35696
- ctx.fillStyle = color;
35848
+ ctx.fillStyle = mark?.kind === "proposed" ? mutedColor : color;
35697
35849
  ctx.fill();
35698
- if (isSelected) {
35850
+ if (mark?.kind === "proposed") {
35851
+ ctx.setLineDash([3, 3]);
35852
+ ctx.strokeStyle = resolveColor3(mark.tint ?? "var(--color-warning)", canvas);
35853
+ ctx.lineWidth = 1.5;
35854
+ } else if (isSelected) {
35699
35855
  ctx.strokeStyle = accentColor;
35700
35856
  ctx.lineWidth = 3;
35701
35857
  } else {
@@ -35703,6 +35859,28 @@ var init_GraphCanvas = __esm({
35703
35859
  ctx.lineWidth = isHovered ? 2 : 1;
35704
35860
  }
35705
35861
  ctx.stroke();
35862
+ ctx.setLineDash([]);
35863
+ if (mark?.kind === "suggested") {
35864
+ ctx.beginPath();
35865
+ ctx.arc(node.x, node.y, radius + 3, 0, Math.PI * 2);
35866
+ ctx.strokeStyle = accentColor;
35867
+ ctx.lineWidth = 2;
35868
+ ctx.stroke();
35869
+ ctx.beginPath();
35870
+ ctx.arc(node.x + radius * 0.7, node.y - radius * 0.7, 2.5, 0, Math.PI * 2);
35871
+ ctx.fillStyle = accentColor;
35872
+ ctx.fill();
35873
+ } else if (mark?.kind === "proposed") {
35874
+ const phase = pulseTick % 60 / 60;
35875
+ const baseAlpha = hoveredNode && !neighbors.has(node.id) ? 0.2 : 0.55;
35876
+ ctx.globalAlpha = baseAlpha * (0.4 + 0.4 * (1 - Math.abs(phase * 2 - 1)));
35877
+ ctx.beginPath();
35878
+ ctx.arc(node.x, node.y, radius + 6 + Math.sin(phase * Math.PI * 2) * 3, 0, Math.PI * 2);
35879
+ ctx.strokeStyle = resolveColor3(mark.tint ?? "var(--color-warning)", canvas);
35880
+ ctx.lineWidth = 1.5;
35881
+ ctx.stroke();
35882
+ ctx.globalAlpha = baseAlpha;
35883
+ }
35706
35884
  if (showLabels && node.label) {
35707
35885
  const displayLabel = truncateLabel(node.label);
35708
35886
  ctx.font = `${isSelected || isHovered ? "700" : "600"} 12px ${fontFamily}`;
@@ -35837,11 +36015,15 @@ var init_GraphCanvas = __esm({
35837
36015
  return;
35838
36016
  }
35839
36017
  }
36018
+ if (node.mark && onMarkClick) {
36019
+ onMarkClick(node);
36020
+ return;
36021
+ }
35840
36022
  handleNodeClick(node);
35841
36023
  }
35842
36024
  }
35843
36025
  },
35844
- [toCoords, nodeAt, handleNodeClick, onBadgeClick, selectedNodeId]
36026
+ [toCoords, nodeAt, handleNodeClick, onBadgeClick, onMarkClick, selectedNodeId]
35845
36027
  );
35846
36028
  const handlePointerLeave = useCallback(() => {
35847
36029
  setHoveredNode(null);
@@ -36368,9 +36550,6 @@ var init_types2 = __esm({
36368
36550
  };
36369
36551
  }
36370
36552
  });
36371
- function humanizeFieldName(name) {
36372
- return name.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1 $2").replace(/[_-]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase()).replace(/\b([A-Z])([A-Z]+)\b/g, (_, first, rest) => first + rest.toLowerCase()).trim();
36373
- }
36374
36553
  function normalizeColumns(columns) {
36375
36554
  return columns.map((col) => {
36376
36555
  if (typeof col === "string") {
@@ -36795,6 +36974,7 @@ var init_DataTable = __esm({
36795
36974
  "components/core/organisms/DataTable.tsx"() {
36796
36975
  "use client";
36797
36976
  init_cn();
36977
+ init_format();
36798
36978
  init_getNestedValue();
36799
36979
  init_atoms();
36800
36980
  init_Box();
@@ -36847,9 +37027,6 @@ function getBadgeVariant(fieldName, value) {
36847
37027
  }
36848
37028
  return "default";
36849
37029
  }
36850
- function formatFieldLabel(fieldName) {
36851
- return fieldName.replace(/([A-Z])/g, " $1").replace(/^./, (str) => str.toUpperCase());
36852
- }
36853
37030
  function formatFieldValue2(value, fieldName) {
36854
37031
  if (typeof value === "number") {
36855
37032
  if (fieldName.toLowerCase().includes("progress") || fieldName.toLowerCase().includes("percent")) {
@@ -36977,7 +37154,7 @@ function buildFieldTypeMap(fields) {
36977
37154
  }
36978
37155
  return map;
36979
37156
  }
36980
- var ReactMarkdown2, DetailPanel;
37157
+ var formatFieldLabel, ReactMarkdown2, DetailPanel;
36981
37158
  var init_DetailPanel = __esm({
36982
37159
  "components/core/organisms/DetailPanel.tsx"() {
36983
37160
  "use client";
@@ -36989,8 +37166,10 @@ var init_DetailPanel = __esm({
36989
37166
  init_ErrorState();
36990
37167
  init_EmptyState();
36991
37168
  init_cn();
37169
+ init_format();
36992
37170
  init_getNestedValue();
36993
37171
  init_useEventBus();
37172
+ formatFieldLabel = humanizeFieldName;
36994
37173
  ReactMarkdown2 = lazy(() => import('react-markdown'));
36995
37174
  DetailPanel = ({
36996
37175
  title: propTitle,
@@ -37319,6 +37498,16 @@ var init_DetailPanel = __esm({
37319
37498
  DetailPanel.displayName = "DetailPanel";
37320
37499
  }
37321
37500
  });
37501
+
37502
+ // components/game/atoms/DrawGroup.tsx
37503
+ function DrawGroup(_props) {
37504
+ return null;
37505
+ }
37506
+ var init_DrawGroup = __esm({
37507
+ "components/game/atoms/DrawGroup.tsx"() {
37508
+ "use client";
37509
+ }
37510
+ });
37322
37511
  function extractTitle(children) {
37323
37512
  if (!React82__default.isValidElement(children)) return void 0;
37324
37513
  const props = children.props;
@@ -38519,7 +38708,7 @@ function formatValue3(value, fieldName) {
38519
38708
  return String(value);
38520
38709
  }
38521
38710
  function formatFieldLabel2(fieldName) {
38522
- return fieldName.replace(/([A-Z])/g, " $1").replace(/^./, (str) => str.toUpperCase()).replace(/Id$/, "").trim();
38711
+ return humanizeFieldName(fieldName).replace(/\sId$/, "").trim();
38523
38712
  }
38524
38713
  var STATUS_STYLES, StatusBadge, ProgressIndicator, List3;
38525
38714
  var init_List = __esm({
@@ -38531,6 +38720,7 @@ var init_List = __esm({
38531
38720
  init_EmptyState();
38532
38721
  init_LoadingState();
38533
38722
  init_cn();
38723
+ init_format();
38534
38724
  init_getNestedValue();
38535
38725
  init_useEventBus();
38536
38726
  init_types2();
@@ -40011,7 +40201,7 @@ function TicksTab({ ticks: ticks2 }) {
40011
40201
  }
40012
40202
  );
40013
40203
  }
40014
- const formatTime2 = (ms) => {
40204
+ const formatTime3 = (ms) => {
40015
40205
  if (ms === 0) return "never";
40016
40206
  const seconds = Math.floor((Date.now() - ms) / 1e3);
40017
40207
  if (seconds < 1) return "just now";
@@ -40037,7 +40227,7 @@ function TicksTab({ ticks: ticks2 }) {
40037
40227
  tick.executionTime.toFixed(1),
40038
40228
  "ms exec"
40039
40229
  ] }),
40040
- /* @__PURE__ */ jsx("span", { children: formatTime2(tick.lastRun) })
40230
+ /* @__PURE__ */ jsx("span", { children: formatTime3(tick.lastRun) })
40041
40231
  ] }),
40042
40232
  tick.guardName && /* @__PURE__ */ jsx("div", { className: "mt-2", children: /* @__PURE__ */ jsxs(Badge, { variant: tick.guardPassed ? "success" : "danger", size: "sm", children: [
40043
40233
  tick.guardName,
@@ -40161,7 +40351,7 @@ function EventFlowTab({ events: events2 }) {
40161
40351
  if (filter === "all") return events2;
40162
40352
  return events2.filter((e) => e.type === filter);
40163
40353
  }, [events2, filter]);
40164
- const formatTime2 = (timestamp) => {
40354
+ const formatTime3 = (timestamp) => {
40165
40355
  const date = new Date(timestamp);
40166
40356
  return date.toLocaleTimeString("en-US", {
40167
40357
  hour12: false,
@@ -40237,7 +40427,7 @@ function EventFlowTab({ events: events2 }) {
40237
40427
  {
40238
40428
  className: "flex items-start gap-2 text-xs py-1 hover:bg-muted/50 rounded px-1",
40239
40429
  children: [
40240
- /* @__PURE__ */ jsx(Typography, { variant: "small", className: "text-muted-foreground font-mono min-w-[65px]", children: formatTime2(event.timestamp) }),
40430
+ /* @__PURE__ */ jsx(Typography, { variant: "small", className: "text-muted-foreground font-mono min-w-[65px]", children: formatTime3(event.timestamp) }),
40241
40431
  /* @__PURE__ */ jsx("span", { children: icon }),
40242
40432
  /* @__PURE__ */ jsx(Badge, { variant, size: "sm", className: "min-w-[60px] justify-center", children: event.source }),
40243
40433
  /* @__PURE__ */ jsx(Typography, { variant: "small", className: "text-muted-foreground", children: event.message })
@@ -40291,7 +40481,7 @@ function GuardsPanel({ guards }) {
40291
40481
  if (filter === "passed") return guards.filter((g) => g.result);
40292
40482
  return guards.filter((g) => !g.result);
40293
40483
  }, [guards, filter]);
40294
- const formatTime2 = (timestamp) => {
40484
+ const formatTime3 = (timestamp) => {
40295
40485
  const date = new Date(timestamp);
40296
40486
  return date.toLocaleTimeString("en-US", {
40297
40487
  hour12: false,
@@ -40306,7 +40496,7 @@ function GuardsPanel({ guards }) {
40306
40496
  /* @__PURE__ */ jsx(Badge, { variant: guard.result ? "success" : "danger", size: "sm", children: guard.result ? "\u2713" : "\u2717" }),
40307
40497
  /* @__PURE__ */ jsx(Typography, { variant: "body", weight: "semibold", className: "text-warning", children: guard.guardName }),
40308
40498
  /* @__PURE__ */ jsx(Typography, { variant: "small", className: "text-muted-foreground", children: guard.context.type === "transition" ? `${guard.context.transitionFrom} \u2192 ${guard.context.transitionTo}` : guard.context.tickName }),
40309
- /* @__PURE__ */ jsx(Typography, { variant: "small", className: "text-muted-foreground ml-auto", children: formatTime2(guard.timestamp) })
40499
+ /* @__PURE__ */ jsx(Typography, { variant: "small", className: "text-muted-foreground ml-auto", children: formatTime3(guard.timestamp) })
40310
40500
  ] }),
40311
40501
  content: /* @__PURE__ */ jsxs(Stack, { gap: "sm", children: [
40312
40502
  /* @__PURE__ */ jsxs("div", { children: [
@@ -40467,7 +40657,7 @@ function TransitionTimeline({ transitions }) {
40467
40657
  }
40468
40658
  );
40469
40659
  }
40470
- const formatTime2 = (ts) => {
40660
+ const formatTime3 = (ts) => {
40471
40661
  const d = new Date(ts);
40472
40662
  return d.toLocaleTimeString("en-US", {
40473
40663
  hour12: false,
@@ -40515,7 +40705,7 @@ function TransitionTimeline({ transitions }) {
40515
40705
  ${hasFailedEffects ? "bg-error" : allPassed ? "bg-success" : "bg-muted-foreground"}
40516
40706
  ` }),
40517
40707
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-xs py-1 px-2", children: [
40518
- /* @__PURE__ */ jsx(Typography, { variant: "small", className: "text-muted-foreground font-mono min-w-[65px]", children: formatTime2(trace.timestamp) }),
40708
+ /* @__PURE__ */ jsx(Typography, { variant: "small", className: "text-muted-foreground font-mono min-w-[65px]", children: formatTime3(trace.timestamp) }),
40519
40709
  /* @__PURE__ */ jsx(Badge, { variant: "primary", size: "sm", className: "min-w-[60px] justify-center", children: trace.traitName }),
40520
40710
  /* @__PURE__ */ jsxs(Typography, { variant: "small", className: "font-mono text-muted-foreground", children: [
40521
40711
  trace.from,
@@ -40590,7 +40780,7 @@ function ServerBridgeTab({ bridge }) {
40590
40780
  }
40591
40781
  );
40592
40782
  }
40593
- const formatTime2 = (ts) => {
40783
+ const formatTime3 = (ts) => {
40594
40784
  if (ts === 0) return t("debug.never");
40595
40785
  const d = new Date(ts);
40596
40786
  return d.toLocaleTimeString("en-US", {
@@ -40633,7 +40823,7 @@ function ServerBridgeTab({ bridge }) {
40633
40823
  StatRow,
40634
40824
  {
40635
40825
  label: t("debug.lastHeartbeat"),
40636
- value: formatTime2(bridge.lastHeartbeat)
40826
+ value: formatTime3(bridge.lastHeartbeat)
40637
40827
  }
40638
40828
  )
40639
40829
  ] })
@@ -42745,7 +42935,7 @@ var init_TeamOrganism = __esm({
42745
42935
  TeamOrganism.displayName = "TeamOrganism";
42746
42936
  }
42747
42937
  });
42748
- function formatDate4(value) {
42938
+ function formatDate2(value) {
42749
42939
  const d = new Date(value);
42750
42940
  if (isNaN(d.getTime())) return value;
42751
42941
  return d.toLocaleDateString(void 0, { year: "numeric", month: "short", day: "numeric" });
@@ -42877,7 +43067,7 @@ var init_Timeline = __esm({
42877
43067
  /* @__PURE__ */ jsxs(VStack, { gap: "xs", className: cn("flex-1 min-w-0", !isLast && "pb-6"), children: [
42878
43068
  /* @__PURE__ */ jsxs(HStack, { justify: "between", align: "start", wrap: true, children: [
42879
43069
  /* @__PURE__ */ jsx(Typography, { variant: "body", weight: "semibold", children: item.title }),
42880
- item.date && /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "secondary", className: "flex-shrink-0", children: formatDate4(item.date) })
43070
+ item.date && /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "secondary", className: "flex-shrink-0", children: formatDate2(item.date) })
42881
43071
  ] }),
42882
43072
  item.description && /* @__PURE__ */ jsx(Typography, { variant: "small", color: "secondary", children: item.description }),
42883
43073
  item.tags && item.tags.length > 0 && /* @__PURE__ */ jsx(HStack, { gap: "xs", wrap: true, children: item.tags.map((tag, tagIdx) => /* @__PURE__ */ jsx(Badge, { variant: "default", children: tag }, tagIdx)) }),
@@ -43040,6 +43230,7 @@ var init_component_registry_generated = __esm({
43040
43230
  init_DocSidebar();
43041
43231
  init_DocTOC();
43042
43232
  init_DocumentViewer();
43233
+ init_DrawGroup();
43043
43234
  init_DrawShape();
43044
43235
  init_DrawShapeLayer();
43045
43236
  init_DrawSprite();
@@ -43306,6 +43497,7 @@ var init_component_registry_generated = __esm({
43306
43497
  "DocSidebar": DocSidebar,
43307
43498
  "DocTOC": DocTOC,
43308
43499
  "DocumentViewer": DocumentViewer,
43500
+ "DrawGroup": DrawGroup,
43309
43501
  "DrawShape": DrawShape,
43310
43502
  "DrawShapeLayer": DrawShapeLayer,
43311
43503
  "DrawSprite": DrawSprite,
@@ -43540,7 +43732,7 @@ function enrichFormFields(fields, entityDef) {
43540
43732
  if (entityField) {
43541
43733
  const enriched = {
43542
43734
  name: field,
43543
- label: field.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/\b\w/g, (c) => c.toUpperCase()),
43735
+ label: humanizeFieldName(field),
43544
43736
  type: entityField.type,
43545
43737
  required: entityField.required ?? false
43546
43738
  };
@@ -43554,7 +43746,7 @@ function enrichFormFields(fields, entityDef) {
43554
43746
  }
43555
43747
  return enriched;
43556
43748
  }
43557
- return { name: field, label: field.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/\b\w/g, (c) => c.toUpperCase()) };
43749
+ return { name: field, label: humanizeFieldName(field) };
43558
43750
  }
43559
43751
  if (field && typeof field === "object" && !Array.isArray(field) && !React82__default.isValidElement(field) && !(field instanceof Date)) {
43560
43752
  const obj = field;
@@ -44209,9 +44401,10 @@ function SlotContentRenderer({
44209
44401
  const slotVal = restProps[slotKey];
44210
44402
  if (slotVal === void 0 || slotVal === null) continue;
44211
44403
  if (React82__default.isValidElement(slotVal) || typeof slotVal === "string" || typeof slotVal === "number" || typeof slotVal === "boolean") continue;
44212
- if (Array.isArray(slotVal) || typeof slotVal === "object" && "type" in slotVal) {
44404
+ const typelessChildren = !Array.isArray(slotVal) && typeof slotVal === "object" && !("type" in slotVal) && Array.isArray(slotVal.children) ? slotVal.children : void 0;
44405
+ if (typelessChildren !== void 0 || Array.isArray(slotVal) || typeof slotVal === "object" && "type" in slotVal) {
44213
44406
  nodeSlotOverrides[slotKey] = renderPatternChildren(
44214
- slotVal,
44407
+ typelessChildren ?? slotVal,
44215
44408
  onDismiss,
44216
44409
  `${content.id}-${slotKey}`,
44217
44410
  `${myPath}.${slotKey}`,
@@ -44390,6 +44583,7 @@ var init_UISlotRenderer = __esm({
44390
44583
  init_useEventBus();
44391
44584
  init_slot_types();
44392
44585
  init_cn();
44586
+ init_format();
44393
44587
  init_ErrorBoundary();
44394
44588
  init_Skeleton();
44395
44589
  init_renderer();
@@ -45736,7 +45930,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
45736
45930
  return;
45737
45931
  }
45738
45932
  crossTraitLog.debug("self:fire", { traitName, busKey: selfBusKey, eventKey });
45739
- enqueueAndDrain(eventKey, event.payload);
45933
+ enqueueAndDrain(eventKey, event.payload, traitName);
45740
45934
  });
45741
45935
  unsubscribes.push(() => {
45742
45936
  crossTraitLog.debug("self:unsubscribe", { traitName, busKey: selfBusKey, eventKey });