@almadar/ui 5.155.0 → 5.156.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.
@@ -7867,10 +7867,21 @@ function ControlGrid({
7867
7867
  directionAssets,
7868
7868
  size = "md",
7869
7869
  disabled,
7870
+ visibility = "auto",
7870
7871
  className
7871
7872
  }) {
7872
7873
  const eventBus = useEventBus();
7873
7874
  const [active, setActive] = React87__namespace.useState(/* @__PURE__ */ new Set());
7875
+ const [coarse, setCoarse] = React87__namespace.useState(
7876
+ () => typeof window !== "undefined" && window.matchMedia("(pointer: coarse)").matches
7877
+ );
7878
+ React87__namespace.useEffect(() => {
7879
+ if (visibility !== "auto" || typeof window === "undefined") return;
7880
+ const mq = window.matchMedia("(pointer: coarse)");
7881
+ const onChange = (e) => setCoarse(e.matches);
7882
+ mq.addEventListener("change", onChange);
7883
+ return () => mq.removeEventListener("change", onChange);
7884
+ }, [visibility]);
7874
7885
  const handlePress = React87__namespace.useCallback(
7875
7886
  (id) => {
7876
7887
  setActive((prev) => new Set(prev).add(id));
@@ -7903,6 +7914,7 @@ function ControlGrid({
7903
7914
  },
7904
7915
  [kind, actionEvent, directionEvent, directionReleaseEvents, eventBus, onAction, onDirection]
7905
7916
  );
7917
+ if (visibility === "auto" && !coarse) return null;
7906
7918
  if (kind === "dpad") {
7907
7919
  const ds = dpadSizeMap[size];
7908
7920
  const dir = (d) => /* @__PURE__ */ jsxRuntime.jsx(
@@ -8052,7 +8064,7 @@ function StatBadge({
8052
8064
  assetUrl,
8053
8065
  iconUrl,
8054
8066
  label,
8055
- value = 0,
8067
+ value,
8056
8068
  max,
8057
8069
  format = "number",
8058
8070
  icon,
@@ -8063,6 +8075,7 @@ function StatBadge({
8063
8075
  source: _source,
8064
8076
  field: _field
8065
8077
  }) {
8078
+ const hasValue = value !== void 0 && value !== null;
8066
8079
  const numValue = typeof value === "number" ? value : parseInt(String(value), 10) || 0;
8067
8080
  const resolvedAsset = iconUrl ?? assetUrl;
8068
8081
  return /* @__PURE__ */ jsxRuntime.jsxs(
@@ -8076,8 +8089,8 @@ function StatBadge({
8076
8089
  ),
8077
8090
  children: [
8078
8091
  resolvedAsset ? /* @__PURE__ */ jsxRuntime.jsx(GameIcon, { assetUrl: resolvedAsset, icon: "image", size: 16, className: "flex-shrink-0" }) : icon ? /* @__PURE__ */ jsxRuntime.jsx(Box, { as: "span", className: "flex-shrink-0 text-lg", children: typeof icon === "string" ? /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: icon, className: "w-4 h-4" }) : /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon, className: "w-4 h-4" }) }) : null,
8079
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { as: "span", className: "text-muted-foreground font-medium", children: label }),
8080
- format === "hearts" && max && /* @__PURE__ */ jsxRuntime.jsx(
8092
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { as: "span", className: "text-muted-foreground font-medium text-xs", children: label }),
8093
+ hasValue && format === "hearts" && max && /* @__PURE__ */ jsxRuntime.jsx(
8081
8094
  HealthBar,
8082
8095
  {
8083
8096
  current: numValue,
@@ -8086,7 +8099,7 @@ function StatBadge({
8086
8099
  size: size === "lg" ? "md" : "sm"
8087
8100
  }
8088
8101
  ),
8089
- format === "bar" && max && /* @__PURE__ */ jsxRuntime.jsx(
8102
+ hasValue && format === "bar" && max && /* @__PURE__ */ jsxRuntime.jsx(
8090
8103
  HealthBar,
8091
8104
  {
8092
8105
  current: numValue,
@@ -8095,14 +8108,15 @@ function StatBadge({
8095
8108
  size: size === "lg" ? "md" : "sm"
8096
8109
  }
8097
8110
  ),
8098
- format === "number" && /* @__PURE__ */ jsxRuntime.jsx(
8111
+ hasValue && format === "number" && /* @__PURE__ */ jsxRuntime.jsx(
8099
8112
  ScoreDisplay,
8100
8113
  {
8101
8114
  value: numValue,
8102
- size: size === "lg" ? "md" : "sm"
8115
+ size: size === "lg" ? "md" : "sm",
8116
+ className: "font-display"
8103
8117
  }
8104
8118
  ),
8105
- format === "text" && /* @__PURE__ */ jsxRuntime.jsx(Typography, { as: "span", className: "font-bold text-foreground", children: value })
8119
+ hasValue && format === "text" && /* @__PURE__ */ jsxRuntime.jsx(Typography, { as: "span", className: "font-bold text-foreground", children: value })
8106
8120
  ]
8107
8121
  }
8108
8122
  );
@@ -8118,6 +8132,7 @@ var init_StatBadge = __esm({
8118
8132
  init_HealthBar();
8119
8133
  init_ScoreDisplay();
8120
8134
  sizeMap6 = {
8135
+ xs: "text-xs px-1.5 py-0.5",
8121
8136
  sm: "text-xs px-2 py-1",
8122
8137
  md: "text-sm px-3 py-1.5",
8123
8138
  lg: "text-base px-4 py-2"
@@ -8160,6 +8175,7 @@ function GameHud({
8160
8175
  items,
8161
8176
  elements,
8162
8177
  size = "md",
8178
+ variant = "floating",
8163
8179
  className,
8164
8180
  transparent = true
8165
8181
  }) {
@@ -8174,7 +8190,7 @@ function GameHud({
8174
8190
  /* @__PURE__ */ jsxRuntime.jsx(Box, { position: "absolute", className: "top-4 right-4 flex flex-col gap-2 items-end pointer-events-auto", children: rightStats.map((stat, i) => /* @__PURE__ */ jsxRuntime.jsx(StatBadge, { ...stat, size }, i)) })
8175
8191
  ] });
8176
8192
  }
8177
- if (position === "top" || position === "bottom") {
8193
+ if ((position === "top" || position === "bottom") && variant === "bar") {
8178
8194
  const mid = Math.ceil(stats.length / 2);
8179
8195
  const leftStats = stats.slice(0, mid);
8180
8196
  const rightStats = stats.slice(mid);
@@ -11662,7 +11678,7 @@ var init_StateJsonView = __esm({
11662
11678
  StateJsonView.displayName = "StateJsonView";
11663
11679
  }
11664
11680
  });
11665
- var FONT_BASE, GAME_FONTS, FONT_FACES, GameShell;
11681
+ var GAME_FONTS, GameShell;
11666
11682
  var init_GameShell = __esm({
11667
11683
  "components/game/templates/GameShell.tsx"() {
11668
11684
  init_cn();
@@ -11670,7 +11686,6 @@ var init_GameShell = __esm({
11670
11686
  init_Card();
11671
11687
  init_Typography();
11672
11688
  init_AtlasImage();
11673
- FONT_BASE = "https://almadar-kflow-assets.web.app/shared/_shared/kenney-fonts/fonts";
11674
11689
  GAME_FONTS = {
11675
11690
  future: "Kenney Future",
11676
11691
  "future-narrow": "Kenney Future Narrow",
@@ -11678,14 +11693,6 @@ var init_GameShell = __esm({
11678
11693
  blocks: "Kenney Blocks",
11679
11694
  mini: "Kenney Mini"
11680
11695
  };
11681
- FONT_FACES = `
11682
- @font-face { font-family: 'Kenney Future'; src: url('${FONT_BASE}/Kenney%20Future.ttf') format('truetype'); font-display: swap; }
11683
- @font-face { font-family: 'Kenney Future Narrow'; src: url('${FONT_BASE}/Kenney%20Future%20Narrow.ttf') format('truetype'); font-display: swap; }
11684
- @font-face { font-family: 'Kenney Pixel'; src: url('${FONT_BASE}/Kenney%20Pixel.ttf') format('truetype'); font-display: swap; }
11685
- @font-face { font-family: 'Kenney Blocks'; src: url('${FONT_BASE}/Kenney%20Blocks.ttf') format('truetype'); font-display: swap; }
11686
- @font-face { font-family: 'Kenney Mini'; src: url('${FONT_BASE}/Kenney%20Mini.ttf') format('truetype'); font-display: swap; }
11687
- .game-shell, .game-shell * { font-family: inherit; }
11688
- `;
11689
11696
  GameShell = ({
11690
11697
  appName = "Game",
11691
11698
  hud,
@@ -11712,10 +11719,12 @@ var init_GameShell = __esm({
11712
11719
  overflow: "hidden",
11713
11720
  background: "var(--color-background, #0a0a0f)",
11714
11721
  color: "var(--color-foreground, #e0e0e0)",
11715
- fontFamily: `'${font}', system-ui, sans-serif`
11722
+ // The fontFamily knob is a scoped override of the theme contract's
11723
+ // display slot: titles/numerics take the game face, body text keeps
11724
+ // the active theme's --font-family-body.
11725
+ "--font-family-display": `'${font}', ui-sans-serif, system-ui, sans-serif`
11716
11726
  },
11717
11727
  children: [
11718
- /* @__PURE__ */ jsxRuntime.jsx("style", { children: FONT_FACES }),
11719
11728
  backgroundAsset && /* @__PURE__ */ jsxRuntime.jsx(
11720
11729
  AtlasPanel,
11721
11730
  {
@@ -11753,6 +11762,7 @@ var init_GameShell = __esm({
11753
11762
  Typography,
11754
11763
  {
11755
11764
  as: "span",
11765
+ className: "font-display",
11756
11766
  style: {
11757
11767
  fontWeight: 700,
11758
11768
  fontSize: "1.05rem",
@@ -11833,6 +11843,11 @@ var init_molecules = __esm({
11833
11843
  init_puzzleObject();
11834
11844
  }
11835
11845
  });
11846
+ function themeBodyFont(el) {
11847
+ if (typeof getComputedStyle !== "function") return "system-ui, sans-serif";
11848
+ const v = getComputedStyle(el).getPropertyValue("--font-family-body").trim();
11849
+ return v || "system-ui, sans-serif";
11850
+ }
11836
11851
  function resolveColor2(color, ctx, fallback) {
11837
11852
  if (!color) return fallback;
11838
11853
  if (color.startsWith("var(")) {
@@ -12032,7 +12047,7 @@ function drawShape(ctx, shape, width, height, allShapes) {
12032
12047
  case "text": {
12033
12048
  if (shape.x == null || shape.y == null || !shape.text) break;
12034
12049
  ctx.fillStyle = stroke;
12035
- ctx.font = `${shape.fontSize ?? 14}px system-ui, sans-serif`;
12050
+ ctx.font = `${shape.fontSize ?? 14}px ${themeBodyFont(ctx.canvas)}`;
12036
12051
  ctx.textAlign = shape.align ?? "left";
12037
12052
  ctx.textBaseline = "middle";
12038
12053
  ctx.fillText(shape.text, shape.x, shape.y);
@@ -21766,7 +21781,22 @@ function eventStartDate(event, startField) {
21766
21781
  const raw = getNestedValue(event, startField);
21767
21782
  return new Date(raw ?? "");
21768
21783
  }
21769
- function generateDefaultTimeSlots(events2, startField) {
21784
+ function eventEndDate(event, endField) {
21785
+ const raw = getNestedValue(event, endField);
21786
+ if (raw === void 0 || raw === null || raw === "") return null;
21787
+ const end = new Date(raw);
21788
+ return Number.isNaN(end.getTime()) ? null : end;
21789
+ }
21790
+ function eventContinuesInSlot(event, day, slotTime, startField, endField) {
21791
+ const eventStart = eventStartDate(event, startField);
21792
+ const eventEnd = eventEndDate(event, endField);
21793
+ if (eventEnd === null || Number.isNaN(eventStart.getTime())) return false;
21794
+ const [slotHour] = slotTime.split(":").map(Number);
21795
+ const slotStart = new Date(day);
21796
+ slotStart.setHours(slotHour, 0, 0, 0);
21797
+ return slotStart.getTime() > eventStart.getTime() && slotStart.getTime() < eventEnd.getTime();
21798
+ }
21799
+ function generateDefaultTimeSlots(events2, startField, endField) {
21770
21800
  let first = DEFAULT_FIRST_HOUR;
21771
21801
  let last = DEFAULT_LAST_HOUR;
21772
21802
  for (const ev of events2) {
@@ -21775,6 +21805,11 @@ function generateDefaultTimeSlots(events2, startField) {
21775
21805
  const hour = start.getHours();
21776
21806
  if (hour < first) first = hour;
21777
21807
  if (hour > last) last = hour;
21808
+ const end = eventEndDate(ev, endField);
21809
+ if (end && end.toDateString() === start.toDateString()) {
21810
+ const endHour = end.getMinutes() === 0 && end.getSeconds() === 0 ? end.getHours() - 1 : end.getHours();
21811
+ if (endHour > last) last = endHour;
21812
+ }
21778
21813
  }
21779
21814
  const slots = [];
21780
21815
  for (let hour = first; hour <= last; hour++) {
@@ -21803,6 +21838,7 @@ function CalendarGrid({
21803
21838
  dayWindow = "auto",
21804
21839
  titleField = "title",
21805
21840
  startField = "startTime",
21841
+ endField = "endTime",
21806
21842
  colorField = "color",
21807
21843
  children,
21808
21844
  renderItem
@@ -21820,8 +21856,8 @@ function CalendarGrid({
21820
21856
  [resolvedWeekStart]
21821
21857
  );
21822
21858
  const resolvedTimeSlots = React87.useMemo(
21823
- () => timeSlots ?? generateDefaultTimeSlots(evs, startField),
21824
- [timeSlots, evs, startField]
21859
+ () => timeSlots ?? generateDefaultTimeSlots(evs, startField, endField),
21860
+ [timeSlots, evs, startField, endField]
21825
21861
  );
21826
21862
  const visibleCount = useDayWindow(dayWindow);
21827
21863
  const [dayOffset, setDayOffset] = React87.useState(0);
@@ -21981,12 +22017,15 @@ function CalendarGrid({
21981
22017
  const slotEvents = evs.filter(
21982
22018
  (ev) => eventInSlot(ev, day, time, startField)
21983
22019
  );
22020
+ const continuingEvents = evs.filter(
22021
+ (ev) => eventContinuesInSlot(ev, day, time, startField, endField)
22022
+ );
21984
22023
  const isToday = day.toDateString() === (/* @__PURE__ */ new Date()).toDateString();
21985
22024
  return /* @__PURE__ */ jsxRuntime.jsx(
21986
22025
  TimeSlotCell,
21987
22026
  {
21988
22027
  time,
21989
- isOccupied: slotEvents.length > 0,
22028
+ isOccupied: slotEvents.length > 0 || continuingEvents.length > 0,
21990
22029
  onClick: () => handleSlotClick(day, time),
21991
22030
  className: cn(
21992
22031
  "border-l border-border",
@@ -21997,7 +22036,25 @@ function CalendarGrid({
21997
22036
  onPointerUp: clearLongPress,
21998
22037
  onPointerCancel: clearLongPress
21999
22038
  } : {},
22000
- children: /* @__PURE__ */ jsxRuntime.jsx(VStack, { gap: "xs", children: slotEvents.map(renderEvent) })
22039
+ children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "xs", children: [
22040
+ slotEvents.map(renderEvent),
22041
+ continuingEvents.map((event) => {
22042
+ const color = getNestedValue(event, colorField);
22043
+ return /* @__PURE__ */ jsxRuntime.jsx(
22044
+ Box,
22045
+ {
22046
+ rounded: "sm",
22047
+ border: true,
22048
+ className: cn(
22049
+ "cursor-pointer h-2",
22050
+ color ? cn(color, "opacity-50") : "bg-primary/10 border-primary/20"
22051
+ ),
22052
+ onClick: (e) => handleEventClick(event, e)
22053
+ },
22054
+ `${event.id}-cont`
22055
+ );
22056
+ })
22057
+ ] })
22001
22058
  },
22002
22059
  `${day.toISOString()}-${time}`
22003
22060
  );
@@ -32056,13 +32113,13 @@ var init_MapView = __esm({
32056
32113
  shadowSize: [41, 41]
32057
32114
  });
32058
32115
  L.Marker.prototype.options.icon = defaultIcon;
32059
- const { useEffect: useEffect65, useRef: useRef64, useCallback: useCallback97, useState: useState95 } = React87__namespace.default;
32116
+ const { useEffect: useEffect67, useRef: useRef64, useCallback: useCallback97, useState: useState95 } = React87__namespace.default;
32060
32117
  const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
32061
32118
  const { useEventBus: useEventBus2 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
32062
32119
  function MapUpdater({ centerLat, centerLng, zoom }) {
32063
32120
  const map = useMap();
32064
32121
  const prevRef = useRef64({ centerLat, centerLng, zoom });
32065
- useEffect65(() => {
32122
+ useEffect67(() => {
32066
32123
  const prev = prevRef.current;
32067
32124
  if (prev.centerLat !== centerLat || prev.centerLng !== centerLng || prev.zoom !== zoom) {
32068
32125
  map.setView([centerLat, centerLng], zoom);
@@ -32073,7 +32130,7 @@ var init_MapView = __esm({
32073
32130
  }
32074
32131
  function MapClickHandler({ onMapClick }) {
32075
32132
  const map = useMap();
32076
- useEffect65(() => {
32133
+ useEffect67(() => {
32077
32134
  if (!onMapClick) return;
32078
32135
  const handler = (e) => {
32079
32136
  onMapClick(e.latlng.lat, e.latlng.lng);
@@ -32602,14 +32659,34 @@ var init_UploadDropZone = __esm({
32602
32659
  if (valid.length > 0) {
32603
32660
  onFiles?.(valid);
32604
32661
  if (action) {
32605
- eventBus.emit(`UI:${action}`, {
32606
- ...actionPayload,
32607
- files: valid.map((f3) => ({ name: f3.name, size: f3.size, type: f3.type }))
32662
+ void Promise.all(
32663
+ valid.map(
32664
+ (f3) => new Promise(
32665
+ (resolvePayload, rejectPayload) => {
32666
+ const reader = new FileReader();
32667
+ reader.onload = () => resolvePayload({
32668
+ name: f3.name,
32669
+ size: f3.size,
32670
+ type: f3.type,
32671
+ content: String(reader.result ?? "")
32672
+ });
32673
+ reader.onerror = () => rejectPayload(reader.error);
32674
+ reader.readAsDataURL(f3);
32675
+ }
32676
+ )
32677
+ )
32678
+ ).then((payloadFiles) => {
32679
+ eventBus.emit(`UI:${action}`, {
32680
+ ...actionPayload,
32681
+ files: payloadFiles
32682
+ });
32683
+ }).catch(() => {
32684
+ setError(t("Could not read the selected file"));
32608
32685
  });
32609
32686
  }
32610
32687
  }
32611
32688
  },
32612
- [validateFiles, onFiles, action, actionPayload, eventBus]
32689
+ [validateFiles, onFiles, action, actionPayload, eventBus, t]
32613
32690
  );
32614
32691
  const handleDragOver = (e) => {
32615
32692
  e.preventDefault();
@@ -42126,22 +42203,6 @@ var init_DataTable = __esm({
42126
42203
  DataTable.displayName = "DataTable";
42127
42204
  }
42128
42205
  });
42129
- function getFieldIcon(fieldName) {
42130
- const name = fieldName.toLowerCase();
42131
- if (name.includes("date") || name.includes("time")) return LucideIcons2.Calendar;
42132
- if (name.includes("status")) return LucideIcons2.Tag;
42133
- if (name.includes("priority")) return LucideIcons2.AlertCircle;
42134
- if (name.includes("progress") || name.includes("percent")) return LucideIcons2.TrendingUp;
42135
- if (name.includes("assignee") || name.includes("owner") || name.includes("user") || name.includes("member"))
42136
- return LucideIcons2.User;
42137
- if (name.includes("due")) return LucideIcons2.Clock;
42138
- if (name.includes("complete")) return LucideIcons2.CheckCircle2;
42139
- if (name.includes("budget") || name.includes("cost") || name.includes("price"))
42140
- return LucideIcons2.DollarSign;
42141
- if (name.includes("description") || name.includes("note") || name.includes("comment"))
42142
- return LucideIcons2.FileText;
42143
- return LucideIcons2.Package;
42144
- }
42145
42206
  function getBadgeVariant(fieldName, value) {
42146
42207
  const name = fieldName.toLowerCase();
42147
42208
  const val = String(value).toLowerCase();
@@ -42191,6 +42252,18 @@ function renderRichFieldValue(value, fieldName, fieldType, meta) {
42191
42252
  }
42192
42253
  ) });
42193
42254
  }
42255
+ if (fieldType === "url" && /^https?:\/\//i.test(str)) {
42256
+ return /* @__PURE__ */ jsxRuntime.jsx(
42257
+ "a",
42258
+ {
42259
+ href: str,
42260
+ target: "_blank",
42261
+ rel: "noreferrer",
42262
+ className: "text-primary hover:underline break-all",
42263
+ children: str
42264
+ }
42265
+ );
42266
+ }
42194
42267
  return str;
42195
42268
  }
42196
42269
  case "markdown":
@@ -42289,6 +42362,23 @@ function renderRichFieldValue(value, fieldName, fieldType, meta) {
42289
42362
  }
42290
42363
  return str;
42291
42364
  }
42365
+ case "boolean": {
42366
+ if (typeof value === "boolean") return value ? "Yes" : "No";
42367
+ if (str === "true") return "Yes";
42368
+ if (str === "false") return "No";
42369
+ return str;
42370
+ }
42371
+ case "array": {
42372
+ if (Array.isArray(value) && value.length > 0) {
42373
+ return /* @__PURE__ */ jsxRuntime.jsx(HStack, { gap: "xs", wrap: true, children: value.map((item, i) => /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: "default", children: String(item) }, i)) });
42374
+ }
42375
+ if (Array.isArray(value)) return "\u2014";
42376
+ return str;
42377
+ }
42378
+ case "email":
42379
+ return /* @__PURE__ */ jsxRuntime.jsx("a", { href: `mailto:${str}`, className: "text-primary hover:underline break-all", children: str });
42380
+ case "phone":
42381
+ return /* @__PURE__ */ jsxRuntime.jsx("a", { href: `tel:${str}`, className: "text-primary hover:underline", children: str });
42292
42382
  default:
42293
42383
  if (meta?.values && meta.values.length > 0 && meta.values.includes(str)) {
42294
42384
  return /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: getBadgeVariant(fieldName, str), children: humanizeEnumValue(str) });
@@ -42361,10 +42451,11 @@ var init_DetailPanel = __esm({
42361
42451
  avatar,
42362
42452
  sections: propSections,
42363
42453
  actions,
42364
- maxInlineActions,
42454
+ maxInlineActions = 2,
42365
42455
  backAction,
42366
42456
  footer,
42367
42457
  slideOver = false,
42458
+ showActions = true,
42368
42459
  className,
42369
42460
  entity,
42370
42461
  fields: propFields,
@@ -42412,9 +42503,6 @@ var init_DetailPanel = __esm({
42412
42503
  },
42413
42504
  [eventBus]
42414
42505
  );
42415
- const handleClose = React87.useCallback(() => {
42416
- eventBus.emit("UI:CLOSE", {});
42417
- }, [eventBus]);
42418
42506
  const entityRecord = Array.isArray(entity) ? entity[0] : entity;
42419
42507
  const data = entityRecord ?? initialData;
42420
42508
  let title = propTitle;
@@ -42428,8 +42516,7 @@ var init_DetailPanel = __esm({
42428
42516
  const value = getNestedValue(normalizedData, field);
42429
42517
  return {
42430
42518
  label: labelFor(field),
42431
- value: formatFieldValue2(value, field),
42432
- icon: getFieldIcon(field)
42519
+ value: formatFieldValue2(value, field)
42433
42520
  };
42434
42521
  }
42435
42522
  return field;
@@ -42463,15 +42550,14 @@ var init_DetailPanel = __esm({
42463
42550
  (f3) => (!titleDerivedFromPrimary || f3 !== primaryField) && !statusFields.includes(f3) && !progressFields.includes(f3) && !metricFields.includes(f3) && !dateFields.includes(f3) && !descriptionFields.includes(f3)
42464
42551
  );
42465
42552
  sections = [];
42466
- if (statusFields.length > 0 || otherFields.length > 0) {
42553
+ if (otherFields.length > 0) {
42467
42554
  const overviewFields = [];
42468
- [...statusFields, ...otherFields].forEach((field) => {
42555
+ otherFields.forEach((field) => {
42469
42556
  const value = getNestedValue(normalizedData, field);
42470
42557
  if (value !== void 0 && value !== null) {
42471
42558
  overviewFields.push({
42472
42559
  label: labelFor(field),
42473
- value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field)),
42474
- icon: getFieldIcon(field)
42560
+ value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field))
42475
42561
  });
42476
42562
  }
42477
42563
  });
@@ -42486,8 +42572,7 @@ var init_DetailPanel = __esm({
42486
42572
  if (value !== void 0 && value !== null) {
42487
42573
  metricsFields.push({
42488
42574
  label: labelFor(field),
42489
- value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field)),
42490
- icon: getFieldIcon(field)
42575
+ value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field))
42491
42576
  });
42492
42577
  }
42493
42578
  });
@@ -42502,8 +42587,7 @@ var init_DetailPanel = __esm({
42502
42587
  if (value !== void 0 && value !== null) {
42503
42588
  timelineFields.push({
42504
42589
  label: labelFor(field),
42505
- value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field)),
42506
- icon: getFieldIcon(field)
42590
+ value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field))
42507
42591
  });
42508
42592
  }
42509
42593
  });
@@ -42518,8 +42602,7 @@ var init_DetailPanel = __esm({
42518
42602
  if (value !== void 0 && value !== null) {
42519
42603
  descFields.push({
42520
42604
  label: labelFor(field),
42521
- value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field)),
42522
- icon: getFieldIcon(field)
42605
+ value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field))
42523
42606
  });
42524
42607
  }
42525
42608
  });
@@ -42528,6 +42611,15 @@ var init_DetailPanel = __esm({
42528
42611
  }
42529
42612
  }
42530
42613
  }
42614
+ const renderSlot = providers.useRenderSlot();
42615
+ const navStack = providers.useNavStack();
42616
+ const { setCurrentLabel } = navStack;
42617
+ const resolvedTitle = normalizedData ? title : void 0;
42618
+ React87.useEffect(() => {
42619
+ if (renderSlot === "main" && !slideOver && resolvedTitle) {
42620
+ setCurrentLabel(resolvedTitle);
42621
+ }
42622
+ }, [renderSlot, slideOver, resolvedTitle, setCurrentLabel]);
42531
42623
  if (isLoading) {
42532
42624
  return /* @__PURE__ */ jsxRuntime.jsx(
42533
42625
  LoadingState,
@@ -42566,8 +42658,7 @@ var init_DetailPanel = __esm({
42566
42658
  const value = normalizedData ? getNestedValue(normalizedData, field) : void 0;
42567
42659
  allFields.push({
42568
42660
  label: labelFor(field),
42569
- value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field)),
42570
- icon: getFieldIcon(field)
42661
+ value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field))
42571
42662
  });
42572
42663
  } else {
42573
42664
  allFields.push(field);
@@ -42579,24 +42670,50 @@ var init_DetailPanel = __esm({
42579
42670
  (a) => a.event === "CLOSE" || a.event === "CANCEL" || a.label?.toLowerCase() === "close"
42580
42671
  );
42581
42672
  const otherActions = actions?.filter((a) => a !== closeAction) ?? [];
42582
- const effectiveCloseAction = closeAction ?? { event: void 0};
42583
- const content = /* @__PURE__ */ jsxRuntime.jsx(Card, { variant: "elevated", children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "md", className: "p-6", children: [
42584
- /* @__PURE__ */ jsxRuntime.jsxs(HStack, { justify: "between", align: "center", gap: "xs", children: [
42585
- /* @__PURE__ */ jsxRuntime.jsx(HStack, { align: "center", gap: "xs", children: backAction && /* @__PURE__ */ jsxRuntime.jsx(
42586
- Button,
42673
+ const statusBadges = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
42674
+ normalizedData && effectiveFieldNames && effectiveFieldNames.filter(
42675
+ (f3) => f3.toLowerCase().includes("status") || f3.toLowerCase().includes("priority")
42676
+ ).map((field) => {
42677
+ const value = getNestedValue(normalizedData, field);
42678
+ if (!value) return null;
42679
+ return /* @__PURE__ */ jsxRuntime.jsx(
42680
+ Badge,
42587
42681
  {
42588
- variant: backAction.variant || "ghost",
42589
- size: "sm",
42590
- action: backAction.navigatesTo ? void 0 : backAction.event,
42591
- actionPayload: { row: normalizedData },
42592
- onClick: backAction.navigatesTo ? () => handleActionClick(backAction, normalizedData) : void 0,
42593
- icon: backAction.icon ?? LucideIcons2.ArrowLeft,
42594
- "data-testid": backAction.event ? `action-${backAction.event}` : "action-back",
42595
- children: backAction.label
42596
- }
42597
- ) }),
42598
- /* @__PURE__ */ jsxRuntime.jsxs(HStack, { justify: "end", align: "center", gap: "xs", children: [
42599
- (maxInlineActions != null ? otherActions.slice(0, maxInlineActions) : otherActions).map((action, idx) => /* @__PURE__ */ jsxRuntime.jsx(
42682
+ variant: getBadgeVariant(field, String(value)),
42683
+ children: humanizeEnumValue(String(value))
42684
+ },
42685
+ field
42686
+ );
42687
+ }),
42688
+ status && /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: status.variant ?? "default", children: status.label })
42689
+ ] });
42690
+ const content = /* @__PURE__ */ jsxRuntime.jsx(Card, { variant: "elevated", children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "md", className: "p-6", children: [
42691
+ /* @__PURE__ */ jsxRuntime.jsxs(HStack, { justify: "between", align: "start", gap: "md", children: [
42692
+ /* @__PURE__ */ jsxRuntime.jsxs(HStack, { align: "start", gap: "sm", className: "min-w-0", children: [
42693
+ backAction && /* @__PURE__ */ jsxRuntime.jsx(
42694
+ Button,
42695
+ {
42696
+ variant: backAction.variant || "ghost",
42697
+ size: "sm",
42698
+ action: backAction.navigatesTo ? void 0 : backAction.event,
42699
+ actionPayload: { row: normalizedData },
42700
+ onClick: backAction.navigatesTo ? () => handleActionClick(backAction, normalizedData) : void 0,
42701
+ icon: backAction.icon ?? LucideIcons2.ArrowLeft,
42702
+ "data-testid": backAction.event ? `action-${backAction.event}` : "action-back",
42703
+ children: backAction.label
42704
+ }
42705
+ ),
42706
+ avatar,
42707
+ /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "xs", className: "min-w-0", children: [
42708
+ /* @__PURE__ */ jsxRuntime.jsxs(HStack, { align: "center", gap: "sm", wrap: true, children: [
42709
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h2", weight: "bold", children: title || "Details" }),
42710
+ statusBadges
42711
+ ] }),
42712
+ subtitle && /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", color: "secondary", children: subtitle })
42713
+ ] })
42714
+ ] }),
42715
+ showActions && /* @__PURE__ */ jsxRuntime.jsxs(HStack, { justify: "end", align: "center", gap: "xs", className: "shrink-0", children: [
42716
+ otherActions.slice(0, maxInlineActions).map((action, idx) => /* @__PURE__ */ jsxRuntime.jsx(
42600
42717
  Button,
42601
42718
  {
42602
42719
  variant: action.variant || "secondary",
@@ -42611,7 +42728,7 @@ var init_DetailPanel = __esm({
42611
42728
  },
42612
42729
  idx
42613
42730
  )),
42614
- maxInlineActions != null && otherActions.length > maxInlineActions && /* @__PURE__ */ jsxRuntime.jsx(
42731
+ otherActions.length > maxInlineActions && /* @__PURE__ */ jsxRuntime.jsx(
42615
42732
  Menu,
42616
42733
  {
42617
42734
  position: "bottom-end",
@@ -42627,40 +42744,20 @@ var init_DetailPanel = __esm({
42627
42744
  }))
42628
42745
  }
42629
42746
  ),
42630
- /* @__PURE__ */ jsxRuntime.jsx(
42747
+ closeAction && /* @__PURE__ */ jsxRuntime.jsx(
42631
42748
  Button,
42632
42749
  {
42633
42750
  variant: "ghost",
42634
42751
  size: "sm",
42635
- action: effectiveCloseAction.event,
42752
+ action: closeAction.event,
42636
42753
  actionPayload: { row: normalizedData },
42637
- onClick: effectiveCloseAction.event ? void 0 : handleClose,
42754
+ onClick: closeAction.event ? void 0 : () => handleActionClick(closeAction, normalizedData),
42638
42755
  icon: LucideIcons2.X,
42639
- "data-testid": effectiveCloseAction.event ? `action-${effectiveCloseAction.event}` : "action-close"
42756
+ "data-testid": closeAction.event ? `action-${closeAction.event}` : "action-close"
42640
42757
  }
42641
42758
  )
42642
42759
  ] })
42643
42760
  ] }),
42644
- avatar,
42645
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h2", weight: "bold", children: title || "Details" }),
42646
- subtitle && /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", color: "secondary", children: subtitle }),
42647
- /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "xs", wrap: true, children: [
42648
- normalizedData && effectiveFieldNames && effectiveFieldNames.filter(
42649
- (f3) => f3.toLowerCase().includes("status") || f3.toLowerCase().includes("priority")
42650
- ).map((field) => {
42651
- const value = getNestedValue(normalizedData, field);
42652
- if (!value) return null;
42653
- return /* @__PURE__ */ jsxRuntime.jsx(
42654
- Badge,
42655
- {
42656
- variant: getBadgeVariant(field, String(value)),
42657
- children: String(value)
42658
- },
42659
- field
42660
- );
42661
- }),
42662
- status && /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: status.variant ?? "default", children: status.label })
42663
- ] }),
42664
42761
  normalizedData && effectiveFieldNames && effectiveFieldNames.filter(
42665
42762
  (f3) => f3.toLowerCase().includes("progress") || f3.toLowerCase().includes("percent")
42666
42763
  ).map((field) => {
@@ -42693,9 +42790,10 @@ var init_DetailPanel = __esm({
42693
42790
  /* @__PURE__ */ jsxRuntime.jsx(
42694
42791
  Typography,
42695
42792
  {
42696
- variant: "small",
42697
- color: "secondary",
42793
+ variant: "caption",
42794
+ color: "muted",
42698
42795
  weight: "medium",
42796
+ className: "uppercase tracking-wider",
42699
42797
  children: field.label
42700
42798
  }
42701
42799
  ),
@@ -42764,7 +42862,7 @@ var init_DrawerSlot = __esm({
42764
42862
  position,
42765
42863
  width: size,
42766
42864
  className,
42767
- children
42865
+ children: /* @__PURE__ */ jsxRuntime.jsx(providers.RenderSlotProvider, { slot: "drawer", children })
42768
42866
  }
42769
42867
  );
42770
42868
  };
@@ -44811,7 +44909,7 @@ var init_ModalSlot = __esm({
44811
44909
  title,
44812
44910
  size,
44813
44911
  className,
44814
- children
44912
+ children: /* @__PURE__ */ jsxRuntime.jsx(providers.RenderSlotProvider, { slot: "modal", children })
44815
44913
  }
44816
44914
  );
44817
44915
  };
@@ -49214,7 +49312,10 @@ function MaybeTraitScope({
49214
49312
  }
49215
49313
  return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children });
49216
49314
  }
49217
- function UISlotComponent({
49315
+ function UISlotComponent(props) {
49316
+ return /* @__PURE__ */ jsxRuntime.jsx(providers.RenderSlotProvider, { slot: props.slot, children: /* @__PURE__ */ jsxRuntime.jsx(UISlotComponentInner, { ...props }) });
49317
+ }
49318
+ function UISlotComponentInner({
49218
49319
  slot,
49219
49320
  portal = false,
49220
49321
  position,
@@ -50783,6 +50884,12 @@ var CurrentPagePathProvider = ({
50783
50884
  }) => /* @__PURE__ */ jsxRuntime.jsx(CurrentPagePathContext.Provider, { value, children });
50784
50885
  CurrentPagePathProvider.displayName = "CurrentPagePathProvider";
50785
50886
  var useCurrentPagePath2 = () => React87.useContext(CurrentPagePathContext);
50887
+ var RenderSlotContext = React87.createContext("main");
50888
+ var RenderSlotProvider4 = ({ slot, children }) => /* @__PURE__ */ jsxRuntime.jsx(RenderSlotContext.Provider, { value: slot, children });
50889
+ RenderSlotProvider4.displayName = "RenderSlotProvider";
50890
+ function useRenderSlot2() {
50891
+ return React87.useContext(RenderSlotContext);
50892
+ }
50786
50893
  var EntitySchemaContext = React87.createContext(null);
50787
50894
  function EntitySchemaProvider({
50788
50895
  entities,
@@ -51573,6 +51680,18 @@ function syncNavStack(state, pages, path, pending) {
51573
51680
  }
51574
51681
  return { state: { ...state, [page.orbital]: stack }, orbital: page.orbital };
51575
51682
  }
51683
+ function relabelCurrent(state, pages, path, label) {
51684
+ const normalized = normalizePath(path);
51685
+ const page = matchNavPage(pages, normalized);
51686
+ if (!page || !label) return state;
51687
+ const stack = state[page.orbital];
51688
+ if (!stack || stack.length === 0) return state;
51689
+ const idx = stack.findIndex((e) => e.href === normalized);
51690
+ if (idx < 0 || stack[idx].label === label) return state;
51691
+ const next = [...stack];
51692
+ next[idx] = { href: normalized, label };
51693
+ return { ...state, [page.orbital]: next };
51694
+ }
51576
51695
  function previousEntry(state, pages, path) {
51577
51696
  const page = matchNavPage(pages, normalizePath(path));
51578
51697
  if (!page) return null;
@@ -51589,10 +51708,11 @@ var INERT_API = {
51589
51708
  canGoBack: false,
51590
51709
  beginNavigate: () => void 0,
51591
51710
  back: () => void 0,
51592
- goTo: () => void 0
51711
+ goTo: () => void 0,
51712
+ setCurrentLabel: () => void 0
51593
51713
  };
51594
51714
  var NavStackContext = React87.createContext(INERT_API);
51595
- function useNavStack2() {
51715
+ function useNavStack3() {
51596
51716
  return React87.useContext(NavStackContext);
51597
51717
  }
51598
51718
  function loadStored(storageKey) {
@@ -51649,6 +51769,16 @@ var NavStackProvider = ({
51649
51769
  },
51650
51770
  [navigate]
51651
51771
  );
51772
+ const setCurrentLabel = React87.useCallback(
51773
+ (label) => {
51774
+ setState((prev) => {
51775
+ const next = relabelCurrent(prev, pages, currentPath, label);
51776
+ if (next !== prev) persistStored(storageKey, next);
51777
+ return next;
51778
+ });
51779
+ },
51780
+ [pages, currentPath, storageKey]
51781
+ );
51652
51782
  const entries = React87.useMemo(
51653
51783
  () => entriesFor(state, pages, currentPath),
51654
51784
  [state, pages, currentPath]
@@ -51659,9 +51789,10 @@ var NavStackProvider = ({
51659
51789
  canGoBack: previousEntry(state, pages, currentPath) !== null,
51660
51790
  beginNavigate,
51661
51791
  back,
51662
- goTo
51792
+ goTo,
51793
+ setCurrentLabel
51663
51794
  }),
51664
- [entries, state, pages, currentPath, beginNavigate, back, goTo]
51795
+ [entries, state, pages, currentPath, beginNavigate, back, goTo, setCurrentLabel]
51665
51796
  );
51666
51797
  return /* @__PURE__ */ jsxRuntime.jsx(NavStackContext.Provider, { value: api, children });
51667
51798
  };
@@ -51718,6 +51849,7 @@ exports.NavigationProvider = NavigationProvider2;
51718
51849
  exports.OfflineModeProvider = OfflineModeProvider;
51719
51850
  exports.OrbitalProvider = OrbitalProvider;
51720
51851
  exports.OrbitalThemeProvider = OrbitalThemeProvider;
51852
+ exports.RenderSlotProvider = RenderSlotProvider4;
51721
51853
  exports.SelectionContext = SelectionContext;
51722
51854
  exports.SelectionProvider = SelectionProvider;
51723
51855
  exports.ServerBridgeProvider = ServerBridgeProvider;
@@ -51746,13 +51878,14 @@ exports.useGameAudioContextOptional = useGameAudioContextOptional2;
51746
51878
  exports.useHasPermission = useHasPermission;
51747
51879
  exports.useHasRole = useHasRole;
51748
51880
  exports.useInitPayload = useInitPayload2;
51749
- exports.useNavStack = useNavStack2;
51881
+ exports.useNavStack = useNavStack3;
51750
51882
  exports.useNavigateTo = useNavigateTo2;
51751
51883
  exports.useNavigation = useNavigation2;
51752
51884
  exports.useNavigationId = useNavigationId2;
51753
51885
  exports.useNavigationState = useNavigationState2;
51754
51886
  exports.useOfflineMode = useOfflineMode;
51755
51887
  exports.useOptionalOfflineMode = useOptionalOfflineMode;
51888
+ exports.useRenderSlot = useRenderSlot2;
51756
51889
  exports.useSelection = useSelection;
51757
51890
  exports.useSelectionOptional = useSelectionOptional;
51758
51891
  exports.useServerBridge = useServerBridge;