@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.
@@ -1221,6 +1221,87 @@ var init_Dialog = __esm({
1221
1221
  }
1222
1222
  });
1223
1223
 
1224
+ // lib/format.ts
1225
+ function humanizeFieldName(name) {
1226
+ 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();
1227
+ }
1228
+ function humanizeEnumValue(value) {
1229
+ return /^[a-z0-9]+(?:[_-][a-z0-9]+)+$/.test(value) ? humanizeFieldName(value) : value;
1230
+ }
1231
+ function formatDate(value) {
1232
+ if (!value) return "";
1233
+ const d = new Date(String(value));
1234
+ if (isNaN(d.getTime())) return String(value);
1235
+ return d.toLocaleDateString(void 0, { year: "numeric", month: "short", day: "numeric" });
1236
+ }
1237
+ function formatTime(value) {
1238
+ if (!value) return "";
1239
+ const d = new Date(String(value));
1240
+ if (isNaN(d.getTime())) return String(value);
1241
+ return d.toLocaleTimeString(void 0, { hour: "numeric", minute: "2-digit" });
1242
+ }
1243
+ function formatDateTime(value) {
1244
+ if (!value) return "";
1245
+ const d = new Date(String(value));
1246
+ if (isNaN(d.getTime())) return String(value);
1247
+ return `${formatDate(value)} ${formatTime(value)}`;
1248
+ }
1249
+ function asYesNo(value) {
1250
+ return value === false || value === 0 || String(value) === "false" ? "No" : "Yes";
1251
+ }
1252
+ function formatValue(value, format) {
1253
+ if (value === void 0 || value === null) return "";
1254
+ if (typeof value === "boolean") return asYesNo(value);
1255
+ switch (format) {
1256
+ case "date":
1257
+ return formatDate(value);
1258
+ case "time":
1259
+ return formatTime(value);
1260
+ case "datetime":
1261
+ return formatDateTime(value);
1262
+ case "currency":
1263
+ return typeof value === "number" ? `$${value.toFixed(2)}` : String(value);
1264
+ case "number":
1265
+ return typeof value === "number" ? value.toLocaleString() : String(value);
1266
+ case "percent":
1267
+ return typeof value === "number" ? `${Math.round(value)}%` : String(value);
1268
+ case "boolean":
1269
+ return asYesNo(value);
1270
+ default:
1271
+ return String(value);
1272
+ }
1273
+ }
1274
+ function compareCellValues(a, b) {
1275
+ const aEmpty = a === null || a === void 0 || a === "";
1276
+ const bEmpty = b === null || b === void 0 || b === "";
1277
+ if (aEmpty || bEmpty) return aEmpty && bEmpty ? 0 : aEmpty ? 1 : -1;
1278
+ if (typeof a === "number" && typeof b === "number") return a - b;
1279
+ if (typeof a === "boolean" && typeof b === "boolean") return Number(a) - Number(b);
1280
+ const aNum = Number(a);
1281
+ const bNum = Number(b);
1282
+ if (Number.isFinite(aNum) && Number.isFinite(bNum)) return aNum - bNum;
1283
+ const aTime = dateLikeTime(a);
1284
+ const bTime = dateLikeTime(b);
1285
+ if (aTime !== null && bTime !== null) return aTime - bTime;
1286
+ return String(a).localeCompare(String(b));
1287
+ }
1288
+ function dateLikeTime(value) {
1289
+ if (value instanceof Date) return value.getTime();
1290
+ const text = String(value);
1291
+ if (!/\d/.test(text) || !/[-/:T]/.test(text)) return null;
1292
+ const time = Date.parse(text);
1293
+ return Number.isNaN(time) ? null : time;
1294
+ }
1295
+ function sortRows(rows, field, direction = "asc") {
1296
+ if (!field) return rows;
1297
+ const dir = direction === "desc" ? -1 : 1;
1298
+ return [...rows].sort((a, b) => dir * compareCellValues(a?.[field], b?.[field]));
1299
+ }
1300
+ var init_format = __esm({
1301
+ "lib/format.ts"() {
1302
+ }
1303
+ });
1304
+
1224
1305
  // components/core/atoms/Typography.tsx
1225
1306
  var Typography_exports = {};
1226
1307
  __export(Typography_exports, {
@@ -1230,6 +1311,7 @@ var variantStyles2, colorStyles, weightStyles, defaultElements, typographySizeSt
1230
1311
  var init_Typography = __esm({
1231
1312
  "components/core/atoms/Typography.tsx"() {
1232
1313
  init_cn();
1314
+ init_format();
1233
1315
  variantStyles2 = {
1234
1316
  h1: "text-4xl font-bold tracking-tight text-foreground",
1235
1317
  h2: "text-3xl font-bold tracking-tight text-foreground",
@@ -1317,11 +1399,16 @@ var init_Typography = __esm({
1317
1399
  id,
1318
1400
  className,
1319
1401
  style,
1402
+ format,
1320
1403
  content,
1321
1404
  children
1322
1405
  }) => {
1323
1406
  const variant = variantProp ?? (level ? `h${level}` : "body1");
1324
1407
  const Component = as || defaultElements[variant];
1408
+ let body = children ?? content;
1409
+ if (format !== void 0 && format !== "none" && (typeof body === "string" || typeof body === "number" || body instanceof Date)) {
1410
+ body = formatValue(body, format);
1411
+ }
1325
1412
  return React84__namespace.default.createElement(
1326
1413
  Component,
1327
1414
  {
@@ -1338,7 +1425,7 @@ var init_Typography = __esm({
1338
1425
  ),
1339
1426
  style
1340
1427
  },
1341
- children ?? content
1428
+ body
1342
1429
  );
1343
1430
  };
1344
1431
  Typography.displayName = "Typography";
@@ -7426,7 +7513,7 @@ var init_ControlButton = __esm({
7426
7513
  ControlButton.displayName = "ControlButton";
7427
7514
  }
7428
7515
  });
7429
- function formatTime(seconds, format) {
7516
+ function formatTime2(seconds, format) {
7430
7517
  const clamped = Math.max(0, Math.floor(seconds));
7431
7518
  if (format === "ss") {
7432
7519
  return `${clamped}s`;
@@ -7463,7 +7550,7 @@ function TimerDisplay({
7463
7550
  ),
7464
7551
  children: [
7465
7552
  iconAsset && /* @__PURE__ */ jsxRuntime.jsx(GameIcon, { assetUrl: iconAsset, icon: "image", size: 16, className: "w-4 h-4 object-contain flex-shrink-0" }),
7466
- formatTime(seconds, format)
7553
+ formatTime2(seconds, format)
7467
7554
  ]
7468
7555
  }
7469
7556
  );
@@ -8785,6 +8872,15 @@ function createWebPainter(ctx, onAssetLoad) {
8785
8872
  ctx.lineWidth = lineWidth;
8786
8873
  ctx.stroke();
8787
8874
  },
8875
+ fillPath(d, color) {
8876
+ ctx.fillStyle = color;
8877
+ ctx.fill(new Path2D(d));
8878
+ },
8879
+ strokePath(d, color, lineWidth = 1) {
8880
+ ctx.strokeStyle = color;
8881
+ ctx.lineWidth = lineWidth;
8882
+ ctx.stroke(new Path2D(d));
8883
+ },
8788
8884
  text(str, x, y, style) {
8789
8885
  if (style.font) ctx.font = style.font;
8790
8886
  ctx.fillStyle = style.color;
@@ -8960,6 +9056,16 @@ var init_DrawShape = __esm({
8960
9056
  if (node.stroke) painter.strokePoly(pts, node.stroke, node.strokeWidth ?? 1, true);
8961
9057
  break;
8962
9058
  }
9059
+ case "path": {
9060
+ if (!node.d) break;
9061
+ const base = dctx.projector.project(node.position);
9062
+ const tw = dctx.projector.tileWidth;
9063
+ painter.translate(base.x, base.y);
9064
+ painter.scale(tw, tw);
9065
+ if (node.fill) painter.fillPath(node.d, node.fill);
9066
+ if (node.stroke) painter.strokePath(node.d, node.stroke, (node.strokeWidth ?? 1) / tw);
9067
+ break;
9068
+ }
8963
9069
  }
8964
9070
  painter.restore();
8965
9071
  };
@@ -9051,6 +9157,19 @@ function paintDrawable(painter, node, dctx) {
9051
9157
  case "draw-text":
9052
9158
  paintText(painter, node, dctx);
9053
9159
  break;
9160
+ case "draw-group": {
9161
+ if (!isValidScenePos(node.position)) break;
9162
+ if (!Array.isArray(node.items)) break;
9163
+ const p = dctx.projector.project(node.position);
9164
+ painter.save();
9165
+ painter.translate(p.x, p.y);
9166
+ if (node.scale !== void 0) painter.scale(node.scale, node.scale);
9167
+ if (node.rotate !== void 0) painter.rotate(node.rotate);
9168
+ if (node.opacity !== void 0 && node.opacity !== 1) painter.setAlpha(node.opacity);
9169
+ for (const item of node.items) paintDrawable(painter, item, dctx);
9170
+ painter.restore();
9171
+ break;
9172
+ }
9054
9173
  case "draw-sprite-layer":
9055
9174
  paintSpriteLayer(painter, node, dctx);
9056
9175
  break;
@@ -9064,6 +9183,7 @@ function paintDrawable(painter, node, dctx) {
9064
9183
  }
9065
9184
  var init_paintDispatch = __esm({
9066
9185
  "lib/drawable/paintDispatch.ts"() {
9186
+ init_contract();
9067
9187
  init_DrawSprite();
9068
9188
  init_DrawShape();
9069
9189
  init_DrawText();
@@ -9081,6 +9201,7 @@ function collectDrawnItems(nodes) {
9081
9201
  case "draw-sprite":
9082
9202
  case "draw-shape":
9083
9203
  case "draw-text":
9204
+ case "draw-group":
9084
9205
  if (isValidScenePos(n.position)) out.push({ pos: n.position, id: n.id });
9085
9206
  break;
9086
9207
  case "draw-sprite-layer":
@@ -19465,9 +19586,6 @@ function normalizeFields(fields) {
19465
19586
  if (!fields) return [];
19466
19587
  return fields.map((f3) => typeof f3 === "string" ? f3 : f3.key ?? f3.name ?? "");
19467
19588
  }
19468
- function fieldLabel(key) {
19469
- return key.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/[_-]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
19470
- }
19471
19589
  function asBooleanValue(value) {
19472
19590
  if (typeof value === "boolean") return value;
19473
19591
  if (value === "true") return true;
@@ -19478,12 +19596,6 @@ function isDateField(key) {
19478
19596
  const lower = key.toLowerCase();
19479
19597
  return lower.includes("date") || lower.includes("time") || lower.endsWith("at") || lower.endsWith("_at");
19480
19598
  }
19481
- function formatDate(value) {
19482
- if (!value) return "";
19483
- const d = new Date(String(value));
19484
- if (isNaN(d.getTime())) return String(value);
19485
- return d.toLocaleDateString(void 0, { year: "numeric", month: "short", day: "numeric" });
19486
- }
19487
19599
  function statusVariant(value) {
19488
19600
  const v = value.toLowerCase();
19489
19601
  if (["active", "completed", "done", "approved", "published", "resolved", "open"].includes(v)) return "success";
@@ -19492,11 +19604,12 @@ function statusVariant(value) {
19492
19604
  if (["new", "created", "scheduled", "queued"].includes(v)) return "info";
19493
19605
  return "default";
19494
19606
  }
19495
- var STATUS_FIELDS, gapStyles3, alignStyles2, CardGrid;
19607
+ var fieldLabel, STATUS_FIELDS, gapStyles3, alignStyles2, CardGrid;
19496
19608
  var init_CardGrid = __esm({
19497
19609
  "components/core/organisms/CardGrid.tsx"() {
19498
19610
  "use client";
19499
19611
  init_cn();
19612
+ init_format();
19500
19613
  init_getNestedValue();
19501
19614
  init_useEventBus();
19502
19615
  init_atoms();
@@ -19505,6 +19618,7 @@ var init_CardGrid = __esm({
19505
19618
  init_Typography();
19506
19619
  init_Stack();
19507
19620
  init_Pagination();
19621
+ fieldLabel = humanizeFieldName;
19508
19622
  STATUS_FIELDS = /* @__PURE__ */ new Set(["status", "state", "priority", "type", "category", "role", "level", "tier"]);
19509
19623
  gapStyles3 = {
19510
19624
  none: "gap-0",
@@ -20012,7 +20126,7 @@ var init_CaseStudyOrganism = __esm({
20012
20126
  CaseStudyOrganism.displayName = "CaseStudyOrganism";
20013
20127
  }
20014
20128
  });
20015
- var CHART_COLORS, seriesColor, monthFormatter, formatTimeLabel, BarChart, PieChart, LineChart, ScatterChart, LOOK_FROM_CHART_TYPE, Chart;
20129
+ var CHART_COLORS, seriesColor, barColor, monthFormatter, formatTimeLabel, BarChart, PieChart, LineChart, ScatterChart, LOOK_FROM_CHART_TYPE, Chart;
20016
20130
  var init_Chart = __esm({
20017
20131
  "components/core/molecules/Chart.tsx"() {
20018
20132
  "use client";
@@ -20032,6 +20146,7 @@ var init_Chart = __esm({
20032
20146
  "var(--color-accent)"
20033
20147
  ];
20034
20148
  seriesColor = (series, idx) => series.color ?? CHART_COLORS[idx % CHART_COLORS.length];
20149
+ barColor = (series, sIdx, catIdx, seriesCount) => series.color ?? CHART_COLORS[(seriesCount === 1 ? catIdx : sIdx) % CHART_COLORS.length];
20035
20150
  monthFormatter = new Intl.DateTimeFormat(void 0, {
20036
20151
  month: "short",
20037
20152
  year: "2-digit"
@@ -20104,7 +20219,7 @@ var init_Chart = __esm({
20104
20219
  children: series.map((s, sIdx) => {
20105
20220
  const value = valueAt(s, label);
20106
20221
  const ratio = stack === "normalize" ? total === 0 ? 0 : value / total * 100 : value / maxValue * 100;
20107
- const color = seriesColor(s, sIdx);
20222
+ const color = barColor(s, sIdx, catIdx, series.length);
20108
20223
  return /* @__PURE__ */ jsxRuntime.jsx(
20109
20224
  Box,
20110
20225
  {
@@ -20159,7 +20274,7 @@ var init_Chart = __esm({
20159
20274
  /* @__PURE__ */ jsxRuntime.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) => {
20160
20275
  const value = valueAt(s, label);
20161
20276
  const barHeight = value / maxValue * 100;
20162
- const color = seriesColor(s, sIdx);
20277
+ const color = barColor(s, sIdx, catIdx, series.length);
20163
20278
  return /* @__PURE__ */ jsxRuntime.jsx(
20164
20279
  Box,
20165
20280
  {
@@ -20210,7 +20325,7 @@ var init_Chart = __esm({
20210
20325
  /* @__PURE__ */ jsxRuntime.jsx(VStack, { gap: "none", className: "w-full flex-1 min-h-0", justify: "end", children: series.map((s, sIdx) => {
20211
20326
  const value = valueAt(s, label);
20212
20327
  const ratio = stack === "normalize" ? total === 0 ? 0 : value / total * 100 : value / maxValue * 100;
20213
- const color = seriesColor(s, sIdx);
20328
+ const color = barColor(s, sIdx, catIdx, series.length);
20214
20329
  return /* @__PURE__ */ jsxRuntime.jsx(
20215
20330
  Box,
20216
20331
  {
@@ -23072,9 +23187,6 @@ var init_useDataDnd = __esm({
23072
23187
  function renderIconInput(icon, props) {
23073
23188
  return typeof icon === "string" ? /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: icon, ...props }) : /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon, ...props });
23074
23189
  }
23075
- function fieldLabel2(key) {
23076
- return key.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/[_-]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
23077
- }
23078
23190
  function statusVariant2(value) {
23079
23191
  const v = value.toLowerCase();
23080
23192
  if (["active", "completed", "done", "approved", "published", "resolved", "open", "online"].includes(v)) return "success";
@@ -23093,29 +23205,6 @@ function resolveBadgeVariant(field, value) {
23093
23205
  }
23094
23206
  return statusVariant2(value);
23095
23207
  }
23096
- function formatDate2(value) {
23097
- if (!value) return "";
23098
- const d = new Date(String(value));
23099
- if (isNaN(d.getTime())) return String(value);
23100
- return d.toLocaleDateString(void 0, { year: "numeric", month: "short", day: "numeric" });
23101
- }
23102
- function formatValue(value, format) {
23103
- if (value === void 0 || value === null) return "";
23104
- switch (format) {
23105
- case "date":
23106
- return formatDate2(value);
23107
- case "currency":
23108
- return typeof value === "number" ? `$${value.toFixed(2)}` : String(value);
23109
- case "number":
23110
- return typeof value === "number" ? value.toLocaleString() : String(value);
23111
- case "percent":
23112
- return typeof value === "number" ? `${Math.round(value)}%` : String(value);
23113
- case "boolean":
23114
- return value ? "Yes" : "No";
23115
- default:
23116
- return String(value);
23117
- }
23118
- }
23119
23208
  function DataGrid({
23120
23209
  entity,
23121
23210
  fields,
@@ -23402,7 +23491,7 @@ function DataGrid({
23402
23491
  if (val === void 0 || val === null || val === "") return null;
23403
23492
  return /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "xs", className: "items-center", children: [
23404
23493
  field.icon && renderIconInput(field.icon, { size: "xs" }),
23405
- /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: resolveBadgeVariant(field, String(val)), children: formatValue(val, field.format) })
23494
+ /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: resolveBadgeVariant(field, String(val)), children: humanizeEnumValue(formatValue(val, field.format)) })
23406
23495
  ] }, field.name);
23407
23496
  }) })
23408
23497
  ] }),
@@ -23512,11 +23601,12 @@ function DataGrid({
23512
23601
  ] })
23513
23602
  );
23514
23603
  }
23515
- var dataGridLog, BADGE_VARIANTS, gapStyles5, lookStyles5;
23604
+ var dataGridLog, fieldLabel2, BADGE_VARIANTS, gapStyles5, lookStyles5;
23516
23605
  var init_DataGrid = __esm({
23517
23606
  "components/core/molecules/DataGrid.tsx"() {
23518
23607
  "use client";
23519
23608
  init_cn();
23609
+ init_format();
23520
23610
  init_getNestedValue();
23521
23611
  init_useEventBus();
23522
23612
  init_Box();
@@ -23529,6 +23619,7 @@ var init_DataGrid = __esm({
23529
23619
  init_Menu();
23530
23620
  init_useDataDnd();
23531
23621
  dataGridLog = logger.createLogger("almadar:ui:data-grid");
23622
+ fieldLabel2 = humanizeFieldName;
23532
23623
  BADGE_VARIANTS = /* @__PURE__ */ new Set([
23533
23624
  "default",
23534
23625
  "primary",
@@ -23560,9 +23651,6 @@ var init_DataGrid = __esm({
23560
23651
  function renderIconInput2(icon, props) {
23561
23652
  return typeof icon === "string" ? /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: icon, ...props }) : /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon, ...props });
23562
23653
  }
23563
- function fieldLabel3(key) {
23564
- return key.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/[_-]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
23565
- }
23566
23654
  function statusVariant3(value) {
23567
23655
  const v = value.toLowerCase();
23568
23656
  if (["active", "completed", "done", "approved", "published", "resolved", "open", "online"].includes(v)) return "success";
@@ -23571,28 +23659,12 @@ function statusVariant3(value) {
23571
23659
  if (["new", "created", "scheduled", "queued", "info"].includes(v)) return "info";
23572
23660
  return "default";
23573
23661
  }
23574
- function formatDate3(value) {
23575
- if (!value) return "";
23576
- const d = new Date(String(value));
23577
- if (isNaN(d.getTime())) return String(value);
23578
- return d.toLocaleDateString(void 0, { year: "numeric", month: "short", day: "numeric" });
23579
- }
23580
23662
  function formatValue2(value, format, boolLabels) {
23581
- if (value === void 0 || value === null) return "";
23582
- switch (format) {
23583
- case "date":
23584
- return formatDate3(value);
23585
- case "currency":
23586
- return typeof value === "number" ? `$${value.toFixed(2)}` : String(value);
23587
- case "number":
23588
- return typeof value === "number" ? value.toLocaleString() : String(value);
23589
- case "percent":
23590
- return typeof value === "number" ? `${Math.round(value)}%` : String(value);
23591
- case "boolean":
23592
- return value ? boolLabels?.yes ?? "Yes" : boolLabels?.no ?? "No";
23593
- default:
23594
- return String(value);
23663
+ if (value !== void 0 && value !== null && (format === "boolean" || typeof value === "boolean")) {
23664
+ const isNo = value === false || value === 0 || String(value) === "false";
23665
+ return isNo ? boolLabels?.no ?? "No" : boolLabels?.yes ?? "Yes";
23595
23666
  }
23667
+ return formatValue(value, format);
23596
23668
  }
23597
23669
  function groupData(items, field) {
23598
23670
  const groups = /* @__PURE__ */ new Map();
@@ -23615,7 +23687,9 @@ function DataList({
23615
23687
  variant = "default",
23616
23688
  groupBy,
23617
23689
  senderField,
23690
+ senderLabelField,
23618
23691
  currentUser,
23692
+ emptyMessage,
23619
23693
  className,
23620
23694
  isLoading = false,
23621
23695
  error = null,
@@ -23634,6 +23708,8 @@ function DataList({
23634
23708
  hasMore,
23635
23709
  children,
23636
23710
  pageSize = 5,
23711
+ sortBy,
23712
+ sortDirection,
23637
23713
  renderItem: schemaRenderItem,
23638
23714
  dragGroup,
23639
23715
  accepts,
@@ -23662,7 +23738,11 @@ function DataList({
23662
23738
  dndItemIdField,
23663
23739
  dndRoot
23664
23740
  });
23665
- const allData = dnd.orderedItems;
23741
+ const orderedData = dnd.orderedItems;
23742
+ const allData = React84__namespace.default.useMemo(
23743
+ () => sortRows(orderedData, sortBy, sortDirection),
23744
+ [orderedData, sortBy, sortDirection]
23745
+ );
23666
23746
  const data = pageSize > 0 ? allData.slice(0, visibleCount) : allData;
23667
23747
  const hasMoreLocal = pageSize > 0 && visibleCount < allData.length;
23668
23748
  const hasRenderProp = typeof children === "function";
@@ -23699,7 +23779,7 @@ function DataList({
23699
23779
  };
23700
23780
  eventBus.emit(`UI:${action.event}`, payload);
23701
23781
  };
23702
- const renderItemActions = (itemData) => {
23782
+ const renderItemActions = (itemData, onPrimary = false) => {
23703
23783
  if (!itemActions || itemActions.length === 0) return null;
23704
23784
  const inline = maxInlineActions != null ? itemActions.slice(0, maxInlineActions) : itemActions;
23705
23785
  const overflow = maxInlineActions != null ? itemActions.slice(maxInlineActions) : [];
@@ -23712,7 +23792,12 @@ function DataList({
23712
23792
  onClick: handleActionClick(action, itemData),
23713
23793
  "data-testid": `action-${action.event}`,
23714
23794
  "data-row-id": String(itemData.id),
23715
- className: cn(action.variant === "danger" && "text-error hover:bg-error/10"),
23795
+ className: cn(
23796
+ action.variant === "danger" && "text-error hover:bg-error/10",
23797
+ // Must sit on the Button itself: the variant's own text colour
23798
+ // beats an inherited one from the row wrapper.
23799
+ onPrimary && "!text-primary-foreground hover:bg-primary-foreground/15"
23800
+ ),
23716
23801
  children: [
23717
23802
  action.icon && renderIconInput2(action.icon, { size: "xs", className: "mr-1" }),
23718
23803
  action.label
@@ -23754,7 +23839,7 @@ function DataList({
23754
23839
  return /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "text-center py-8", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", color: "error", children: error.message }) });
23755
23840
  }
23756
23841
  if (data.length === 0) {
23757
- const emptyNode = /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "text-center py-12", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", color: "secondary", children: t("empty.noItems") }) });
23842
+ const emptyNode = /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "text-center py-12", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", color: "secondary", children: emptyMessage || t("empty.noItems") }) });
23758
23843
  return dnd.enabled ? /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: dnd.wrapContainer(emptyNode) }) : emptyNode;
23759
23844
  }
23760
23845
  const gapClass = {
@@ -23770,51 +23855,93 @@ function DataList({
23770
23855
  const items2 = [...data];
23771
23856
  const groups2 = groupBy ? groupData(items2, groupBy) : [{ label: "", items: items2 }];
23772
23857
  const contentField = titleField?.name ?? fieldDefs[0]?.name ?? "";
23773
- return /* @__PURE__ */ jsxRuntime.jsx(VStack, { gap: "sm", className: cn("py-2", className), children: groups2.map((group, gi) => /* @__PURE__ */ jsxRuntime.jsxs(React84__namespace.default.Fragment, { children: [
23774
- group.label && /* @__PURE__ */ jsxRuntime.jsx(Divider, { label: group.label, className: "my-2" }),
23775
- group.items.map((itemData, index) => {
23776
- const id = itemData.id || `${gi}-${index}`;
23777
- const sender = senderField ? String(getNestedValue(itemData, senderField) ?? "") : "";
23778
- const isSent = Boolean(currentUser && sender === currentUser);
23779
- const content = getNestedValue(itemData, contentField);
23780
- const timestampField = fieldDefs.find((f3) => f3.format === "date");
23781
- const timestamp = timestampField ? getNestedValue(itemData, timestampField.name) : null;
23782
- return /* @__PURE__ */ jsxRuntime.jsx(
23783
- Box,
23784
- {
23785
- className: cn(
23786
- "flex px-4",
23787
- isSent ? "justify-end" : "justify-start"
23788
- ),
23789
- children: /* @__PURE__ */ jsxRuntime.jsxs(
23790
- Box,
23791
- {
23792
- className: cn(
23793
- "max-w-[75%] px-4 py-2",
23794
- isSent ? "bg-primary text-primary-foreground rounded-2xl rounded-br-sm" : "bg-muted text-foreground rounded-2xl rounded-bl-sm"
23795
- ),
23796
- children: [
23797
- !isSent && senderField && /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", className: "font-semibold mb-0.5", children: sender }),
23798
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", className: cn(isSent && "text-primary-foreground"), children: content !== void 0 && content !== null ? String(content) : "" }),
23799
- timestamp != null ? /* @__PURE__ */ jsxRuntime.jsx(
23800
- Typography,
23801
- {
23802
- variant: "caption",
23803
- className: cn(
23804
- "mt-1 text-xs",
23805
- isSent ? "opacity-70" : "text-muted-foreground"
23806
- ),
23807
- children: formatDate3(timestamp)
23808
- }
23809
- ) : null
23810
- ]
23811
- }
23812
- )
23813
- },
23814
- id
23815
- );
23816
- })
23817
- ] }, gi)) });
23858
+ const senderLabel = (itemData, raw) => {
23859
+ if (!senderLabelField) return raw;
23860
+ const v = getNestedValue(itemData, senderLabelField);
23861
+ return v === void 0 || v === null || v === "" ? raw : String(v);
23862
+ };
23863
+ return /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", className: cn("py-2", className), children: [
23864
+ groups2.map((group, gi) => /* @__PURE__ */ jsxRuntime.jsxs(React84__namespace.default.Fragment, { children: [
23865
+ group.label && /* @__PURE__ */ jsxRuntime.jsx(Divider, { label: group.label, className: "my-2" }),
23866
+ group.items.map((itemData, index) => {
23867
+ const id = itemData.id || `${gi}-${index}`;
23868
+ const sender = senderField ? String(getNestedValue(itemData, senderField) ?? "") : "";
23869
+ const isSent = Boolean(currentUser && sender === currentUser);
23870
+ const content = getNestedValue(itemData, contentField);
23871
+ const timestampField = fieldDefs.find((f3) => f3.format === "date");
23872
+ const timestamp = timestampField ? getNestedValue(itemData, timestampField.name) : null;
23873
+ const metaFields = fieldDefs.filter(
23874
+ (f3) => f3.name !== contentField && f3.name !== timestampField?.name
23875
+ );
23876
+ return /* @__PURE__ */ jsxRuntime.jsx(
23877
+ Box,
23878
+ {
23879
+ "data-entity-row": true,
23880
+ "data-entity-id": id,
23881
+ onClick: itemClickEvent ? handleRowClick(itemData) : void 0,
23882
+ className: cn(
23883
+ "flex px-4 group/rowactions",
23884
+ itemClickEvent && "cursor-pointer",
23885
+ isSent ? "justify-end" : "justify-start"
23886
+ ),
23887
+ children: /* @__PURE__ */ jsxRuntime.jsxs(
23888
+ Box,
23889
+ {
23890
+ className: cn(
23891
+ "max-w-[75%] px-4 py-2",
23892
+ isSent ? "bg-primary text-primary-foreground rounded-2xl rounded-br-sm" : "bg-muted text-foreground rounded-2xl rounded-bl-sm"
23893
+ ),
23894
+ children: [
23895
+ !isSent && senderField && /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", className: "font-semibold mb-0.5", children: senderLabel(itemData, sender) }),
23896
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", className: cn(isSent && "text-primary-foreground"), children: content !== void 0 && content !== null ? String(content) : "" }),
23897
+ metaFields.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(HStack, { gap: "xs", className: "mt-1 flex-wrap", children: metaFields.map((f3) => {
23898
+ const v = getNestedValue(itemData, f3.name);
23899
+ if (v === void 0 || v === null || v === "") return null;
23900
+ return f3.variant === "badge" ? /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: statusVariant3(String(v)), children: String(v) }, f3.name) : /* @__PURE__ */ jsxRuntime.jsx(
23901
+ Typography,
23902
+ {
23903
+ variant: "caption",
23904
+ className: cn("text-xs", isSent ? "opacity-70" : "text-muted-foreground"),
23905
+ children: formatValue2(v, f3.format)
23906
+ },
23907
+ f3.name
23908
+ );
23909
+ }) }),
23910
+ /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "xs", className: "mt-1 items-center justify-between", children: [
23911
+ timestamp != null ? /* @__PURE__ */ jsxRuntime.jsx(
23912
+ Typography,
23913
+ {
23914
+ variant: "caption",
23915
+ className: cn("text-xs", isSent ? "opacity-70" : "text-muted-foreground"),
23916
+ children: formatDate(timestamp)
23917
+ }
23918
+ ) : /* @__PURE__ */ jsxRuntime.jsx("span", {}),
23919
+ renderItemActions(itemData, isSent)
23920
+ ] })
23921
+ ]
23922
+ }
23923
+ )
23924
+ },
23925
+ id
23926
+ );
23927
+ })
23928
+ ] }, gi)),
23929
+ hasMoreLocal && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "flex justify-center py-3", children: /* @__PURE__ */ jsxRuntime.jsxs(
23930
+ Button,
23931
+ {
23932
+ variant: "ghost",
23933
+ size: "sm",
23934
+ onClick: () => setVisibleCount((prev) => prev + (pageSize || 5)),
23935
+ children: [
23936
+ /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "chevron-down", size: "xs", className: "mr-1" }),
23937
+ t("common.showMore"),
23938
+ " (",
23939
+ t("common.remaining", { count: allData.length - visibleCount }),
23940
+ ")"
23941
+ ]
23942
+ }
23943
+ ) })
23944
+ ] });
23818
23945
  }
23819
23946
  const items = [...data];
23820
23947
  const groups = groupBy ? groupData(items, groupBy) : [{ label: "", items }];
@@ -23963,11 +24090,12 @@ function DataList({
23963
24090
  )
23964
24091
  );
23965
24092
  }
23966
- var dataListLog, listLookStyles;
24093
+ var dataListLog, fieldLabel3, listLookStyles;
23967
24094
  var init_DataList = __esm({
23968
24095
  "components/core/molecules/DataList.tsx"() {
23969
24096
  "use client";
23970
24097
  init_cn();
24098
+ init_format();
23971
24099
  init_getNestedValue();
23972
24100
  init_useEventBus();
23973
24101
  init_Box();
@@ -23982,6 +24110,7 @@ var init_DataList = __esm({
23982
24110
  init_Menu();
23983
24111
  init_useDataDnd();
23984
24112
  dataListLog = logger.createLogger("almadar:ui:data-list");
24113
+ fieldLabel3 = humanizeFieldName;
23985
24114
  listLookStyles = {
23986
24115
  dense: "[&_[data-entity-row]>div]:!py-1 [&_[data-entity-row]>div]:!px-3",
23987
24116
  spacious: "[&_[data-entity-row]>div]:!py-5 [&_[data-entity-row]>div]:!px-8",
@@ -28337,7 +28466,7 @@ function renderIconInput3(icon, props) {
28337
28466
  return typeof icon === "string" ? /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: icon, ...props }) : /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon, ...props });
28338
28467
  }
28339
28468
  function columnLabel(col) {
28340
- return col.header ?? col.label ?? col.key.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/[_-]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
28469
+ return col.header ?? col.label ?? humanizeFieldName(col.key);
28341
28470
  }
28342
28471
  function asFieldValue(v) {
28343
28472
  if (v === void 0 || v === null) return v;
@@ -28356,25 +28485,6 @@ function statusVariant4(value) {
28356
28485
  if (["new", "created", "scheduled", "queued", "info"].includes(v)) return "info";
28357
28486
  return "default";
28358
28487
  }
28359
- function formatCell(value, format) {
28360
- if (value === void 0 || value === null) return "";
28361
- switch (format) {
28362
- case "date": {
28363
- const d = new Date(String(value));
28364
- return isNaN(d.getTime()) ? String(value) : d.toLocaleDateString(void 0, { year: "numeric", month: "short", day: "numeric" });
28365
- }
28366
- case "currency":
28367
- return typeof value === "number" ? `$${value.toFixed(2)}` : String(value);
28368
- case "number":
28369
- return typeof value === "number" ? value.toLocaleString() : String(value);
28370
- case "percent":
28371
- return typeof value === "number" ? `${Math.round(value)}%` : String(value);
28372
- case "boolean":
28373
- return value ? "Yes" : "No";
28374
- default:
28375
- return String(value);
28376
- }
28377
- }
28378
28488
  function groupData2(items, field) {
28379
28489
  const groups = /* @__PURE__ */ new Map();
28380
28490
  for (const item of items) {
@@ -28420,7 +28530,8 @@ function TableView({
28420
28530
  const { t } = hooks.useTranslate();
28421
28531
  const [visibleCount, setVisibleCount] = React84__namespace.default.useState(pageSize > 0 ? pageSize : Infinity);
28422
28532
  const [localSelected, setLocalSelected] = React84__namespace.default.useState(/* @__PURE__ */ new Set());
28423
- const colDefs = columns ?? fields ?? [];
28533
+ const colDefs = (Array.isArray(columns) ? columns : void 0) ?? (Array.isArray(fields) ? fields : void 0) ?? [];
28534
+ const actionDefs = Array.isArray(itemActions) ? itemActions : [];
28424
28535
  const allDataRaw = Array.isArray(entity) ? entity : entity ? [entity] : [];
28425
28536
  const dnd = useDataDnd({
28426
28537
  items: allDataRaw,
@@ -28440,6 +28551,23 @@ function TableView({
28440
28551
  const hasRenderProp = typeof children === "function";
28441
28552
  const idField = dndItemIdField ?? "id";
28442
28553
  const isCoarsePointer = useMediaQuery("(pointer: coarse)");
28554
+ React84__namespace.default.useEffect(() => {
28555
+ tableViewLog.debug("render", {
28556
+ rowCount: data.length,
28557
+ colCount: colDefs.length,
28558
+ look,
28559
+ isLoading: Boolean(isLoading),
28560
+ hasError: Boolean(error),
28561
+ dnd: dnd.enabled
28562
+ });
28563
+ if (data.length > 0 && !hasRenderProp && colDefs.length === 0) {
28564
+ tableViewLog.warn("columns-unresolved", {
28565
+ rowCount: data.length,
28566
+ columnsIsArray: Array.isArray(columns),
28567
+ fieldsIsArray: Array.isArray(fields)
28568
+ });
28569
+ }
28570
+ }, [data, colDefs, look, isLoading, error, dnd.enabled, hasRenderProp, columns, fields]);
28443
28571
  const selected = selectedIds ? new Set(selectedIds) : localSelected;
28444
28572
  const emitSelection = (next) => {
28445
28573
  if (!selectedIds) setLocalSelected(next);
@@ -28483,21 +28611,12 @@ function TableView({
28483
28611
  }),
28484
28612
  [colDefs, data]
28485
28613
  );
28486
- if (isLoading) {
28487
- return /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "text-center py-8", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", color: "secondary", children: t("loading.items") }) });
28488
- }
28489
- if (error) {
28490
- return /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "text-center py-8", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", color: "error", children: error.message }) });
28491
- }
28492
- if (data.length === 0) {
28493
- const emptyNode = /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "text-center py-12", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", color: "secondary", children: emptyMessage || t("empty.noItems") }) });
28494
- return dnd.enabled ? /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: dnd.wrapContainer(emptyNode) }) : emptyNode;
28495
- }
28614
+ const statusNode = isLoading ? /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "text-center py-8", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", color: "secondary", children: t("loading.items") }) }) : error ? /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "text-center py-8", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", color: "error", children: error.message }) }) : data.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "text-center py-12", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", color: "secondary", children: emptyMessage || t("empty.noItems") }) }) : null;
28496
28615
  const lk = LOOKS[look];
28497
- const hasActions = Boolean(itemActions && itemActions.length > 0);
28616
+ const hasActions = actionDefs.length > 0;
28498
28617
  const effectiveMaxInline = isCoarsePointer ? 0 : maxInlineActions;
28499
- const inlineActionCount = hasActions ? effectiveMaxInline != null ? Math.min(itemActions.length, effectiveMaxInline) : itemActions.length : 0;
28500
- const hasOverflowActions = hasActions && effectiveMaxInline != null && itemActions.length > effectiveMaxInline;
28618
+ const inlineActionCount = hasActions ? effectiveMaxInline != null ? Math.min(actionDefs.length, effectiveMaxInline) : actionDefs.length : 0;
28619
+ const hasOverflowActions = hasActions && effectiveMaxInline != null && actionDefs.length > effectiveMaxInline;
28501
28620
  const actionsTrack = hasActions ? `${inlineActionCount * 6 + (hasOverflowActions ? 3 : 0)}rem` : null;
28502
28621
  const gridTemplateColumns = [
28503
28622
  selectable ? "auto" : null,
@@ -28585,11 +28704,11 @@ function TableView({
28585
28704
  col.className
28586
28705
  );
28587
28706
  if (col.format === "badge" && raw != null && raw !== "") {
28588
- return /* @__PURE__ */ jsxRuntime.jsx(Box, { role: "cell", className: cellBase, children: /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: statusVariant4(String(raw)), size: "sm", className: "whitespace-nowrap", children: String(raw) }) }, col.key);
28707
+ return /* @__PURE__ */ jsxRuntime.jsx(Box, { role: "cell", className: cellBase, children: /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: statusVariant4(String(raw)), size: "sm", className: "whitespace-nowrap", children: humanizeEnumValue(String(raw)) }) }, col.key);
28589
28708
  }
28590
28709
  return /* @__PURE__ */ jsxRuntime.jsx(Box, { role: "cell", className: cellBase, children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate text-foreground", children: formatCell(raw, col.format) }) }, col.key);
28591
28710
  }),
28592
- itemActions && itemActions.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(
28711
+ hasActions && /* @__PURE__ */ jsxRuntime.jsxs(
28593
28712
  HStack,
28594
28713
  {
28595
28714
  gap: "xs",
@@ -28601,7 +28720,7 @@ function TableView({
28601
28720
  lk.striped && index % 2 === 1 ? "bg-[var(--color-surface-subtle)]" : "bg-[var(--color-card)] group-hover:bg-[var(--color-surface-subtle)]"
28602
28721
  ),
28603
28722
  children: [
28604
- (effectiveMaxInline != null ? itemActions.slice(0, effectiveMaxInline) : itemActions).map((action, i) => /* @__PURE__ */ jsxRuntime.jsxs(
28723
+ (effectiveMaxInline != null ? actionDefs.slice(0, effectiveMaxInline) : actionDefs).map((action, i) => /* @__PURE__ */ jsxRuntime.jsxs(
28605
28724
  Button,
28606
28725
  {
28607
28726
  variant: action.variant === "primary" ? "primary" : "ghost",
@@ -28617,12 +28736,12 @@ function TableView({
28617
28736
  },
28618
28737
  i
28619
28738
  )),
28620
- effectiveMaxInline != null && itemActions.length > effectiveMaxInline && /* @__PURE__ */ jsxRuntime.jsx(
28739
+ effectiveMaxInline != null && actionDefs.length > effectiveMaxInline && /* @__PURE__ */ jsxRuntime.jsx(
28621
28740
  Menu,
28622
28741
  {
28623
28742
  position: "bottom-end",
28624
28743
  trigger: /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "ghost", size: "sm", "aria-label": t("common.actions"), "data-testid": "action-overflow", children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "more-horizontal", size: "xs" }) }),
28625
- items: itemActions.slice(effectiveMaxInline).map((action) => ({
28744
+ items: actionDefs.slice(effectiveMaxInline).map((action) => ({
28626
28745
  label: action.label,
28627
28746
  icon: action.icon,
28628
28747
  event: action.event,
@@ -28649,15 +28768,16 @@ function TableView({
28649
28768
  group.label && /* @__PURE__ */ jsxRuntime.jsx(Divider, { label: group.label, className: gi > 0 ? "mt-3" : "mt-0" }),
28650
28769
  group.items.map((row) => renderRow(row, runningIndex++))
28651
28770
  ] }, gi)) });
28771
+ const showHeader = colDefs.length > 0 || hasRenderProp;
28652
28772
  return /* @__PURE__ */ jsxRuntime.jsxs(
28653
28773
  Box,
28654
28774
  {
28655
28775
  role: "table",
28656
28776
  className: cn("w-full text-sm", className),
28657
28777
  children: [
28658
- header,
28659
- dnd.wrapContainer(body),
28660
- hasMore && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "flex justify-center py-3", children: /* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "ghost", size: "sm", onClick: () => setVisibleCount((p) => p + (pageSize || 5)), children: [
28778
+ showHeader && header,
28779
+ dnd.wrapContainer(statusNode ? /* @__PURE__ */ jsxRuntime.jsx(Box, { role: "rowgroup", children: statusNode }) : body),
28780
+ !statusNode && hasMore && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "flex justify-center py-3", children: /* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "ghost", size: "sm", onClick: () => setVisibleCount((p) => p + (pageSize || 5)), children: [
28661
28781
  /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "chevron-down", size: "xs", className: "mr-1" }),
28662
28782
  t("common.showMore"),
28663
28783
  " (",
@@ -28668,11 +28788,12 @@ function TableView({
28668
28788
  }
28669
28789
  );
28670
28790
  }
28671
- var MAX_MEASURED_COL_CH, BADGE_CHROME_CH, alignClass, weightClass, LOOKS;
28791
+ var tableViewLog, formatCell, MAX_MEASURED_COL_CH, BADGE_CHROME_CH, alignClass, weightClass, LOOKS;
28672
28792
  var init_TableView = __esm({
28673
28793
  "components/core/molecules/TableView.tsx"() {
28674
28794
  "use client";
28675
28795
  init_cn();
28796
+ init_format();
28676
28797
  init_getNestedValue();
28677
28798
  init_useEventBus();
28678
28799
  init_useMediaQuery();
@@ -28686,7 +28807,8 @@ var init_TableView = __esm({
28686
28807
  init_Divider();
28687
28808
  init_Menu();
28688
28809
  init_useDataDnd();
28689
- logger.createLogger("almadar:ui:table-view");
28810
+ tableViewLog = logger.createLogger("almadar:ui:table-view");
28811
+ formatCell = (value, format) => formatValue(value, format);
28690
28812
  MAX_MEASURED_COL_CH = 32;
28691
28813
  BADGE_CHROME_CH = 4;
28692
28814
  alignClass = {
@@ -36070,6 +36192,7 @@ var init_GraphCanvas = __esm({
36070
36192
  title,
36071
36193
  nodes: propNodes = NO_NODES,
36072
36194
  edges: propEdges = NO_EDGES,
36195
+ proposedEdges = NO_EDGES,
36073
36196
  similarity: propSimilarity = NO_SIM,
36074
36197
  height = 400,
36075
36198
  showLabels = true,
@@ -36077,6 +36200,7 @@ var init_GraphCanvas = __esm({
36077
36200
  draggable = true,
36078
36201
  actions,
36079
36202
  onNodeClick,
36203
+ onMarkClick,
36080
36204
  onNodeDoubleClick,
36081
36205
  onBadgeClick,
36082
36206
  nodeClickEvent,
@@ -36105,6 +36229,18 @@ var init_GraphCanvas = __esm({
36105
36229
  const laidOutRef = React84.useRef(false);
36106
36230
  const [, forceUpdate] = React84.useState(0);
36107
36231
  const [logicalW, setLogicalW] = React84.useState(800);
36232
+ const hasProposed = React84.useMemo(() => propNodes.some((n) => n.mark?.kind === "proposed"), [propNodes]);
36233
+ const [pulseTick, setPulseTick] = React84.useState(0);
36234
+ React84.useEffect(() => {
36235
+ if (!hasProposed) return;
36236
+ let raf = 0;
36237
+ const loop = () => {
36238
+ setPulseTick((t2) => (t2 + 1) % 1e6);
36239
+ raf = requestAnimationFrame(loop);
36240
+ };
36241
+ raf = requestAnimationFrame(loop);
36242
+ return () => cancelAnimationFrame(raf);
36243
+ }, [hasProposed]);
36108
36244
  React84.useEffect(() => {
36109
36245
  const canvas = canvasRef.current;
36110
36246
  if (!canvas || typeof ResizeObserver === "undefined") return;
@@ -36406,18 +36542,38 @@ var init_GraphCanvas = __esm({
36406
36542
  }
36407
36543
  }
36408
36544
  ctx.globalAlpha = 1;
36545
+ ctx.setLineDash([4, 4]);
36546
+ for (const edge of proposedEdges) {
36547
+ const source = nodes.find((n) => n.id === edge.source);
36548
+ const target = nodes.find((n) => n.id === edge.target);
36549
+ if (!source || !target) continue;
36550
+ ctx.globalAlpha = 0.4;
36551
+ ctx.beginPath();
36552
+ ctx.moveTo(source.x, source.y);
36553
+ ctx.lineTo(target.x, target.y);
36554
+ ctx.strokeStyle = edge.color || mutedColor;
36555
+ ctx.lineWidth = 1;
36556
+ ctx.stroke();
36557
+ }
36558
+ ctx.setLineDash([]);
36559
+ ctx.globalAlpha = 1;
36409
36560
  for (const node of nodes) {
36410
36561
  const size = node.size || 8;
36411
36562
  const color = resolveColor3(node.color || getGroupColor(node.group, groups), canvas);
36412
36563
  const isHovered = hoveredNode === node.id;
36413
36564
  const isSelected = selectedNodeId !== void 0 && node.id === selectedNodeId;
36414
- ctx.globalAlpha = hoveredNode && !neighbors.has(node.id) ? 0.2 : 1;
36565
+ const mark = node.mark;
36566
+ ctx.globalAlpha = hoveredNode && !neighbors.has(node.id) ? 0.2 : mark?.kind === "proposed" ? 0.55 : 1;
36415
36567
  const radius = isSelected ? size + 4 : isHovered ? size + 2 : size;
36416
36568
  ctx.beginPath();
36417
36569
  ctx.arc(node.x, node.y, radius, 0, Math.PI * 2);
36418
- ctx.fillStyle = color;
36570
+ ctx.fillStyle = mark?.kind === "proposed" ? mutedColor : color;
36419
36571
  ctx.fill();
36420
- if (isSelected) {
36572
+ if (mark?.kind === "proposed") {
36573
+ ctx.setLineDash([3, 3]);
36574
+ ctx.strokeStyle = resolveColor3(mark.tint ?? "var(--color-warning)", canvas);
36575
+ ctx.lineWidth = 1.5;
36576
+ } else if (isSelected) {
36421
36577
  ctx.strokeStyle = accentColor;
36422
36578
  ctx.lineWidth = 3;
36423
36579
  } else {
@@ -36425,6 +36581,28 @@ var init_GraphCanvas = __esm({
36425
36581
  ctx.lineWidth = isHovered ? 2 : 1;
36426
36582
  }
36427
36583
  ctx.stroke();
36584
+ ctx.setLineDash([]);
36585
+ if (mark?.kind === "suggested") {
36586
+ ctx.beginPath();
36587
+ ctx.arc(node.x, node.y, radius + 3, 0, Math.PI * 2);
36588
+ ctx.strokeStyle = accentColor;
36589
+ ctx.lineWidth = 2;
36590
+ ctx.stroke();
36591
+ ctx.beginPath();
36592
+ ctx.arc(node.x + radius * 0.7, node.y - radius * 0.7, 2.5, 0, Math.PI * 2);
36593
+ ctx.fillStyle = accentColor;
36594
+ ctx.fill();
36595
+ } else if (mark?.kind === "proposed") {
36596
+ const phase = pulseTick % 60 / 60;
36597
+ const baseAlpha = hoveredNode && !neighbors.has(node.id) ? 0.2 : 0.55;
36598
+ ctx.globalAlpha = baseAlpha * (0.4 + 0.4 * (1 - Math.abs(phase * 2 - 1)));
36599
+ ctx.beginPath();
36600
+ ctx.arc(node.x, node.y, radius + 6 + Math.sin(phase * Math.PI * 2) * 3, 0, Math.PI * 2);
36601
+ ctx.strokeStyle = resolveColor3(mark.tint ?? "var(--color-warning)", canvas);
36602
+ ctx.lineWidth = 1.5;
36603
+ ctx.stroke();
36604
+ ctx.globalAlpha = baseAlpha;
36605
+ }
36428
36606
  if (showLabels && node.label) {
36429
36607
  const displayLabel = truncateLabel(node.label);
36430
36608
  ctx.font = `${isSelected || isHovered ? "700" : "600"} 12px ${fontFamily}`;
@@ -36559,11 +36737,15 @@ var init_GraphCanvas = __esm({
36559
36737
  return;
36560
36738
  }
36561
36739
  }
36740
+ if (node.mark && onMarkClick) {
36741
+ onMarkClick(node);
36742
+ return;
36743
+ }
36562
36744
  handleNodeClick(node);
36563
36745
  }
36564
36746
  }
36565
36747
  },
36566
- [toCoords, nodeAt, handleNodeClick, onBadgeClick, selectedNodeId]
36748
+ [toCoords, nodeAt, handleNodeClick, onBadgeClick, onMarkClick, selectedNodeId]
36567
36749
  );
36568
36750
  const handlePointerLeave = React84.useCallback(() => {
36569
36751
  setHoveredNode(null);
@@ -37090,9 +37272,6 @@ var init_types2 = __esm({
37090
37272
  };
37091
37273
  }
37092
37274
  });
37093
- function humanizeFieldName(name) {
37094
- 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();
37095
- }
37096
37275
  function normalizeColumns(columns) {
37097
37276
  return columns.map((col) => {
37098
37277
  if (typeof col === "string") {
@@ -37517,6 +37696,7 @@ var init_DataTable = __esm({
37517
37696
  "components/core/organisms/DataTable.tsx"() {
37518
37697
  "use client";
37519
37698
  init_cn();
37699
+ init_format();
37520
37700
  init_getNestedValue();
37521
37701
  init_atoms();
37522
37702
  init_Box();
@@ -37569,9 +37749,6 @@ function getBadgeVariant(fieldName, value) {
37569
37749
  }
37570
37750
  return "default";
37571
37751
  }
37572
- function formatFieldLabel(fieldName) {
37573
- return fieldName.replace(/([A-Z])/g, " $1").replace(/^./, (str) => str.toUpperCase());
37574
- }
37575
37752
  function formatFieldValue2(value, fieldName) {
37576
37753
  if (typeof value === "number") {
37577
37754
  if (fieldName.toLowerCase().includes("progress") || fieldName.toLowerCase().includes("percent")) {
@@ -37699,7 +37876,7 @@ function buildFieldTypeMap(fields) {
37699
37876
  }
37700
37877
  return map;
37701
37878
  }
37702
- var ReactMarkdown2, DetailPanel;
37879
+ var formatFieldLabel, ReactMarkdown2, DetailPanel;
37703
37880
  var init_DetailPanel = __esm({
37704
37881
  "components/core/organisms/DetailPanel.tsx"() {
37705
37882
  "use client";
@@ -37711,8 +37888,10 @@ var init_DetailPanel = __esm({
37711
37888
  init_ErrorState();
37712
37889
  init_EmptyState();
37713
37890
  init_cn();
37891
+ init_format();
37714
37892
  init_getNestedValue();
37715
37893
  init_useEventBus();
37894
+ formatFieldLabel = humanizeFieldName;
37716
37895
  ReactMarkdown2 = React84.lazy(() => import('react-markdown'));
37717
37896
  DetailPanel = ({
37718
37897
  title: propTitle,
@@ -38041,6 +38220,16 @@ var init_DetailPanel = __esm({
38041
38220
  DetailPanel.displayName = "DetailPanel";
38042
38221
  }
38043
38222
  });
38223
+
38224
+ // components/game/atoms/DrawGroup.tsx
38225
+ function DrawGroup(_props) {
38226
+ return null;
38227
+ }
38228
+ var init_DrawGroup = __esm({
38229
+ "components/game/atoms/DrawGroup.tsx"() {
38230
+ "use client";
38231
+ }
38232
+ });
38044
38233
  function extractTitle(children) {
38045
38234
  if (!React84__namespace.default.isValidElement(children)) return void 0;
38046
38235
  const props = children.props;
@@ -39241,7 +39430,7 @@ function formatValue3(value, fieldName) {
39241
39430
  return String(value);
39242
39431
  }
39243
39432
  function formatFieldLabel2(fieldName) {
39244
- return fieldName.replace(/([A-Z])/g, " $1").replace(/^./, (str) => str.toUpperCase()).replace(/Id$/, "").trim();
39433
+ return humanizeFieldName(fieldName).replace(/\sId$/, "").trim();
39245
39434
  }
39246
39435
  var STATUS_STYLES, StatusBadge, ProgressIndicator, List3;
39247
39436
  var init_List = __esm({
@@ -39253,6 +39442,7 @@ var init_List = __esm({
39253
39442
  init_EmptyState();
39254
39443
  init_LoadingState();
39255
39444
  init_cn();
39445
+ init_format();
39256
39446
  init_getNestedValue();
39257
39447
  init_useEventBus();
39258
39448
  init_types2();
@@ -40714,7 +40904,7 @@ function TicksTab({ ticks: ticks2 }) {
40714
40904
  }
40715
40905
  );
40716
40906
  }
40717
- const formatTime2 = (ms) => {
40907
+ const formatTime3 = (ms) => {
40718
40908
  if (ms === 0) return "never";
40719
40909
  const seconds = Math.floor((Date.now() - ms) / 1e3);
40720
40910
  if (seconds < 1) return "just now";
@@ -40740,7 +40930,7 @@ function TicksTab({ ticks: ticks2 }) {
40740
40930
  tick.executionTime.toFixed(1),
40741
40931
  "ms exec"
40742
40932
  ] }),
40743
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: formatTime2(tick.lastRun) })
40933
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: formatTime3(tick.lastRun) })
40744
40934
  ] }),
40745
40935
  tick.guardName && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-2", children: /* @__PURE__ */ jsxRuntime.jsxs(Badge, { variant: tick.guardPassed ? "success" : "danger", size: "sm", children: [
40746
40936
  tick.guardName,
@@ -40864,7 +41054,7 @@ function EventFlowTab({ events: events2 }) {
40864
41054
  if (filter === "all") return events2;
40865
41055
  return events2.filter((e) => e.type === filter);
40866
41056
  }, [events2, filter]);
40867
- const formatTime2 = (timestamp) => {
41057
+ const formatTime3 = (timestamp) => {
40868
41058
  const date = new Date(timestamp);
40869
41059
  return date.toLocaleTimeString("en-US", {
40870
41060
  hour12: false,
@@ -40940,7 +41130,7 @@ function EventFlowTab({ events: events2 }) {
40940
41130
  {
40941
41131
  className: "flex items-start gap-2 text-xs py-1 hover:bg-muted/50 rounded px-1",
40942
41132
  children: [
40943
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", className: "text-muted-foreground font-mono min-w-[65px]", children: formatTime2(event.timestamp) }),
41133
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", className: "text-muted-foreground font-mono min-w-[65px]", children: formatTime3(event.timestamp) }),
40944
41134
  /* @__PURE__ */ jsxRuntime.jsx("span", { children: icon }),
40945
41135
  /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant, size: "sm", className: "min-w-[60px] justify-center", children: event.source }),
40946
41136
  /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", className: "text-muted-foreground", children: event.message })
@@ -40994,7 +41184,7 @@ function GuardsPanel({ guards }) {
40994
41184
  if (filter === "passed") return guards.filter((g) => g.result);
40995
41185
  return guards.filter((g) => !g.result);
40996
41186
  }, [guards, filter]);
40997
- const formatTime2 = (timestamp) => {
41187
+ const formatTime3 = (timestamp) => {
40998
41188
  const date = new Date(timestamp);
40999
41189
  return date.toLocaleTimeString("en-US", {
41000
41190
  hour12: false,
@@ -41009,7 +41199,7 @@ function GuardsPanel({ guards }) {
41009
41199
  /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: guard.result ? "success" : "danger", size: "sm", children: guard.result ? "\u2713" : "\u2717" }),
41010
41200
  /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", weight: "semibold", className: "text-warning", children: guard.guardName }),
41011
41201
  /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", className: "text-muted-foreground", children: guard.context.type === "transition" ? `${guard.context.transitionFrom} \u2192 ${guard.context.transitionTo}` : guard.context.tickName }),
41012
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", className: "text-muted-foreground ml-auto", children: formatTime2(guard.timestamp) })
41202
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", className: "text-muted-foreground ml-auto", children: formatTime3(guard.timestamp) })
41013
41203
  ] }),
41014
41204
  content: /* @__PURE__ */ jsxRuntime.jsxs(Stack, { gap: "sm", children: [
41015
41205
  /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
@@ -41170,7 +41360,7 @@ function TransitionTimeline({ transitions }) {
41170
41360
  }
41171
41361
  );
41172
41362
  }
41173
- const formatTime2 = (ts) => {
41363
+ const formatTime3 = (ts) => {
41174
41364
  const d = new Date(ts);
41175
41365
  return d.toLocaleTimeString("en-US", {
41176
41366
  hour12: false,
@@ -41218,7 +41408,7 @@ function TransitionTimeline({ transitions }) {
41218
41408
  ${hasFailedEffects ? "bg-error" : allPassed ? "bg-success" : "bg-muted-foreground"}
41219
41409
  ` }),
41220
41410
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 text-xs py-1 px-2", children: [
41221
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", className: "text-muted-foreground font-mono min-w-[65px]", children: formatTime2(trace.timestamp) }),
41411
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", className: "text-muted-foreground font-mono min-w-[65px]", children: formatTime3(trace.timestamp) }),
41222
41412
  /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: "primary", size: "sm", className: "min-w-[60px] justify-center", children: trace.traitName }),
41223
41413
  /* @__PURE__ */ jsxRuntime.jsxs(Typography, { variant: "small", className: "font-mono text-muted-foreground", children: [
41224
41414
  trace.from,
@@ -41293,7 +41483,7 @@ function ServerBridgeTab({ bridge }) {
41293
41483
  }
41294
41484
  );
41295
41485
  }
41296
- const formatTime2 = (ts) => {
41486
+ const formatTime3 = (ts) => {
41297
41487
  if (ts === 0) return t("debug.never");
41298
41488
  const d = new Date(ts);
41299
41489
  return d.toLocaleTimeString("en-US", {
@@ -41336,7 +41526,7 @@ function ServerBridgeTab({ bridge }) {
41336
41526
  StatRow,
41337
41527
  {
41338
41528
  label: t("debug.lastHeartbeat"),
41339
- value: formatTime2(bridge.lastHeartbeat)
41529
+ value: formatTime3(bridge.lastHeartbeat)
41340
41530
  }
41341
41531
  )
41342
41532
  ] })
@@ -43448,7 +43638,7 @@ var init_TeamOrganism = __esm({
43448
43638
  TeamOrganism.displayName = "TeamOrganism";
43449
43639
  }
43450
43640
  });
43451
- function formatDate4(value) {
43641
+ function formatDate2(value) {
43452
43642
  const d = new Date(value);
43453
43643
  if (isNaN(d.getTime())) return value;
43454
43644
  return d.toLocaleDateString(void 0, { year: "numeric", month: "short", day: "numeric" });
@@ -43580,7 +43770,7 @@ var init_Timeline = __esm({
43580
43770
  /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "xs", className: cn("flex-1 min-w-0", !isLast && "pb-6"), children: [
43581
43771
  /* @__PURE__ */ jsxRuntime.jsxs(HStack, { justify: "between", align: "start", wrap: true, children: [
43582
43772
  /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", weight: "semibold", children: item.title }),
43583
- item.date && /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", color: "secondary", className: "flex-shrink-0", children: formatDate4(item.date) })
43773
+ item.date && /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", color: "secondary", className: "flex-shrink-0", children: formatDate2(item.date) })
43584
43774
  ] }),
43585
43775
  item.description && /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", color: "secondary", children: item.description }),
43586
43776
  item.tags && item.tags.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(HStack, { gap: "xs", wrap: true, children: item.tags.map((tag, tagIdx) => /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: "default", children: tag }, tagIdx)) }),
@@ -43743,6 +43933,7 @@ var init_component_registry_generated = __esm({
43743
43933
  init_DocSidebar();
43744
43934
  init_DocTOC();
43745
43935
  init_DocumentViewer();
43936
+ init_DrawGroup();
43746
43937
  init_DrawShape();
43747
43938
  init_DrawShapeLayer();
43748
43939
  init_DrawSprite();
@@ -44009,6 +44200,7 @@ var init_component_registry_generated = __esm({
44009
44200
  "DocSidebar": DocSidebar,
44010
44201
  "DocTOC": DocTOC,
44011
44202
  "DocumentViewer": DocumentViewer,
44203
+ "DrawGroup": DrawGroup,
44012
44204
  "DrawShape": DrawShape,
44013
44205
  "DrawShapeLayer": DrawShapeLayer,
44014
44206
  "DrawSprite": DrawSprite,
@@ -44243,7 +44435,7 @@ function enrichFormFields(fields, entityDef) {
44243
44435
  if (entityField) {
44244
44436
  const enriched = {
44245
44437
  name: field,
44246
- label: field.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/\b\w/g, (c) => c.toUpperCase()),
44438
+ label: humanizeFieldName(field),
44247
44439
  type: entityField.type,
44248
44440
  required: entityField.required ?? false
44249
44441
  };
@@ -44257,7 +44449,7 @@ function enrichFormFields(fields, entityDef) {
44257
44449
  }
44258
44450
  return enriched;
44259
44451
  }
44260
- return { name: field, label: field.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/\b\w/g, (c) => c.toUpperCase()) };
44452
+ return { name: field, label: humanizeFieldName(field) };
44261
44453
  }
44262
44454
  if (field && typeof field === "object" && !Array.isArray(field) && !React84__namespace.default.isValidElement(field) && !(field instanceof Date)) {
44263
44455
  const obj = field;
@@ -44912,9 +45104,10 @@ function SlotContentRenderer({
44912
45104
  const slotVal = restProps[slotKey];
44913
45105
  if (slotVal === void 0 || slotVal === null) continue;
44914
45106
  if (React84__namespace.default.isValidElement(slotVal) || typeof slotVal === "string" || typeof slotVal === "number" || typeof slotVal === "boolean") continue;
44915
- if (Array.isArray(slotVal) || typeof slotVal === "object" && "type" in slotVal) {
45107
+ const typelessChildren = !Array.isArray(slotVal) && typeof slotVal === "object" && !("type" in slotVal) && Array.isArray(slotVal.children) ? slotVal.children : void 0;
45108
+ if (typelessChildren !== void 0 || Array.isArray(slotVal) || typeof slotVal === "object" && "type" in slotVal) {
44916
45109
  nodeSlotOverrides[slotKey] = renderPatternChildren(
44917
- slotVal,
45110
+ typelessChildren ?? slotVal,
44918
45111
  onDismiss,
44919
45112
  `${content.id}-${slotKey}`,
44920
45113
  `${myPath}.${slotKey}`,
@@ -45093,6 +45286,7 @@ var init_UISlotRenderer = __esm({
45093
45286
  init_useEventBus();
45094
45287
  init_slot_types();
45095
45288
  init_cn();
45289
+ init_format();
45096
45290
  init_ErrorBoundary();
45097
45291
  init_Skeleton();
45098
45292
  init_renderer();