@almadar/ui 5.132.0 → 5.134.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.
@@ -3905,6 +3905,61 @@ var init_TextHighlight = __esm({
3905
3905
  }
3906
3906
  });
3907
3907
 
3908
+ // lib/format.ts
3909
+ function humanizeFieldName(name) {
3910
+ 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();
3911
+ }
3912
+ function humanizeEnumValue(value) {
3913
+ return /^[a-z0-9]+(?:[_-][a-z0-9]+)+$/.test(value) ? humanizeFieldName(value) : value;
3914
+ }
3915
+ function formatDate(value) {
3916
+ if (!value) return "";
3917
+ const d = new Date(String(value));
3918
+ if (isNaN(d.getTime())) return String(value);
3919
+ return d.toLocaleDateString(void 0, { year: "numeric", month: "short", day: "numeric" });
3920
+ }
3921
+ function formatTime(value) {
3922
+ if (!value) return "";
3923
+ const d = new Date(String(value));
3924
+ if (isNaN(d.getTime())) return String(value);
3925
+ return d.toLocaleTimeString(void 0, { hour: "numeric", minute: "2-digit" });
3926
+ }
3927
+ function formatDateTime(value) {
3928
+ if (!value) return "";
3929
+ const d = new Date(String(value));
3930
+ if (isNaN(d.getTime())) return String(value);
3931
+ return `${formatDate(value)} ${formatTime(value)}`;
3932
+ }
3933
+ function asYesNo(value) {
3934
+ return value === false || value === 0 || String(value) === "false" ? "No" : "Yes";
3935
+ }
3936
+ function formatValue(value, format) {
3937
+ if (value === void 0 || value === null) return "";
3938
+ if (typeof value === "boolean") return asYesNo(value);
3939
+ switch (format) {
3940
+ case "date":
3941
+ return formatDate(value);
3942
+ case "time":
3943
+ return formatTime(value);
3944
+ case "datetime":
3945
+ return formatDateTime(value);
3946
+ case "currency":
3947
+ return typeof value === "number" ? `$${value.toFixed(2)}` : String(value);
3948
+ case "number":
3949
+ return typeof value === "number" ? value.toLocaleString() : String(value);
3950
+ case "percent":
3951
+ return typeof value === "number" ? `${Math.round(value)}%` : String(value);
3952
+ case "boolean":
3953
+ return asYesNo(value);
3954
+ default:
3955
+ return String(value);
3956
+ }
3957
+ }
3958
+ var init_format = __esm({
3959
+ "lib/format.ts"() {
3960
+ }
3961
+ });
3962
+
3908
3963
  // components/core/atoms/Typography.tsx
3909
3964
  var Typography_exports = {};
3910
3965
  __export(Typography_exports, {
@@ -3914,6 +3969,7 @@ var variantStyles6, colorStyles, weightStyles, defaultElements, typographySizeSt
3914
3969
  var init_Typography = __esm({
3915
3970
  "components/core/atoms/Typography.tsx"() {
3916
3971
  init_cn();
3972
+ init_format();
3917
3973
  variantStyles6 = {
3918
3974
  h1: "text-4xl font-bold tracking-tight text-foreground",
3919
3975
  h2: "text-3xl font-bold tracking-tight text-foreground",
@@ -4001,11 +4057,16 @@ var init_Typography = __esm({
4001
4057
  id,
4002
4058
  className,
4003
4059
  style,
4060
+ format,
4004
4061
  content,
4005
4062
  children
4006
4063
  }) => {
4007
4064
  const variant = variantProp ?? (level ? `h${level}` : "body1");
4008
4065
  const Component = as || defaultElements[variant];
4066
+ let body = children ?? content;
4067
+ if (format !== void 0 && format !== "none" && (typeof body === "string" || typeof body === "number" || body instanceof Date)) {
4068
+ body = formatValue(body, format);
4069
+ }
4009
4070
  return React74__default.createElement(
4010
4071
  Component,
4011
4072
  {
@@ -4022,7 +4083,7 @@ var init_Typography = __esm({
4022
4083
  ),
4023
4084
  style
4024
4085
  },
4025
- children ?? content
4086
+ body
4026
4087
  );
4027
4088
  };
4028
4089
  Typography.displayName = "Typography";
@@ -16597,9 +16658,6 @@ function normalizeFields(fields) {
16597
16658
  if (!fields) return [];
16598
16659
  return fields.map((f3) => typeof f3 === "string" ? f3 : f3.key ?? f3.name ?? "");
16599
16660
  }
16600
- function fieldLabel(key) {
16601
- return key.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/[_-]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
16602
- }
16603
16661
  function asBooleanValue(value) {
16604
16662
  if (typeof value === "boolean") return value;
16605
16663
  if (value === "true") return true;
@@ -16610,12 +16668,6 @@ function isDateField(key) {
16610
16668
  const lower = key.toLowerCase();
16611
16669
  return lower.includes("date") || lower.includes("time") || lower.endsWith("at") || lower.endsWith("_at");
16612
16670
  }
16613
- function formatDate(value) {
16614
- if (!value) return "";
16615
- const d = new Date(String(value));
16616
- if (isNaN(d.getTime())) return String(value);
16617
- return d.toLocaleDateString(void 0, { year: "numeric", month: "short", day: "numeric" });
16618
- }
16619
16671
  function statusVariant(value) {
16620
16672
  const v = value.toLowerCase();
16621
16673
  if (["active", "completed", "done", "approved", "published", "resolved", "open"].includes(v)) return "success";
@@ -16624,11 +16676,12 @@ function statusVariant(value) {
16624
16676
  if (["new", "created", "scheduled", "queued"].includes(v)) return "info";
16625
16677
  return "default";
16626
16678
  }
16627
- var STATUS_FIELDS, gapStyles3, alignStyles2, CardGrid;
16679
+ var fieldLabel, STATUS_FIELDS, gapStyles3, alignStyles2, CardGrid;
16628
16680
  var init_CardGrid = __esm({
16629
16681
  "components/core/organisms/CardGrid.tsx"() {
16630
16682
  "use client";
16631
16683
  init_cn();
16684
+ init_format();
16632
16685
  init_getNestedValue();
16633
16686
  init_useEventBus();
16634
16687
  init_atoms();
@@ -16637,6 +16690,7 @@ var init_CardGrid = __esm({
16637
16690
  init_Typography();
16638
16691
  init_Stack();
16639
16692
  init_Pagination();
16693
+ fieldLabel = humanizeFieldName;
16640
16694
  STATUS_FIELDS = /* @__PURE__ */ new Set(["status", "state", "priority", "type", "category", "role", "level", "tier"]);
16641
16695
  gapStyles3 = {
16642
16696
  none: "gap-0",
@@ -17144,7 +17198,7 @@ var init_CaseStudyOrganism = __esm({
17144
17198
  CaseStudyOrganism.displayName = "CaseStudyOrganism";
17145
17199
  }
17146
17200
  });
17147
- var CHART_COLORS, seriesColor, monthFormatter, formatTimeLabel, BarChart, PieChart, LineChart, ScatterChart, LOOK_FROM_CHART_TYPE, Chart;
17201
+ var CHART_COLORS, seriesColor, barColor, monthFormatter, formatTimeLabel, BarChart, PieChart, LineChart, ScatterChart, LOOK_FROM_CHART_TYPE, Chart;
17148
17202
  var init_Chart = __esm({
17149
17203
  "components/core/molecules/Chart.tsx"() {
17150
17204
  "use client";
@@ -17164,6 +17218,7 @@ var init_Chart = __esm({
17164
17218
  "var(--color-accent)"
17165
17219
  ];
17166
17220
  seriesColor = (series, idx) => series.color ?? CHART_COLORS[idx % CHART_COLORS.length];
17221
+ barColor = (series, sIdx, catIdx, seriesCount) => series.color ?? CHART_COLORS[(seriesCount === 1 ? catIdx : sIdx) % CHART_COLORS.length];
17167
17222
  monthFormatter = new Intl.DateTimeFormat(void 0, {
17168
17223
  month: "short",
17169
17224
  year: "2-digit"
@@ -17236,7 +17291,7 @@ var init_Chart = __esm({
17236
17291
  children: series.map((s, sIdx) => {
17237
17292
  const value = valueAt(s, label);
17238
17293
  const ratio = stack === "normalize" ? total === 0 ? 0 : value / total * 100 : value / maxValue * 100;
17239
- const color = seriesColor(s, sIdx);
17294
+ const color = barColor(s, sIdx, catIdx, series.length);
17240
17295
  return /* @__PURE__ */ jsx(
17241
17296
  Box,
17242
17297
  {
@@ -17291,7 +17346,7 @@ var init_Chart = __esm({
17291
17346
  /* @__PURE__ */ jsx(HStack, { gap: histogram ? "none" : "xs", align: "end", justify: histogram ? void 0 : "center", className: "w-full flex-1 min-h-0", children: series.map((s, sIdx) => {
17292
17347
  const value = valueAt(s, label);
17293
17348
  const barHeight = value / maxValue * 100;
17294
- const color = seriesColor(s, sIdx);
17349
+ const color = barColor(s, sIdx, catIdx, series.length);
17295
17350
  return /* @__PURE__ */ jsx(
17296
17351
  Box,
17297
17352
  {
@@ -17342,7 +17397,7 @@ var init_Chart = __esm({
17342
17397
  /* @__PURE__ */ jsx(VStack, { gap: "none", className: "w-full flex-1 min-h-0", justify: "end", children: series.map((s, sIdx) => {
17343
17398
  const value = valueAt(s, label);
17344
17399
  const ratio = stack === "normalize" ? total === 0 ? 0 : value / total * 100 : value / maxValue * 100;
17345
- const color = seriesColor(s, sIdx);
17400
+ const color = barColor(s, sIdx, catIdx, series.length);
17346
17401
  return /* @__PURE__ */ jsx(
17347
17402
  Box,
17348
17403
  {
@@ -20577,9 +20632,6 @@ var init_useDataDnd = __esm({
20577
20632
  function renderIconInput(icon, props) {
20578
20633
  return typeof icon === "string" ? /* @__PURE__ */ jsx(Icon, { name: icon, ...props }) : /* @__PURE__ */ jsx(Icon, { icon, ...props });
20579
20634
  }
20580
- function fieldLabel2(key) {
20581
- return key.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/[_-]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
20582
- }
20583
20635
  function statusVariant2(value) {
20584
20636
  const v = value.toLowerCase();
20585
20637
  if (["active", "completed", "done", "approved", "published", "resolved", "open", "online"].includes(v)) return "success";
@@ -20598,29 +20650,6 @@ function resolveBadgeVariant(field, value) {
20598
20650
  }
20599
20651
  return statusVariant2(value);
20600
20652
  }
20601
- function formatDate2(value) {
20602
- if (!value) return "";
20603
- const d = new Date(String(value));
20604
- if (isNaN(d.getTime())) return String(value);
20605
- return d.toLocaleDateString(void 0, { year: "numeric", month: "short", day: "numeric" });
20606
- }
20607
- function formatValue(value, format) {
20608
- if (value === void 0 || value === null) return "";
20609
- switch (format) {
20610
- case "date":
20611
- return formatDate2(value);
20612
- case "currency":
20613
- return typeof value === "number" ? `$${value.toFixed(2)}` : String(value);
20614
- case "number":
20615
- return typeof value === "number" ? value.toLocaleString() : String(value);
20616
- case "percent":
20617
- return typeof value === "number" ? `${Math.round(value)}%` : String(value);
20618
- case "boolean":
20619
- return value ? "Yes" : "No";
20620
- default:
20621
- return String(value);
20622
- }
20623
- }
20624
20653
  function DataGrid({
20625
20654
  entity,
20626
20655
  fields,
@@ -20907,7 +20936,7 @@ function DataGrid({
20907
20936
  if (val === void 0 || val === null || val === "") return null;
20908
20937
  return /* @__PURE__ */ jsxs(HStack, { gap: "xs", className: "items-center", children: [
20909
20938
  field.icon && renderIconInput(field.icon, { size: "xs" }),
20910
- /* @__PURE__ */ jsx(Badge, { variant: resolveBadgeVariant(field, String(val)), children: formatValue(val, field.format) })
20939
+ /* @__PURE__ */ jsx(Badge, { variant: resolveBadgeVariant(field, String(val)), children: humanizeEnumValue(formatValue(val, field.format)) })
20911
20940
  ] }, field.name);
20912
20941
  }) })
20913
20942
  ] }),
@@ -21017,11 +21046,12 @@ function DataGrid({
21017
21046
  ] })
21018
21047
  );
21019
21048
  }
21020
- var dataGridLog, BADGE_VARIANTS, gapStyles5, lookStyles5;
21049
+ var dataGridLog, fieldLabel2, BADGE_VARIANTS, gapStyles5, lookStyles5;
21021
21050
  var init_DataGrid = __esm({
21022
21051
  "components/core/molecules/DataGrid.tsx"() {
21023
21052
  "use client";
21024
21053
  init_cn();
21054
+ init_format();
21025
21055
  init_getNestedValue();
21026
21056
  init_useEventBus();
21027
21057
  init_Box();
@@ -21034,6 +21064,7 @@ var init_DataGrid = __esm({
21034
21064
  init_Menu();
21035
21065
  init_useDataDnd();
21036
21066
  dataGridLog = createLogger("almadar:ui:data-grid");
21067
+ fieldLabel2 = humanizeFieldName;
21037
21068
  BADGE_VARIANTS = /* @__PURE__ */ new Set([
21038
21069
  "default",
21039
21070
  "primary",
@@ -21065,9 +21096,6 @@ var init_DataGrid = __esm({
21065
21096
  function renderIconInput2(icon, props) {
21066
21097
  return typeof icon === "string" ? /* @__PURE__ */ jsx(Icon, { name: icon, ...props }) : /* @__PURE__ */ jsx(Icon, { icon, ...props });
21067
21098
  }
21068
- function fieldLabel3(key) {
21069
- return key.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/[_-]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
21070
- }
21071
21099
  function statusVariant3(value) {
21072
21100
  const v = value.toLowerCase();
21073
21101
  if (["active", "completed", "done", "approved", "published", "resolved", "open", "online"].includes(v)) return "success";
@@ -21076,28 +21104,12 @@ function statusVariant3(value) {
21076
21104
  if (["new", "created", "scheduled", "queued", "info"].includes(v)) return "info";
21077
21105
  return "default";
21078
21106
  }
21079
- function formatDate3(value) {
21080
- if (!value) return "";
21081
- const d = new Date(String(value));
21082
- if (isNaN(d.getTime())) return String(value);
21083
- return d.toLocaleDateString(void 0, { year: "numeric", month: "short", day: "numeric" });
21084
- }
21085
21107
  function formatValue2(value, format, boolLabels) {
21086
- if (value === void 0 || value === null) return "";
21087
- switch (format) {
21088
- case "date":
21089
- return formatDate3(value);
21090
- case "currency":
21091
- return typeof value === "number" ? `$${value.toFixed(2)}` : String(value);
21092
- case "number":
21093
- return typeof value === "number" ? value.toLocaleString() : String(value);
21094
- case "percent":
21095
- return typeof value === "number" ? `${Math.round(value)}%` : String(value);
21096
- case "boolean":
21097
- return value ? boolLabels?.yes ?? "Yes" : boolLabels?.no ?? "No";
21098
- default:
21099
- return String(value);
21108
+ if (value !== void 0 && value !== null && (format === "boolean" || typeof value === "boolean")) {
21109
+ const isNo = value === false || value === 0 || String(value) === "false";
21110
+ return isNo ? boolLabels?.no ?? "No" : boolLabels?.yes ?? "Yes";
21100
21111
  }
21112
+ return formatValue(value, format);
21101
21113
  }
21102
21114
  function groupData(items, field) {
21103
21115
  const groups = /* @__PURE__ */ new Map();
@@ -21120,7 +21132,9 @@ function DataList({
21120
21132
  variant = "default",
21121
21133
  groupBy,
21122
21134
  senderField,
21135
+ senderLabelField,
21123
21136
  currentUser,
21137
+ emptyMessage,
21124
21138
  className,
21125
21139
  isLoading = false,
21126
21140
  error = null,
@@ -21259,7 +21273,7 @@ function DataList({
21259
21273
  return /* @__PURE__ */ jsx(Box, { className: "text-center py-8", children: /* @__PURE__ */ jsx(Typography, { variant: "body", color: "error", children: error.message }) });
21260
21274
  }
21261
21275
  if (data.length === 0) {
21262
- const emptyNode = /* @__PURE__ */ jsx(Box, { className: "text-center py-12", children: /* @__PURE__ */ jsx(Typography, { variant: "body", color: "secondary", children: t("empty.noItems") }) });
21276
+ const emptyNode = /* @__PURE__ */ jsx(Box, { className: "text-center py-12", children: /* @__PURE__ */ jsx(Typography, { variant: "body", color: "secondary", children: emptyMessage || t("empty.noItems") }) });
21263
21277
  return dnd.enabled ? /* @__PURE__ */ jsx(Fragment, { children: dnd.wrapContainer(emptyNode) }) : emptyNode;
21264
21278
  }
21265
21279
  const gapClass = {
@@ -21275,51 +21289,93 @@ function DataList({
21275
21289
  const items2 = [...data];
21276
21290
  const groups2 = groupBy ? groupData(items2, groupBy) : [{ label: "", items: items2 }];
21277
21291
  const contentField = titleField?.name ?? fieldDefs[0]?.name ?? "";
21278
- return /* @__PURE__ */ jsx(VStack, { gap: "sm", className: cn("py-2", className), children: groups2.map((group, gi) => /* @__PURE__ */ jsxs(React74__default.Fragment, { children: [
21279
- group.label && /* @__PURE__ */ jsx(Divider, { label: group.label, className: "my-2" }),
21280
- group.items.map((itemData, index) => {
21281
- const id = itemData.id || `${gi}-${index}`;
21282
- const sender = senderField ? String(getNestedValue(itemData, senderField) ?? "") : "";
21283
- const isSent = Boolean(currentUser && sender === currentUser);
21284
- const content = getNestedValue(itemData, contentField);
21285
- const timestampField = fieldDefs.find((f3) => f3.format === "date");
21286
- const timestamp = timestampField ? getNestedValue(itemData, timestampField.name) : null;
21287
- return /* @__PURE__ */ jsx(
21288
- Box,
21289
- {
21290
- className: cn(
21291
- "flex px-4",
21292
- isSent ? "justify-end" : "justify-start"
21293
- ),
21294
- children: /* @__PURE__ */ jsxs(
21295
- Box,
21296
- {
21297
- className: cn(
21298
- "max-w-[75%] px-4 py-2",
21299
- isSent ? "bg-primary text-primary-foreground rounded-2xl rounded-br-sm" : "bg-muted text-foreground rounded-2xl rounded-bl-sm"
21300
- ),
21301
- children: [
21302
- !isSent && senderField && /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "font-semibold mb-0.5", children: sender }),
21303
- /* @__PURE__ */ jsx(Typography, { variant: "body", className: cn(isSent && "text-primary-foreground"), children: content !== void 0 && content !== null ? String(content) : "" }),
21304
- timestamp != null ? /* @__PURE__ */ jsx(
21305
- Typography,
21306
- {
21307
- variant: "caption",
21308
- className: cn(
21309
- "mt-1 text-xs",
21310
- isSent ? "opacity-70" : "text-muted-foreground"
21311
- ),
21312
- children: formatDate3(timestamp)
21313
- }
21314
- ) : null
21315
- ]
21316
- }
21317
- )
21318
- },
21319
- id
21320
- );
21321
- })
21322
- ] }, gi)) });
21292
+ const senderLabel = (itemData, raw) => {
21293
+ if (!senderLabelField) return raw;
21294
+ const v = getNestedValue(itemData, senderLabelField);
21295
+ return v === void 0 || v === null || v === "" ? raw : String(v);
21296
+ };
21297
+ return /* @__PURE__ */ jsxs(VStack, { gap: "sm", className: cn("py-2", className), children: [
21298
+ groups2.map((group, gi) => /* @__PURE__ */ jsxs(React74__default.Fragment, { children: [
21299
+ group.label && /* @__PURE__ */ jsx(Divider, { label: group.label, className: "my-2" }),
21300
+ group.items.map((itemData, index) => {
21301
+ const id = itemData.id || `${gi}-${index}`;
21302
+ const sender = senderField ? String(getNestedValue(itemData, senderField) ?? "") : "";
21303
+ const isSent = Boolean(currentUser && sender === currentUser);
21304
+ const content = getNestedValue(itemData, contentField);
21305
+ const timestampField = fieldDefs.find((f3) => f3.format === "date");
21306
+ const timestamp = timestampField ? getNestedValue(itemData, timestampField.name) : null;
21307
+ const metaFields = fieldDefs.filter(
21308
+ (f3) => f3.name !== contentField && f3.name !== timestampField?.name
21309
+ );
21310
+ return /* @__PURE__ */ jsx(
21311
+ Box,
21312
+ {
21313
+ "data-entity-row": true,
21314
+ "data-entity-id": id,
21315
+ onClick: itemClickEvent ? handleRowClick(itemData) : void 0,
21316
+ className: cn(
21317
+ "flex px-4 group/rowactions",
21318
+ itemClickEvent && "cursor-pointer",
21319
+ isSent ? "justify-end" : "justify-start"
21320
+ ),
21321
+ children: /* @__PURE__ */ jsxs(
21322
+ Box,
21323
+ {
21324
+ className: cn(
21325
+ "max-w-[75%] px-4 py-2",
21326
+ isSent ? "bg-primary text-primary-foreground rounded-2xl rounded-br-sm" : "bg-muted text-foreground rounded-2xl rounded-bl-sm"
21327
+ ),
21328
+ children: [
21329
+ !isSent && senderField && /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "font-semibold mb-0.5", children: senderLabel(itemData, sender) }),
21330
+ /* @__PURE__ */ jsx(Typography, { variant: "body", className: cn(isSent && "text-primary-foreground"), children: content !== void 0 && content !== null ? String(content) : "" }),
21331
+ metaFields.length > 0 && /* @__PURE__ */ jsx(HStack, { gap: "xs", className: "mt-1 flex-wrap", children: metaFields.map((f3) => {
21332
+ const v = getNestedValue(itemData, f3.name);
21333
+ if (v === void 0 || v === null || v === "") return null;
21334
+ return f3.variant === "badge" ? /* @__PURE__ */ jsx(Badge, { variant: statusVariant3(String(v)), children: String(v) }, f3.name) : /* @__PURE__ */ jsx(
21335
+ Typography,
21336
+ {
21337
+ variant: "caption",
21338
+ className: cn("text-xs", isSent ? "opacity-70" : "text-muted-foreground"),
21339
+ children: formatValue2(v, f3.format)
21340
+ },
21341
+ f3.name
21342
+ );
21343
+ }) }),
21344
+ /* @__PURE__ */ jsxs(HStack, { gap: "xs", className: "mt-1 items-center justify-between", children: [
21345
+ timestamp != null ? /* @__PURE__ */ jsx(
21346
+ Typography,
21347
+ {
21348
+ variant: "caption",
21349
+ className: cn("text-xs", isSent ? "opacity-70" : "text-muted-foreground"),
21350
+ children: formatDate(timestamp)
21351
+ }
21352
+ ) : /* @__PURE__ */ jsx("span", {}),
21353
+ renderItemActions(itemData)
21354
+ ] })
21355
+ ]
21356
+ }
21357
+ )
21358
+ },
21359
+ id
21360
+ );
21361
+ })
21362
+ ] }, gi)),
21363
+ hasMoreLocal && /* @__PURE__ */ jsx(Box, { className: "flex justify-center py-3", children: /* @__PURE__ */ jsxs(
21364
+ Button,
21365
+ {
21366
+ variant: "ghost",
21367
+ size: "sm",
21368
+ onClick: () => setVisibleCount((prev) => prev + (pageSize || 5)),
21369
+ children: [
21370
+ /* @__PURE__ */ jsx(Icon, { name: "chevron-down", size: "xs", className: "mr-1" }),
21371
+ t("common.showMore"),
21372
+ " (",
21373
+ t("common.remaining", { count: allData.length - visibleCount }),
21374
+ ")"
21375
+ ]
21376
+ }
21377
+ ) })
21378
+ ] });
21323
21379
  }
21324
21380
  const items = [...data];
21325
21381
  const groups = groupBy ? groupData(items, groupBy) : [{ label: "", items }];
@@ -21468,11 +21524,12 @@ function DataList({
21468
21524
  )
21469
21525
  );
21470
21526
  }
21471
- var dataListLog, listLookStyles;
21527
+ var dataListLog, fieldLabel3, listLookStyles;
21472
21528
  var init_DataList = __esm({
21473
21529
  "components/core/molecules/DataList.tsx"() {
21474
21530
  "use client";
21475
21531
  init_cn();
21532
+ init_format();
21476
21533
  init_getNestedValue();
21477
21534
  init_useEventBus();
21478
21535
  init_Box();
@@ -21487,6 +21544,7 @@ var init_DataList = __esm({
21487
21544
  init_Menu();
21488
21545
  init_useDataDnd();
21489
21546
  dataListLog = createLogger("almadar:ui:data-list");
21547
+ fieldLabel3 = humanizeFieldName;
21490
21548
  listLookStyles = {
21491
21549
  dense: "[&_[data-entity-row]>div]:!py-1 [&_[data-entity-row]>div]:!px-3",
21492
21550
  spacious: "[&_[data-entity-row]>div]:!py-5 [&_[data-entity-row]>div]:!px-8",
@@ -25955,7 +26013,7 @@ var init_ScoreDisplay = __esm({
25955
26013
  ScoreDisplay.displayName = "ScoreDisplay";
25956
26014
  }
25957
26015
  });
25958
- function formatTime(seconds, format) {
26016
+ function formatTime2(seconds, format) {
25959
26017
  const clamped = Math.max(0, Math.floor(seconds));
25960
26018
  if (format === "ss") {
25961
26019
  return `${clamped}s`;
@@ -25992,7 +26050,7 @@ function TimerDisplay({
25992
26050
  ),
25993
26051
  children: [
25994
26052
  iconAsset && /* @__PURE__ */ jsx(GameIcon, { assetUrl: iconAsset, icon: "image", size: 16, className: "w-4 h-4 object-contain flex-shrink-0" }),
25995
- formatTime(seconds, format)
26053
+ formatTime2(seconds, format)
25996
26054
  ]
25997
26055
  }
25998
26056
  );
@@ -26060,6 +26118,210 @@ var init_DialogueBubble = __esm({
26060
26118
  DialogueBubble.displayName = "DialogueBubble";
26061
26119
  }
26062
26120
  });
26121
+ function SvgStage({
26122
+ cols,
26123
+ rows: rows2,
26124
+ tileSize = 32,
26125
+ background = "var(--color-background)",
26126
+ tileClickEvent,
26127
+ tileHoverEvent,
26128
+ tileLeaveEvent,
26129
+ keyMap,
26130
+ keyUpMap,
26131
+ className,
26132
+ children
26133
+ }) {
26134
+ const eventBus = useEventBus();
26135
+ const svgRef = useRef(null);
26136
+ const pointerDownRef = useRef(null);
26137
+ const cellFromClient = useCallback((clientX, clientY) => {
26138
+ const svg = svgRef.current;
26139
+ if (!svg) return null;
26140
+ const rect = svg.getBoundingClientRect();
26141
+ if (rect.width === 0 || rect.height === 0) return null;
26142
+ const vbW = cols * tileSize;
26143
+ const vbH = rows2 * tileSize;
26144
+ const meet = Math.min(rect.width / vbW, rect.height / vbH);
26145
+ const offsetX = (rect.width - vbW * meet) / 2;
26146
+ const offsetY = (rect.height - vbH * meet) / 2;
26147
+ const svgX = (clientX - rect.left - offsetX) / meet;
26148
+ const svgY = (clientY - rect.top - offsetY) / meet;
26149
+ return {
26150
+ x: Math.min(Math.max(Math.floor(svgX / tileSize), 0), cols - 1),
26151
+ y: Math.min(Math.max(Math.floor(svgY / tileSize), 0), rows2 - 1)
26152
+ };
26153
+ }, [cols, rows2, tileSize]);
26154
+ const handlePointerDown = useCallback((e) => {
26155
+ pointerDownRef.current = { clientX: e.clientX, clientY: e.clientY };
26156
+ }, []);
26157
+ const handlePointerUp = useCallback((e) => {
26158
+ const down = pointerDownRef.current;
26159
+ pointerDownRef.current = null;
26160
+ if (!tileClickEvent) return;
26161
+ if (down && Math.hypot(e.clientX - down.clientX, e.clientY - down.clientY) > 5) return;
26162
+ const cell = cellFromClient(e.clientX, e.clientY);
26163
+ if (cell) eventBus.emit(`UI:${tileClickEvent}`, cell);
26164
+ }, [cellFromClient, tileClickEvent, eventBus]);
26165
+ const handlePointerMove = useCallback((e) => {
26166
+ if (!tileHoverEvent) return;
26167
+ const cell = cellFromClient(e.clientX, e.clientY);
26168
+ if (cell) eventBus.emit(`UI:${tileHoverEvent}`, cell);
26169
+ }, [cellFromClient, tileHoverEvent, eventBus]);
26170
+ const handlePointerLeave = useCallback(() => {
26171
+ pointerDownRef.current = null;
26172
+ if (tileLeaveEvent) eventBus.emit(`UI:${tileLeaveEvent}`, {});
26173
+ }, [tileLeaveEvent, eventBus]);
26174
+ useEffect(() => {
26175
+ if (!keyMap && !keyUpMap) return;
26176
+ const onDown = (e) => {
26177
+ const ev = keyMap?.[e.code];
26178
+ if (ev) {
26179
+ eventBus.emit(`UI:${ev}`, {});
26180
+ e.preventDefault();
26181
+ }
26182
+ };
26183
+ const onUp = (e) => {
26184
+ const ev = keyUpMap?.[e.code];
26185
+ if (ev) eventBus.emit(`UI:${ev}`, {});
26186
+ };
26187
+ window.addEventListener("keydown", onDown);
26188
+ window.addEventListener("keyup", onUp);
26189
+ return () => {
26190
+ window.removeEventListener("keydown", onDown);
26191
+ window.removeEventListener("keyup", onUp);
26192
+ };
26193
+ }, [keyMap, keyUpMap, eventBus]);
26194
+ useEffect(() => {
26195
+ if (!keyMap && !keyUpMap) return;
26196
+ svgRef.current?.focus();
26197
+ }, [keyMap, keyUpMap]);
26198
+ const stageContext = useMemo(() => ({ tileSize }), [tileSize]);
26199
+ return /* @__PURE__ */ jsxs(
26200
+ "svg",
26201
+ {
26202
+ ref: svgRef,
26203
+ "data-testid": "svg-stage",
26204
+ viewBox: `0 0 ${cols * tileSize} ${rows2 * tileSize}`,
26205
+ preserveAspectRatio: "xMidYMid meet",
26206
+ className: cn("block h-full w-full", className),
26207
+ tabIndex: keyMap || keyUpMap ? 0 : void 0,
26208
+ onPointerDown: handlePointerDown,
26209
+ onPointerMove: handlePointerMove,
26210
+ onPointerUp: handlePointerUp,
26211
+ onPointerLeave: handlePointerLeave,
26212
+ children: [
26213
+ /* @__PURE__ */ jsx("rect", { width: cols * tileSize, height: rows2 * tileSize, fill: background }),
26214
+ /* @__PURE__ */ jsx(SvgStageContext.Provider, { value: stageContext, children })
26215
+ ]
26216
+ }
26217
+ );
26218
+ }
26219
+ var SvgStageContext;
26220
+ var init_SvgStage = __esm({
26221
+ "components/game/molecules/SvgStage.tsx"() {
26222
+ "use client";
26223
+ init_cn();
26224
+ init_useEventBus();
26225
+ SvgStageContext = React74.createContext({ tileSize: 1 });
26226
+ SvgStage.displayName = "SvgStage";
26227
+ }
26228
+ });
26229
+ function SvgDrawShape({
26230
+ shape,
26231
+ x,
26232
+ y,
26233
+ width,
26234
+ height,
26235
+ radius,
26236
+ radiusY,
26237
+ points,
26238
+ d,
26239
+ x2,
26240
+ y2,
26241
+ fill,
26242
+ stroke,
26243
+ strokeWidth,
26244
+ opacity,
26245
+ className
26246
+ }) {
26247
+ const { tileSize } = useContext(SvgStageContext);
26248
+ const cell = (v) => v === void 0 ? void 0 : v * tileSize;
26249
+ const paint = {
26250
+ fill: fill ?? (stroke === void 0 ? "var(--color-primary)" : "none"),
26251
+ stroke,
26252
+ strokeWidth,
26253
+ opacity,
26254
+ className
26255
+ };
26256
+ return /* @__PURE__ */ jsxs("g", { transform: `translate(${x * tileSize} ${y * tileSize})`, children: [
26257
+ shape === "rect" && /* @__PURE__ */ jsx("rect", { width: cell(width), height: cell(height), ...paint }),
26258
+ shape === "circle" && /* @__PURE__ */ jsx("circle", { r: cell(radius), ...paint }),
26259
+ shape === "ellipse" && /* @__PURE__ */ jsx("ellipse", { rx: cell(radius), ry: cell(radiusY ?? radius), ...paint }),
26260
+ shape === "polygon" && /* @__PURE__ */ jsx("polygon", { points, ...paint }),
26261
+ shape === "polyline" && /* @__PURE__ */ jsx("polyline", { points, ...paint }),
26262
+ shape === "path" && /* @__PURE__ */ jsx("path", { d, ...paint }),
26263
+ shape === "line" && /* @__PURE__ */ jsx("line", { x2: cell(x2), y2: cell(y2), ...paint })
26264
+ ] });
26265
+ }
26266
+ var init_SvgDrawShape = __esm({
26267
+ "components/game/atoms/SvgDrawShape.tsx"() {
26268
+ "use client";
26269
+ init_SvgStage();
26270
+ SvgDrawShape.displayName = "SvgDrawShape";
26271
+ }
26272
+ });
26273
+ function SvgDrawGroup({
26274
+ x = 0,
26275
+ y = 0,
26276
+ scale,
26277
+ rotate,
26278
+ opacity,
26279
+ className,
26280
+ children
26281
+ }) {
26282
+ const { tileSize } = useContext(SvgStageContext);
26283
+ const transforms = [`translate(${x * tileSize} ${y * tileSize})`];
26284
+ if (rotate !== void 0) transforms.push(`rotate(${rotate})`);
26285
+ if (scale !== void 0) transforms.push(`scale(${scale})`);
26286
+ return /* @__PURE__ */ jsx("g", { transform: transforms.join(" "), opacity, className, children });
26287
+ }
26288
+ var init_SvgDrawGroup = __esm({
26289
+ "components/game/atoms/SvgDrawGroup.tsx"() {
26290
+ "use client";
26291
+ init_SvgStage();
26292
+ SvgDrawGroup.displayName = "SvgDrawGroup";
26293
+ }
26294
+ });
26295
+ function SvgDrawText({
26296
+ x,
26297
+ y,
26298
+ text,
26299
+ size = 12,
26300
+ fill = "var(--color-foreground)",
26301
+ anchor = "middle",
26302
+ className
26303
+ }) {
26304
+ const { tileSize } = useContext(SvgStageContext);
26305
+ return /* @__PURE__ */ jsx(
26306
+ "text",
26307
+ {
26308
+ x: x * tileSize,
26309
+ y: y * tileSize,
26310
+ fontSize: size,
26311
+ fill,
26312
+ textAnchor: anchor,
26313
+ className,
26314
+ children: text
26315
+ }
26316
+ );
26317
+ }
26318
+ var init_SvgDrawText = __esm({
26319
+ "components/game/atoms/SvgDrawText.tsx"() {
26320
+ "use client";
26321
+ init_SvgStage();
26322
+ SvgDrawText.displayName = "SvgDrawText";
26323
+ }
26324
+ });
26063
26325
  function StatBadge({
26064
26326
  assetUrl,
26065
26327
  iconUrl,
@@ -26550,6 +26812,32 @@ var init_StateGraph = __esm({
26550
26812
  init_TransitionArrow();
26551
26813
  }
26552
26814
  });
26815
+ function SvgDrawShapeLayer({
26816
+ items,
26817
+ fill,
26818
+ stroke,
26819
+ strokeWidth,
26820
+ opacity
26821
+ }) {
26822
+ return /* @__PURE__ */ jsx("g", { children: items.map(({ id, ...shape }) => /* @__PURE__ */ jsx(
26823
+ SvgDrawShape,
26824
+ {
26825
+ ...shape,
26826
+ fill: shape.fill ?? fill,
26827
+ stroke: shape.stroke ?? stroke,
26828
+ strokeWidth: shape.strokeWidth ?? strokeWidth,
26829
+ opacity: shape.opacity ?? opacity
26830
+ },
26831
+ id
26832
+ )) });
26833
+ }
26834
+ var init_SvgDrawShapeLayer = __esm({
26835
+ "components/game/molecules/SvgDrawShapeLayer.tsx"() {
26836
+ "use client";
26837
+ init_SvgDrawShape();
26838
+ SvgDrawShapeLayer.displayName = "SvgDrawShapeLayer";
26839
+ }
26840
+ });
26553
26841
  function unitAtlasUrl(unit) {
26554
26842
  return unit.spriteSheet?.url ?? null;
26555
26843
  }
@@ -28017,6 +28305,9 @@ var init_molecules = __esm({
28017
28305
  init_TimerDisplay();
28018
28306
  init_DialogueBubble();
28019
28307
  init_ChoiceButton();
28308
+ init_SvgDrawShape();
28309
+ init_SvgDrawGroup();
28310
+ init_SvgDrawText();
28020
28311
  init_ControlGrid();
28021
28312
  init_StatBadge();
28022
28313
  init_GameHud();
@@ -28024,6 +28315,8 @@ var init_molecules = __esm({
28024
28315
  init_StateGraph();
28025
28316
  init_Canvas2D();
28026
28317
  init_Canvas();
28318
+ init_SvgDrawShapeLayer();
28319
+ init_SvgStage();
28027
28320
  init_useUnitSpriteAtlas();
28028
28321
  init_GameAudioToggle();
28029
28322
  init_useGameAudio();
@@ -28641,13 +28934,13 @@ var init_MapView = __esm({
28641
28934
  shadowSize: [41, 41]
28642
28935
  });
28643
28936
  L.Marker.prototype.options.icon = defaultIcon;
28644
- const { useEffect: useEffect65, useRef: useRef63, useCallback: useCallback106, useState: useState102 } = React74__default;
28937
+ const { useEffect: useEffect66, useRef: useRef64, useCallback: useCallback107, useState: useState102 } = React74__default;
28645
28938
  const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
28646
28939
  const { useEventBus: useEventBus2 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
28647
28940
  function MapUpdater({ centerLat, centerLng, zoom }) {
28648
28941
  const map = useMap();
28649
- const prevRef = useRef63({ centerLat, centerLng, zoom });
28650
- useEffect65(() => {
28942
+ const prevRef = useRef64({ centerLat, centerLng, zoom });
28943
+ useEffect66(() => {
28651
28944
  const prev = prevRef.current;
28652
28945
  if (prev.centerLat !== centerLat || prev.centerLng !== centerLng || prev.zoom !== zoom) {
28653
28946
  map.setView([centerLat, centerLng], zoom);
@@ -28658,7 +28951,7 @@ var init_MapView = __esm({
28658
28951
  }
28659
28952
  function MapClickHandler({ onMapClick }) {
28660
28953
  const map = useMap();
28661
- useEffect65(() => {
28954
+ useEffect66(() => {
28662
28955
  if (!onMapClick) return;
28663
28956
  const handler = (e) => {
28664
28957
  onMapClick(e.latlng.lat, e.latlng.lng);
@@ -28687,7 +28980,7 @@ var init_MapView = __esm({
28687
28980
  }) {
28688
28981
  const eventBus = useEventBus2();
28689
28982
  const [clickedPosition, setClickedPosition] = useState102(null);
28690
- const handleMapClick = useCallback106((lat, lng) => {
28983
+ const handleMapClick = useCallback107((lat, lng) => {
28691
28984
  if (showClickedPin) {
28692
28985
  setClickedPosition({ lat, lng });
28693
28986
  }
@@ -28696,7 +28989,7 @@ var init_MapView = __esm({
28696
28989
  eventBus.emit(`UI:${mapClickEvent}`, { latitude: lat, longitude: lng });
28697
28990
  }
28698
28991
  }, [onMapClick, mapClickEvent, eventBus, showClickedPin]);
28699
- const handleMarkerClick = useCallback106((marker) => {
28992
+ const handleMarkerClick = useCallback107((marker) => {
28700
28993
  onMarkerClick?.(marker);
28701
28994
  if (markerClickEvent) {
28702
28995
  eventBus.emit(`UI:${markerClickEvent}`, { ...marker });
@@ -29496,7 +29789,7 @@ function renderIconInput3(icon, props) {
29496
29789
  return typeof icon === "string" ? /* @__PURE__ */ jsx(Icon, { name: icon, ...props }) : /* @__PURE__ */ jsx(Icon, { icon, ...props });
29497
29790
  }
29498
29791
  function columnLabel(col) {
29499
- return col.header ?? col.label ?? col.key.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/[_-]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
29792
+ return col.header ?? col.label ?? humanizeFieldName(col.key);
29500
29793
  }
29501
29794
  function asFieldValue(v) {
29502
29795
  if (v === void 0 || v === null) return v;
@@ -29515,25 +29808,6 @@ function statusVariant4(value) {
29515
29808
  if (["new", "created", "scheduled", "queued", "info"].includes(v)) return "info";
29516
29809
  return "default";
29517
29810
  }
29518
- function formatCell(value, format) {
29519
- if (value === void 0 || value === null) return "";
29520
- switch (format) {
29521
- case "date": {
29522
- const d = new Date(String(value));
29523
- return isNaN(d.getTime()) ? String(value) : d.toLocaleDateString(void 0, { year: "numeric", month: "short", day: "numeric" });
29524
- }
29525
- case "currency":
29526
- return typeof value === "number" ? `$${value.toFixed(2)}` : String(value);
29527
- case "number":
29528
- return typeof value === "number" ? value.toLocaleString() : String(value);
29529
- case "percent":
29530
- return typeof value === "number" ? `${Math.round(value)}%` : String(value);
29531
- case "boolean":
29532
- return value ? "Yes" : "No";
29533
- default:
29534
- return String(value);
29535
- }
29536
- }
29537
29811
  function groupData2(items, field) {
29538
29812
  const groups = /* @__PURE__ */ new Map();
29539
29813
  for (const item of items) {
@@ -29579,7 +29853,8 @@ function TableView({
29579
29853
  const { t } = useTranslate();
29580
29854
  const [visibleCount, setVisibleCount] = React74__default.useState(pageSize > 0 ? pageSize : Infinity);
29581
29855
  const [localSelected, setLocalSelected] = React74__default.useState(/* @__PURE__ */ new Set());
29582
- const colDefs = columns ?? fields ?? [];
29856
+ const colDefs = (Array.isArray(columns) ? columns : void 0) ?? (Array.isArray(fields) ? fields : void 0) ?? [];
29857
+ const actionDefs = Array.isArray(itemActions) ? itemActions : [];
29583
29858
  const allDataRaw = Array.isArray(entity) ? entity : entity ? [entity] : [];
29584
29859
  const dnd = useDataDnd({
29585
29860
  items: allDataRaw,
@@ -29599,6 +29874,23 @@ function TableView({
29599
29874
  const hasRenderProp = typeof children === "function";
29600
29875
  const idField = dndItemIdField ?? "id";
29601
29876
  const isCoarsePointer = useMediaQuery("(pointer: coarse)");
29877
+ React74__default.useEffect(() => {
29878
+ tableViewLog.debug("render", {
29879
+ rowCount: data.length,
29880
+ colCount: colDefs.length,
29881
+ look,
29882
+ isLoading: Boolean(isLoading),
29883
+ hasError: Boolean(error),
29884
+ dnd: dnd.enabled
29885
+ });
29886
+ if (data.length > 0 && !hasRenderProp && colDefs.length === 0) {
29887
+ tableViewLog.warn("columns-unresolved", {
29888
+ rowCount: data.length,
29889
+ columnsIsArray: Array.isArray(columns),
29890
+ fieldsIsArray: Array.isArray(fields)
29891
+ });
29892
+ }
29893
+ }, [data, colDefs, look, isLoading, error, dnd.enabled, hasRenderProp, columns, fields]);
29602
29894
  const selected = selectedIds ? new Set(selectedIds) : localSelected;
29603
29895
  const emitSelection = (next) => {
29604
29896
  if (!selectedIds) setLocalSelected(next);
@@ -29642,21 +29934,12 @@ function TableView({
29642
29934
  }),
29643
29935
  [colDefs, data]
29644
29936
  );
29645
- if (isLoading) {
29646
- return /* @__PURE__ */ jsx(Box, { className: "text-center py-8", children: /* @__PURE__ */ jsx(Typography, { variant: "body", color: "secondary", children: t("loading.items") }) });
29647
- }
29648
- if (error) {
29649
- return /* @__PURE__ */ jsx(Box, { className: "text-center py-8", children: /* @__PURE__ */ jsx(Typography, { variant: "body", color: "error", children: error.message }) });
29650
- }
29651
- if (data.length === 0) {
29652
- const emptyNode = /* @__PURE__ */ jsx(Box, { className: "text-center py-12", children: /* @__PURE__ */ jsx(Typography, { variant: "body", color: "secondary", children: emptyMessage || t("empty.noItems") }) });
29653
- return dnd.enabled ? /* @__PURE__ */ jsx(Fragment, { children: dnd.wrapContainer(emptyNode) }) : emptyNode;
29654
- }
29937
+ const statusNode = isLoading ? /* @__PURE__ */ jsx(Box, { className: "text-center py-8", children: /* @__PURE__ */ jsx(Typography, { variant: "body", color: "secondary", children: t("loading.items") }) }) : error ? /* @__PURE__ */ jsx(Box, { className: "text-center py-8", children: /* @__PURE__ */ jsx(Typography, { variant: "body", color: "error", children: error.message }) }) : data.length === 0 ? /* @__PURE__ */ jsx(Box, { className: "text-center py-12", children: /* @__PURE__ */ jsx(Typography, { variant: "body", color: "secondary", children: emptyMessage || t("empty.noItems") }) }) : null;
29655
29938
  const lk = LOOKS[look];
29656
- const hasActions = Boolean(itemActions && itemActions.length > 0);
29939
+ const hasActions = actionDefs.length > 0;
29657
29940
  const effectiveMaxInline = isCoarsePointer ? 0 : maxInlineActions;
29658
- const inlineActionCount = hasActions ? effectiveMaxInline != null ? Math.min(itemActions.length, effectiveMaxInline) : itemActions.length : 0;
29659
- const hasOverflowActions = hasActions && effectiveMaxInline != null && itemActions.length > effectiveMaxInline;
29941
+ const inlineActionCount = hasActions ? effectiveMaxInline != null ? Math.min(actionDefs.length, effectiveMaxInline) : actionDefs.length : 0;
29942
+ const hasOverflowActions = hasActions && effectiveMaxInline != null && actionDefs.length > effectiveMaxInline;
29660
29943
  const actionsTrack = hasActions ? `${inlineActionCount * 6 + (hasOverflowActions ? 3 : 0)}rem` : null;
29661
29944
  const gridTemplateColumns = [
29662
29945
  selectable ? "auto" : null,
@@ -29744,11 +30027,11 @@ function TableView({
29744
30027
  col.className
29745
30028
  );
29746
30029
  if (col.format === "badge" && raw != null && raw !== "") {
29747
- return /* @__PURE__ */ jsx(Box, { role: "cell", className: cellBase, children: /* @__PURE__ */ jsx(Badge, { variant: statusVariant4(String(raw)), size: "sm", className: "whitespace-nowrap", children: String(raw) }) }, col.key);
30030
+ return /* @__PURE__ */ jsx(Box, { role: "cell", className: cellBase, children: /* @__PURE__ */ jsx(Badge, { variant: statusVariant4(String(raw)), size: "sm", className: "whitespace-nowrap", children: humanizeEnumValue(String(raw)) }) }, col.key);
29748
30031
  }
29749
30032
  return /* @__PURE__ */ jsx(Box, { role: "cell", className: cellBase, children: /* @__PURE__ */ jsx("span", { className: "truncate text-foreground", children: formatCell(raw, col.format) }) }, col.key);
29750
30033
  }),
29751
- itemActions && itemActions.length > 0 && /* @__PURE__ */ jsxs(
30034
+ hasActions && /* @__PURE__ */ jsxs(
29752
30035
  HStack,
29753
30036
  {
29754
30037
  gap: "xs",
@@ -29760,7 +30043,7 @@ function TableView({
29760
30043
  lk.striped && index % 2 === 1 ? "bg-[var(--color-surface-subtle)]" : "bg-[var(--color-card)] group-hover:bg-[var(--color-surface-subtle)]"
29761
30044
  ),
29762
30045
  children: [
29763
- (effectiveMaxInline != null ? itemActions.slice(0, effectiveMaxInline) : itemActions).map((action, i) => /* @__PURE__ */ jsxs(
30046
+ (effectiveMaxInline != null ? actionDefs.slice(0, effectiveMaxInline) : actionDefs).map((action, i) => /* @__PURE__ */ jsxs(
29764
30047
  Button,
29765
30048
  {
29766
30049
  variant: action.variant === "primary" ? "primary" : "ghost",
@@ -29776,12 +30059,12 @@ function TableView({
29776
30059
  },
29777
30060
  i
29778
30061
  )),
29779
- effectiveMaxInline != null && itemActions.length > effectiveMaxInline && /* @__PURE__ */ jsx(
30062
+ effectiveMaxInline != null && actionDefs.length > effectiveMaxInline && /* @__PURE__ */ jsx(
29780
30063
  Menu,
29781
30064
  {
29782
30065
  position: "bottom-end",
29783
30066
  trigger: /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", "aria-label": t("common.actions"), "data-testid": "action-overflow", children: /* @__PURE__ */ jsx(Icon, { name: "more-horizontal", size: "xs" }) }),
29784
- items: itemActions.slice(effectiveMaxInline).map((action) => ({
30067
+ items: actionDefs.slice(effectiveMaxInline).map((action) => ({
29785
30068
  label: action.label,
29786
30069
  icon: action.icon,
29787
30070
  event: action.event,
@@ -29808,15 +30091,16 @@ function TableView({
29808
30091
  group.label && /* @__PURE__ */ jsx(Divider, { label: group.label, className: gi > 0 ? "mt-3" : "mt-0" }),
29809
30092
  group.items.map((row) => renderRow(row, runningIndex++))
29810
30093
  ] }, gi)) });
30094
+ const showHeader = colDefs.length > 0 || hasRenderProp;
29811
30095
  return /* @__PURE__ */ jsxs(
29812
30096
  Box,
29813
30097
  {
29814
30098
  role: "table",
29815
30099
  className: cn("w-full text-sm", className),
29816
30100
  children: [
29817
- header,
29818
- dnd.wrapContainer(body),
29819
- hasMore && /* @__PURE__ */ jsx(Box, { className: "flex justify-center py-3", children: /* @__PURE__ */ jsxs(Button, { variant: "ghost", size: "sm", onClick: () => setVisibleCount((p) => p + (pageSize || 5)), children: [
30101
+ showHeader && header,
30102
+ dnd.wrapContainer(statusNode ? /* @__PURE__ */ jsx(Box, { role: "rowgroup", children: statusNode }) : body),
30103
+ !statusNode && hasMore && /* @__PURE__ */ jsx(Box, { className: "flex justify-center py-3", children: /* @__PURE__ */ jsxs(Button, { variant: "ghost", size: "sm", onClick: () => setVisibleCount((p) => p + (pageSize || 5)), children: [
29820
30104
  /* @__PURE__ */ jsx(Icon, { name: "chevron-down", size: "xs", className: "mr-1" }),
29821
30105
  t("common.showMore"),
29822
30106
  " (",
@@ -29827,11 +30111,12 @@ function TableView({
29827
30111
  }
29828
30112
  );
29829
30113
  }
29830
- var MAX_MEASURED_COL_CH, BADGE_CHROME_CH, alignClass, weightClass, LOOKS;
30114
+ var tableViewLog, formatCell, MAX_MEASURED_COL_CH, BADGE_CHROME_CH, alignClass, weightClass, LOOKS;
29831
30115
  var init_TableView = __esm({
29832
30116
  "components/core/molecules/TableView.tsx"() {
29833
30117
  "use client";
29834
30118
  init_cn();
30119
+ init_format();
29835
30120
  init_getNestedValue();
29836
30121
  init_useEventBus();
29837
30122
  init_useMediaQuery();
@@ -29845,7 +30130,8 @@ var init_TableView = __esm({
29845
30130
  init_Divider();
29846
30131
  init_Menu();
29847
30132
  init_useDataDnd();
29848
- createLogger("almadar:ui:table-view");
30133
+ tableViewLog = createLogger("almadar:ui:table-view");
30134
+ formatCell = (value, format) => formatValue(value, format);
29849
30135
  MAX_MEASURED_COL_CH = 32;
29850
30136
  BADGE_CHROME_CH = 4;
29851
30137
  alignClass = {
@@ -37229,6 +37515,7 @@ var init_GraphCanvas = __esm({
37229
37515
  title,
37230
37516
  nodes: propNodes = NO_NODES,
37231
37517
  edges: propEdges = NO_EDGES,
37518
+ proposedEdges = NO_EDGES,
37232
37519
  similarity: propSimilarity = NO_SIM,
37233
37520
  height = 400,
37234
37521
  showLabels = true,
@@ -37236,6 +37523,7 @@ var init_GraphCanvas = __esm({
37236
37523
  draggable = true,
37237
37524
  actions,
37238
37525
  onNodeClick,
37526
+ onMarkClick,
37239
37527
  onNodeDoubleClick,
37240
37528
  onBadgeClick,
37241
37529
  nodeClickEvent,
@@ -37264,6 +37552,18 @@ var init_GraphCanvas = __esm({
37264
37552
  const laidOutRef = useRef(false);
37265
37553
  const [, forceUpdate] = useState(0);
37266
37554
  const [logicalW, setLogicalW] = useState(800);
37555
+ const hasProposed = useMemo(() => propNodes.some((n) => n.mark?.kind === "proposed"), [propNodes]);
37556
+ const [pulseTick, setPulseTick] = useState(0);
37557
+ useEffect(() => {
37558
+ if (!hasProposed) return;
37559
+ let raf = 0;
37560
+ const loop = () => {
37561
+ setPulseTick((t2) => (t2 + 1) % 1e6);
37562
+ raf = requestAnimationFrame(loop);
37563
+ };
37564
+ raf = requestAnimationFrame(loop);
37565
+ return () => cancelAnimationFrame(raf);
37566
+ }, [hasProposed]);
37267
37567
  useEffect(() => {
37268
37568
  const canvas = canvasRef.current;
37269
37569
  if (!canvas || typeof ResizeObserver === "undefined") return;
@@ -37565,18 +37865,38 @@ var init_GraphCanvas = __esm({
37565
37865
  }
37566
37866
  }
37567
37867
  ctx.globalAlpha = 1;
37868
+ ctx.setLineDash([4, 4]);
37869
+ for (const edge of proposedEdges) {
37870
+ const source = nodes.find((n) => n.id === edge.source);
37871
+ const target = nodes.find((n) => n.id === edge.target);
37872
+ if (!source || !target) continue;
37873
+ ctx.globalAlpha = 0.4;
37874
+ ctx.beginPath();
37875
+ ctx.moveTo(source.x, source.y);
37876
+ ctx.lineTo(target.x, target.y);
37877
+ ctx.strokeStyle = edge.color || mutedColor;
37878
+ ctx.lineWidth = 1;
37879
+ ctx.stroke();
37880
+ }
37881
+ ctx.setLineDash([]);
37882
+ ctx.globalAlpha = 1;
37568
37883
  for (const node of nodes) {
37569
37884
  const size = node.size || 8;
37570
37885
  const color = resolveColor3(node.color || getGroupColor(node.group, groups), canvas);
37571
37886
  const isHovered = hoveredNode === node.id;
37572
37887
  const isSelected = selectedNodeId !== void 0 && node.id === selectedNodeId;
37573
- ctx.globalAlpha = hoveredNode && !neighbors.has(node.id) ? 0.2 : 1;
37888
+ const mark = node.mark;
37889
+ ctx.globalAlpha = hoveredNode && !neighbors.has(node.id) ? 0.2 : mark?.kind === "proposed" ? 0.55 : 1;
37574
37890
  const radius = isSelected ? size + 4 : isHovered ? size + 2 : size;
37575
37891
  ctx.beginPath();
37576
37892
  ctx.arc(node.x, node.y, radius, 0, Math.PI * 2);
37577
- ctx.fillStyle = color;
37893
+ ctx.fillStyle = mark?.kind === "proposed" ? mutedColor : color;
37578
37894
  ctx.fill();
37579
- if (isSelected) {
37895
+ if (mark?.kind === "proposed") {
37896
+ ctx.setLineDash([3, 3]);
37897
+ ctx.strokeStyle = resolveColor3(mark.tint ?? "var(--color-warning)", canvas);
37898
+ ctx.lineWidth = 1.5;
37899
+ } else if (isSelected) {
37580
37900
  ctx.strokeStyle = accentColor;
37581
37901
  ctx.lineWidth = 3;
37582
37902
  } else {
@@ -37584,6 +37904,28 @@ var init_GraphCanvas = __esm({
37584
37904
  ctx.lineWidth = isHovered ? 2 : 1;
37585
37905
  }
37586
37906
  ctx.stroke();
37907
+ ctx.setLineDash([]);
37908
+ if (mark?.kind === "suggested") {
37909
+ ctx.beginPath();
37910
+ ctx.arc(node.x, node.y, radius + 3, 0, Math.PI * 2);
37911
+ ctx.strokeStyle = accentColor;
37912
+ ctx.lineWidth = 2;
37913
+ ctx.stroke();
37914
+ ctx.beginPath();
37915
+ ctx.arc(node.x + radius * 0.7, node.y - radius * 0.7, 2.5, 0, Math.PI * 2);
37916
+ ctx.fillStyle = accentColor;
37917
+ ctx.fill();
37918
+ } else if (mark?.kind === "proposed") {
37919
+ const phase = pulseTick % 60 / 60;
37920
+ const baseAlpha = hoveredNode && !neighbors.has(node.id) ? 0.2 : 0.55;
37921
+ ctx.globalAlpha = baseAlpha * (0.4 + 0.4 * (1 - Math.abs(phase * 2 - 1)));
37922
+ ctx.beginPath();
37923
+ ctx.arc(node.x, node.y, radius + 6 + Math.sin(phase * Math.PI * 2) * 3, 0, Math.PI * 2);
37924
+ ctx.strokeStyle = resolveColor3(mark.tint ?? "var(--color-warning)", canvas);
37925
+ ctx.lineWidth = 1.5;
37926
+ ctx.stroke();
37927
+ ctx.globalAlpha = baseAlpha;
37928
+ }
37587
37929
  if (showLabels && node.label) {
37588
37930
  const displayLabel = truncateLabel(node.label);
37589
37931
  ctx.font = `${isSelected || isHovered ? "700" : "600"} 12px ${fontFamily}`;
@@ -37718,11 +38060,15 @@ var init_GraphCanvas = __esm({
37718
38060
  return;
37719
38061
  }
37720
38062
  }
38063
+ if (node.mark && onMarkClick) {
38064
+ onMarkClick(node);
38065
+ return;
38066
+ }
37721
38067
  handleNodeClick(node);
37722
38068
  }
37723
38069
  }
37724
38070
  },
37725
- [toCoords, nodeAt, handleNodeClick, onBadgeClick, selectedNodeId]
38071
+ [toCoords, nodeAt, handleNodeClick, onBadgeClick, onMarkClick, selectedNodeId]
37726
38072
  );
37727
38073
  const handlePointerLeave = useCallback(() => {
37728
38074
  setHoveredNode(null);
@@ -38470,9 +38816,6 @@ var init_types2 = __esm({
38470
38816
  };
38471
38817
  }
38472
38818
  });
38473
- function humanizeFieldName(name) {
38474
- 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();
38475
- }
38476
38819
  function normalizeColumns(columns) {
38477
38820
  return columns.map((col) => {
38478
38821
  if (typeof col === "string") {
@@ -38534,7 +38877,17 @@ function DataTable({
38534
38877
  const currentPageSize = pageSize ?? 20;
38535
38878
  const total = totalCount ?? items.length;
38536
38879
  const totalPages = Math.ceil(total / currentPageSize);
38537
- const rowActions = externalRowActions ?? itemActions?.filter((a) => a.placement !== "bulk").map((action) => ({
38880
+ const withEventClick = (action) => {
38881
+ if (action.onClick || !action.event) return action;
38882
+ const event = action.event;
38883
+ return {
38884
+ ...action,
38885
+ onClick: (row) => {
38886
+ eventBus.emit(`UI:${event}`, { row });
38887
+ }
38888
+ };
38889
+ };
38890
+ const rowActions = (externalRowActions ?? itemActions?.filter((a) => a.placement !== "bulk").map((action) => ({
38538
38891
  label: action.label,
38539
38892
  icon: action.icon,
38540
38893
  variant: action.variant,
@@ -38554,7 +38907,17 @@ function DataTable({
38554
38907
  });
38555
38908
  }
38556
38909
  }
38557
- }));
38910
+ })))?.map(withEventClick);
38911
+ const normalizedBulkActions = bulkActions?.map((action) => {
38912
+ if (action.onClick || !action.event) return action;
38913
+ const event = action.event;
38914
+ return {
38915
+ ...action,
38916
+ onClick: (selectedRows2) => {
38917
+ eventBus.emit(`UI:${event}`, { rows: selectedRows2 });
38918
+ }
38919
+ };
38920
+ });
38558
38921
  const viewAction = itemActions?.find(
38559
38922
  (a) => a.event === "VIEW" || a.navigatesTo
38560
38923
  );
@@ -38662,7 +39025,7 @@ function DataTable({
38662
39025
  }
38663
39026
  )
38664
39027
  ] }),
38665
- bulkActions && selectedIds.length > 0 && /* @__PURE__ */ jsxs(HStack, { className: "items-center gap-2 pl-0 sm:pl-3 border-l-0 sm:border-l border-border", children: [
39028
+ normalizedBulkActions && selectedIds.length > 0 && /* @__PURE__ */ jsxs(HStack, { className: "items-center gap-2 pl-0 sm:pl-3 border-l-0 sm:border-l border-border", children: [
38666
39029
  /* @__PURE__ */ jsx(
38667
39030
  Typography,
38668
39031
  {
@@ -38673,13 +39036,13 @@ function DataTable({
38673
39036
  })
38674
39037
  }
38675
39038
  ),
38676
- /* @__PURE__ */ jsx(HStack, { className: "flex-wrap gap-2", children: bulkActions.map((action, idx) => /* @__PURE__ */ jsx(
39039
+ /* @__PURE__ */ jsx(HStack, { className: "flex-wrap gap-2", children: normalizedBulkActions.map((action, idx) => /* @__PURE__ */ jsx(
38677
39040
  Button,
38678
39041
  {
38679
39042
  variant: action.variant === "danger" ? "danger" : "secondary",
38680
39043
  size: "sm",
38681
39044
  leftIcon: action.icon,
38682
- onClick: () => action.onClick(selectedRows),
39045
+ onClick: () => action.onClick?.(selectedRows),
38683
39046
  children: action.label
38684
39047
  },
38685
39048
  idx
@@ -38841,7 +39204,7 @@ function DataTable({
38841
39204
  ),
38842
39205
  onClick: (e) => {
38843
39206
  e.stopPropagation();
38844
- action.onClick(row);
39207
+ action.onClick?.(row);
38845
39208
  setOpenActionMenu(null);
38846
39209
  },
38847
39210
  children: [
@@ -38877,6 +39240,7 @@ var init_DataTable = __esm({
38877
39240
  "components/core/organisms/DataTable.tsx"() {
38878
39241
  "use client";
38879
39242
  init_cn();
39243
+ init_format();
38880
39244
  init_getNestedValue();
38881
39245
  init_atoms();
38882
39246
  init_Box();
@@ -38929,9 +39293,6 @@ function getBadgeVariant(fieldName, value) {
38929
39293
  }
38930
39294
  return "default";
38931
39295
  }
38932
- function formatFieldLabel(fieldName) {
38933
- return fieldName.replace(/([A-Z])/g, " $1").replace(/^./, (str2) => str2.toUpperCase());
38934
- }
38935
39296
  function formatFieldValue2(value, fieldName) {
38936
39297
  if (typeof value === "number") {
38937
39298
  if (fieldName.toLowerCase().includes("progress") || fieldName.toLowerCase().includes("percent")) {
@@ -39059,7 +39420,7 @@ function buildFieldTypeMap(fields) {
39059
39420
  }
39060
39421
  return map;
39061
39422
  }
39062
- var ReactMarkdown2, DetailPanel;
39423
+ var formatFieldLabel, ReactMarkdown2, DetailPanel;
39063
39424
  var init_DetailPanel = __esm({
39064
39425
  "components/core/organisms/DetailPanel.tsx"() {
39065
39426
  "use client";
@@ -39071,8 +39432,10 @@ var init_DetailPanel = __esm({
39071
39432
  init_ErrorState();
39072
39433
  init_EmptyState();
39073
39434
  init_cn();
39435
+ init_format();
39074
39436
  init_getNestedValue();
39075
39437
  init_useEventBus();
39438
+ formatFieldLabel = humanizeFieldName;
39076
39439
  ReactMarkdown2 = lazy(() => import('react-markdown'));
39077
39440
  DetailPanel = ({
39078
39441
  title: propTitle,
@@ -40601,7 +40964,7 @@ function formatValue3(value, fieldName) {
40601
40964
  return String(value);
40602
40965
  }
40603
40966
  function formatFieldLabel2(fieldName) {
40604
- return fieldName.replace(/([A-Z])/g, " $1").replace(/^./, (str2) => str2.toUpperCase()).replace(/Id$/, "").trim();
40967
+ return humanizeFieldName(fieldName).replace(/\sId$/, "").trim();
40605
40968
  }
40606
40969
  var STATUS_STYLES, StatusBadge, ProgressIndicator, List3;
40607
40970
  var init_List = __esm({
@@ -40613,6 +40976,7 @@ var init_List = __esm({
40613
40976
  init_EmptyState();
40614
40977
  init_LoadingState();
40615
40978
  init_cn();
40979
+ init_format();
40616
40980
  init_getNestedValue();
40617
40981
  init_useEventBus();
40618
40982
  init_types2();
@@ -42074,7 +42438,7 @@ function TicksTab({ ticks: ticks2 }) {
42074
42438
  }
42075
42439
  );
42076
42440
  }
42077
- const formatTime2 = (ms) => {
42441
+ const formatTime3 = (ms) => {
42078
42442
  if (ms === 0) return "never";
42079
42443
  const seconds = Math.floor((Date.now() - ms) / 1e3);
42080
42444
  if (seconds < 1) return "just now";
@@ -42100,7 +42464,7 @@ function TicksTab({ ticks: ticks2 }) {
42100
42464
  tick.executionTime.toFixed(1),
42101
42465
  "ms exec"
42102
42466
  ] }),
42103
- /* @__PURE__ */ jsx("span", { children: formatTime2(tick.lastRun) })
42467
+ /* @__PURE__ */ jsx("span", { children: formatTime3(tick.lastRun) })
42104
42468
  ] }),
42105
42469
  tick.guardName && /* @__PURE__ */ jsx("div", { className: "mt-2", children: /* @__PURE__ */ jsxs(Badge, { variant: tick.guardPassed ? "success" : "danger", size: "sm", children: [
42106
42470
  tick.guardName,
@@ -42224,7 +42588,7 @@ function EventFlowTab({ events: events2 }) {
42224
42588
  if (filter === "all") return events2;
42225
42589
  return events2.filter((e) => e.type === filter);
42226
42590
  }, [events2, filter]);
42227
- const formatTime2 = (timestamp) => {
42591
+ const formatTime3 = (timestamp) => {
42228
42592
  const date = new Date(timestamp);
42229
42593
  return date.toLocaleTimeString("en-US", {
42230
42594
  hour12: false,
@@ -42300,7 +42664,7 @@ function EventFlowTab({ events: events2 }) {
42300
42664
  {
42301
42665
  className: "flex items-start gap-2 text-xs py-1 hover:bg-muted/50 rounded px-1",
42302
42666
  children: [
42303
- /* @__PURE__ */ jsx(Typography, { variant: "small", className: "text-muted-foreground font-mono min-w-[65px]", children: formatTime2(event.timestamp) }),
42667
+ /* @__PURE__ */ jsx(Typography, { variant: "small", className: "text-muted-foreground font-mono min-w-[65px]", children: formatTime3(event.timestamp) }),
42304
42668
  /* @__PURE__ */ jsx("span", { children: icon }),
42305
42669
  /* @__PURE__ */ jsx(Badge, { variant, size: "sm", className: "min-w-[60px] justify-center", children: event.source }),
42306
42670
  /* @__PURE__ */ jsx(Typography, { variant: "small", className: "text-muted-foreground", children: event.message })
@@ -42354,7 +42718,7 @@ function GuardsPanel({ guards }) {
42354
42718
  if (filter === "passed") return guards.filter((g) => g.result);
42355
42719
  return guards.filter((g) => !g.result);
42356
42720
  }, [guards, filter]);
42357
- const formatTime2 = (timestamp) => {
42721
+ const formatTime3 = (timestamp) => {
42358
42722
  const date = new Date(timestamp);
42359
42723
  return date.toLocaleTimeString("en-US", {
42360
42724
  hour12: false,
@@ -42369,7 +42733,7 @@ function GuardsPanel({ guards }) {
42369
42733
  /* @__PURE__ */ jsx(Badge, { variant: guard.result ? "success" : "danger", size: "sm", children: guard.result ? "\u2713" : "\u2717" }),
42370
42734
  /* @__PURE__ */ jsx(Typography, { variant: "body", weight: "semibold", className: "text-warning", children: guard.guardName }),
42371
42735
  /* @__PURE__ */ jsx(Typography, { variant: "small", className: "text-muted-foreground", children: guard.context.type === "transition" ? `${guard.context.transitionFrom} \u2192 ${guard.context.transitionTo}` : guard.context.tickName }),
42372
- /* @__PURE__ */ jsx(Typography, { variant: "small", className: "text-muted-foreground ml-auto", children: formatTime2(guard.timestamp) })
42736
+ /* @__PURE__ */ jsx(Typography, { variant: "small", className: "text-muted-foreground ml-auto", children: formatTime3(guard.timestamp) })
42373
42737
  ] }),
42374
42738
  content: /* @__PURE__ */ jsxs(Stack, { gap: "sm", children: [
42375
42739
  /* @__PURE__ */ jsxs("div", { children: [
@@ -42530,7 +42894,7 @@ function TransitionTimeline({ transitions }) {
42530
42894
  }
42531
42895
  );
42532
42896
  }
42533
- const formatTime2 = (ts) => {
42897
+ const formatTime3 = (ts) => {
42534
42898
  const d = new Date(ts);
42535
42899
  return d.toLocaleTimeString("en-US", {
42536
42900
  hour12: false,
@@ -42578,7 +42942,7 @@ function TransitionTimeline({ transitions }) {
42578
42942
  ${hasFailedEffects ? "bg-error" : allPassed ? "bg-success" : "bg-muted-foreground"}
42579
42943
  ` }),
42580
42944
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-xs py-1 px-2", children: [
42581
- /* @__PURE__ */ jsx(Typography, { variant: "small", className: "text-muted-foreground font-mono min-w-[65px]", children: formatTime2(trace.timestamp) }),
42945
+ /* @__PURE__ */ jsx(Typography, { variant: "small", className: "text-muted-foreground font-mono min-w-[65px]", children: formatTime3(trace.timestamp) }),
42582
42946
  /* @__PURE__ */ jsx(Badge, { variant: "primary", size: "sm", className: "min-w-[60px] justify-center", children: trace.traitName }),
42583
42947
  /* @__PURE__ */ jsxs(Typography, { variant: "small", className: "font-mono text-muted-foreground", children: [
42584
42948
  trace.from,
@@ -42653,7 +43017,7 @@ function ServerBridgeTab({ bridge }) {
42653
43017
  }
42654
43018
  );
42655
43019
  }
42656
- const formatTime2 = (ts) => {
43020
+ const formatTime3 = (ts) => {
42657
43021
  if (ts === 0) return t("debug.never");
42658
43022
  const d = new Date(ts);
42659
43023
  return d.toLocaleTimeString("en-US", {
@@ -42696,7 +43060,7 @@ function ServerBridgeTab({ bridge }) {
42696
43060
  StatRow,
42697
43061
  {
42698
43062
  label: t("debug.lastHeartbeat"),
42699
- value: formatTime2(bridge.lastHeartbeat)
43063
+ value: formatTime3(bridge.lastHeartbeat)
42700
43064
  }
42701
43065
  )
42702
43066
  ] })
@@ -44808,7 +45172,7 @@ var init_TeamOrganism = __esm({
44808
45172
  TeamOrganism.displayName = "TeamOrganism";
44809
45173
  }
44810
45174
  });
44811
- function formatDate4(value) {
45175
+ function formatDate2(value) {
44812
45176
  const d = new Date(value);
44813
45177
  if (isNaN(d.getTime())) return value;
44814
45178
  return d.toLocaleDateString(void 0, { year: "numeric", month: "short", day: "numeric" });
@@ -44940,7 +45304,7 @@ var init_Timeline = __esm({
44940
45304
  /* @__PURE__ */ jsxs(VStack, { gap: "xs", className: cn("flex-1 min-w-0", !isLast && "pb-6"), children: [
44941
45305
  /* @__PURE__ */ jsxs(HStack, { justify: "between", align: "start", wrap: true, children: [
44942
45306
  /* @__PURE__ */ jsx(Typography, { variant: "body", weight: "semibold", children: item.title }),
44943
- item.date && /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "secondary", className: "flex-shrink-0", children: formatDate4(item.date) })
45307
+ item.date && /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "secondary", className: "flex-shrink-0", children: formatDate2(item.date) })
44944
45308
  ] }),
44945
45309
  item.description && /* @__PURE__ */ jsx(Typography, { variant: "small", color: "secondary", children: item.description }),
44946
45310
  item.tags && item.tags.length > 0 && /* @__PURE__ */ jsx(HStack, { gap: "xs", wrap: true, children: item.tags.map((tag, tagIdx) => /* @__PURE__ */ jsx(Badge, { variant: "default", children: tag }, tagIdx)) }),
@@ -45246,6 +45610,10 @@ var init_component_registry_generated = __esm({
45246
45610
  init_SubagentTracePanel();
45247
45611
  init_SvgBranch();
45248
45612
  init_SvgConnection();
45613
+ init_SvgDrawGroup();
45614
+ init_SvgDrawShape();
45615
+ init_SvgDrawShapeLayer();
45616
+ init_SvgDrawText();
45249
45617
  init_SvgFlow();
45250
45618
  init_SvgGrid();
45251
45619
  init_SvgLobe();
@@ -45256,6 +45624,7 @@ var init_component_registry_generated = __esm({
45256
45624
  init_SvgRing();
45257
45625
  init_SvgShield();
45258
45626
  init_SvgStack();
45627
+ init_SvgStage();
45259
45628
  init_SwipeableRow();
45260
45629
  init_Switch();
45261
45630
  init_TabbedContainer();
@@ -45517,6 +45886,10 @@ var init_component_registry_generated = __esm({
45517
45886
  "SubagentTracePanel": SubagentTracePanel,
45518
45887
  "SvgBranch": SvgBranch,
45519
45888
  "SvgConnection": SvgConnection,
45889
+ "SvgDrawGroup": SvgDrawGroup,
45890
+ "SvgDrawShape": SvgDrawShape,
45891
+ "SvgDrawShapeLayer": SvgDrawShapeLayer,
45892
+ "SvgDrawText": SvgDrawText,
45520
45893
  "SvgFlow": SvgFlow,
45521
45894
  "SvgGrid": SvgGrid,
45522
45895
  "SvgLobe": SvgLobe,
@@ -45527,6 +45900,7 @@ var init_component_registry_generated = __esm({
45527
45900
  "SvgRing": SvgRing,
45528
45901
  "SvgShield": SvgShield,
45529
45902
  "SvgStack": SvgStack,
45903
+ "SvgStage": SvgStage,
45530
45904
  "SwipeableRow": SwipeableRow,
45531
45905
  "Switch": Switch,
45532
45906
  "TabbedContainer": TabbedContainer,
@@ -45603,7 +45977,7 @@ function enrichFormFields(fields, entityDef) {
45603
45977
  if (entityField) {
45604
45978
  const enriched = {
45605
45979
  name: field,
45606
- label: field.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/\b\w/g, (c) => c.toUpperCase()),
45980
+ label: humanizeFieldName(field),
45607
45981
  type: entityField.type,
45608
45982
  required: entityField.required ?? false
45609
45983
  };
@@ -45617,7 +45991,7 @@ function enrichFormFields(fields, entityDef) {
45617
45991
  }
45618
45992
  return enriched;
45619
45993
  }
45620
- return { name: field, label: field.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/\b\w/g, (c) => c.toUpperCase()) };
45994
+ return { name: field, label: humanizeFieldName(field) };
45621
45995
  }
45622
45996
  if (field && typeof field === "object" && !Array.isArray(field) && !React74__default.isValidElement(field) && !(field instanceof Date)) {
45623
45997
  const obj = field;
@@ -46272,9 +46646,10 @@ function SlotContentRenderer({
46272
46646
  const slotVal = restProps[slotKey];
46273
46647
  if (slotVal === void 0 || slotVal === null) continue;
46274
46648
  if (React74__default.isValidElement(slotVal) || typeof slotVal === "string" || typeof slotVal === "number" || typeof slotVal === "boolean") continue;
46275
- if (Array.isArray(slotVal) || typeof slotVal === "object" && "type" in slotVal) {
46649
+ const typelessChildren = !Array.isArray(slotVal) && typeof slotVal === "object" && !("type" in slotVal) && Array.isArray(slotVal.children) ? slotVal.children : void 0;
46650
+ if (typelessChildren !== void 0 || Array.isArray(slotVal) || typeof slotVal === "object" && "type" in slotVal) {
46276
46651
  nodeSlotOverrides[slotKey] = renderPatternChildren(
46277
- slotVal,
46652
+ typelessChildren ?? slotVal,
46278
46653
  onDismiss,
46279
46654
  `${content.id}-${slotKey}`,
46280
46655
  `${myPath}.${slotKey}`,
@@ -46453,6 +46828,7 @@ var init_UISlotRenderer = __esm({
46453
46828
  init_useEventBus();
46454
46829
  init_slot_types();
46455
46830
  init_cn();
46831
+ init_format();
46456
46832
  init_ErrorBoundary();
46457
46833
  init_Skeleton();
46458
46834
  init_renderer();
@@ -48980,4 +49356,4 @@ function useGitHubBranches(owner, repo, enabled = true) {
48980
49356
  });
48981
49357
  }
48982
49358
 
48983
- export { ALL_PRESETS, ALMADAR_DND_MIME, AR_BOOK_FIELDS, AboutPageTemplate, Accordion, Card2 as ActionCard, ActionPalette, ActionTile, ActivationBlock, Alert, AlgorithmCanvas, AnimatedCounter, AnimatedGraphic, AnimatedReveal, ArticleSection, Aside, AssetPicker, AtlasImage, AtlasPanel, AuthLayout, Avatar, Badge, BehaviorView, BiologyCanvas, BloomQuizBlock, BookChapterView, BookCoverPage, BookNavBar, BookTableOfContents, BookViewer, Box, BranchingLogicBuilder, Breadcrumb, Button, ButtonGroup, CTABanner, CalendarGrid, Canvas, Canvas2D, Card, CardBody, CardContent, CardFooter, CardGrid, CardHeader, CardTitle, Carousel, CaseStudyCard, CaseStudyOrganism, Center, Chart, ChartLegend, ChatBar, Checkbox, ChemistryCanvas, ChoiceButton, Coachmark, CodeBlock, CodeRunnerPanel, CollapsibleSection, CommunityLinks, ConditionalWrapper, ConfettiEffect, ConfirmDialog, ConnectionBlock, Container, ContentRenderer, ContentSection, ControlButton, ControlGrid, CounterTemplate, DEFAULT_LIKERT_OPTIONS, DEFAULT_MATRIX_COLUMNS, DEFAULT_SLOTS, DIAMOND_TOP_Y, DashboardGrid, DashboardLayout, DataGrid, DataList, DataTable, DateRangePicker, DateRangeSelector, DayCell, DetailPanel, Dialog, DialogueBubble, Divider, DocBreadcrumb, DocPagination, DocSearch, DocSidebar, DocTOC, DocumentViewer, StateMachineView as DomStateMachineVisualizer, Drawer, DrawerSlot, ELEMENT_SELECTED_EVENT, EdgeDecoration, EditorCheckbox, EditorSelect, EditorSlider, EditorTextInput, EditorToolbar, EmptyState, EntityDisplayEvents, ErrorBoundary, ErrorState, FEATURE_COLORS, FEATURE_TYPES, FLOOR_HEIGHT, FeatureCard, FeatureDetailPageTemplate, FeatureGrid, FeatureGridOrganism, FileTree, FilterGroup, FilterPill, Flex, FlipCard, FlipContainer, FloatingActionButton, Form, FormActions, FormField, FormLayout, FormSection, FormSectionHeader, GameAudioToggle, GameHud, GameIcon, GameMenu, GameShell, GenericAppTemplate, GeometricPattern, GradientDivider, GraphCanvas, GraphView, Grid, GridPicker, HStack, Header, HealthBar, HeroOrganism, HeroSection, I18nProvider, IDENTITY_BOOK_FIELDS, Icon, IconPicker, ImportPreviewTree, ImportProgress, ImportSourcePicker, InfiniteScrollSentinel, Input, InputGroup, InstallBox, JazariStateMachine, JsonTreeEditor, Label, LandingPageTemplate, LawReferenceTooltip, LearningCanvas, Lightbox, LikertScale, LineChart2 as LineChart, List3 as List, LoadingState, MapView, MarkdownContent, MarketingFooter, MarketingStatCard, MasterDetail, MasterDetailLayout, MathCanvas, MatrixQuestion, MediaGallery, Menu, Meter, Modal, ModalSlot, ModuleCard, Navigation, NodeSlotEditor, NotifyListener, NumberStepper, OnboardingSpotlight, OptionConstraintGroup, StateMachineView as OrbitalStateMachineView, OrbitalVisualization, Overlay, PageHeader, PageTransition, Pagination, PatternTile, PhysicsCanvas, Popover, PositionedCanvas, Presence, PricingCard, PricingGrid, PricingOrganism, PricingPageTemplate, ProgressBar, ProgressDots, PropertyInspector, PullQuote, PullToRefresh, QrScanner, QuizBlock, Radio, RangeSlider, ReflectionBlock, RelationSelect, RepeatableFormSection, ReplyTree, RichBlockEditor, RuntimeDebugger, SHEET_COLUMNS, SPRITE_SHEET_LAYOUT, ScaledDiagram, ScoreDisplay, SearchInput, Section, SectionHeader, SegmentRenderer, Select, SequenceBar, ServiceCatalog, SharedEntityStoreContext, ShowcaseCard, ShowcaseOrganism, SidePanel, Sidebar, SignaturePad, SimpleGrid, Skeleton, SlotContentRenderer, SocialProof, SortableList, Spacer, Sparkline, Spinner, Split, SplitPane, SplitSection, Stack, StarRating, StatBadge, StatCard, StatDisplay, StateGraph, StateJsonView, StateMachineView, StateNode2 as StateNode, StatsGrid, StatsOrganism, StatusBar, StatusDot, StepFlow, StepFlowOrganism, SubagentTracePanel, SvgBranch, SvgConnection, SvgFlow, SvgGrid, SvgLobe, SvgMesh, SvgMorph, SvgNode, SvgPulse, SvgRing, SvgShield, SvgStack, SwipeableRow, Switch, TERRAIN_COLORS, TILE_HEIGHT, TILE_WIDTH, TabbedContainer, TableView, Tabs, TagCloud, TagInput, TeamCard, TeamOrganism, TerrainPalette, TextHighlight, Textarea, ThemeToggle, TimeSlotCell, Timeline, TimerDisplay, Toast, ToastSlot, Tooltip, TraitFrame, TraitSlot, TraitStateViewer, TransitionArrow, TrendIndicator, TypewriterText, Typography, UISlotComponent, UISlotRenderer, UploadDropZone, VStack, VersionDiff, ViolationAlert, VoteStack, WizardContainer, WizardNavigation, WizardProgress, boardEntity, bool, calculateAttackTargets, calculateValidMoves, cn, createInitialGameState, createSharedEntityStore, createTranslate, createUnitAnimationState, getCurrentFrame, getTileDimensions, inferDirection, isoToScreen, makeAsset, makeAssetMap, mapBookData, num, objAvailableActions, objAvailableEvents, objCurrentState, objIcon, objId, objMaxRules, objName, objRules, objStates, parseEditFocus, parseLessonSegments, parseMarkdownWithCodeBlocks, parseQueryBinding, pendulum, projectileMotion, registerCodeLanguageLoader, resolveFieldMap, resolveFrame, resolveSheetDirection, rows, runTickFrame, screenToIso, springOscillator, str, tickAnimationState, toCodeLanguage, transitionAnimation, unitHealth, unitPosition, unitTeam, useAgentChat, useAnchorRect, useAtlasSliceDataUrl, useAuthContext, useCamera, useCanvasGestures, useCompile, useConnectGitHub, useDeepAgentGeneration, useDisconnectGitHub, useDragReorder, useDraggable, useDropZone, useEmitEvent, useEventBus, useEventListener, useExtensions, useFileEditor, useFileSystem, useGameAudio, useGitHubBranches, useGitHubRepo, useGitHubRepos, useGitHubStatus, useImageCache, useInfiniteScroll, useLongPress, useMediaQuery, useOrbitalHistory, usePresence, usePreview, usePullToRefresh, useQuerySingleton, useRenderInterpolation, useSharedEntitySnapshot, useSharedEntityStore, useSharedEntityStoreContext, useSwipeGesture, useTapReveal, useTraitListens, useTranslate114 as useTranslate, useUIEvents, useUISlotManager, useUnitSpriteAtlas, useValidation, vec2 };
49359
+ export { ALL_PRESETS, ALMADAR_DND_MIME, AR_BOOK_FIELDS, AboutPageTemplate, Accordion, Card2 as ActionCard, ActionPalette, ActionTile, ActivationBlock, Alert, AlgorithmCanvas, AnimatedCounter, AnimatedGraphic, AnimatedReveal, ArticleSection, Aside, AssetPicker, AtlasImage, AtlasPanel, AuthLayout, Avatar, Badge, BehaviorView, BiologyCanvas, BloomQuizBlock, BookChapterView, BookCoverPage, BookNavBar, BookTableOfContents, BookViewer, Box, BranchingLogicBuilder, Breadcrumb, Button, ButtonGroup, CTABanner, CalendarGrid, Canvas, Canvas2D, Card, CardBody, CardContent, CardFooter, CardGrid, CardHeader, CardTitle, Carousel, CaseStudyCard, CaseStudyOrganism, Center, Chart, ChartLegend, ChatBar, Checkbox, ChemistryCanvas, ChoiceButton, Coachmark, CodeBlock, CodeRunnerPanel, CollapsibleSection, CommunityLinks, ConditionalWrapper, ConfettiEffect, ConfirmDialog, ConnectionBlock, Container, ContentRenderer, ContentSection, ControlButton, ControlGrid, CounterTemplate, DEFAULT_LIKERT_OPTIONS, DEFAULT_MATRIX_COLUMNS, DEFAULT_SLOTS, DIAMOND_TOP_Y, DashboardGrid, DashboardLayout, DataGrid, DataList, DataTable, DateRangePicker, DateRangeSelector, DayCell, DetailPanel, Dialog, DialogueBubble, Divider, DocBreadcrumb, DocPagination, DocSearch, DocSidebar, DocTOC, DocumentViewer, StateMachineView as DomStateMachineVisualizer, Drawer, DrawerSlot, ELEMENT_SELECTED_EVENT, EdgeDecoration, EditorCheckbox, EditorSelect, EditorSlider, EditorTextInput, EditorToolbar, EmptyState, EntityDisplayEvents, ErrorBoundary, ErrorState, FEATURE_COLORS, FEATURE_TYPES, FLOOR_HEIGHT, FeatureCard, FeatureDetailPageTemplate, FeatureGrid, FeatureGridOrganism, FileTree, FilterGroup, FilterPill, Flex, FlipCard, FlipContainer, FloatingActionButton, Form, FormActions, FormField, FormLayout, FormSection, FormSectionHeader, GameAudioToggle, GameHud, GameIcon, GameMenu, GameShell, GenericAppTemplate, GeometricPattern, GradientDivider, GraphCanvas, GraphView, Grid, GridPicker, HStack, Header, HealthBar, HeroOrganism, HeroSection, I18nProvider, IDENTITY_BOOK_FIELDS, Icon, IconPicker, ImportPreviewTree, ImportProgress, ImportSourcePicker, InfiniteScrollSentinel, Input, InputGroup, InstallBox, JazariStateMachine, JsonTreeEditor, Label, LandingPageTemplate, LawReferenceTooltip, LearningCanvas, Lightbox, LikertScale, LineChart2 as LineChart, List3 as List, LoadingState, MapView, MarkdownContent, MarketingFooter, MarketingStatCard, MasterDetail, MasterDetailLayout, MathCanvas, MatrixQuestion, MediaGallery, Menu, Meter, Modal, ModalSlot, ModuleCard, Navigation, NodeSlotEditor, NotifyListener, NumberStepper, OnboardingSpotlight, OptionConstraintGroup, StateMachineView as OrbitalStateMachineView, OrbitalVisualization, Overlay, PageHeader, PageTransition, Pagination, PatternTile, PhysicsCanvas, Popover, PositionedCanvas, Presence, PricingCard, PricingGrid, PricingOrganism, PricingPageTemplate, ProgressBar, ProgressDots, PropertyInspector, PullQuote, PullToRefresh, QrScanner, QuizBlock, Radio, RangeSlider, ReflectionBlock, RelationSelect, RepeatableFormSection, ReplyTree, RichBlockEditor, RuntimeDebugger, SHEET_COLUMNS, SPRITE_SHEET_LAYOUT, ScaledDiagram, ScoreDisplay, SearchInput, Section, SectionHeader, SegmentRenderer, Select, SequenceBar, ServiceCatalog, SharedEntityStoreContext, ShowcaseCard, ShowcaseOrganism, SidePanel, Sidebar, SignaturePad, SimpleGrid, Skeleton, SlotContentRenderer, SocialProof, SortableList, Spacer, Sparkline, Spinner, Split, SplitPane, SplitSection, Stack, StarRating, StatBadge, StatCard, StatDisplay, StateGraph, StateJsonView, StateMachineView, StateNode2 as StateNode, StatsGrid, StatsOrganism, StatusBar, StatusDot, StepFlow, StepFlowOrganism, SubagentTracePanel, SvgBranch, SvgConnection, SvgDrawGroup, SvgDrawShape, SvgDrawShapeLayer, SvgDrawText, SvgFlow, SvgGrid, SvgLobe, SvgMesh, SvgMorph, SvgNode, SvgPulse, SvgRing, SvgShield, SvgStack, SvgStage, SvgStageContext, SwipeableRow, Switch, TERRAIN_COLORS, TILE_HEIGHT, TILE_WIDTH, TabbedContainer, TableView, Tabs, TagCloud, TagInput, TeamCard, TeamOrganism, TerrainPalette, TextHighlight, Textarea, ThemeToggle, TimeSlotCell, Timeline, TimerDisplay, Toast, ToastSlot, Tooltip, TraitFrame, TraitSlot, TraitStateViewer, TransitionArrow, TrendIndicator, TypewriterText, Typography, UISlotComponent, UISlotRenderer, UploadDropZone, VStack, VersionDiff, ViolationAlert, VoteStack, WizardContainer, WizardNavigation, WizardProgress, boardEntity, bool, calculateAttackTargets, calculateValidMoves, cn, createInitialGameState, createSharedEntityStore, createTranslate, createUnitAnimationState, getCurrentFrame, getTileDimensions, inferDirection, isoToScreen, makeAsset, makeAssetMap, mapBookData, num, objAvailableActions, objAvailableEvents, objCurrentState, objIcon, objId, objMaxRules, objName, objRules, objStates, parseEditFocus, parseLessonSegments, parseMarkdownWithCodeBlocks, parseQueryBinding, pendulum, projectileMotion, registerCodeLanguageLoader, resolveFieldMap, resolveFrame, resolveSheetDirection, rows, runTickFrame, screenToIso, springOscillator, str, tickAnimationState, toCodeLanguage, transitionAnimation, unitHealth, unitPosition, unitTeam, useAgentChat, useAnchorRect, useAtlasSliceDataUrl, useAuthContext, useCamera, useCanvasGestures, useCompile, useConnectGitHub, useDeepAgentGeneration, useDisconnectGitHub, useDragReorder, useDraggable, useDropZone, useEmitEvent, useEventBus, useEventListener, useExtensions, useFileEditor, useFileSystem, useGameAudio, useGitHubBranches, useGitHubRepo, useGitHubRepos, useGitHubStatus, useImageCache, useInfiniteScroll, useLongPress, useMediaQuery, useOrbitalHistory, usePresence, usePreview, usePullToRefresh, useQuerySingleton, useRenderInterpolation, useSharedEntitySnapshot, useSharedEntityStore, useSharedEntityStoreContext, useSwipeGesture, useTapReveal, useTraitListens, useTranslate114 as useTranslate, useUIEvents, useUISlotManager, useUnitSpriteAtlas, useValidation, vec2 };