@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.
@@ -1147,6 +1147,87 @@ var init_Dialog = __esm({
1147
1147
  }
1148
1148
  });
1149
1149
 
1150
+ // lib/format.ts
1151
+ function humanizeFieldName(name) {
1152
+ 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();
1153
+ }
1154
+ function humanizeEnumValue(value) {
1155
+ return /^[a-z0-9]+(?:[_-][a-z0-9]+)+$/.test(value) ? humanizeFieldName(value) : value;
1156
+ }
1157
+ function formatDate(value) {
1158
+ if (!value) return "";
1159
+ const d = new Date(String(value));
1160
+ if (isNaN(d.getTime())) return String(value);
1161
+ return d.toLocaleDateString(void 0, { year: "numeric", month: "short", day: "numeric" });
1162
+ }
1163
+ function formatTime(value) {
1164
+ if (!value) return "";
1165
+ const d = new Date(String(value));
1166
+ if (isNaN(d.getTime())) return String(value);
1167
+ return d.toLocaleTimeString(void 0, { hour: "numeric", minute: "2-digit" });
1168
+ }
1169
+ function formatDateTime(value) {
1170
+ if (!value) return "";
1171
+ const d = new Date(String(value));
1172
+ if (isNaN(d.getTime())) return String(value);
1173
+ return `${formatDate(value)} ${formatTime(value)}`;
1174
+ }
1175
+ function asYesNo(value) {
1176
+ return value === false || value === 0 || String(value) === "false" ? "No" : "Yes";
1177
+ }
1178
+ function formatValue(value, format) {
1179
+ if (value === void 0 || value === null) return "";
1180
+ if (typeof value === "boolean") return asYesNo(value);
1181
+ switch (format) {
1182
+ case "date":
1183
+ return formatDate(value);
1184
+ case "time":
1185
+ return formatTime(value);
1186
+ case "datetime":
1187
+ return formatDateTime(value);
1188
+ case "currency":
1189
+ return typeof value === "number" ? `$${value.toFixed(2)}` : String(value);
1190
+ case "number":
1191
+ return typeof value === "number" ? value.toLocaleString() : String(value);
1192
+ case "percent":
1193
+ return typeof value === "number" ? `${Math.round(value)}%` : String(value);
1194
+ case "boolean":
1195
+ return asYesNo(value);
1196
+ default:
1197
+ return String(value);
1198
+ }
1199
+ }
1200
+ function compareCellValues(a, b) {
1201
+ const aEmpty = a === null || a === void 0 || a === "";
1202
+ const bEmpty = b === null || b === void 0 || b === "";
1203
+ if (aEmpty || bEmpty) return aEmpty && bEmpty ? 0 : aEmpty ? 1 : -1;
1204
+ if (typeof a === "number" && typeof b === "number") return a - b;
1205
+ if (typeof a === "boolean" && typeof b === "boolean") return Number(a) - Number(b);
1206
+ const aNum = Number(a);
1207
+ const bNum = Number(b);
1208
+ if (Number.isFinite(aNum) && Number.isFinite(bNum)) return aNum - bNum;
1209
+ const aTime = dateLikeTime(a);
1210
+ const bTime = dateLikeTime(b);
1211
+ if (aTime !== null && bTime !== null) return aTime - bTime;
1212
+ return String(a).localeCompare(String(b));
1213
+ }
1214
+ function dateLikeTime(value) {
1215
+ if (value instanceof Date) return value.getTime();
1216
+ const text = String(value);
1217
+ if (!/\d/.test(text) || !/[-/:T]/.test(text)) return null;
1218
+ const time = Date.parse(text);
1219
+ return Number.isNaN(time) ? null : time;
1220
+ }
1221
+ function sortRows(rows, field, direction = "asc") {
1222
+ if (!field) return rows;
1223
+ const dir = direction === "desc" ? -1 : 1;
1224
+ return [...rows].sort((a, b) => dir * compareCellValues(a?.[field], b?.[field]));
1225
+ }
1226
+ var init_format = __esm({
1227
+ "lib/format.ts"() {
1228
+ }
1229
+ });
1230
+
1150
1231
  // components/core/atoms/Typography.tsx
1151
1232
  var Typography_exports = {};
1152
1233
  __export(Typography_exports, {
@@ -1156,6 +1237,7 @@ var variantStyles2, colorStyles, weightStyles, defaultElements, typographySizeSt
1156
1237
  var init_Typography = __esm({
1157
1238
  "components/core/atoms/Typography.tsx"() {
1158
1239
  init_cn();
1240
+ init_format();
1159
1241
  variantStyles2 = {
1160
1242
  h1: "text-4xl font-bold tracking-tight text-foreground",
1161
1243
  h2: "text-3xl font-bold tracking-tight text-foreground",
@@ -1243,11 +1325,16 @@ var init_Typography = __esm({
1243
1325
  id,
1244
1326
  className,
1245
1327
  style,
1328
+ format,
1246
1329
  content,
1247
1330
  children
1248
1331
  }) => {
1249
1332
  const variant = variantProp ?? (level ? `h${level}` : "body1");
1250
1333
  const Component = as || defaultElements[variant];
1334
+ let body = children ?? content;
1335
+ if (format !== void 0 && format !== "none" && (typeof body === "string" || typeof body === "number" || body instanceof Date)) {
1336
+ body = formatValue(body, format);
1337
+ }
1251
1338
  return React84__default.createElement(
1252
1339
  Component,
1253
1340
  {
@@ -1264,7 +1351,7 @@ var init_Typography = __esm({
1264
1351
  ),
1265
1352
  style
1266
1353
  },
1267
- children ?? content
1354
+ body
1268
1355
  );
1269
1356
  };
1270
1357
  Typography.displayName = "Typography";
@@ -7352,7 +7439,7 @@ var init_ControlButton = __esm({
7352
7439
  ControlButton.displayName = "ControlButton";
7353
7440
  }
7354
7441
  });
7355
- function formatTime(seconds, format) {
7442
+ function formatTime2(seconds, format) {
7356
7443
  const clamped = Math.max(0, Math.floor(seconds));
7357
7444
  if (format === "ss") {
7358
7445
  return `${clamped}s`;
@@ -7389,7 +7476,7 @@ function TimerDisplay({
7389
7476
  ),
7390
7477
  children: [
7391
7478
  iconAsset && /* @__PURE__ */ jsx(GameIcon, { assetUrl: iconAsset, icon: "image", size: 16, className: "w-4 h-4 object-contain flex-shrink-0" }),
7392
- formatTime(seconds, format)
7479
+ formatTime2(seconds, format)
7393
7480
  ]
7394
7481
  }
7395
7482
  );
@@ -8711,6 +8798,15 @@ function createWebPainter(ctx, onAssetLoad) {
8711
8798
  ctx.lineWidth = lineWidth;
8712
8799
  ctx.stroke();
8713
8800
  },
8801
+ fillPath(d, color) {
8802
+ ctx.fillStyle = color;
8803
+ ctx.fill(new Path2D(d));
8804
+ },
8805
+ strokePath(d, color, lineWidth = 1) {
8806
+ ctx.strokeStyle = color;
8807
+ ctx.lineWidth = lineWidth;
8808
+ ctx.stroke(new Path2D(d));
8809
+ },
8714
8810
  text(str, x, y, style) {
8715
8811
  if (style.font) ctx.font = style.font;
8716
8812
  ctx.fillStyle = style.color;
@@ -8886,6 +8982,16 @@ var init_DrawShape = __esm({
8886
8982
  if (node.stroke) painter.strokePoly(pts, node.stroke, node.strokeWidth ?? 1, true);
8887
8983
  break;
8888
8984
  }
8985
+ case "path": {
8986
+ if (!node.d) break;
8987
+ const base = dctx.projector.project(node.position);
8988
+ const tw = dctx.projector.tileWidth;
8989
+ painter.translate(base.x, base.y);
8990
+ painter.scale(tw, tw);
8991
+ if (node.fill) painter.fillPath(node.d, node.fill);
8992
+ if (node.stroke) painter.strokePath(node.d, node.stroke, (node.strokeWidth ?? 1) / tw);
8993
+ break;
8994
+ }
8889
8995
  }
8890
8996
  painter.restore();
8891
8997
  };
@@ -8977,6 +9083,19 @@ function paintDrawable(painter, node, dctx) {
8977
9083
  case "draw-text":
8978
9084
  paintText(painter, node, dctx);
8979
9085
  break;
9086
+ case "draw-group": {
9087
+ if (!isValidScenePos(node.position)) break;
9088
+ if (!Array.isArray(node.items)) break;
9089
+ const p = dctx.projector.project(node.position);
9090
+ painter.save();
9091
+ painter.translate(p.x, p.y);
9092
+ if (node.scale !== void 0) painter.scale(node.scale, node.scale);
9093
+ if (node.rotate !== void 0) painter.rotate(node.rotate);
9094
+ if (node.opacity !== void 0 && node.opacity !== 1) painter.setAlpha(node.opacity);
9095
+ for (const item of node.items) paintDrawable(painter, item, dctx);
9096
+ painter.restore();
9097
+ break;
9098
+ }
8980
9099
  case "draw-sprite-layer":
8981
9100
  paintSpriteLayer(painter, node, dctx);
8982
9101
  break;
@@ -8990,6 +9109,7 @@ function paintDrawable(painter, node, dctx) {
8990
9109
  }
8991
9110
  var init_paintDispatch = __esm({
8992
9111
  "lib/drawable/paintDispatch.ts"() {
9112
+ init_contract();
8993
9113
  init_DrawSprite();
8994
9114
  init_DrawShape();
8995
9115
  init_DrawText();
@@ -9007,6 +9127,7 @@ function collectDrawnItems(nodes) {
9007
9127
  case "draw-sprite":
9008
9128
  case "draw-shape":
9009
9129
  case "draw-text":
9130
+ case "draw-group":
9010
9131
  if (isValidScenePos(n.position)) out.push({ pos: n.position, id: n.id });
9011
9132
  break;
9012
9133
  case "draw-sprite-layer":
@@ -19391,9 +19512,6 @@ function normalizeFields(fields) {
19391
19512
  if (!fields) return [];
19392
19513
  return fields.map((f3) => typeof f3 === "string" ? f3 : f3.key ?? f3.name ?? "");
19393
19514
  }
19394
- function fieldLabel(key) {
19395
- return key.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/[_-]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
19396
- }
19397
19515
  function asBooleanValue(value) {
19398
19516
  if (typeof value === "boolean") return value;
19399
19517
  if (value === "true") return true;
@@ -19404,12 +19522,6 @@ function isDateField(key) {
19404
19522
  const lower = key.toLowerCase();
19405
19523
  return lower.includes("date") || lower.includes("time") || lower.endsWith("at") || lower.endsWith("_at");
19406
19524
  }
19407
- function formatDate(value) {
19408
- if (!value) return "";
19409
- const d = new Date(String(value));
19410
- if (isNaN(d.getTime())) return String(value);
19411
- return d.toLocaleDateString(void 0, { year: "numeric", month: "short", day: "numeric" });
19412
- }
19413
19525
  function statusVariant(value) {
19414
19526
  const v = value.toLowerCase();
19415
19527
  if (["active", "completed", "done", "approved", "published", "resolved", "open"].includes(v)) return "success";
@@ -19418,11 +19530,12 @@ function statusVariant(value) {
19418
19530
  if (["new", "created", "scheduled", "queued"].includes(v)) return "info";
19419
19531
  return "default";
19420
19532
  }
19421
- var STATUS_FIELDS, gapStyles3, alignStyles2, CardGrid;
19533
+ var fieldLabel, STATUS_FIELDS, gapStyles3, alignStyles2, CardGrid;
19422
19534
  var init_CardGrid = __esm({
19423
19535
  "components/core/organisms/CardGrid.tsx"() {
19424
19536
  "use client";
19425
19537
  init_cn();
19538
+ init_format();
19426
19539
  init_getNestedValue();
19427
19540
  init_useEventBus();
19428
19541
  init_atoms();
@@ -19431,6 +19544,7 @@ var init_CardGrid = __esm({
19431
19544
  init_Typography();
19432
19545
  init_Stack();
19433
19546
  init_Pagination();
19547
+ fieldLabel = humanizeFieldName;
19434
19548
  STATUS_FIELDS = /* @__PURE__ */ new Set(["status", "state", "priority", "type", "category", "role", "level", "tier"]);
19435
19549
  gapStyles3 = {
19436
19550
  none: "gap-0",
@@ -19938,7 +20052,7 @@ var init_CaseStudyOrganism = __esm({
19938
20052
  CaseStudyOrganism.displayName = "CaseStudyOrganism";
19939
20053
  }
19940
20054
  });
19941
- var CHART_COLORS, seriesColor, monthFormatter, formatTimeLabel, BarChart, PieChart, LineChart, ScatterChart, LOOK_FROM_CHART_TYPE, Chart;
20055
+ var CHART_COLORS, seriesColor, barColor, monthFormatter, formatTimeLabel, BarChart, PieChart, LineChart, ScatterChart, LOOK_FROM_CHART_TYPE, Chart;
19942
20056
  var init_Chart = __esm({
19943
20057
  "components/core/molecules/Chart.tsx"() {
19944
20058
  "use client";
@@ -19958,6 +20072,7 @@ var init_Chart = __esm({
19958
20072
  "var(--color-accent)"
19959
20073
  ];
19960
20074
  seriesColor = (series, idx) => series.color ?? CHART_COLORS[idx % CHART_COLORS.length];
20075
+ barColor = (series, sIdx, catIdx, seriesCount) => series.color ?? CHART_COLORS[(seriesCount === 1 ? catIdx : sIdx) % CHART_COLORS.length];
19961
20076
  monthFormatter = new Intl.DateTimeFormat(void 0, {
19962
20077
  month: "short",
19963
20078
  year: "2-digit"
@@ -20030,7 +20145,7 @@ var init_Chart = __esm({
20030
20145
  children: series.map((s, sIdx) => {
20031
20146
  const value = valueAt(s, label);
20032
20147
  const ratio = stack === "normalize" ? total === 0 ? 0 : value / total * 100 : value / maxValue * 100;
20033
- const color = seriesColor(s, sIdx);
20148
+ const color = barColor(s, sIdx, catIdx, series.length);
20034
20149
  return /* @__PURE__ */ jsx(
20035
20150
  Box,
20036
20151
  {
@@ -20085,7 +20200,7 @@ var init_Chart = __esm({
20085
20200
  /* @__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) => {
20086
20201
  const value = valueAt(s, label);
20087
20202
  const barHeight = value / maxValue * 100;
20088
- const color = seriesColor(s, sIdx);
20203
+ const color = barColor(s, sIdx, catIdx, series.length);
20089
20204
  return /* @__PURE__ */ jsx(
20090
20205
  Box,
20091
20206
  {
@@ -20136,7 +20251,7 @@ var init_Chart = __esm({
20136
20251
  /* @__PURE__ */ jsx(VStack, { gap: "none", className: "w-full flex-1 min-h-0", justify: "end", children: series.map((s, sIdx) => {
20137
20252
  const value = valueAt(s, label);
20138
20253
  const ratio = stack === "normalize" ? total === 0 ? 0 : value / total * 100 : value / maxValue * 100;
20139
- const color = seriesColor(s, sIdx);
20254
+ const color = barColor(s, sIdx, catIdx, series.length);
20140
20255
  return /* @__PURE__ */ jsx(
20141
20256
  Box,
20142
20257
  {
@@ -22998,9 +23113,6 @@ var init_useDataDnd = __esm({
22998
23113
  function renderIconInput(icon, props) {
22999
23114
  return typeof icon === "string" ? /* @__PURE__ */ jsx(Icon, { name: icon, ...props }) : /* @__PURE__ */ jsx(Icon, { icon, ...props });
23000
23115
  }
23001
- function fieldLabel2(key) {
23002
- return key.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/[_-]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
23003
- }
23004
23116
  function statusVariant2(value) {
23005
23117
  const v = value.toLowerCase();
23006
23118
  if (["active", "completed", "done", "approved", "published", "resolved", "open", "online"].includes(v)) return "success";
@@ -23019,29 +23131,6 @@ function resolveBadgeVariant(field, value) {
23019
23131
  }
23020
23132
  return statusVariant2(value);
23021
23133
  }
23022
- function formatDate2(value) {
23023
- if (!value) return "";
23024
- const d = new Date(String(value));
23025
- if (isNaN(d.getTime())) return String(value);
23026
- return d.toLocaleDateString(void 0, { year: "numeric", month: "short", day: "numeric" });
23027
- }
23028
- function formatValue(value, format) {
23029
- if (value === void 0 || value === null) return "";
23030
- switch (format) {
23031
- case "date":
23032
- return formatDate2(value);
23033
- case "currency":
23034
- return typeof value === "number" ? `$${value.toFixed(2)}` : String(value);
23035
- case "number":
23036
- return typeof value === "number" ? value.toLocaleString() : String(value);
23037
- case "percent":
23038
- return typeof value === "number" ? `${Math.round(value)}%` : String(value);
23039
- case "boolean":
23040
- return value ? "Yes" : "No";
23041
- default:
23042
- return String(value);
23043
- }
23044
- }
23045
23134
  function DataGrid({
23046
23135
  entity,
23047
23136
  fields,
@@ -23328,7 +23417,7 @@ function DataGrid({
23328
23417
  if (val === void 0 || val === null || val === "") return null;
23329
23418
  return /* @__PURE__ */ jsxs(HStack, { gap: "xs", className: "items-center", children: [
23330
23419
  field.icon && renderIconInput(field.icon, { size: "xs" }),
23331
- /* @__PURE__ */ jsx(Badge, { variant: resolveBadgeVariant(field, String(val)), children: formatValue(val, field.format) })
23420
+ /* @__PURE__ */ jsx(Badge, { variant: resolveBadgeVariant(field, String(val)), children: humanizeEnumValue(formatValue(val, field.format)) })
23332
23421
  ] }, field.name);
23333
23422
  }) })
23334
23423
  ] }),
@@ -23438,11 +23527,12 @@ function DataGrid({
23438
23527
  ] })
23439
23528
  );
23440
23529
  }
23441
- var dataGridLog, BADGE_VARIANTS, gapStyles5, lookStyles5;
23530
+ var dataGridLog, fieldLabel2, BADGE_VARIANTS, gapStyles5, lookStyles5;
23442
23531
  var init_DataGrid = __esm({
23443
23532
  "components/core/molecules/DataGrid.tsx"() {
23444
23533
  "use client";
23445
23534
  init_cn();
23535
+ init_format();
23446
23536
  init_getNestedValue();
23447
23537
  init_useEventBus();
23448
23538
  init_Box();
@@ -23455,6 +23545,7 @@ var init_DataGrid = __esm({
23455
23545
  init_Menu();
23456
23546
  init_useDataDnd();
23457
23547
  dataGridLog = createLogger("almadar:ui:data-grid");
23548
+ fieldLabel2 = humanizeFieldName;
23458
23549
  BADGE_VARIANTS = /* @__PURE__ */ new Set([
23459
23550
  "default",
23460
23551
  "primary",
@@ -23486,9 +23577,6 @@ var init_DataGrid = __esm({
23486
23577
  function renderIconInput2(icon, props) {
23487
23578
  return typeof icon === "string" ? /* @__PURE__ */ jsx(Icon, { name: icon, ...props }) : /* @__PURE__ */ jsx(Icon, { icon, ...props });
23488
23579
  }
23489
- function fieldLabel3(key) {
23490
- return key.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/[_-]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
23491
- }
23492
23580
  function statusVariant3(value) {
23493
23581
  const v = value.toLowerCase();
23494
23582
  if (["active", "completed", "done", "approved", "published", "resolved", "open", "online"].includes(v)) return "success";
@@ -23497,28 +23585,12 @@ function statusVariant3(value) {
23497
23585
  if (["new", "created", "scheduled", "queued", "info"].includes(v)) return "info";
23498
23586
  return "default";
23499
23587
  }
23500
- function formatDate3(value) {
23501
- if (!value) return "";
23502
- const d = new Date(String(value));
23503
- if (isNaN(d.getTime())) return String(value);
23504
- return d.toLocaleDateString(void 0, { year: "numeric", month: "short", day: "numeric" });
23505
- }
23506
23588
  function formatValue2(value, format, boolLabels) {
23507
- if (value === void 0 || value === null) return "";
23508
- switch (format) {
23509
- case "date":
23510
- return formatDate3(value);
23511
- case "currency":
23512
- return typeof value === "number" ? `$${value.toFixed(2)}` : String(value);
23513
- case "number":
23514
- return typeof value === "number" ? value.toLocaleString() : String(value);
23515
- case "percent":
23516
- return typeof value === "number" ? `${Math.round(value)}%` : String(value);
23517
- case "boolean":
23518
- return value ? boolLabels?.yes ?? "Yes" : boolLabels?.no ?? "No";
23519
- default:
23520
- return String(value);
23589
+ if (value !== void 0 && value !== null && (format === "boolean" || typeof value === "boolean")) {
23590
+ const isNo = value === false || value === 0 || String(value) === "false";
23591
+ return isNo ? boolLabels?.no ?? "No" : boolLabels?.yes ?? "Yes";
23521
23592
  }
23593
+ return formatValue(value, format);
23522
23594
  }
23523
23595
  function groupData(items, field) {
23524
23596
  const groups = /* @__PURE__ */ new Map();
@@ -23541,7 +23613,9 @@ function DataList({
23541
23613
  variant = "default",
23542
23614
  groupBy,
23543
23615
  senderField,
23616
+ senderLabelField,
23544
23617
  currentUser,
23618
+ emptyMessage,
23545
23619
  className,
23546
23620
  isLoading = false,
23547
23621
  error = null,
@@ -23560,6 +23634,8 @@ function DataList({
23560
23634
  hasMore,
23561
23635
  children,
23562
23636
  pageSize = 5,
23637
+ sortBy,
23638
+ sortDirection,
23563
23639
  renderItem: schemaRenderItem,
23564
23640
  dragGroup,
23565
23641
  accepts,
@@ -23588,7 +23664,11 @@ function DataList({
23588
23664
  dndItemIdField,
23589
23665
  dndRoot
23590
23666
  });
23591
- const allData = dnd.orderedItems;
23667
+ const orderedData = dnd.orderedItems;
23668
+ const allData = React84__default.useMemo(
23669
+ () => sortRows(orderedData, sortBy, sortDirection),
23670
+ [orderedData, sortBy, sortDirection]
23671
+ );
23592
23672
  const data = pageSize > 0 ? allData.slice(0, visibleCount) : allData;
23593
23673
  const hasMoreLocal = pageSize > 0 && visibleCount < allData.length;
23594
23674
  const hasRenderProp = typeof children === "function";
@@ -23625,7 +23705,7 @@ function DataList({
23625
23705
  };
23626
23706
  eventBus.emit(`UI:${action.event}`, payload);
23627
23707
  };
23628
- const renderItemActions = (itemData) => {
23708
+ const renderItemActions = (itemData, onPrimary = false) => {
23629
23709
  if (!itemActions || itemActions.length === 0) return null;
23630
23710
  const inline = maxInlineActions != null ? itemActions.slice(0, maxInlineActions) : itemActions;
23631
23711
  const overflow = maxInlineActions != null ? itemActions.slice(maxInlineActions) : [];
@@ -23638,7 +23718,12 @@ function DataList({
23638
23718
  onClick: handleActionClick(action, itemData),
23639
23719
  "data-testid": `action-${action.event}`,
23640
23720
  "data-row-id": String(itemData.id),
23641
- className: cn(action.variant === "danger" && "text-error hover:bg-error/10"),
23721
+ className: cn(
23722
+ action.variant === "danger" && "text-error hover:bg-error/10",
23723
+ // Must sit on the Button itself: the variant's own text colour
23724
+ // beats an inherited one from the row wrapper.
23725
+ onPrimary && "!text-primary-foreground hover:bg-primary-foreground/15"
23726
+ ),
23642
23727
  children: [
23643
23728
  action.icon && renderIconInput2(action.icon, { size: "xs", className: "mr-1" }),
23644
23729
  action.label
@@ -23680,7 +23765,7 @@ function DataList({
23680
23765
  return /* @__PURE__ */ jsx(Box, { className: "text-center py-8", children: /* @__PURE__ */ jsx(Typography, { variant: "body", color: "error", children: error.message }) });
23681
23766
  }
23682
23767
  if (data.length === 0) {
23683
- const emptyNode = /* @__PURE__ */ jsx(Box, { className: "text-center py-12", children: /* @__PURE__ */ jsx(Typography, { variant: "body", color: "secondary", children: t("empty.noItems") }) });
23768
+ const emptyNode = /* @__PURE__ */ jsx(Box, { className: "text-center py-12", children: /* @__PURE__ */ jsx(Typography, { variant: "body", color: "secondary", children: emptyMessage || t("empty.noItems") }) });
23684
23769
  return dnd.enabled ? /* @__PURE__ */ jsx(Fragment, { children: dnd.wrapContainer(emptyNode) }) : emptyNode;
23685
23770
  }
23686
23771
  const gapClass = {
@@ -23696,51 +23781,93 @@ function DataList({
23696
23781
  const items2 = [...data];
23697
23782
  const groups2 = groupBy ? groupData(items2, groupBy) : [{ label: "", items: items2 }];
23698
23783
  const contentField = titleField?.name ?? fieldDefs[0]?.name ?? "";
23699
- return /* @__PURE__ */ jsx(VStack, { gap: "sm", className: cn("py-2", className), children: groups2.map((group, gi) => /* @__PURE__ */ jsxs(React84__default.Fragment, { children: [
23700
- group.label && /* @__PURE__ */ jsx(Divider, { label: group.label, className: "my-2" }),
23701
- group.items.map((itemData, index) => {
23702
- const id = itemData.id || `${gi}-${index}`;
23703
- const sender = senderField ? String(getNestedValue(itemData, senderField) ?? "") : "";
23704
- const isSent = Boolean(currentUser && sender === currentUser);
23705
- const content = getNestedValue(itemData, contentField);
23706
- const timestampField = fieldDefs.find((f3) => f3.format === "date");
23707
- const timestamp = timestampField ? getNestedValue(itemData, timestampField.name) : null;
23708
- return /* @__PURE__ */ jsx(
23709
- Box,
23710
- {
23711
- className: cn(
23712
- "flex px-4",
23713
- isSent ? "justify-end" : "justify-start"
23714
- ),
23715
- children: /* @__PURE__ */ jsxs(
23716
- Box,
23717
- {
23718
- className: cn(
23719
- "max-w-[75%] px-4 py-2",
23720
- isSent ? "bg-primary text-primary-foreground rounded-2xl rounded-br-sm" : "bg-muted text-foreground rounded-2xl rounded-bl-sm"
23721
- ),
23722
- children: [
23723
- !isSent && senderField && /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "font-semibold mb-0.5", children: sender }),
23724
- /* @__PURE__ */ jsx(Typography, { variant: "body", className: cn(isSent && "text-primary-foreground"), children: content !== void 0 && content !== null ? String(content) : "" }),
23725
- timestamp != null ? /* @__PURE__ */ jsx(
23726
- Typography,
23727
- {
23728
- variant: "caption",
23729
- className: cn(
23730
- "mt-1 text-xs",
23731
- isSent ? "opacity-70" : "text-muted-foreground"
23732
- ),
23733
- children: formatDate3(timestamp)
23734
- }
23735
- ) : null
23736
- ]
23737
- }
23738
- )
23739
- },
23740
- id
23741
- );
23742
- })
23743
- ] }, gi)) });
23784
+ const senderLabel = (itemData, raw) => {
23785
+ if (!senderLabelField) return raw;
23786
+ const v = getNestedValue(itemData, senderLabelField);
23787
+ return v === void 0 || v === null || v === "" ? raw : String(v);
23788
+ };
23789
+ return /* @__PURE__ */ jsxs(VStack, { gap: "sm", className: cn("py-2", className), children: [
23790
+ groups2.map((group, gi) => /* @__PURE__ */ jsxs(React84__default.Fragment, { children: [
23791
+ group.label && /* @__PURE__ */ jsx(Divider, { label: group.label, className: "my-2" }),
23792
+ group.items.map((itemData, index) => {
23793
+ const id = itemData.id || `${gi}-${index}`;
23794
+ const sender = senderField ? String(getNestedValue(itemData, senderField) ?? "") : "";
23795
+ const isSent = Boolean(currentUser && sender === currentUser);
23796
+ const content = getNestedValue(itemData, contentField);
23797
+ const timestampField = fieldDefs.find((f3) => f3.format === "date");
23798
+ const timestamp = timestampField ? getNestedValue(itemData, timestampField.name) : null;
23799
+ const metaFields = fieldDefs.filter(
23800
+ (f3) => f3.name !== contentField && f3.name !== timestampField?.name
23801
+ );
23802
+ return /* @__PURE__ */ jsx(
23803
+ Box,
23804
+ {
23805
+ "data-entity-row": true,
23806
+ "data-entity-id": id,
23807
+ onClick: itemClickEvent ? handleRowClick(itemData) : void 0,
23808
+ className: cn(
23809
+ "flex px-4 group/rowactions",
23810
+ itemClickEvent && "cursor-pointer",
23811
+ isSent ? "justify-end" : "justify-start"
23812
+ ),
23813
+ children: /* @__PURE__ */ jsxs(
23814
+ Box,
23815
+ {
23816
+ className: cn(
23817
+ "max-w-[75%] px-4 py-2",
23818
+ isSent ? "bg-primary text-primary-foreground rounded-2xl rounded-br-sm" : "bg-muted text-foreground rounded-2xl rounded-bl-sm"
23819
+ ),
23820
+ children: [
23821
+ !isSent && senderField && /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "font-semibold mb-0.5", children: senderLabel(itemData, sender) }),
23822
+ /* @__PURE__ */ jsx(Typography, { variant: "body", className: cn(isSent && "text-primary-foreground"), children: content !== void 0 && content !== null ? String(content) : "" }),
23823
+ metaFields.length > 0 && /* @__PURE__ */ jsx(HStack, { gap: "xs", className: "mt-1 flex-wrap", children: metaFields.map((f3) => {
23824
+ const v = getNestedValue(itemData, f3.name);
23825
+ if (v === void 0 || v === null || v === "") return null;
23826
+ return f3.variant === "badge" ? /* @__PURE__ */ jsx(Badge, { variant: statusVariant3(String(v)), children: String(v) }, f3.name) : /* @__PURE__ */ jsx(
23827
+ Typography,
23828
+ {
23829
+ variant: "caption",
23830
+ className: cn("text-xs", isSent ? "opacity-70" : "text-muted-foreground"),
23831
+ children: formatValue2(v, f3.format)
23832
+ },
23833
+ f3.name
23834
+ );
23835
+ }) }),
23836
+ /* @__PURE__ */ jsxs(HStack, { gap: "xs", className: "mt-1 items-center justify-between", children: [
23837
+ timestamp != null ? /* @__PURE__ */ jsx(
23838
+ Typography,
23839
+ {
23840
+ variant: "caption",
23841
+ className: cn("text-xs", isSent ? "opacity-70" : "text-muted-foreground"),
23842
+ children: formatDate(timestamp)
23843
+ }
23844
+ ) : /* @__PURE__ */ jsx("span", {}),
23845
+ renderItemActions(itemData, isSent)
23846
+ ] })
23847
+ ]
23848
+ }
23849
+ )
23850
+ },
23851
+ id
23852
+ );
23853
+ })
23854
+ ] }, gi)),
23855
+ hasMoreLocal && /* @__PURE__ */ jsx(Box, { className: "flex justify-center py-3", children: /* @__PURE__ */ jsxs(
23856
+ Button,
23857
+ {
23858
+ variant: "ghost",
23859
+ size: "sm",
23860
+ onClick: () => setVisibleCount((prev) => prev + (pageSize || 5)),
23861
+ children: [
23862
+ /* @__PURE__ */ jsx(Icon, { name: "chevron-down", size: "xs", className: "mr-1" }),
23863
+ t("common.showMore"),
23864
+ " (",
23865
+ t("common.remaining", { count: allData.length - visibleCount }),
23866
+ ")"
23867
+ ]
23868
+ }
23869
+ ) })
23870
+ ] });
23744
23871
  }
23745
23872
  const items = [...data];
23746
23873
  const groups = groupBy ? groupData(items, groupBy) : [{ label: "", items }];
@@ -23889,11 +24016,12 @@ function DataList({
23889
24016
  )
23890
24017
  );
23891
24018
  }
23892
- var dataListLog, listLookStyles;
24019
+ var dataListLog, fieldLabel3, listLookStyles;
23893
24020
  var init_DataList = __esm({
23894
24021
  "components/core/molecules/DataList.tsx"() {
23895
24022
  "use client";
23896
24023
  init_cn();
24024
+ init_format();
23897
24025
  init_getNestedValue();
23898
24026
  init_useEventBus();
23899
24027
  init_Box();
@@ -23908,6 +24036,7 @@ var init_DataList = __esm({
23908
24036
  init_Menu();
23909
24037
  init_useDataDnd();
23910
24038
  dataListLog = createLogger("almadar:ui:data-list");
24039
+ fieldLabel3 = humanizeFieldName;
23911
24040
  listLookStyles = {
23912
24041
  dense: "[&_[data-entity-row]>div]:!py-1 [&_[data-entity-row]>div]:!px-3",
23913
24042
  spacious: "[&_[data-entity-row]>div]:!py-5 [&_[data-entity-row]>div]:!px-8",
@@ -28263,7 +28392,7 @@ function renderIconInput3(icon, props) {
28263
28392
  return typeof icon === "string" ? /* @__PURE__ */ jsx(Icon, { name: icon, ...props }) : /* @__PURE__ */ jsx(Icon, { icon, ...props });
28264
28393
  }
28265
28394
  function columnLabel(col) {
28266
- return col.header ?? col.label ?? col.key.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/[_-]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
28395
+ return col.header ?? col.label ?? humanizeFieldName(col.key);
28267
28396
  }
28268
28397
  function asFieldValue(v) {
28269
28398
  if (v === void 0 || v === null) return v;
@@ -28282,25 +28411,6 @@ function statusVariant4(value) {
28282
28411
  if (["new", "created", "scheduled", "queued", "info"].includes(v)) return "info";
28283
28412
  return "default";
28284
28413
  }
28285
- function formatCell(value, format) {
28286
- if (value === void 0 || value === null) return "";
28287
- switch (format) {
28288
- case "date": {
28289
- const d = new Date(String(value));
28290
- return isNaN(d.getTime()) ? String(value) : d.toLocaleDateString(void 0, { year: "numeric", month: "short", day: "numeric" });
28291
- }
28292
- case "currency":
28293
- return typeof value === "number" ? `$${value.toFixed(2)}` : String(value);
28294
- case "number":
28295
- return typeof value === "number" ? value.toLocaleString() : String(value);
28296
- case "percent":
28297
- return typeof value === "number" ? `${Math.round(value)}%` : String(value);
28298
- case "boolean":
28299
- return value ? "Yes" : "No";
28300
- default:
28301
- return String(value);
28302
- }
28303
- }
28304
28414
  function groupData2(items, field) {
28305
28415
  const groups = /* @__PURE__ */ new Map();
28306
28416
  for (const item of items) {
@@ -28346,7 +28456,8 @@ function TableView({
28346
28456
  const { t } = useTranslate();
28347
28457
  const [visibleCount, setVisibleCount] = React84__default.useState(pageSize > 0 ? pageSize : Infinity);
28348
28458
  const [localSelected, setLocalSelected] = React84__default.useState(/* @__PURE__ */ new Set());
28349
- const colDefs = columns ?? fields ?? [];
28459
+ const colDefs = (Array.isArray(columns) ? columns : void 0) ?? (Array.isArray(fields) ? fields : void 0) ?? [];
28460
+ const actionDefs = Array.isArray(itemActions) ? itemActions : [];
28350
28461
  const allDataRaw = Array.isArray(entity) ? entity : entity ? [entity] : [];
28351
28462
  const dnd = useDataDnd({
28352
28463
  items: allDataRaw,
@@ -28366,6 +28477,23 @@ function TableView({
28366
28477
  const hasRenderProp = typeof children === "function";
28367
28478
  const idField = dndItemIdField ?? "id";
28368
28479
  const isCoarsePointer = useMediaQuery("(pointer: coarse)");
28480
+ React84__default.useEffect(() => {
28481
+ tableViewLog.debug("render", {
28482
+ rowCount: data.length,
28483
+ colCount: colDefs.length,
28484
+ look,
28485
+ isLoading: Boolean(isLoading),
28486
+ hasError: Boolean(error),
28487
+ dnd: dnd.enabled
28488
+ });
28489
+ if (data.length > 0 && !hasRenderProp && colDefs.length === 0) {
28490
+ tableViewLog.warn("columns-unresolved", {
28491
+ rowCount: data.length,
28492
+ columnsIsArray: Array.isArray(columns),
28493
+ fieldsIsArray: Array.isArray(fields)
28494
+ });
28495
+ }
28496
+ }, [data, colDefs, look, isLoading, error, dnd.enabled, hasRenderProp, columns, fields]);
28369
28497
  const selected = selectedIds ? new Set(selectedIds) : localSelected;
28370
28498
  const emitSelection = (next) => {
28371
28499
  if (!selectedIds) setLocalSelected(next);
@@ -28409,21 +28537,12 @@ function TableView({
28409
28537
  }),
28410
28538
  [colDefs, data]
28411
28539
  );
28412
- if (isLoading) {
28413
- return /* @__PURE__ */ jsx(Box, { className: "text-center py-8", children: /* @__PURE__ */ jsx(Typography, { variant: "body", color: "secondary", children: t("loading.items") }) });
28414
- }
28415
- if (error) {
28416
- return /* @__PURE__ */ jsx(Box, { className: "text-center py-8", children: /* @__PURE__ */ jsx(Typography, { variant: "body", color: "error", children: error.message }) });
28417
- }
28418
- if (data.length === 0) {
28419
- const emptyNode = /* @__PURE__ */ jsx(Box, { className: "text-center py-12", children: /* @__PURE__ */ jsx(Typography, { variant: "body", color: "secondary", children: emptyMessage || t("empty.noItems") }) });
28420
- return dnd.enabled ? /* @__PURE__ */ jsx(Fragment, { children: dnd.wrapContainer(emptyNode) }) : emptyNode;
28421
- }
28540
+ 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;
28422
28541
  const lk = LOOKS[look];
28423
- const hasActions = Boolean(itemActions && itemActions.length > 0);
28542
+ const hasActions = actionDefs.length > 0;
28424
28543
  const effectiveMaxInline = isCoarsePointer ? 0 : maxInlineActions;
28425
- const inlineActionCount = hasActions ? effectiveMaxInline != null ? Math.min(itemActions.length, effectiveMaxInline) : itemActions.length : 0;
28426
- const hasOverflowActions = hasActions && effectiveMaxInline != null && itemActions.length > effectiveMaxInline;
28544
+ const inlineActionCount = hasActions ? effectiveMaxInline != null ? Math.min(actionDefs.length, effectiveMaxInline) : actionDefs.length : 0;
28545
+ const hasOverflowActions = hasActions && effectiveMaxInline != null && actionDefs.length > effectiveMaxInline;
28427
28546
  const actionsTrack = hasActions ? `${inlineActionCount * 6 + (hasOverflowActions ? 3 : 0)}rem` : null;
28428
28547
  const gridTemplateColumns = [
28429
28548
  selectable ? "auto" : null,
@@ -28511,11 +28630,11 @@ function TableView({
28511
28630
  col.className
28512
28631
  );
28513
28632
  if (col.format === "badge" && raw != null && raw !== "") {
28514
- 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);
28633
+ 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);
28515
28634
  }
28516
28635
  return /* @__PURE__ */ jsx(Box, { role: "cell", className: cellBase, children: /* @__PURE__ */ jsx("span", { className: "truncate text-foreground", children: formatCell(raw, col.format) }) }, col.key);
28517
28636
  }),
28518
- itemActions && itemActions.length > 0 && /* @__PURE__ */ jsxs(
28637
+ hasActions && /* @__PURE__ */ jsxs(
28519
28638
  HStack,
28520
28639
  {
28521
28640
  gap: "xs",
@@ -28527,7 +28646,7 @@ function TableView({
28527
28646
  lk.striped && index % 2 === 1 ? "bg-[var(--color-surface-subtle)]" : "bg-[var(--color-card)] group-hover:bg-[var(--color-surface-subtle)]"
28528
28647
  ),
28529
28648
  children: [
28530
- (effectiveMaxInline != null ? itemActions.slice(0, effectiveMaxInline) : itemActions).map((action, i) => /* @__PURE__ */ jsxs(
28649
+ (effectiveMaxInline != null ? actionDefs.slice(0, effectiveMaxInline) : actionDefs).map((action, i) => /* @__PURE__ */ jsxs(
28531
28650
  Button,
28532
28651
  {
28533
28652
  variant: action.variant === "primary" ? "primary" : "ghost",
@@ -28543,12 +28662,12 @@ function TableView({
28543
28662
  },
28544
28663
  i
28545
28664
  )),
28546
- effectiveMaxInline != null && itemActions.length > effectiveMaxInline && /* @__PURE__ */ jsx(
28665
+ effectiveMaxInline != null && actionDefs.length > effectiveMaxInline && /* @__PURE__ */ jsx(
28547
28666
  Menu,
28548
28667
  {
28549
28668
  position: "bottom-end",
28550
28669
  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" }) }),
28551
- items: itemActions.slice(effectiveMaxInline).map((action) => ({
28670
+ items: actionDefs.slice(effectiveMaxInline).map((action) => ({
28552
28671
  label: action.label,
28553
28672
  icon: action.icon,
28554
28673
  event: action.event,
@@ -28575,15 +28694,16 @@ function TableView({
28575
28694
  group.label && /* @__PURE__ */ jsx(Divider, { label: group.label, className: gi > 0 ? "mt-3" : "mt-0" }),
28576
28695
  group.items.map((row) => renderRow(row, runningIndex++))
28577
28696
  ] }, gi)) });
28697
+ const showHeader = colDefs.length > 0 || hasRenderProp;
28578
28698
  return /* @__PURE__ */ jsxs(
28579
28699
  Box,
28580
28700
  {
28581
28701
  role: "table",
28582
28702
  className: cn("w-full text-sm", className),
28583
28703
  children: [
28584
- header,
28585
- dnd.wrapContainer(body),
28586
- 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: [
28704
+ showHeader && header,
28705
+ dnd.wrapContainer(statusNode ? /* @__PURE__ */ jsx(Box, { role: "rowgroup", children: statusNode }) : body),
28706
+ !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: [
28587
28707
  /* @__PURE__ */ jsx(Icon, { name: "chevron-down", size: "xs", className: "mr-1" }),
28588
28708
  t("common.showMore"),
28589
28709
  " (",
@@ -28594,11 +28714,12 @@ function TableView({
28594
28714
  }
28595
28715
  );
28596
28716
  }
28597
- var MAX_MEASURED_COL_CH, BADGE_CHROME_CH, alignClass, weightClass, LOOKS;
28717
+ var tableViewLog, formatCell, MAX_MEASURED_COL_CH, BADGE_CHROME_CH, alignClass, weightClass, LOOKS;
28598
28718
  var init_TableView = __esm({
28599
28719
  "components/core/molecules/TableView.tsx"() {
28600
28720
  "use client";
28601
28721
  init_cn();
28722
+ init_format();
28602
28723
  init_getNestedValue();
28603
28724
  init_useEventBus();
28604
28725
  init_useMediaQuery();
@@ -28612,7 +28733,8 @@ var init_TableView = __esm({
28612
28733
  init_Divider();
28613
28734
  init_Menu();
28614
28735
  init_useDataDnd();
28615
- createLogger("almadar:ui:table-view");
28736
+ tableViewLog = createLogger("almadar:ui:table-view");
28737
+ formatCell = (value, format) => formatValue(value, format);
28616
28738
  MAX_MEASURED_COL_CH = 32;
28617
28739
  BADGE_CHROME_CH = 4;
28618
28740
  alignClass = {
@@ -35996,6 +36118,7 @@ var init_GraphCanvas = __esm({
35996
36118
  title,
35997
36119
  nodes: propNodes = NO_NODES,
35998
36120
  edges: propEdges = NO_EDGES,
36121
+ proposedEdges = NO_EDGES,
35999
36122
  similarity: propSimilarity = NO_SIM,
36000
36123
  height = 400,
36001
36124
  showLabels = true,
@@ -36003,6 +36126,7 @@ var init_GraphCanvas = __esm({
36003
36126
  draggable = true,
36004
36127
  actions,
36005
36128
  onNodeClick,
36129
+ onMarkClick,
36006
36130
  onNodeDoubleClick,
36007
36131
  onBadgeClick,
36008
36132
  nodeClickEvent,
@@ -36031,6 +36155,18 @@ var init_GraphCanvas = __esm({
36031
36155
  const laidOutRef = useRef(false);
36032
36156
  const [, forceUpdate] = useState(0);
36033
36157
  const [logicalW, setLogicalW] = useState(800);
36158
+ const hasProposed = useMemo(() => propNodes.some((n) => n.mark?.kind === "proposed"), [propNodes]);
36159
+ const [pulseTick, setPulseTick] = useState(0);
36160
+ useEffect(() => {
36161
+ if (!hasProposed) return;
36162
+ let raf = 0;
36163
+ const loop = () => {
36164
+ setPulseTick((t2) => (t2 + 1) % 1e6);
36165
+ raf = requestAnimationFrame(loop);
36166
+ };
36167
+ raf = requestAnimationFrame(loop);
36168
+ return () => cancelAnimationFrame(raf);
36169
+ }, [hasProposed]);
36034
36170
  useEffect(() => {
36035
36171
  const canvas = canvasRef.current;
36036
36172
  if (!canvas || typeof ResizeObserver === "undefined") return;
@@ -36332,18 +36468,38 @@ var init_GraphCanvas = __esm({
36332
36468
  }
36333
36469
  }
36334
36470
  ctx.globalAlpha = 1;
36471
+ ctx.setLineDash([4, 4]);
36472
+ for (const edge of proposedEdges) {
36473
+ const source = nodes.find((n) => n.id === edge.source);
36474
+ const target = nodes.find((n) => n.id === edge.target);
36475
+ if (!source || !target) continue;
36476
+ ctx.globalAlpha = 0.4;
36477
+ ctx.beginPath();
36478
+ ctx.moveTo(source.x, source.y);
36479
+ ctx.lineTo(target.x, target.y);
36480
+ ctx.strokeStyle = edge.color || mutedColor;
36481
+ ctx.lineWidth = 1;
36482
+ ctx.stroke();
36483
+ }
36484
+ ctx.setLineDash([]);
36485
+ ctx.globalAlpha = 1;
36335
36486
  for (const node of nodes) {
36336
36487
  const size = node.size || 8;
36337
36488
  const color = resolveColor3(node.color || getGroupColor(node.group, groups), canvas);
36338
36489
  const isHovered = hoveredNode === node.id;
36339
36490
  const isSelected = selectedNodeId !== void 0 && node.id === selectedNodeId;
36340
- ctx.globalAlpha = hoveredNode && !neighbors.has(node.id) ? 0.2 : 1;
36491
+ const mark = node.mark;
36492
+ ctx.globalAlpha = hoveredNode && !neighbors.has(node.id) ? 0.2 : mark?.kind === "proposed" ? 0.55 : 1;
36341
36493
  const radius = isSelected ? size + 4 : isHovered ? size + 2 : size;
36342
36494
  ctx.beginPath();
36343
36495
  ctx.arc(node.x, node.y, radius, 0, Math.PI * 2);
36344
- ctx.fillStyle = color;
36496
+ ctx.fillStyle = mark?.kind === "proposed" ? mutedColor : color;
36345
36497
  ctx.fill();
36346
- if (isSelected) {
36498
+ if (mark?.kind === "proposed") {
36499
+ ctx.setLineDash([3, 3]);
36500
+ ctx.strokeStyle = resolveColor3(mark.tint ?? "var(--color-warning)", canvas);
36501
+ ctx.lineWidth = 1.5;
36502
+ } else if (isSelected) {
36347
36503
  ctx.strokeStyle = accentColor;
36348
36504
  ctx.lineWidth = 3;
36349
36505
  } else {
@@ -36351,6 +36507,28 @@ var init_GraphCanvas = __esm({
36351
36507
  ctx.lineWidth = isHovered ? 2 : 1;
36352
36508
  }
36353
36509
  ctx.stroke();
36510
+ ctx.setLineDash([]);
36511
+ if (mark?.kind === "suggested") {
36512
+ ctx.beginPath();
36513
+ ctx.arc(node.x, node.y, radius + 3, 0, Math.PI * 2);
36514
+ ctx.strokeStyle = accentColor;
36515
+ ctx.lineWidth = 2;
36516
+ ctx.stroke();
36517
+ ctx.beginPath();
36518
+ ctx.arc(node.x + radius * 0.7, node.y - radius * 0.7, 2.5, 0, Math.PI * 2);
36519
+ ctx.fillStyle = accentColor;
36520
+ ctx.fill();
36521
+ } else if (mark?.kind === "proposed") {
36522
+ const phase = pulseTick % 60 / 60;
36523
+ const baseAlpha = hoveredNode && !neighbors.has(node.id) ? 0.2 : 0.55;
36524
+ ctx.globalAlpha = baseAlpha * (0.4 + 0.4 * (1 - Math.abs(phase * 2 - 1)));
36525
+ ctx.beginPath();
36526
+ ctx.arc(node.x, node.y, radius + 6 + Math.sin(phase * Math.PI * 2) * 3, 0, Math.PI * 2);
36527
+ ctx.strokeStyle = resolveColor3(mark.tint ?? "var(--color-warning)", canvas);
36528
+ ctx.lineWidth = 1.5;
36529
+ ctx.stroke();
36530
+ ctx.globalAlpha = baseAlpha;
36531
+ }
36354
36532
  if (showLabels && node.label) {
36355
36533
  const displayLabel = truncateLabel(node.label);
36356
36534
  ctx.font = `${isSelected || isHovered ? "700" : "600"} 12px ${fontFamily}`;
@@ -36485,11 +36663,15 @@ var init_GraphCanvas = __esm({
36485
36663
  return;
36486
36664
  }
36487
36665
  }
36666
+ if (node.mark && onMarkClick) {
36667
+ onMarkClick(node);
36668
+ return;
36669
+ }
36488
36670
  handleNodeClick(node);
36489
36671
  }
36490
36672
  }
36491
36673
  },
36492
- [toCoords, nodeAt, handleNodeClick, onBadgeClick, selectedNodeId]
36674
+ [toCoords, nodeAt, handleNodeClick, onBadgeClick, onMarkClick, selectedNodeId]
36493
36675
  );
36494
36676
  const handlePointerLeave = useCallback(() => {
36495
36677
  setHoveredNode(null);
@@ -37016,9 +37198,6 @@ var init_types2 = __esm({
37016
37198
  };
37017
37199
  }
37018
37200
  });
37019
- function humanizeFieldName(name) {
37020
- 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();
37021
- }
37022
37201
  function normalizeColumns(columns) {
37023
37202
  return columns.map((col) => {
37024
37203
  if (typeof col === "string") {
@@ -37443,6 +37622,7 @@ var init_DataTable = __esm({
37443
37622
  "components/core/organisms/DataTable.tsx"() {
37444
37623
  "use client";
37445
37624
  init_cn();
37625
+ init_format();
37446
37626
  init_getNestedValue();
37447
37627
  init_atoms();
37448
37628
  init_Box();
@@ -37495,9 +37675,6 @@ function getBadgeVariant(fieldName, value) {
37495
37675
  }
37496
37676
  return "default";
37497
37677
  }
37498
- function formatFieldLabel(fieldName) {
37499
- return fieldName.replace(/([A-Z])/g, " $1").replace(/^./, (str) => str.toUpperCase());
37500
- }
37501
37678
  function formatFieldValue2(value, fieldName) {
37502
37679
  if (typeof value === "number") {
37503
37680
  if (fieldName.toLowerCase().includes("progress") || fieldName.toLowerCase().includes("percent")) {
@@ -37625,7 +37802,7 @@ function buildFieldTypeMap(fields) {
37625
37802
  }
37626
37803
  return map;
37627
37804
  }
37628
- var ReactMarkdown2, DetailPanel;
37805
+ var formatFieldLabel, ReactMarkdown2, DetailPanel;
37629
37806
  var init_DetailPanel = __esm({
37630
37807
  "components/core/organisms/DetailPanel.tsx"() {
37631
37808
  "use client";
@@ -37637,8 +37814,10 @@ var init_DetailPanel = __esm({
37637
37814
  init_ErrorState();
37638
37815
  init_EmptyState();
37639
37816
  init_cn();
37817
+ init_format();
37640
37818
  init_getNestedValue();
37641
37819
  init_useEventBus();
37820
+ formatFieldLabel = humanizeFieldName;
37642
37821
  ReactMarkdown2 = lazy(() => import('react-markdown'));
37643
37822
  DetailPanel = ({
37644
37823
  title: propTitle,
@@ -37967,6 +38146,16 @@ var init_DetailPanel = __esm({
37967
38146
  DetailPanel.displayName = "DetailPanel";
37968
38147
  }
37969
38148
  });
38149
+
38150
+ // components/game/atoms/DrawGroup.tsx
38151
+ function DrawGroup(_props) {
38152
+ return null;
38153
+ }
38154
+ var init_DrawGroup = __esm({
38155
+ "components/game/atoms/DrawGroup.tsx"() {
38156
+ "use client";
38157
+ }
38158
+ });
37970
38159
  function extractTitle(children) {
37971
38160
  if (!React84__default.isValidElement(children)) return void 0;
37972
38161
  const props = children.props;
@@ -39167,7 +39356,7 @@ function formatValue3(value, fieldName) {
39167
39356
  return String(value);
39168
39357
  }
39169
39358
  function formatFieldLabel2(fieldName) {
39170
- return fieldName.replace(/([A-Z])/g, " $1").replace(/^./, (str) => str.toUpperCase()).replace(/Id$/, "").trim();
39359
+ return humanizeFieldName(fieldName).replace(/\sId$/, "").trim();
39171
39360
  }
39172
39361
  var STATUS_STYLES, StatusBadge, ProgressIndicator, List3;
39173
39362
  var init_List = __esm({
@@ -39179,6 +39368,7 @@ var init_List = __esm({
39179
39368
  init_EmptyState();
39180
39369
  init_LoadingState();
39181
39370
  init_cn();
39371
+ init_format();
39182
39372
  init_getNestedValue();
39183
39373
  init_useEventBus();
39184
39374
  init_types2();
@@ -40640,7 +40830,7 @@ function TicksTab({ ticks: ticks2 }) {
40640
40830
  }
40641
40831
  );
40642
40832
  }
40643
- const formatTime2 = (ms) => {
40833
+ const formatTime3 = (ms) => {
40644
40834
  if (ms === 0) return "never";
40645
40835
  const seconds = Math.floor((Date.now() - ms) / 1e3);
40646
40836
  if (seconds < 1) return "just now";
@@ -40666,7 +40856,7 @@ function TicksTab({ ticks: ticks2 }) {
40666
40856
  tick.executionTime.toFixed(1),
40667
40857
  "ms exec"
40668
40858
  ] }),
40669
- /* @__PURE__ */ jsx("span", { children: formatTime2(tick.lastRun) })
40859
+ /* @__PURE__ */ jsx("span", { children: formatTime3(tick.lastRun) })
40670
40860
  ] }),
40671
40861
  tick.guardName && /* @__PURE__ */ jsx("div", { className: "mt-2", children: /* @__PURE__ */ jsxs(Badge, { variant: tick.guardPassed ? "success" : "danger", size: "sm", children: [
40672
40862
  tick.guardName,
@@ -40790,7 +40980,7 @@ function EventFlowTab({ events: events2 }) {
40790
40980
  if (filter === "all") return events2;
40791
40981
  return events2.filter((e) => e.type === filter);
40792
40982
  }, [events2, filter]);
40793
- const formatTime2 = (timestamp) => {
40983
+ const formatTime3 = (timestamp) => {
40794
40984
  const date = new Date(timestamp);
40795
40985
  return date.toLocaleTimeString("en-US", {
40796
40986
  hour12: false,
@@ -40866,7 +41056,7 @@ function EventFlowTab({ events: events2 }) {
40866
41056
  {
40867
41057
  className: "flex items-start gap-2 text-xs py-1 hover:bg-muted/50 rounded px-1",
40868
41058
  children: [
40869
- /* @__PURE__ */ jsx(Typography, { variant: "small", className: "text-muted-foreground font-mono min-w-[65px]", children: formatTime2(event.timestamp) }),
41059
+ /* @__PURE__ */ jsx(Typography, { variant: "small", className: "text-muted-foreground font-mono min-w-[65px]", children: formatTime3(event.timestamp) }),
40870
41060
  /* @__PURE__ */ jsx("span", { children: icon }),
40871
41061
  /* @__PURE__ */ jsx(Badge, { variant, size: "sm", className: "min-w-[60px] justify-center", children: event.source }),
40872
41062
  /* @__PURE__ */ jsx(Typography, { variant: "small", className: "text-muted-foreground", children: event.message })
@@ -40920,7 +41110,7 @@ function GuardsPanel({ guards }) {
40920
41110
  if (filter === "passed") return guards.filter((g) => g.result);
40921
41111
  return guards.filter((g) => !g.result);
40922
41112
  }, [guards, filter]);
40923
- const formatTime2 = (timestamp) => {
41113
+ const formatTime3 = (timestamp) => {
40924
41114
  const date = new Date(timestamp);
40925
41115
  return date.toLocaleTimeString("en-US", {
40926
41116
  hour12: false,
@@ -40935,7 +41125,7 @@ function GuardsPanel({ guards }) {
40935
41125
  /* @__PURE__ */ jsx(Badge, { variant: guard.result ? "success" : "danger", size: "sm", children: guard.result ? "\u2713" : "\u2717" }),
40936
41126
  /* @__PURE__ */ jsx(Typography, { variant: "body", weight: "semibold", className: "text-warning", children: guard.guardName }),
40937
41127
  /* @__PURE__ */ jsx(Typography, { variant: "small", className: "text-muted-foreground", children: guard.context.type === "transition" ? `${guard.context.transitionFrom} \u2192 ${guard.context.transitionTo}` : guard.context.tickName }),
40938
- /* @__PURE__ */ jsx(Typography, { variant: "small", className: "text-muted-foreground ml-auto", children: formatTime2(guard.timestamp) })
41128
+ /* @__PURE__ */ jsx(Typography, { variant: "small", className: "text-muted-foreground ml-auto", children: formatTime3(guard.timestamp) })
40939
41129
  ] }),
40940
41130
  content: /* @__PURE__ */ jsxs(Stack, { gap: "sm", children: [
40941
41131
  /* @__PURE__ */ jsxs("div", { children: [
@@ -41096,7 +41286,7 @@ function TransitionTimeline({ transitions }) {
41096
41286
  }
41097
41287
  );
41098
41288
  }
41099
- const formatTime2 = (ts) => {
41289
+ const formatTime3 = (ts) => {
41100
41290
  const d = new Date(ts);
41101
41291
  return d.toLocaleTimeString("en-US", {
41102
41292
  hour12: false,
@@ -41144,7 +41334,7 @@ function TransitionTimeline({ transitions }) {
41144
41334
  ${hasFailedEffects ? "bg-error" : allPassed ? "bg-success" : "bg-muted-foreground"}
41145
41335
  ` }),
41146
41336
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-xs py-1 px-2", children: [
41147
- /* @__PURE__ */ jsx(Typography, { variant: "small", className: "text-muted-foreground font-mono min-w-[65px]", children: formatTime2(trace.timestamp) }),
41337
+ /* @__PURE__ */ jsx(Typography, { variant: "small", className: "text-muted-foreground font-mono min-w-[65px]", children: formatTime3(trace.timestamp) }),
41148
41338
  /* @__PURE__ */ jsx(Badge, { variant: "primary", size: "sm", className: "min-w-[60px] justify-center", children: trace.traitName }),
41149
41339
  /* @__PURE__ */ jsxs(Typography, { variant: "small", className: "font-mono text-muted-foreground", children: [
41150
41340
  trace.from,
@@ -41219,7 +41409,7 @@ function ServerBridgeTab({ bridge }) {
41219
41409
  }
41220
41410
  );
41221
41411
  }
41222
- const formatTime2 = (ts) => {
41412
+ const formatTime3 = (ts) => {
41223
41413
  if (ts === 0) return t("debug.never");
41224
41414
  const d = new Date(ts);
41225
41415
  return d.toLocaleTimeString("en-US", {
@@ -41262,7 +41452,7 @@ function ServerBridgeTab({ bridge }) {
41262
41452
  StatRow,
41263
41453
  {
41264
41454
  label: t("debug.lastHeartbeat"),
41265
- value: formatTime2(bridge.lastHeartbeat)
41455
+ value: formatTime3(bridge.lastHeartbeat)
41266
41456
  }
41267
41457
  )
41268
41458
  ] })
@@ -43374,7 +43564,7 @@ var init_TeamOrganism = __esm({
43374
43564
  TeamOrganism.displayName = "TeamOrganism";
43375
43565
  }
43376
43566
  });
43377
- function formatDate4(value) {
43567
+ function formatDate2(value) {
43378
43568
  const d = new Date(value);
43379
43569
  if (isNaN(d.getTime())) return value;
43380
43570
  return d.toLocaleDateString(void 0, { year: "numeric", month: "short", day: "numeric" });
@@ -43506,7 +43696,7 @@ var init_Timeline = __esm({
43506
43696
  /* @__PURE__ */ jsxs(VStack, { gap: "xs", className: cn("flex-1 min-w-0", !isLast && "pb-6"), children: [
43507
43697
  /* @__PURE__ */ jsxs(HStack, { justify: "between", align: "start", wrap: true, children: [
43508
43698
  /* @__PURE__ */ jsx(Typography, { variant: "body", weight: "semibold", children: item.title }),
43509
- item.date && /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "secondary", className: "flex-shrink-0", children: formatDate4(item.date) })
43699
+ item.date && /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "secondary", className: "flex-shrink-0", children: formatDate2(item.date) })
43510
43700
  ] }),
43511
43701
  item.description && /* @__PURE__ */ jsx(Typography, { variant: "small", color: "secondary", children: item.description }),
43512
43702
  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)) }),
@@ -43669,6 +43859,7 @@ var init_component_registry_generated = __esm({
43669
43859
  init_DocSidebar();
43670
43860
  init_DocTOC();
43671
43861
  init_DocumentViewer();
43862
+ init_DrawGroup();
43672
43863
  init_DrawShape();
43673
43864
  init_DrawShapeLayer();
43674
43865
  init_DrawSprite();
@@ -43935,6 +44126,7 @@ var init_component_registry_generated = __esm({
43935
44126
  "DocSidebar": DocSidebar,
43936
44127
  "DocTOC": DocTOC,
43937
44128
  "DocumentViewer": DocumentViewer,
44129
+ "DrawGroup": DrawGroup,
43938
44130
  "DrawShape": DrawShape,
43939
44131
  "DrawShapeLayer": DrawShapeLayer,
43940
44132
  "DrawSprite": DrawSprite,
@@ -44169,7 +44361,7 @@ function enrichFormFields(fields, entityDef) {
44169
44361
  if (entityField) {
44170
44362
  const enriched = {
44171
44363
  name: field,
44172
- label: field.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/\b\w/g, (c) => c.toUpperCase()),
44364
+ label: humanizeFieldName(field),
44173
44365
  type: entityField.type,
44174
44366
  required: entityField.required ?? false
44175
44367
  };
@@ -44183,7 +44375,7 @@ function enrichFormFields(fields, entityDef) {
44183
44375
  }
44184
44376
  return enriched;
44185
44377
  }
44186
- return { name: field, label: field.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/\b\w/g, (c) => c.toUpperCase()) };
44378
+ return { name: field, label: humanizeFieldName(field) };
44187
44379
  }
44188
44380
  if (field && typeof field === "object" && !Array.isArray(field) && !React84__default.isValidElement(field) && !(field instanceof Date)) {
44189
44381
  const obj = field;
@@ -44838,9 +45030,10 @@ function SlotContentRenderer({
44838
45030
  const slotVal = restProps[slotKey];
44839
45031
  if (slotVal === void 0 || slotVal === null) continue;
44840
45032
  if (React84__default.isValidElement(slotVal) || typeof slotVal === "string" || typeof slotVal === "number" || typeof slotVal === "boolean") continue;
44841
- if (Array.isArray(slotVal) || typeof slotVal === "object" && "type" in slotVal) {
45033
+ const typelessChildren = !Array.isArray(slotVal) && typeof slotVal === "object" && !("type" in slotVal) && Array.isArray(slotVal.children) ? slotVal.children : void 0;
45034
+ if (typelessChildren !== void 0 || Array.isArray(slotVal) || typeof slotVal === "object" && "type" in slotVal) {
44842
45035
  nodeSlotOverrides[slotKey] = renderPatternChildren(
44843
- slotVal,
45036
+ typelessChildren ?? slotVal,
44844
45037
  onDismiss,
44845
45038
  `${content.id}-${slotKey}`,
44846
45039
  `${myPath}.${slotKey}`,
@@ -45019,6 +45212,7 @@ var init_UISlotRenderer = __esm({
45019
45212
  init_useEventBus();
45020
45213
  init_slot_types();
45021
45214
  init_cn();
45215
+ init_format();
45022
45216
  init_ErrorBoundary();
45023
45217
  init_Skeleton();
45024
45218
  init_renderer();