@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.
@@ -3980,6 +3980,87 @@ var init_TextHighlight = __esm({
3980
3980
  }
3981
3981
  });
3982
3982
 
3983
+ // lib/format.ts
3984
+ function humanizeFieldName(name) {
3985
+ 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();
3986
+ }
3987
+ function humanizeEnumValue(value) {
3988
+ return /^[a-z0-9]+(?:[_-][a-z0-9]+)+$/.test(value) ? humanizeFieldName(value) : value;
3989
+ }
3990
+ function formatDate(value) {
3991
+ if (!value) return "";
3992
+ const d = new Date(String(value));
3993
+ if (isNaN(d.getTime())) return String(value);
3994
+ return d.toLocaleDateString(void 0, { year: "numeric", month: "short", day: "numeric" });
3995
+ }
3996
+ function formatTime(value) {
3997
+ if (!value) return "";
3998
+ const d = new Date(String(value));
3999
+ if (isNaN(d.getTime())) return String(value);
4000
+ return d.toLocaleTimeString(void 0, { hour: "numeric", minute: "2-digit" });
4001
+ }
4002
+ function formatDateTime(value) {
4003
+ if (!value) return "";
4004
+ const d = new Date(String(value));
4005
+ if (isNaN(d.getTime())) return String(value);
4006
+ return `${formatDate(value)} ${formatTime(value)}`;
4007
+ }
4008
+ function asYesNo(value) {
4009
+ return value === false || value === 0 || String(value) === "false" ? "No" : "Yes";
4010
+ }
4011
+ function formatValue(value, format) {
4012
+ if (value === void 0 || value === null) return "";
4013
+ if (typeof value === "boolean") return asYesNo(value);
4014
+ switch (format) {
4015
+ case "date":
4016
+ return formatDate(value);
4017
+ case "time":
4018
+ return formatTime(value);
4019
+ case "datetime":
4020
+ return formatDateTime(value);
4021
+ case "currency":
4022
+ return typeof value === "number" ? `$${value.toFixed(2)}` : String(value);
4023
+ case "number":
4024
+ return typeof value === "number" ? value.toLocaleString() : String(value);
4025
+ case "percent":
4026
+ return typeof value === "number" ? `${Math.round(value)}%` : String(value);
4027
+ case "boolean":
4028
+ return asYesNo(value);
4029
+ default:
4030
+ return String(value);
4031
+ }
4032
+ }
4033
+ function compareCellValues(a, b) {
4034
+ const aEmpty = a === null || a === void 0 || a === "";
4035
+ const bEmpty = b === null || b === void 0 || b === "";
4036
+ if (aEmpty || bEmpty) return aEmpty && bEmpty ? 0 : aEmpty ? 1 : -1;
4037
+ if (typeof a === "number" && typeof b === "number") return a - b;
4038
+ if (typeof a === "boolean" && typeof b === "boolean") return Number(a) - Number(b);
4039
+ const aNum = Number(a);
4040
+ const bNum = Number(b);
4041
+ if (Number.isFinite(aNum) && Number.isFinite(bNum)) return aNum - bNum;
4042
+ const aTime = dateLikeTime(a);
4043
+ const bTime = dateLikeTime(b);
4044
+ if (aTime !== null && bTime !== null) return aTime - bTime;
4045
+ return String(a).localeCompare(String(b));
4046
+ }
4047
+ function dateLikeTime(value) {
4048
+ if (value instanceof Date) return value.getTime();
4049
+ const text = String(value);
4050
+ if (!/\d/.test(text) || !/[-/:T]/.test(text)) return null;
4051
+ const time = Date.parse(text);
4052
+ return Number.isNaN(time) ? null : time;
4053
+ }
4054
+ function sortRows(rows2, field, direction = "asc") {
4055
+ if (!field) return rows2;
4056
+ const dir = direction === "desc" ? -1 : 1;
4057
+ return [...rows2].sort((a, b) => dir * compareCellValues(a?.[field], b?.[field]));
4058
+ }
4059
+ var init_format = __esm({
4060
+ "lib/format.ts"() {
4061
+ }
4062
+ });
4063
+
3983
4064
  // components/core/atoms/Typography.tsx
3984
4065
  var Typography_exports = {};
3985
4066
  __export(Typography_exports, {
@@ -3989,6 +4070,7 @@ var variantStyles6, colorStyles, weightStyles, defaultElements, typographySizeSt
3989
4070
  var init_Typography = __esm({
3990
4071
  "components/core/atoms/Typography.tsx"() {
3991
4072
  init_cn();
4073
+ init_format();
3992
4074
  variantStyles6 = {
3993
4075
  h1: "text-4xl font-bold tracking-tight text-foreground",
3994
4076
  h2: "text-3xl font-bold tracking-tight text-foreground",
@@ -4076,11 +4158,16 @@ var init_Typography = __esm({
4076
4158
  id,
4077
4159
  className,
4078
4160
  style,
4161
+ format,
4079
4162
  content,
4080
4163
  children
4081
4164
  }) => {
4082
4165
  const variant = variantProp ?? (level ? `h${level}` : "body1");
4083
4166
  const Component = as || defaultElements[variant];
4167
+ let body = children ?? content;
4168
+ if (format !== void 0 && format !== "none" && (typeof body === "string" || typeof body === "number" || body instanceof Date)) {
4169
+ body = formatValue(body, format);
4170
+ }
4084
4171
  return React74__namespace.default.createElement(
4085
4172
  Component,
4086
4173
  {
@@ -4097,7 +4184,7 @@ var init_Typography = __esm({
4097
4184
  ),
4098
4185
  style
4099
4186
  },
4100
- children ?? content
4187
+ body
4101
4188
  );
4102
4189
  };
4103
4190
  exports.Typography.displayName = "Typography";
@@ -15594,6 +15681,15 @@ function createWebPainter(ctx, onAssetLoad) {
15594
15681
  ctx.lineWidth = lineWidth;
15595
15682
  ctx.stroke();
15596
15683
  },
15684
+ fillPath(d, color) {
15685
+ ctx.fillStyle = color;
15686
+ ctx.fill(new Path2D(d));
15687
+ },
15688
+ strokePath(d, color, lineWidth = 1) {
15689
+ ctx.strokeStyle = color;
15690
+ ctx.lineWidth = lineWidth;
15691
+ ctx.stroke(new Path2D(d));
15692
+ },
15597
15693
  text(str2, x, y, style) {
15598
15694
  if (style.font) ctx.font = style.font;
15599
15695
  ctx.fillStyle = style.color;
@@ -15831,6 +15927,16 @@ var init_DrawShape = __esm({
15831
15927
  if (node.stroke) painter.strokePoly(pts, node.stroke, node.strokeWidth ?? 1, true);
15832
15928
  break;
15833
15929
  }
15930
+ case "path": {
15931
+ if (!node.d) break;
15932
+ const base = dctx.projector.project(node.position);
15933
+ const tw = dctx.projector.tileWidth;
15934
+ painter.translate(base.x, base.y);
15935
+ painter.scale(tw, tw);
15936
+ if (node.fill) painter.fillPath(node.d, node.fill);
15937
+ if (node.stroke) painter.strokePath(node.d, node.stroke, (node.strokeWidth ?? 1) / tw);
15938
+ break;
15939
+ }
15834
15940
  }
15835
15941
  painter.restore();
15836
15942
  };
@@ -15922,6 +16028,19 @@ function paintDrawable(painter, node, dctx) {
15922
16028
  case "draw-text":
15923
16029
  paintText(painter, node, dctx);
15924
16030
  break;
16031
+ case "draw-group": {
16032
+ if (!isValidScenePos(node.position)) break;
16033
+ if (!Array.isArray(node.items)) break;
16034
+ const p = dctx.projector.project(node.position);
16035
+ painter.save();
16036
+ painter.translate(p.x, p.y);
16037
+ if (node.scale !== void 0) painter.scale(node.scale, node.scale);
16038
+ if (node.rotate !== void 0) painter.rotate(node.rotate);
16039
+ if (node.opacity !== void 0 && node.opacity !== 1) painter.setAlpha(node.opacity);
16040
+ for (const item of node.items) paintDrawable(painter, item, dctx);
16041
+ painter.restore();
16042
+ break;
16043
+ }
15925
16044
  case "draw-sprite-layer":
15926
16045
  paintSpriteLayer(painter, node, dctx);
15927
16046
  break;
@@ -15935,6 +16054,7 @@ function paintDrawable(painter, node, dctx) {
15935
16054
  }
15936
16055
  var init_paintDispatch = __esm({
15937
16056
  "lib/drawable/paintDispatch.ts"() {
16057
+ init_contract();
15938
16058
  init_DrawSprite();
15939
16059
  init_DrawShape();
15940
16060
  init_DrawText();
@@ -15952,6 +16072,7 @@ function collectDrawnItems(nodes) {
15952
16072
  case "draw-sprite":
15953
16073
  case "draw-shape":
15954
16074
  case "draw-text":
16075
+ case "draw-group":
15955
16076
  if (isValidScenePos(n.position)) out.push({ pos: n.position, id: n.id });
15956
16077
  break;
15957
16078
  case "draw-sprite-layer":
@@ -16672,9 +16793,6 @@ function normalizeFields(fields) {
16672
16793
  if (!fields) return [];
16673
16794
  return fields.map((f3) => typeof f3 === "string" ? f3 : f3.key ?? f3.name ?? "");
16674
16795
  }
16675
- function fieldLabel(key) {
16676
- return key.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/[_-]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
16677
- }
16678
16796
  function asBooleanValue(value) {
16679
16797
  if (typeof value === "boolean") return value;
16680
16798
  if (value === "true") return true;
@@ -16685,12 +16803,6 @@ function isDateField(key) {
16685
16803
  const lower = key.toLowerCase();
16686
16804
  return lower.includes("date") || lower.includes("time") || lower.endsWith("at") || lower.endsWith("_at");
16687
16805
  }
16688
- function formatDate(value) {
16689
- if (!value) return "";
16690
- const d = new Date(String(value));
16691
- if (isNaN(d.getTime())) return String(value);
16692
- return d.toLocaleDateString(void 0, { year: "numeric", month: "short", day: "numeric" });
16693
- }
16694
16806
  function statusVariant(value) {
16695
16807
  const v = value.toLowerCase();
16696
16808
  if (["active", "completed", "done", "approved", "published", "resolved", "open"].includes(v)) return "success";
@@ -16699,11 +16811,12 @@ function statusVariant(value) {
16699
16811
  if (["new", "created", "scheduled", "queued"].includes(v)) return "info";
16700
16812
  return "default";
16701
16813
  }
16702
- var STATUS_FIELDS, gapStyles3, alignStyles2; exports.CardGrid = void 0;
16814
+ var fieldLabel, STATUS_FIELDS, gapStyles3, alignStyles2; exports.CardGrid = void 0;
16703
16815
  var init_CardGrid = __esm({
16704
16816
  "components/core/organisms/CardGrid.tsx"() {
16705
16817
  "use client";
16706
16818
  init_cn();
16819
+ init_format();
16707
16820
  init_getNestedValue();
16708
16821
  init_useEventBus();
16709
16822
  init_atoms();
@@ -16712,6 +16825,7 @@ var init_CardGrid = __esm({
16712
16825
  init_Typography();
16713
16826
  init_Stack();
16714
16827
  init_Pagination();
16828
+ fieldLabel = humanizeFieldName;
16715
16829
  STATUS_FIELDS = /* @__PURE__ */ new Set(["status", "state", "priority", "type", "category", "role", "level", "tier"]);
16716
16830
  gapStyles3 = {
16717
16831
  none: "gap-0",
@@ -17219,7 +17333,7 @@ var init_CaseStudyOrganism = __esm({
17219
17333
  exports.CaseStudyOrganism.displayName = "CaseStudyOrganism";
17220
17334
  }
17221
17335
  });
17222
- var CHART_COLORS, seriesColor, monthFormatter, formatTimeLabel, BarChart, PieChart, LineChart, ScatterChart, LOOK_FROM_CHART_TYPE; exports.Chart = void 0;
17336
+ var CHART_COLORS, seriesColor, barColor, monthFormatter, formatTimeLabel, BarChart, PieChart, LineChart, ScatterChart, LOOK_FROM_CHART_TYPE; exports.Chart = void 0;
17223
17337
  var init_Chart = __esm({
17224
17338
  "components/core/molecules/Chart.tsx"() {
17225
17339
  "use client";
@@ -17239,6 +17353,7 @@ var init_Chart = __esm({
17239
17353
  "var(--color-accent)"
17240
17354
  ];
17241
17355
  seriesColor = (series, idx) => series.color ?? CHART_COLORS[idx % CHART_COLORS.length];
17356
+ barColor = (series, sIdx, catIdx, seriesCount) => series.color ?? CHART_COLORS[(seriesCount === 1 ? catIdx : sIdx) % CHART_COLORS.length];
17242
17357
  monthFormatter = new Intl.DateTimeFormat(void 0, {
17243
17358
  month: "short",
17244
17359
  year: "2-digit"
@@ -17311,7 +17426,7 @@ var init_Chart = __esm({
17311
17426
  children: series.map((s, sIdx) => {
17312
17427
  const value = valueAt(s, label);
17313
17428
  const ratio = stack === "normalize" ? total === 0 ? 0 : value / total * 100 : value / maxValue * 100;
17314
- const color = seriesColor(s, sIdx);
17429
+ const color = barColor(s, sIdx, catIdx, series.length);
17315
17430
  return /* @__PURE__ */ jsxRuntime.jsx(
17316
17431
  exports.Box,
17317
17432
  {
@@ -17366,7 +17481,7 @@ var init_Chart = __esm({
17366
17481
  /* @__PURE__ */ jsxRuntime.jsx(exports.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) => {
17367
17482
  const value = valueAt(s, label);
17368
17483
  const barHeight = value / maxValue * 100;
17369
- const color = seriesColor(s, sIdx);
17484
+ const color = barColor(s, sIdx, catIdx, series.length);
17370
17485
  return /* @__PURE__ */ jsxRuntime.jsx(
17371
17486
  exports.Box,
17372
17487
  {
@@ -17417,7 +17532,7 @@ var init_Chart = __esm({
17417
17532
  /* @__PURE__ */ jsxRuntime.jsx(exports.VStack, { gap: "none", className: "w-full flex-1 min-h-0", justify: "end", children: series.map((s, sIdx) => {
17418
17533
  const value = valueAt(s, label);
17419
17534
  const ratio = stack === "normalize" ? total === 0 ? 0 : value / total * 100 : value / maxValue * 100;
17420
- const color = seriesColor(s, sIdx);
17535
+ const color = barColor(s, sIdx, catIdx, series.length);
17421
17536
  return /* @__PURE__ */ jsxRuntime.jsx(
17422
17537
  exports.Box,
17423
17538
  {
@@ -20652,9 +20767,6 @@ var init_useDataDnd = __esm({
20652
20767
  function renderIconInput(icon, props) {
20653
20768
  return typeof icon === "string" ? /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { name: icon, ...props }) : /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { icon, ...props });
20654
20769
  }
20655
- function fieldLabel2(key) {
20656
- return key.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/[_-]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
20657
- }
20658
20770
  function statusVariant2(value) {
20659
20771
  const v = value.toLowerCase();
20660
20772
  if (["active", "completed", "done", "approved", "published", "resolved", "open", "online"].includes(v)) return "success";
@@ -20673,29 +20785,6 @@ function resolveBadgeVariant(field, value) {
20673
20785
  }
20674
20786
  return statusVariant2(value);
20675
20787
  }
20676
- function formatDate2(value) {
20677
- if (!value) return "";
20678
- const d = new Date(String(value));
20679
- if (isNaN(d.getTime())) return String(value);
20680
- return d.toLocaleDateString(void 0, { year: "numeric", month: "short", day: "numeric" });
20681
- }
20682
- function formatValue(value, format) {
20683
- if (value === void 0 || value === null) return "";
20684
- switch (format) {
20685
- case "date":
20686
- return formatDate2(value);
20687
- case "currency":
20688
- return typeof value === "number" ? `$${value.toFixed(2)}` : String(value);
20689
- case "number":
20690
- return typeof value === "number" ? value.toLocaleString() : String(value);
20691
- case "percent":
20692
- return typeof value === "number" ? `${Math.round(value)}%` : String(value);
20693
- case "boolean":
20694
- return value ? "Yes" : "No";
20695
- default:
20696
- return String(value);
20697
- }
20698
- }
20699
20788
  function DataGrid({
20700
20789
  entity,
20701
20790
  fields,
@@ -20982,7 +21071,7 @@ function DataGrid({
20982
21071
  if (val === void 0 || val === null || val === "") return null;
20983
21072
  return /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { gap: "xs", className: "items-center", children: [
20984
21073
  field.icon && renderIconInput(field.icon, { size: "xs" }),
20985
- /* @__PURE__ */ jsxRuntime.jsx(exports.Badge, { variant: resolveBadgeVariant(field, String(val)), children: formatValue(val, field.format) })
21074
+ /* @__PURE__ */ jsxRuntime.jsx(exports.Badge, { variant: resolveBadgeVariant(field, String(val)), children: humanizeEnumValue(formatValue(val, field.format)) })
20986
21075
  ] }, field.name);
20987
21076
  }) })
20988
21077
  ] }),
@@ -21092,11 +21181,12 @@ function DataGrid({
21092
21181
  ] })
21093
21182
  );
21094
21183
  }
21095
- var dataGridLog, BADGE_VARIANTS, gapStyles5, lookStyles5;
21184
+ var dataGridLog, fieldLabel2, BADGE_VARIANTS, gapStyles5, lookStyles5;
21096
21185
  var init_DataGrid = __esm({
21097
21186
  "components/core/molecules/DataGrid.tsx"() {
21098
21187
  "use client";
21099
21188
  init_cn();
21189
+ init_format();
21100
21190
  init_getNestedValue();
21101
21191
  init_useEventBus();
21102
21192
  init_Box();
@@ -21109,6 +21199,7 @@ var init_DataGrid = __esm({
21109
21199
  init_Menu();
21110
21200
  init_useDataDnd();
21111
21201
  dataGridLog = logger.createLogger("almadar:ui:data-grid");
21202
+ fieldLabel2 = humanizeFieldName;
21112
21203
  BADGE_VARIANTS = /* @__PURE__ */ new Set([
21113
21204
  "default",
21114
21205
  "primary",
@@ -21140,9 +21231,6 @@ var init_DataGrid = __esm({
21140
21231
  function renderIconInput2(icon, props) {
21141
21232
  return typeof icon === "string" ? /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { name: icon, ...props }) : /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { icon, ...props });
21142
21233
  }
21143
- function fieldLabel3(key) {
21144
- return key.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/[_-]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
21145
- }
21146
21234
  function statusVariant3(value) {
21147
21235
  const v = value.toLowerCase();
21148
21236
  if (["active", "completed", "done", "approved", "published", "resolved", "open", "online"].includes(v)) return "success";
@@ -21151,28 +21239,12 @@ function statusVariant3(value) {
21151
21239
  if (["new", "created", "scheduled", "queued", "info"].includes(v)) return "info";
21152
21240
  return "default";
21153
21241
  }
21154
- function formatDate3(value) {
21155
- if (!value) return "";
21156
- const d = new Date(String(value));
21157
- if (isNaN(d.getTime())) return String(value);
21158
- return d.toLocaleDateString(void 0, { year: "numeric", month: "short", day: "numeric" });
21159
- }
21160
21242
  function formatValue2(value, format, boolLabels) {
21161
- if (value === void 0 || value === null) return "";
21162
- switch (format) {
21163
- case "date":
21164
- return formatDate3(value);
21165
- case "currency":
21166
- return typeof value === "number" ? `$${value.toFixed(2)}` : String(value);
21167
- case "number":
21168
- return typeof value === "number" ? value.toLocaleString() : String(value);
21169
- case "percent":
21170
- return typeof value === "number" ? `${Math.round(value)}%` : String(value);
21171
- case "boolean":
21172
- return value ? boolLabels?.yes ?? "Yes" : boolLabels?.no ?? "No";
21173
- default:
21174
- return String(value);
21243
+ if (value !== void 0 && value !== null && (format === "boolean" || typeof value === "boolean")) {
21244
+ const isNo = value === false || value === 0 || String(value) === "false";
21245
+ return isNo ? boolLabels?.no ?? "No" : boolLabels?.yes ?? "Yes";
21175
21246
  }
21247
+ return formatValue(value, format);
21176
21248
  }
21177
21249
  function groupData(items, field) {
21178
21250
  const groups = /* @__PURE__ */ new Map();
@@ -21195,7 +21267,9 @@ function DataList({
21195
21267
  variant = "default",
21196
21268
  groupBy,
21197
21269
  senderField,
21270
+ senderLabelField,
21198
21271
  currentUser,
21272
+ emptyMessage,
21199
21273
  className,
21200
21274
  isLoading = false,
21201
21275
  error = null,
@@ -21214,6 +21288,8 @@ function DataList({
21214
21288
  hasMore,
21215
21289
  children,
21216
21290
  pageSize = 5,
21291
+ sortBy,
21292
+ sortDirection,
21217
21293
  renderItem: schemaRenderItem,
21218
21294
  dragGroup,
21219
21295
  accepts,
@@ -21242,7 +21318,11 @@ function DataList({
21242
21318
  dndItemIdField,
21243
21319
  dndRoot
21244
21320
  });
21245
- const allData = dnd.orderedItems;
21321
+ const orderedData = dnd.orderedItems;
21322
+ const allData = React74__namespace.default.useMemo(
21323
+ () => sortRows(orderedData, sortBy, sortDirection),
21324
+ [orderedData, sortBy, sortDirection]
21325
+ );
21246
21326
  const data = pageSize > 0 ? allData.slice(0, visibleCount) : allData;
21247
21327
  const hasMoreLocal = pageSize > 0 && visibleCount < allData.length;
21248
21328
  const hasRenderProp = typeof children === "function";
@@ -21279,7 +21359,7 @@ function DataList({
21279
21359
  };
21280
21360
  eventBus.emit(`UI:${action.event}`, payload);
21281
21361
  };
21282
- const renderItemActions = (itemData) => {
21362
+ const renderItemActions = (itemData, onPrimary = false) => {
21283
21363
  if (!itemActions || itemActions.length === 0) return null;
21284
21364
  const inline = maxInlineActions != null ? itemActions.slice(0, maxInlineActions) : itemActions;
21285
21365
  const overflow = maxInlineActions != null ? itemActions.slice(maxInlineActions) : [];
@@ -21292,7 +21372,12 @@ function DataList({
21292
21372
  onClick: handleActionClick(action, itemData),
21293
21373
  "data-testid": `action-${action.event}`,
21294
21374
  "data-row-id": String(itemData.id),
21295
- className: cn(action.variant === "danger" && "text-error hover:bg-error/10"),
21375
+ className: cn(
21376
+ action.variant === "danger" && "text-error hover:bg-error/10",
21377
+ // Must sit on the Button itself: the variant's own text colour
21378
+ // beats an inherited one from the row wrapper.
21379
+ onPrimary && "!text-primary-foreground hover:bg-primary-foreground/15"
21380
+ ),
21296
21381
  children: [
21297
21382
  action.icon && renderIconInput2(action.icon, { size: "xs", className: "mr-1" }),
21298
21383
  action.label
@@ -21334,7 +21419,7 @@ function DataList({
21334
21419
  return /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: "text-center py-8", children: /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "body", color: "error", children: error.message }) });
21335
21420
  }
21336
21421
  if (data.length === 0) {
21337
- const emptyNode = /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: "text-center py-12", children: /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "body", color: "secondary", children: t("empty.noItems") }) });
21422
+ const emptyNode = /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: "text-center py-12", children: /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "body", color: "secondary", children: emptyMessage || t("empty.noItems") }) });
21338
21423
  return dnd.enabled ? /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: dnd.wrapContainer(emptyNode) }) : emptyNode;
21339
21424
  }
21340
21425
  const gapClass = {
@@ -21350,51 +21435,93 @@ function DataList({
21350
21435
  const items2 = [...data];
21351
21436
  const groups2 = groupBy ? groupData(items2, groupBy) : [{ label: "", items: items2 }];
21352
21437
  const contentField = titleField?.name ?? fieldDefs[0]?.name ?? "";
21353
- return /* @__PURE__ */ jsxRuntime.jsx(exports.VStack, { gap: "sm", className: cn("py-2", className), children: groups2.map((group, gi) => /* @__PURE__ */ jsxRuntime.jsxs(React74__namespace.default.Fragment, { children: [
21354
- group.label && /* @__PURE__ */ jsxRuntime.jsx(exports.Divider, { label: group.label, className: "my-2" }),
21355
- group.items.map((itemData, index) => {
21356
- const id = itemData.id || `${gi}-${index}`;
21357
- const sender = senderField ? String(getNestedValue(itemData, senderField) ?? "") : "";
21358
- const isSent = Boolean(currentUser && sender === currentUser);
21359
- const content = getNestedValue(itemData, contentField);
21360
- const timestampField = fieldDefs.find((f3) => f3.format === "date");
21361
- const timestamp = timestampField ? getNestedValue(itemData, timestampField.name) : null;
21362
- return /* @__PURE__ */ jsxRuntime.jsx(
21363
- exports.Box,
21364
- {
21365
- className: cn(
21366
- "flex px-4",
21367
- isSent ? "justify-end" : "justify-start"
21368
- ),
21369
- children: /* @__PURE__ */ jsxRuntime.jsxs(
21370
- exports.Box,
21371
- {
21372
- className: cn(
21373
- "max-w-[75%] px-4 py-2",
21374
- isSent ? "bg-primary text-primary-foreground rounded-2xl rounded-br-sm" : "bg-muted text-foreground rounded-2xl rounded-bl-sm"
21375
- ),
21376
- children: [
21377
- !isSent && senderField && /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "caption", className: "font-semibold mb-0.5", children: sender }),
21378
- /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "body", className: cn(isSent && "text-primary-foreground"), children: content !== void 0 && content !== null ? String(content) : "" }),
21379
- timestamp != null ? /* @__PURE__ */ jsxRuntime.jsx(
21380
- exports.Typography,
21381
- {
21382
- variant: "caption",
21383
- className: cn(
21384
- "mt-1 text-xs",
21385
- isSent ? "opacity-70" : "text-muted-foreground"
21386
- ),
21387
- children: formatDate3(timestamp)
21388
- }
21389
- ) : null
21390
- ]
21391
- }
21392
- )
21393
- },
21394
- id
21395
- );
21396
- })
21397
- ] }, gi)) });
21438
+ const senderLabel = (itemData, raw) => {
21439
+ if (!senderLabelField) return raw;
21440
+ const v = getNestedValue(itemData, senderLabelField);
21441
+ return v === void 0 || v === null || v === "" ? raw : String(v);
21442
+ };
21443
+ return /* @__PURE__ */ jsxRuntime.jsxs(exports.VStack, { gap: "sm", className: cn("py-2", className), children: [
21444
+ groups2.map((group, gi) => /* @__PURE__ */ jsxRuntime.jsxs(React74__namespace.default.Fragment, { children: [
21445
+ group.label && /* @__PURE__ */ jsxRuntime.jsx(exports.Divider, { label: group.label, className: "my-2" }),
21446
+ group.items.map((itemData, index) => {
21447
+ const id = itemData.id || `${gi}-${index}`;
21448
+ const sender = senderField ? String(getNestedValue(itemData, senderField) ?? "") : "";
21449
+ const isSent = Boolean(currentUser && sender === currentUser);
21450
+ const content = getNestedValue(itemData, contentField);
21451
+ const timestampField = fieldDefs.find((f3) => f3.format === "date");
21452
+ const timestamp = timestampField ? getNestedValue(itemData, timestampField.name) : null;
21453
+ const metaFields = fieldDefs.filter(
21454
+ (f3) => f3.name !== contentField && f3.name !== timestampField?.name
21455
+ );
21456
+ return /* @__PURE__ */ jsxRuntime.jsx(
21457
+ exports.Box,
21458
+ {
21459
+ "data-entity-row": true,
21460
+ "data-entity-id": id,
21461
+ onClick: itemClickEvent ? handleRowClick(itemData) : void 0,
21462
+ className: cn(
21463
+ "flex px-4 group/rowactions",
21464
+ itemClickEvent && "cursor-pointer",
21465
+ isSent ? "justify-end" : "justify-start"
21466
+ ),
21467
+ children: /* @__PURE__ */ jsxRuntime.jsxs(
21468
+ exports.Box,
21469
+ {
21470
+ className: cn(
21471
+ "max-w-[75%] px-4 py-2",
21472
+ isSent ? "bg-primary text-primary-foreground rounded-2xl rounded-br-sm" : "bg-muted text-foreground rounded-2xl rounded-bl-sm"
21473
+ ),
21474
+ children: [
21475
+ !isSent && senderField && /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "caption", className: "font-semibold mb-0.5", children: senderLabel(itemData, sender) }),
21476
+ /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "body", className: cn(isSent && "text-primary-foreground"), children: content !== void 0 && content !== null ? String(content) : "" }),
21477
+ metaFields.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(exports.HStack, { gap: "xs", className: "mt-1 flex-wrap", children: metaFields.map((f3) => {
21478
+ const v = getNestedValue(itemData, f3.name);
21479
+ if (v === void 0 || v === null || v === "") return null;
21480
+ return f3.variant === "badge" ? /* @__PURE__ */ jsxRuntime.jsx(exports.Badge, { variant: statusVariant3(String(v)), children: String(v) }, f3.name) : /* @__PURE__ */ jsxRuntime.jsx(
21481
+ exports.Typography,
21482
+ {
21483
+ variant: "caption",
21484
+ className: cn("text-xs", isSent ? "opacity-70" : "text-muted-foreground"),
21485
+ children: formatValue2(v, f3.format)
21486
+ },
21487
+ f3.name
21488
+ );
21489
+ }) }),
21490
+ /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { gap: "xs", className: "mt-1 items-center justify-between", children: [
21491
+ timestamp != null ? /* @__PURE__ */ jsxRuntime.jsx(
21492
+ exports.Typography,
21493
+ {
21494
+ variant: "caption",
21495
+ className: cn("text-xs", isSent ? "opacity-70" : "text-muted-foreground"),
21496
+ children: formatDate(timestamp)
21497
+ }
21498
+ ) : /* @__PURE__ */ jsxRuntime.jsx("span", {}),
21499
+ renderItemActions(itemData, isSent)
21500
+ ] })
21501
+ ]
21502
+ }
21503
+ )
21504
+ },
21505
+ id
21506
+ );
21507
+ })
21508
+ ] }, gi)),
21509
+ hasMoreLocal && /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: "flex justify-center py-3", children: /* @__PURE__ */ jsxRuntime.jsxs(
21510
+ exports.Button,
21511
+ {
21512
+ variant: "ghost",
21513
+ size: "sm",
21514
+ onClick: () => setVisibleCount((prev) => prev + (pageSize || 5)),
21515
+ children: [
21516
+ /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { name: "chevron-down", size: "xs", className: "mr-1" }),
21517
+ t("common.showMore"),
21518
+ " (",
21519
+ t("common.remaining", { count: allData.length - visibleCount }),
21520
+ ")"
21521
+ ]
21522
+ }
21523
+ ) })
21524
+ ] });
21398
21525
  }
21399
21526
  const items = [...data];
21400
21527
  const groups = groupBy ? groupData(items, groupBy) : [{ label: "", items }];
@@ -21543,11 +21670,12 @@ function DataList({
21543
21670
  )
21544
21671
  );
21545
21672
  }
21546
- var dataListLog, listLookStyles;
21673
+ var dataListLog, fieldLabel3, listLookStyles;
21547
21674
  var init_DataList = __esm({
21548
21675
  "components/core/molecules/DataList.tsx"() {
21549
21676
  "use client";
21550
21677
  init_cn();
21678
+ init_format();
21551
21679
  init_getNestedValue();
21552
21680
  init_useEventBus();
21553
21681
  init_Box();
@@ -21562,6 +21690,7 @@ var init_DataList = __esm({
21562
21690
  init_Menu();
21563
21691
  init_useDataDnd();
21564
21692
  dataListLog = logger.createLogger("almadar:ui:data-list");
21693
+ fieldLabel3 = humanizeFieldName;
21565
21694
  listLookStyles = {
21566
21695
  dense: "[&_[data-entity-row]>div]:!py-1 [&_[data-entity-row]>div]:!px-3",
21567
21696
  spacious: "[&_[data-entity-row]>div]:!py-5 [&_[data-entity-row]>div]:!px-8",
@@ -26030,7 +26159,7 @@ var init_ScoreDisplay = __esm({
26030
26159
  ScoreDisplay.displayName = "ScoreDisplay";
26031
26160
  }
26032
26161
  });
26033
- function formatTime(seconds, format) {
26162
+ function formatTime2(seconds, format) {
26034
26163
  const clamped = Math.max(0, Math.floor(seconds));
26035
26164
  if (format === "ss") {
26036
26165
  return `${clamped}s`;
@@ -26067,7 +26196,7 @@ function TimerDisplay({
26067
26196
  ),
26068
26197
  children: [
26069
26198
  iconAsset && /* @__PURE__ */ jsxRuntime.jsx(GameIcon, { assetUrl: iconAsset, icon: "image", size: 16, className: "w-4 h-4 object-contain flex-shrink-0" }),
26070
- formatTime(seconds, format)
26199
+ formatTime2(seconds, format)
26071
26200
  ]
26072
26201
  }
26073
26202
  );
@@ -29571,7 +29700,7 @@ function renderIconInput3(icon, props) {
29571
29700
  return typeof icon === "string" ? /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { name: icon, ...props }) : /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { icon, ...props });
29572
29701
  }
29573
29702
  function columnLabel(col) {
29574
- return col.header ?? col.label ?? col.key.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/[_-]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
29703
+ return col.header ?? col.label ?? humanizeFieldName(col.key);
29575
29704
  }
29576
29705
  function asFieldValue(v) {
29577
29706
  if (v === void 0 || v === null) return v;
@@ -29590,25 +29719,6 @@ function statusVariant4(value) {
29590
29719
  if (["new", "created", "scheduled", "queued", "info"].includes(v)) return "info";
29591
29720
  return "default";
29592
29721
  }
29593
- function formatCell(value, format) {
29594
- if (value === void 0 || value === null) return "";
29595
- switch (format) {
29596
- case "date": {
29597
- const d = new Date(String(value));
29598
- return isNaN(d.getTime()) ? String(value) : d.toLocaleDateString(void 0, { year: "numeric", month: "short", day: "numeric" });
29599
- }
29600
- case "currency":
29601
- return typeof value === "number" ? `$${value.toFixed(2)}` : String(value);
29602
- case "number":
29603
- return typeof value === "number" ? value.toLocaleString() : String(value);
29604
- case "percent":
29605
- return typeof value === "number" ? `${Math.round(value)}%` : String(value);
29606
- case "boolean":
29607
- return value ? "Yes" : "No";
29608
- default:
29609
- return String(value);
29610
- }
29611
- }
29612
29722
  function groupData2(items, field) {
29613
29723
  const groups = /* @__PURE__ */ new Map();
29614
29724
  for (const item of items) {
@@ -29654,7 +29764,8 @@ function TableView({
29654
29764
  const { t } = hooks.useTranslate();
29655
29765
  const [visibleCount, setVisibleCount] = React74__namespace.default.useState(pageSize > 0 ? pageSize : Infinity);
29656
29766
  const [localSelected, setLocalSelected] = React74__namespace.default.useState(/* @__PURE__ */ new Set());
29657
- const colDefs = columns ?? fields ?? [];
29767
+ const colDefs = (Array.isArray(columns) ? columns : void 0) ?? (Array.isArray(fields) ? fields : void 0) ?? [];
29768
+ const actionDefs = Array.isArray(itemActions) ? itemActions : [];
29658
29769
  const allDataRaw = Array.isArray(entity) ? entity : entity ? [entity] : [];
29659
29770
  const dnd = useDataDnd({
29660
29771
  items: allDataRaw,
@@ -29674,6 +29785,23 @@ function TableView({
29674
29785
  const hasRenderProp = typeof children === "function";
29675
29786
  const idField = dndItemIdField ?? "id";
29676
29787
  const isCoarsePointer = useMediaQuery("(pointer: coarse)");
29788
+ React74__namespace.default.useEffect(() => {
29789
+ tableViewLog.debug("render", {
29790
+ rowCount: data.length,
29791
+ colCount: colDefs.length,
29792
+ look,
29793
+ isLoading: Boolean(isLoading),
29794
+ hasError: Boolean(error),
29795
+ dnd: dnd.enabled
29796
+ });
29797
+ if (data.length > 0 && !hasRenderProp && colDefs.length === 0) {
29798
+ tableViewLog.warn("columns-unresolved", {
29799
+ rowCount: data.length,
29800
+ columnsIsArray: Array.isArray(columns),
29801
+ fieldsIsArray: Array.isArray(fields)
29802
+ });
29803
+ }
29804
+ }, [data, colDefs, look, isLoading, error, dnd.enabled, hasRenderProp, columns, fields]);
29677
29805
  const selected = selectedIds ? new Set(selectedIds) : localSelected;
29678
29806
  const emitSelection = (next) => {
29679
29807
  if (!selectedIds) setLocalSelected(next);
@@ -29717,21 +29845,12 @@ function TableView({
29717
29845
  }),
29718
29846
  [colDefs, data]
29719
29847
  );
29720
- if (isLoading) {
29721
- return /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: "text-center py-8", children: /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "body", color: "secondary", children: t("loading.items") }) });
29722
- }
29723
- if (error) {
29724
- return /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: "text-center py-8", children: /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "body", color: "error", children: error.message }) });
29725
- }
29726
- if (data.length === 0) {
29727
- const emptyNode = /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: "text-center py-12", children: /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "body", color: "secondary", children: emptyMessage || t("empty.noItems") }) });
29728
- return dnd.enabled ? /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: dnd.wrapContainer(emptyNode) }) : emptyNode;
29729
- }
29848
+ const statusNode = isLoading ? /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: "text-center py-8", children: /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "body", color: "secondary", children: t("loading.items") }) }) : error ? /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: "text-center py-8", children: /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "body", color: "error", children: error.message }) }) : data.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: "text-center py-12", children: /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "body", color: "secondary", children: emptyMessage || t("empty.noItems") }) }) : null;
29730
29849
  const lk = LOOKS[look];
29731
- const hasActions = Boolean(itemActions && itemActions.length > 0);
29850
+ const hasActions = actionDefs.length > 0;
29732
29851
  const effectiveMaxInline = isCoarsePointer ? 0 : maxInlineActions;
29733
- const inlineActionCount = hasActions ? effectiveMaxInline != null ? Math.min(itemActions.length, effectiveMaxInline) : itemActions.length : 0;
29734
- const hasOverflowActions = hasActions && effectiveMaxInline != null && itemActions.length > effectiveMaxInline;
29852
+ const inlineActionCount = hasActions ? effectiveMaxInline != null ? Math.min(actionDefs.length, effectiveMaxInline) : actionDefs.length : 0;
29853
+ const hasOverflowActions = hasActions && effectiveMaxInline != null && actionDefs.length > effectiveMaxInline;
29735
29854
  const actionsTrack = hasActions ? `${inlineActionCount * 6 + (hasOverflowActions ? 3 : 0)}rem` : null;
29736
29855
  const gridTemplateColumns = [
29737
29856
  selectable ? "auto" : null,
@@ -29819,11 +29938,11 @@ function TableView({
29819
29938
  col.className
29820
29939
  );
29821
29940
  if (col.format === "badge" && raw != null && raw !== "") {
29822
- return /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { role: "cell", className: cellBase, children: /* @__PURE__ */ jsxRuntime.jsx(exports.Badge, { variant: statusVariant4(String(raw)), size: "sm", className: "whitespace-nowrap", children: String(raw) }) }, col.key);
29941
+ return /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { role: "cell", className: cellBase, children: /* @__PURE__ */ jsxRuntime.jsx(exports.Badge, { variant: statusVariant4(String(raw)), size: "sm", className: "whitespace-nowrap", children: humanizeEnumValue(String(raw)) }) }, col.key);
29823
29942
  }
29824
29943
  return /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { role: "cell", className: cellBase, children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate text-foreground", children: formatCell(raw, col.format) }) }, col.key);
29825
29944
  }),
29826
- itemActions && itemActions.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(
29945
+ hasActions && /* @__PURE__ */ jsxRuntime.jsxs(
29827
29946
  exports.HStack,
29828
29947
  {
29829
29948
  gap: "xs",
@@ -29835,7 +29954,7 @@ function TableView({
29835
29954
  lk.striped && index % 2 === 1 ? "bg-[var(--color-surface-subtle)]" : "bg-[var(--color-card)] group-hover:bg-[var(--color-surface-subtle)]"
29836
29955
  ),
29837
29956
  children: [
29838
- (effectiveMaxInline != null ? itemActions.slice(0, effectiveMaxInline) : itemActions).map((action, i) => /* @__PURE__ */ jsxRuntime.jsxs(
29957
+ (effectiveMaxInline != null ? actionDefs.slice(0, effectiveMaxInline) : actionDefs).map((action, i) => /* @__PURE__ */ jsxRuntime.jsxs(
29839
29958
  exports.Button,
29840
29959
  {
29841
29960
  variant: action.variant === "primary" ? "primary" : "ghost",
@@ -29851,12 +29970,12 @@ function TableView({
29851
29970
  },
29852
29971
  i
29853
29972
  )),
29854
- effectiveMaxInline != null && itemActions.length > effectiveMaxInline && /* @__PURE__ */ jsxRuntime.jsx(
29973
+ effectiveMaxInline != null && actionDefs.length > effectiveMaxInline && /* @__PURE__ */ jsxRuntime.jsx(
29855
29974
  exports.Menu,
29856
29975
  {
29857
29976
  position: "bottom-end",
29858
29977
  trigger: /* @__PURE__ */ jsxRuntime.jsx(exports.Button, { variant: "ghost", size: "sm", "aria-label": t("common.actions"), "data-testid": "action-overflow", children: /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { name: "more-horizontal", size: "xs" }) }),
29859
- items: itemActions.slice(effectiveMaxInline).map((action) => ({
29978
+ items: actionDefs.slice(effectiveMaxInline).map((action) => ({
29860
29979
  label: action.label,
29861
29980
  icon: action.icon,
29862
29981
  event: action.event,
@@ -29883,15 +30002,16 @@ function TableView({
29883
30002
  group.label && /* @__PURE__ */ jsxRuntime.jsx(exports.Divider, { label: group.label, className: gi > 0 ? "mt-3" : "mt-0" }),
29884
30003
  group.items.map((row) => renderRow(row, runningIndex++))
29885
30004
  ] }, gi)) });
30005
+ const showHeader = colDefs.length > 0 || hasRenderProp;
29886
30006
  return /* @__PURE__ */ jsxRuntime.jsxs(
29887
30007
  exports.Box,
29888
30008
  {
29889
30009
  role: "table",
29890
30010
  className: cn("w-full text-sm", className),
29891
30011
  children: [
29892
- header,
29893
- dnd.wrapContainer(body),
29894
- hasMore && /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: "flex justify-center py-3", children: /* @__PURE__ */ jsxRuntime.jsxs(exports.Button, { variant: "ghost", size: "sm", onClick: () => setVisibleCount((p) => p + (pageSize || 5)), children: [
30012
+ showHeader && header,
30013
+ dnd.wrapContainer(statusNode ? /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { role: "rowgroup", children: statusNode }) : body),
30014
+ !statusNode && hasMore && /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: "flex justify-center py-3", children: /* @__PURE__ */ jsxRuntime.jsxs(exports.Button, { variant: "ghost", size: "sm", onClick: () => setVisibleCount((p) => p + (pageSize || 5)), children: [
29895
30015
  /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { name: "chevron-down", size: "xs", className: "mr-1" }),
29896
30016
  t("common.showMore"),
29897
30017
  " (",
@@ -29902,11 +30022,12 @@ function TableView({
29902
30022
  }
29903
30023
  );
29904
30024
  }
29905
- var MAX_MEASURED_COL_CH, BADGE_CHROME_CH, alignClass, weightClass, LOOKS;
30025
+ var tableViewLog, formatCell, MAX_MEASURED_COL_CH, BADGE_CHROME_CH, alignClass, weightClass, LOOKS;
29906
30026
  var init_TableView = __esm({
29907
30027
  "components/core/molecules/TableView.tsx"() {
29908
30028
  "use client";
29909
30029
  init_cn();
30030
+ init_format();
29910
30031
  init_getNestedValue();
29911
30032
  init_useEventBus();
29912
30033
  init_useMediaQuery();
@@ -29920,7 +30041,8 @@ var init_TableView = __esm({
29920
30041
  init_Divider();
29921
30042
  init_Menu();
29922
30043
  init_useDataDnd();
29923
- logger.createLogger("almadar:ui:table-view");
30044
+ tableViewLog = logger.createLogger("almadar:ui:table-view");
30045
+ formatCell = (value, format) => formatValue(value, format);
29924
30046
  MAX_MEASURED_COL_CH = 32;
29925
30047
  BADGE_CHROME_CH = 4;
29926
30048
  alignClass = {
@@ -37304,6 +37426,7 @@ var init_GraphCanvas = __esm({
37304
37426
  title,
37305
37427
  nodes: propNodes = NO_NODES,
37306
37428
  edges: propEdges = NO_EDGES,
37429
+ proposedEdges = NO_EDGES,
37307
37430
  similarity: propSimilarity = NO_SIM,
37308
37431
  height = 400,
37309
37432
  showLabels = true,
@@ -37311,6 +37434,7 @@ var init_GraphCanvas = __esm({
37311
37434
  draggable = true,
37312
37435
  actions,
37313
37436
  onNodeClick,
37437
+ onMarkClick,
37314
37438
  onNodeDoubleClick,
37315
37439
  onBadgeClick,
37316
37440
  nodeClickEvent,
@@ -37339,6 +37463,18 @@ var init_GraphCanvas = __esm({
37339
37463
  const laidOutRef = React74.useRef(false);
37340
37464
  const [, forceUpdate] = React74.useState(0);
37341
37465
  const [logicalW, setLogicalW] = React74.useState(800);
37466
+ const hasProposed = React74.useMemo(() => propNodes.some((n) => n.mark?.kind === "proposed"), [propNodes]);
37467
+ const [pulseTick, setPulseTick] = React74.useState(0);
37468
+ React74.useEffect(() => {
37469
+ if (!hasProposed) return;
37470
+ let raf = 0;
37471
+ const loop = () => {
37472
+ setPulseTick((t2) => (t2 + 1) % 1e6);
37473
+ raf = requestAnimationFrame(loop);
37474
+ };
37475
+ raf = requestAnimationFrame(loop);
37476
+ return () => cancelAnimationFrame(raf);
37477
+ }, [hasProposed]);
37342
37478
  React74.useEffect(() => {
37343
37479
  const canvas = canvasRef.current;
37344
37480
  if (!canvas || typeof ResizeObserver === "undefined") return;
@@ -37640,18 +37776,38 @@ var init_GraphCanvas = __esm({
37640
37776
  }
37641
37777
  }
37642
37778
  ctx.globalAlpha = 1;
37779
+ ctx.setLineDash([4, 4]);
37780
+ for (const edge of proposedEdges) {
37781
+ const source = nodes.find((n) => n.id === edge.source);
37782
+ const target = nodes.find((n) => n.id === edge.target);
37783
+ if (!source || !target) continue;
37784
+ ctx.globalAlpha = 0.4;
37785
+ ctx.beginPath();
37786
+ ctx.moveTo(source.x, source.y);
37787
+ ctx.lineTo(target.x, target.y);
37788
+ ctx.strokeStyle = edge.color || mutedColor;
37789
+ ctx.lineWidth = 1;
37790
+ ctx.stroke();
37791
+ }
37792
+ ctx.setLineDash([]);
37793
+ ctx.globalAlpha = 1;
37643
37794
  for (const node of nodes) {
37644
37795
  const size = node.size || 8;
37645
37796
  const color = resolveColor3(node.color || getGroupColor(node.group, groups), canvas);
37646
37797
  const isHovered = hoveredNode === node.id;
37647
37798
  const isSelected = selectedNodeId !== void 0 && node.id === selectedNodeId;
37648
- ctx.globalAlpha = hoveredNode && !neighbors.has(node.id) ? 0.2 : 1;
37799
+ const mark = node.mark;
37800
+ ctx.globalAlpha = hoveredNode && !neighbors.has(node.id) ? 0.2 : mark?.kind === "proposed" ? 0.55 : 1;
37649
37801
  const radius = isSelected ? size + 4 : isHovered ? size + 2 : size;
37650
37802
  ctx.beginPath();
37651
37803
  ctx.arc(node.x, node.y, radius, 0, Math.PI * 2);
37652
- ctx.fillStyle = color;
37804
+ ctx.fillStyle = mark?.kind === "proposed" ? mutedColor : color;
37653
37805
  ctx.fill();
37654
- if (isSelected) {
37806
+ if (mark?.kind === "proposed") {
37807
+ ctx.setLineDash([3, 3]);
37808
+ ctx.strokeStyle = resolveColor3(mark.tint ?? "var(--color-warning)", canvas);
37809
+ ctx.lineWidth = 1.5;
37810
+ } else if (isSelected) {
37655
37811
  ctx.strokeStyle = accentColor;
37656
37812
  ctx.lineWidth = 3;
37657
37813
  } else {
@@ -37659,6 +37815,28 @@ var init_GraphCanvas = __esm({
37659
37815
  ctx.lineWidth = isHovered ? 2 : 1;
37660
37816
  }
37661
37817
  ctx.stroke();
37818
+ ctx.setLineDash([]);
37819
+ if (mark?.kind === "suggested") {
37820
+ ctx.beginPath();
37821
+ ctx.arc(node.x, node.y, radius + 3, 0, Math.PI * 2);
37822
+ ctx.strokeStyle = accentColor;
37823
+ ctx.lineWidth = 2;
37824
+ ctx.stroke();
37825
+ ctx.beginPath();
37826
+ ctx.arc(node.x + radius * 0.7, node.y - radius * 0.7, 2.5, 0, Math.PI * 2);
37827
+ ctx.fillStyle = accentColor;
37828
+ ctx.fill();
37829
+ } else if (mark?.kind === "proposed") {
37830
+ const phase = pulseTick % 60 / 60;
37831
+ const baseAlpha = hoveredNode && !neighbors.has(node.id) ? 0.2 : 0.55;
37832
+ ctx.globalAlpha = baseAlpha * (0.4 + 0.4 * (1 - Math.abs(phase * 2 - 1)));
37833
+ ctx.beginPath();
37834
+ ctx.arc(node.x, node.y, radius + 6 + Math.sin(phase * Math.PI * 2) * 3, 0, Math.PI * 2);
37835
+ ctx.strokeStyle = resolveColor3(mark.tint ?? "var(--color-warning)", canvas);
37836
+ ctx.lineWidth = 1.5;
37837
+ ctx.stroke();
37838
+ ctx.globalAlpha = baseAlpha;
37839
+ }
37662
37840
  if (showLabels && node.label) {
37663
37841
  const displayLabel = truncateLabel(node.label);
37664
37842
  ctx.font = `${isSelected || isHovered ? "700" : "600"} 12px ${fontFamily}`;
@@ -37793,11 +37971,15 @@ var init_GraphCanvas = __esm({
37793
37971
  return;
37794
37972
  }
37795
37973
  }
37974
+ if (node.mark && onMarkClick) {
37975
+ onMarkClick(node);
37976
+ return;
37977
+ }
37796
37978
  handleNodeClick(node);
37797
37979
  }
37798
37980
  }
37799
37981
  },
37800
- [toCoords, nodeAt, handleNodeClick, onBadgeClick, selectedNodeId]
37982
+ [toCoords, nodeAt, handleNodeClick, onBadgeClick, onMarkClick, selectedNodeId]
37801
37983
  );
37802
37984
  const handlePointerLeave = React74.useCallback(() => {
37803
37985
  setHoveredNode(null);
@@ -38545,9 +38727,6 @@ var init_types2 = __esm({
38545
38727
  };
38546
38728
  }
38547
38729
  });
38548
- function humanizeFieldName(name) {
38549
- 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();
38550
- }
38551
38730
  function normalizeColumns(columns) {
38552
38731
  return columns.map((col) => {
38553
38732
  if (typeof col === "string") {
@@ -38972,6 +39151,7 @@ var init_DataTable = __esm({
38972
39151
  "components/core/organisms/DataTable.tsx"() {
38973
39152
  "use client";
38974
39153
  init_cn();
39154
+ init_format();
38975
39155
  init_getNestedValue();
38976
39156
  init_atoms();
38977
39157
  init_Box();
@@ -39024,9 +39204,6 @@ function getBadgeVariant(fieldName, value) {
39024
39204
  }
39025
39205
  return "default";
39026
39206
  }
39027
- function formatFieldLabel(fieldName) {
39028
- return fieldName.replace(/([A-Z])/g, " $1").replace(/^./, (str2) => str2.toUpperCase());
39029
- }
39030
39207
  function formatFieldValue2(value, fieldName) {
39031
39208
  if (typeof value === "number") {
39032
39209
  if (fieldName.toLowerCase().includes("progress") || fieldName.toLowerCase().includes("percent")) {
@@ -39154,7 +39331,7 @@ function buildFieldTypeMap(fields) {
39154
39331
  }
39155
39332
  return map;
39156
39333
  }
39157
- var ReactMarkdown2; exports.DetailPanel = void 0;
39334
+ var formatFieldLabel, ReactMarkdown2; exports.DetailPanel = void 0;
39158
39335
  var init_DetailPanel = __esm({
39159
39336
  "components/core/organisms/DetailPanel.tsx"() {
39160
39337
  "use client";
@@ -39166,8 +39343,10 @@ var init_DetailPanel = __esm({
39166
39343
  init_ErrorState();
39167
39344
  init_EmptyState();
39168
39345
  init_cn();
39346
+ init_format();
39169
39347
  init_getNestedValue();
39170
39348
  init_useEventBus();
39349
+ formatFieldLabel = humanizeFieldName;
39171
39350
  ReactMarkdown2 = React74.lazy(() => import('react-markdown'));
39172
39351
  exports.DetailPanel = ({
39173
39352
  title: propTitle,
@@ -39496,6 +39675,16 @@ var init_DetailPanel = __esm({
39496
39675
  exports.DetailPanel.displayName = "DetailPanel";
39497
39676
  }
39498
39677
  });
39678
+
39679
+ // components/game/atoms/DrawGroup.tsx
39680
+ function DrawGroup(_props) {
39681
+ return null;
39682
+ }
39683
+ var init_DrawGroup = __esm({
39684
+ "components/game/atoms/DrawGroup.tsx"() {
39685
+ "use client";
39686
+ }
39687
+ });
39499
39688
  function extractTitle(children) {
39500
39689
  if (!React74__namespace.default.isValidElement(children)) return void 0;
39501
39690
  const props = children.props;
@@ -40696,7 +40885,7 @@ function formatValue3(value, fieldName) {
40696
40885
  return String(value);
40697
40886
  }
40698
40887
  function formatFieldLabel2(fieldName) {
40699
- return fieldName.replace(/([A-Z])/g, " $1").replace(/^./, (str2) => str2.toUpperCase()).replace(/Id$/, "").trim();
40888
+ return humanizeFieldName(fieldName).replace(/\sId$/, "").trim();
40700
40889
  }
40701
40890
  var STATUS_STYLES, StatusBadge, ProgressIndicator; exports.List = void 0;
40702
40891
  var init_List = __esm({
@@ -40708,6 +40897,7 @@ var init_List = __esm({
40708
40897
  init_EmptyState();
40709
40898
  init_LoadingState();
40710
40899
  init_cn();
40900
+ init_format();
40711
40901
  init_getNestedValue();
40712
40902
  init_useEventBus();
40713
40903
  init_types2();
@@ -42169,7 +42359,7 @@ function TicksTab({ ticks: ticks2 }) {
42169
42359
  }
42170
42360
  );
42171
42361
  }
42172
- const formatTime2 = (ms) => {
42362
+ const formatTime3 = (ms) => {
42173
42363
  if (ms === 0) return "never";
42174
42364
  const seconds = Math.floor((Date.now() - ms) / 1e3);
42175
42365
  if (seconds < 1) return "just now";
@@ -42195,7 +42385,7 @@ function TicksTab({ ticks: ticks2 }) {
42195
42385
  tick.executionTime.toFixed(1),
42196
42386
  "ms exec"
42197
42387
  ] }),
42198
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: formatTime2(tick.lastRun) })
42388
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: formatTime3(tick.lastRun) })
42199
42389
  ] }),
42200
42390
  tick.guardName && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-2", children: /* @__PURE__ */ jsxRuntime.jsxs(exports.Badge, { variant: tick.guardPassed ? "success" : "danger", size: "sm", children: [
42201
42391
  tick.guardName,
@@ -42319,7 +42509,7 @@ function EventFlowTab({ events: events2 }) {
42319
42509
  if (filter === "all") return events2;
42320
42510
  return events2.filter((e) => e.type === filter);
42321
42511
  }, [events2, filter]);
42322
- const formatTime2 = (timestamp) => {
42512
+ const formatTime3 = (timestamp) => {
42323
42513
  const date = new Date(timestamp);
42324
42514
  return date.toLocaleTimeString("en-US", {
42325
42515
  hour12: false,
@@ -42395,7 +42585,7 @@ function EventFlowTab({ events: events2 }) {
42395
42585
  {
42396
42586
  className: "flex items-start gap-2 text-xs py-1 hover:bg-muted/50 rounded px-1",
42397
42587
  children: [
42398
- /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "small", className: "text-muted-foreground font-mono min-w-[65px]", children: formatTime2(event.timestamp) }),
42588
+ /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "small", className: "text-muted-foreground font-mono min-w-[65px]", children: formatTime3(event.timestamp) }),
42399
42589
  /* @__PURE__ */ jsxRuntime.jsx("span", { children: icon }),
42400
42590
  /* @__PURE__ */ jsxRuntime.jsx(exports.Badge, { variant, size: "sm", className: "min-w-[60px] justify-center", children: event.source }),
42401
42591
  /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "small", className: "text-muted-foreground", children: event.message })
@@ -42449,7 +42639,7 @@ function GuardsPanel({ guards }) {
42449
42639
  if (filter === "passed") return guards.filter((g) => g.result);
42450
42640
  return guards.filter((g) => !g.result);
42451
42641
  }, [guards, filter]);
42452
- const formatTime2 = (timestamp) => {
42642
+ const formatTime3 = (timestamp) => {
42453
42643
  const date = new Date(timestamp);
42454
42644
  return date.toLocaleTimeString("en-US", {
42455
42645
  hour12: false,
@@ -42464,7 +42654,7 @@ function GuardsPanel({ guards }) {
42464
42654
  /* @__PURE__ */ jsxRuntime.jsx(exports.Badge, { variant: guard.result ? "success" : "danger", size: "sm", children: guard.result ? "\u2713" : "\u2717" }),
42465
42655
  /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "body", weight: "semibold", className: "text-warning", children: guard.guardName }),
42466
42656
  /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "small", className: "text-muted-foreground", children: guard.context.type === "transition" ? `${guard.context.transitionFrom} \u2192 ${guard.context.transitionTo}` : guard.context.tickName }),
42467
- /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "small", className: "text-muted-foreground ml-auto", children: formatTime2(guard.timestamp) })
42657
+ /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "small", className: "text-muted-foreground ml-auto", children: formatTime3(guard.timestamp) })
42468
42658
  ] }),
42469
42659
  content: /* @__PURE__ */ jsxRuntime.jsxs(exports.Stack, { gap: "sm", children: [
42470
42660
  /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
@@ -42625,7 +42815,7 @@ function TransitionTimeline({ transitions }) {
42625
42815
  }
42626
42816
  );
42627
42817
  }
42628
- const formatTime2 = (ts) => {
42818
+ const formatTime3 = (ts) => {
42629
42819
  const d = new Date(ts);
42630
42820
  return d.toLocaleTimeString("en-US", {
42631
42821
  hour12: false,
@@ -42673,7 +42863,7 @@ function TransitionTimeline({ transitions }) {
42673
42863
  ${hasFailedEffects ? "bg-error" : allPassed ? "bg-success" : "bg-muted-foreground"}
42674
42864
  ` }),
42675
42865
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 text-xs py-1 px-2", children: [
42676
- /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "small", className: "text-muted-foreground font-mono min-w-[65px]", children: formatTime2(trace.timestamp) }),
42866
+ /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "small", className: "text-muted-foreground font-mono min-w-[65px]", children: formatTime3(trace.timestamp) }),
42677
42867
  /* @__PURE__ */ jsxRuntime.jsx(exports.Badge, { variant: "primary", size: "sm", className: "min-w-[60px] justify-center", children: trace.traitName }),
42678
42868
  /* @__PURE__ */ jsxRuntime.jsxs(exports.Typography, { variant: "small", className: "font-mono text-muted-foreground", children: [
42679
42869
  trace.from,
@@ -42748,7 +42938,7 @@ function ServerBridgeTab({ bridge }) {
42748
42938
  }
42749
42939
  );
42750
42940
  }
42751
- const formatTime2 = (ts) => {
42941
+ const formatTime3 = (ts) => {
42752
42942
  if (ts === 0) return t("debug.never");
42753
42943
  const d = new Date(ts);
42754
42944
  return d.toLocaleTimeString("en-US", {
@@ -42791,7 +42981,7 @@ function ServerBridgeTab({ bridge }) {
42791
42981
  StatRow,
42792
42982
  {
42793
42983
  label: t("debug.lastHeartbeat"),
42794
- value: formatTime2(bridge.lastHeartbeat)
42984
+ value: formatTime3(bridge.lastHeartbeat)
42795
42985
  }
42796
42986
  )
42797
42987
  ] })
@@ -44903,7 +45093,7 @@ var init_TeamOrganism = __esm({
44903
45093
  exports.TeamOrganism.displayName = "TeamOrganism";
44904
45094
  }
44905
45095
  });
44906
- function formatDate4(value) {
45096
+ function formatDate2(value) {
44907
45097
  const d = new Date(value);
44908
45098
  if (isNaN(d.getTime())) return value;
44909
45099
  return d.toLocaleDateString(void 0, { year: "numeric", month: "short", day: "numeric" });
@@ -45035,7 +45225,7 @@ var init_Timeline = __esm({
45035
45225
  /* @__PURE__ */ jsxRuntime.jsxs(exports.VStack, { gap: "xs", className: cn("flex-1 min-w-0", !isLast && "pb-6"), children: [
45036
45226
  /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { justify: "between", align: "start", wrap: true, children: [
45037
45227
  /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "body", weight: "semibold", children: item.title }),
45038
- item.date && /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "caption", color: "secondary", className: "flex-shrink-0", children: formatDate4(item.date) })
45228
+ item.date && /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "caption", color: "secondary", className: "flex-shrink-0", children: formatDate2(item.date) })
45039
45229
  ] }),
45040
45230
  item.description && /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "small", color: "secondary", children: item.description }),
45041
45231
  item.tags && item.tags.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(exports.HStack, { gap: "xs", wrap: true, children: item.tags.map((tag, tagIdx) => /* @__PURE__ */ jsxRuntime.jsx(exports.Badge, { variant: "default", children: tag }, tagIdx)) }),
@@ -45198,6 +45388,7 @@ var init_component_registry_generated = __esm({
45198
45388
  init_DocSidebar();
45199
45389
  init_DocTOC();
45200
45390
  init_DocumentViewer();
45391
+ init_DrawGroup();
45201
45392
  init_DrawShape();
45202
45393
  init_DrawShapeLayer();
45203
45394
  init_DrawSprite();
@@ -45464,6 +45655,7 @@ var init_component_registry_generated = __esm({
45464
45655
  "DocSidebar": exports.DocSidebar,
45465
45656
  "DocTOC": exports.DocTOC,
45466
45657
  "DocumentViewer": exports.DocumentViewer,
45658
+ "DrawGroup": DrawGroup,
45467
45659
  "DrawShape": DrawShape,
45468
45660
  "DrawShapeLayer": DrawShapeLayer,
45469
45661
  "DrawSprite": DrawSprite,
@@ -45698,7 +45890,7 @@ function enrichFormFields(fields, entityDef) {
45698
45890
  if (entityField) {
45699
45891
  const enriched = {
45700
45892
  name: field,
45701
- label: field.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/\b\w/g, (c) => c.toUpperCase()),
45893
+ label: humanizeFieldName(field),
45702
45894
  type: entityField.type,
45703
45895
  required: entityField.required ?? false
45704
45896
  };
@@ -45712,7 +45904,7 @@ function enrichFormFields(fields, entityDef) {
45712
45904
  }
45713
45905
  return enriched;
45714
45906
  }
45715
- return { name: field, label: field.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/\b\w/g, (c) => c.toUpperCase()) };
45907
+ return { name: field, label: humanizeFieldName(field) };
45716
45908
  }
45717
45909
  if (field && typeof field === "object" && !Array.isArray(field) && !React74__namespace.default.isValidElement(field) && !(field instanceof Date)) {
45718
45910
  const obj = field;
@@ -46367,9 +46559,10 @@ function SlotContentRenderer({
46367
46559
  const slotVal = restProps[slotKey];
46368
46560
  if (slotVal === void 0 || slotVal === null) continue;
46369
46561
  if (React74__namespace.default.isValidElement(slotVal) || typeof slotVal === "string" || typeof slotVal === "number" || typeof slotVal === "boolean") continue;
46370
- if (Array.isArray(slotVal) || typeof slotVal === "object" && "type" in slotVal) {
46562
+ const typelessChildren = !Array.isArray(slotVal) && typeof slotVal === "object" && !("type" in slotVal) && Array.isArray(slotVal.children) ? slotVal.children : void 0;
46563
+ if (typelessChildren !== void 0 || Array.isArray(slotVal) || typeof slotVal === "object" && "type" in slotVal) {
46371
46564
  nodeSlotOverrides[slotKey] = renderPatternChildren(
46372
- slotVal,
46565
+ typelessChildren ?? slotVal,
46373
46566
  onDismiss,
46374
46567
  `${content.id}-${slotKey}`,
46375
46568
  `${myPath}.${slotKey}`,
@@ -46548,6 +46741,7 @@ var init_UISlotRenderer = __esm({
46548
46741
  init_useEventBus();
46549
46742
  init_slot_types();
46550
46743
  init_cn();
46744
+ init_format();
46551
46745
  init_ErrorBoundary();
46552
46746
  init_Skeleton();
46553
46747
  init_renderer();