@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.
@@ -1,6 +1,6 @@
1
1
  import * as React87 from 'react';
2
2
  import React87__default, { createContext, useContext, useMemo, useRef, useEffect, useCallback, Suspense, useState, useSyncExternalStore, useLayoutEffect, lazy, useId } from 'react';
3
- import { EventBusContext, useTraitScopeChain, useEntitySchemaOptional, useEntityBindingSnapshot, useTraitScope, TraitScopeProvider, useNavStack, useCurrentPagePath, useGameAudioContextOptional, matchPathAmong } from '@almadar/ui/providers';
3
+ import { EventBusContext, useTraitScopeChain, RenderSlotProvider, useEntitySchemaOptional, useEntityBindingSnapshot, useTraitScope, TraitScopeProvider, useNavStack, useCurrentPagePath, useRenderSlot, useGameAudioContextOptional, matchPathAmong } from '@almadar/ui/providers';
4
4
  import { createLogger, isLogLevelEnabled } from '@almadar/logger';
5
5
  import { createContextFromBindings, interpolateValue } from '@almadar/runtime';
6
6
  import { ANONYMOUS_USER, isRenderBindingMarker, isFileValue, isInlineTrait, ANIMATION_NAMES } from '@almadar/core';
@@ -8,7 +8,7 @@ export { ANONYMOUS_USER } from '@almadar/core';
8
8
  import { clsx } from 'clsx';
9
9
  import { twMerge } from 'tailwind-merge';
10
10
  import * as LucideIcons2 from 'lucide-react';
11
- import { Loader2, X, Lightbulb, CheckCircle, List, Printer, ChevronRight, ChevronLeft, GitBranch, Pencil, Eye, Plus, ArrowRight, Trash, Code, FileText, WrapText, Check, Copy, RotateCcw, Play, Terminal, XCircle, AlertTriangle, Trash2, Link2, ArrowLeft, ZoomOut, ZoomIn, Download, DollarSign, ChevronDown, Menu as Menu$1, Package, Calendar, MoreHorizontal, Image as Image$1, Upload, HelpCircle, PauseCircle, Search, Type, Heading1, Heading2, Heading3, ListOrdered, Quote, Minus, Eraser, TrendingUp, TrendingDown, AlertCircle, Circle, Clock, CheckCircle2, ChevronUp, Tag, User } from 'lucide-react';
11
+ import { Loader2, X, Lightbulb, CheckCircle, List, Printer, ChevronRight, ChevronLeft, GitBranch, Pencil, Eye, Plus, ArrowRight, Trash, Code, FileText, WrapText, Check, Copy, RotateCcw, Play, Terminal, XCircle, AlertTriangle, Trash2, Link2, ArrowLeft, ZoomOut, ZoomIn, Download, DollarSign, ChevronDown, Menu as Menu$1, Package, Calendar, MoreHorizontal, Image as Image$1, Upload, HelpCircle, PauseCircle, Search, Type, Heading1, Heading2, Heading3, ListOrdered, Quote, Minus, Eraser, TrendingUp, TrendingDown, AlertCircle, Circle, Clock, CheckCircle2, ChevronUp } from 'lucide-react';
12
12
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
13
13
  import { createPortal } from 'react-dom';
14
14
  import { useTranslate } from '@almadar/ui/hooks';
@@ -7793,10 +7793,21 @@ function ControlGrid({
7793
7793
  directionAssets,
7794
7794
  size = "md",
7795
7795
  disabled,
7796
+ visibility = "auto",
7796
7797
  className
7797
7798
  }) {
7798
7799
  const eventBus = useEventBus();
7799
7800
  const [active, setActive] = React87.useState(/* @__PURE__ */ new Set());
7801
+ const [coarse, setCoarse] = React87.useState(
7802
+ () => typeof window !== "undefined" && window.matchMedia("(pointer: coarse)").matches
7803
+ );
7804
+ React87.useEffect(() => {
7805
+ if (visibility !== "auto" || typeof window === "undefined") return;
7806
+ const mq = window.matchMedia("(pointer: coarse)");
7807
+ const onChange = (e) => setCoarse(e.matches);
7808
+ mq.addEventListener("change", onChange);
7809
+ return () => mq.removeEventListener("change", onChange);
7810
+ }, [visibility]);
7800
7811
  const handlePress = React87.useCallback(
7801
7812
  (id) => {
7802
7813
  setActive((prev) => new Set(prev).add(id));
@@ -7829,6 +7840,7 @@ function ControlGrid({
7829
7840
  },
7830
7841
  [kind, actionEvent, directionEvent, directionReleaseEvents, eventBus, onAction, onDirection]
7831
7842
  );
7843
+ if (visibility === "auto" && !coarse) return null;
7832
7844
  if (kind === "dpad") {
7833
7845
  const ds = dpadSizeMap[size];
7834
7846
  const dir = (d) => /* @__PURE__ */ jsx(
@@ -7978,7 +7990,7 @@ function StatBadge({
7978
7990
  assetUrl,
7979
7991
  iconUrl,
7980
7992
  label,
7981
- value = 0,
7993
+ value,
7982
7994
  max,
7983
7995
  format = "number",
7984
7996
  icon,
@@ -7989,6 +8001,7 @@ function StatBadge({
7989
8001
  source: _source,
7990
8002
  field: _field
7991
8003
  }) {
8004
+ const hasValue = value !== void 0 && value !== null;
7992
8005
  const numValue = typeof value === "number" ? value : parseInt(String(value), 10) || 0;
7993
8006
  const resolvedAsset = iconUrl ?? assetUrl;
7994
8007
  return /* @__PURE__ */ jsxs(
@@ -8002,8 +8015,8 @@ function StatBadge({
8002
8015
  ),
8003
8016
  children: [
8004
8017
  resolvedAsset ? /* @__PURE__ */ jsx(GameIcon, { assetUrl: resolvedAsset, icon: "image", size: 16, className: "flex-shrink-0" }) : icon ? /* @__PURE__ */ jsx(Box, { as: "span", className: "flex-shrink-0 text-lg", children: typeof icon === "string" ? /* @__PURE__ */ jsx(Icon, { name: icon, className: "w-4 h-4" }) : /* @__PURE__ */ jsx(Icon, { icon, className: "w-4 h-4" }) }) : null,
8005
- /* @__PURE__ */ jsx(Typography, { as: "span", className: "text-muted-foreground font-medium", children: label }),
8006
- format === "hearts" && max && /* @__PURE__ */ jsx(
8018
+ /* @__PURE__ */ jsx(Typography, { as: "span", className: "text-muted-foreground font-medium text-xs", children: label }),
8019
+ hasValue && format === "hearts" && max && /* @__PURE__ */ jsx(
8007
8020
  HealthBar,
8008
8021
  {
8009
8022
  current: numValue,
@@ -8012,7 +8025,7 @@ function StatBadge({
8012
8025
  size: size === "lg" ? "md" : "sm"
8013
8026
  }
8014
8027
  ),
8015
- format === "bar" && max && /* @__PURE__ */ jsx(
8028
+ hasValue && format === "bar" && max && /* @__PURE__ */ jsx(
8016
8029
  HealthBar,
8017
8030
  {
8018
8031
  current: numValue,
@@ -8021,14 +8034,15 @@ function StatBadge({
8021
8034
  size: size === "lg" ? "md" : "sm"
8022
8035
  }
8023
8036
  ),
8024
- format === "number" && /* @__PURE__ */ jsx(
8037
+ hasValue && format === "number" && /* @__PURE__ */ jsx(
8025
8038
  ScoreDisplay,
8026
8039
  {
8027
8040
  value: numValue,
8028
- size: size === "lg" ? "md" : "sm"
8041
+ size: size === "lg" ? "md" : "sm",
8042
+ className: "font-display"
8029
8043
  }
8030
8044
  ),
8031
- format === "text" && /* @__PURE__ */ jsx(Typography, { as: "span", className: "font-bold text-foreground", children: value })
8045
+ hasValue && format === "text" && /* @__PURE__ */ jsx(Typography, { as: "span", className: "font-bold text-foreground", children: value })
8032
8046
  ]
8033
8047
  }
8034
8048
  );
@@ -8044,6 +8058,7 @@ var init_StatBadge = __esm({
8044
8058
  init_HealthBar();
8045
8059
  init_ScoreDisplay();
8046
8060
  sizeMap6 = {
8061
+ xs: "text-xs px-1.5 py-0.5",
8047
8062
  sm: "text-xs px-2 py-1",
8048
8063
  md: "text-sm px-3 py-1.5",
8049
8064
  lg: "text-base px-4 py-2"
@@ -8086,6 +8101,7 @@ function GameHud({
8086
8101
  items,
8087
8102
  elements,
8088
8103
  size = "md",
8104
+ variant = "floating",
8089
8105
  className,
8090
8106
  transparent = true
8091
8107
  }) {
@@ -8100,7 +8116,7 @@ function GameHud({
8100
8116
  /* @__PURE__ */ 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__ */ jsx(StatBadge, { ...stat, size }, i)) })
8101
8117
  ] });
8102
8118
  }
8103
- if (position === "top" || position === "bottom") {
8119
+ if ((position === "top" || position === "bottom") && variant === "bar") {
8104
8120
  const mid = Math.ceil(stats.length / 2);
8105
8121
  const leftStats = stats.slice(0, mid);
8106
8122
  const rightStats = stats.slice(mid);
@@ -11588,7 +11604,7 @@ var init_StateJsonView = __esm({
11588
11604
  StateJsonView.displayName = "StateJsonView";
11589
11605
  }
11590
11606
  });
11591
- var FONT_BASE, GAME_FONTS, FONT_FACES, GameShell;
11607
+ var GAME_FONTS, GameShell;
11592
11608
  var init_GameShell = __esm({
11593
11609
  "components/game/templates/GameShell.tsx"() {
11594
11610
  init_cn();
@@ -11596,7 +11612,6 @@ var init_GameShell = __esm({
11596
11612
  init_Card();
11597
11613
  init_Typography();
11598
11614
  init_AtlasImage();
11599
- FONT_BASE = "https://almadar-kflow-assets.web.app/shared/_shared/kenney-fonts/fonts";
11600
11615
  GAME_FONTS = {
11601
11616
  future: "Kenney Future",
11602
11617
  "future-narrow": "Kenney Future Narrow",
@@ -11604,14 +11619,6 @@ var init_GameShell = __esm({
11604
11619
  blocks: "Kenney Blocks",
11605
11620
  mini: "Kenney Mini"
11606
11621
  };
11607
- FONT_FACES = `
11608
- @font-face { font-family: 'Kenney Future'; src: url('${FONT_BASE}/Kenney%20Future.ttf') format('truetype'); font-display: swap; }
11609
- @font-face { font-family: 'Kenney Future Narrow'; src: url('${FONT_BASE}/Kenney%20Future%20Narrow.ttf') format('truetype'); font-display: swap; }
11610
- @font-face { font-family: 'Kenney Pixel'; src: url('${FONT_BASE}/Kenney%20Pixel.ttf') format('truetype'); font-display: swap; }
11611
- @font-face { font-family: 'Kenney Blocks'; src: url('${FONT_BASE}/Kenney%20Blocks.ttf') format('truetype'); font-display: swap; }
11612
- @font-face { font-family: 'Kenney Mini'; src: url('${FONT_BASE}/Kenney%20Mini.ttf') format('truetype'); font-display: swap; }
11613
- .game-shell, .game-shell * { font-family: inherit; }
11614
- `;
11615
11622
  GameShell = ({
11616
11623
  appName = "Game",
11617
11624
  hud,
@@ -11638,10 +11645,12 @@ var init_GameShell = __esm({
11638
11645
  overflow: "hidden",
11639
11646
  background: "var(--color-background, #0a0a0f)",
11640
11647
  color: "var(--color-foreground, #e0e0e0)",
11641
- fontFamily: `'${font}', system-ui, sans-serif`
11648
+ // The fontFamily knob is a scoped override of the theme contract's
11649
+ // display slot: titles/numerics take the game face, body text keeps
11650
+ // the active theme's --font-family-body.
11651
+ "--font-family-display": `'${font}', ui-sans-serif, system-ui, sans-serif`
11642
11652
  },
11643
11653
  children: [
11644
- /* @__PURE__ */ jsx("style", { children: FONT_FACES }),
11645
11654
  backgroundAsset && /* @__PURE__ */ jsx(
11646
11655
  AtlasPanel,
11647
11656
  {
@@ -11679,6 +11688,7 @@ var init_GameShell = __esm({
11679
11688
  Typography,
11680
11689
  {
11681
11690
  as: "span",
11691
+ className: "font-display",
11682
11692
  style: {
11683
11693
  fontWeight: 700,
11684
11694
  fontSize: "1.05rem",
@@ -11759,6 +11769,11 @@ var init_molecules = __esm({
11759
11769
  init_puzzleObject();
11760
11770
  }
11761
11771
  });
11772
+ function themeBodyFont(el) {
11773
+ if (typeof getComputedStyle !== "function") return "system-ui, sans-serif";
11774
+ const v = getComputedStyle(el).getPropertyValue("--font-family-body").trim();
11775
+ return v || "system-ui, sans-serif";
11776
+ }
11762
11777
  function resolveColor2(color, ctx, fallback) {
11763
11778
  if (!color) return fallback;
11764
11779
  if (color.startsWith("var(")) {
@@ -11958,7 +11973,7 @@ function drawShape(ctx, shape, width, height, allShapes) {
11958
11973
  case "text": {
11959
11974
  if (shape.x == null || shape.y == null || !shape.text) break;
11960
11975
  ctx.fillStyle = stroke;
11961
- ctx.font = `${shape.fontSize ?? 14}px system-ui, sans-serif`;
11976
+ ctx.font = `${shape.fontSize ?? 14}px ${themeBodyFont(ctx.canvas)}`;
11962
11977
  ctx.textAlign = shape.align ?? "left";
11963
11978
  ctx.textBaseline = "middle";
11964
11979
  ctx.fillText(shape.text, shape.x, shape.y);
@@ -21692,7 +21707,22 @@ function eventStartDate(event, startField) {
21692
21707
  const raw = getNestedValue(event, startField);
21693
21708
  return new Date(raw ?? "");
21694
21709
  }
21695
- function generateDefaultTimeSlots(events2, startField) {
21710
+ function eventEndDate(event, endField) {
21711
+ const raw = getNestedValue(event, endField);
21712
+ if (raw === void 0 || raw === null || raw === "") return null;
21713
+ const end = new Date(raw);
21714
+ return Number.isNaN(end.getTime()) ? null : end;
21715
+ }
21716
+ function eventContinuesInSlot(event, day, slotTime, startField, endField) {
21717
+ const eventStart = eventStartDate(event, startField);
21718
+ const eventEnd = eventEndDate(event, endField);
21719
+ if (eventEnd === null || Number.isNaN(eventStart.getTime())) return false;
21720
+ const [slotHour] = slotTime.split(":").map(Number);
21721
+ const slotStart = new Date(day);
21722
+ slotStart.setHours(slotHour, 0, 0, 0);
21723
+ return slotStart.getTime() > eventStart.getTime() && slotStart.getTime() < eventEnd.getTime();
21724
+ }
21725
+ function generateDefaultTimeSlots(events2, startField, endField) {
21696
21726
  let first = DEFAULT_FIRST_HOUR;
21697
21727
  let last = DEFAULT_LAST_HOUR;
21698
21728
  for (const ev of events2) {
@@ -21701,6 +21731,11 @@ function generateDefaultTimeSlots(events2, startField) {
21701
21731
  const hour = start.getHours();
21702
21732
  if (hour < first) first = hour;
21703
21733
  if (hour > last) last = hour;
21734
+ const end = eventEndDate(ev, endField);
21735
+ if (end && end.toDateString() === start.toDateString()) {
21736
+ const endHour = end.getMinutes() === 0 && end.getSeconds() === 0 ? end.getHours() - 1 : end.getHours();
21737
+ if (endHour > last) last = endHour;
21738
+ }
21704
21739
  }
21705
21740
  const slots = [];
21706
21741
  for (let hour = first; hour <= last; hour++) {
@@ -21729,6 +21764,7 @@ function CalendarGrid({
21729
21764
  dayWindow = "auto",
21730
21765
  titleField = "title",
21731
21766
  startField = "startTime",
21767
+ endField = "endTime",
21732
21768
  colorField = "color",
21733
21769
  children,
21734
21770
  renderItem
@@ -21746,8 +21782,8 @@ function CalendarGrid({
21746
21782
  [resolvedWeekStart]
21747
21783
  );
21748
21784
  const resolvedTimeSlots = useMemo(
21749
- () => timeSlots ?? generateDefaultTimeSlots(evs, startField),
21750
- [timeSlots, evs, startField]
21785
+ () => timeSlots ?? generateDefaultTimeSlots(evs, startField, endField),
21786
+ [timeSlots, evs, startField, endField]
21751
21787
  );
21752
21788
  const visibleCount = useDayWindow(dayWindow);
21753
21789
  const [dayOffset, setDayOffset] = useState(0);
@@ -21907,12 +21943,15 @@ function CalendarGrid({
21907
21943
  const slotEvents = evs.filter(
21908
21944
  (ev) => eventInSlot(ev, day, time, startField)
21909
21945
  );
21946
+ const continuingEvents = evs.filter(
21947
+ (ev) => eventContinuesInSlot(ev, day, time, startField, endField)
21948
+ );
21910
21949
  const isToday = day.toDateString() === (/* @__PURE__ */ new Date()).toDateString();
21911
21950
  return /* @__PURE__ */ jsx(
21912
21951
  TimeSlotCell,
21913
21952
  {
21914
21953
  time,
21915
- isOccupied: slotEvents.length > 0,
21954
+ isOccupied: slotEvents.length > 0 || continuingEvents.length > 0,
21916
21955
  onClick: () => handleSlotClick(day, time),
21917
21956
  className: cn(
21918
21957
  "border-l border-border",
@@ -21923,7 +21962,25 @@ function CalendarGrid({
21923
21962
  onPointerUp: clearLongPress,
21924
21963
  onPointerCancel: clearLongPress
21925
21964
  } : {},
21926
- children: /* @__PURE__ */ jsx(VStack, { gap: "xs", children: slotEvents.map(renderEvent) })
21965
+ children: /* @__PURE__ */ jsxs(VStack, { gap: "xs", children: [
21966
+ slotEvents.map(renderEvent),
21967
+ continuingEvents.map((event) => {
21968
+ const color = getNestedValue(event, colorField);
21969
+ return /* @__PURE__ */ jsx(
21970
+ Box,
21971
+ {
21972
+ rounded: "sm",
21973
+ border: true,
21974
+ className: cn(
21975
+ "cursor-pointer h-2",
21976
+ color ? cn(color, "opacity-50") : "bg-primary/10 border-primary/20"
21977
+ ),
21978
+ onClick: (e) => handleEventClick(event, e)
21979
+ },
21980
+ `${event.id}-cont`
21981
+ );
21982
+ })
21983
+ ] })
21927
21984
  },
21928
21985
  `${day.toISOString()}-${time}`
21929
21986
  );
@@ -31982,13 +32039,13 @@ var init_MapView = __esm({
31982
32039
  shadowSize: [41, 41]
31983
32040
  });
31984
32041
  L.Marker.prototype.options.icon = defaultIcon;
31985
- const { useEffect: useEffect65, useRef: useRef64, useCallback: useCallback97, useState: useState95 } = React87__default;
32042
+ const { useEffect: useEffect67, useRef: useRef64, useCallback: useCallback97, useState: useState95 } = React87__default;
31986
32043
  const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
31987
32044
  const { useEventBus: useEventBus2 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
31988
32045
  function MapUpdater({ centerLat, centerLng, zoom }) {
31989
32046
  const map = useMap();
31990
32047
  const prevRef = useRef64({ centerLat, centerLng, zoom });
31991
- useEffect65(() => {
32048
+ useEffect67(() => {
31992
32049
  const prev = prevRef.current;
31993
32050
  if (prev.centerLat !== centerLat || prev.centerLng !== centerLng || prev.zoom !== zoom) {
31994
32051
  map.setView([centerLat, centerLng], zoom);
@@ -31999,7 +32056,7 @@ var init_MapView = __esm({
31999
32056
  }
32000
32057
  function MapClickHandler({ onMapClick }) {
32001
32058
  const map = useMap();
32002
- useEffect65(() => {
32059
+ useEffect67(() => {
32003
32060
  if (!onMapClick) return;
32004
32061
  const handler = (e) => {
32005
32062
  onMapClick(e.latlng.lat, e.latlng.lng);
@@ -32528,14 +32585,34 @@ var init_UploadDropZone = __esm({
32528
32585
  if (valid.length > 0) {
32529
32586
  onFiles?.(valid);
32530
32587
  if (action) {
32531
- eventBus.emit(`UI:${action}`, {
32532
- ...actionPayload,
32533
- files: valid.map((f3) => ({ name: f3.name, size: f3.size, type: f3.type }))
32588
+ void Promise.all(
32589
+ valid.map(
32590
+ (f3) => new Promise(
32591
+ (resolvePayload, rejectPayload) => {
32592
+ const reader = new FileReader();
32593
+ reader.onload = () => resolvePayload({
32594
+ name: f3.name,
32595
+ size: f3.size,
32596
+ type: f3.type,
32597
+ content: String(reader.result ?? "")
32598
+ });
32599
+ reader.onerror = () => rejectPayload(reader.error);
32600
+ reader.readAsDataURL(f3);
32601
+ }
32602
+ )
32603
+ )
32604
+ ).then((payloadFiles) => {
32605
+ eventBus.emit(`UI:${action}`, {
32606
+ ...actionPayload,
32607
+ files: payloadFiles
32608
+ });
32609
+ }).catch(() => {
32610
+ setError(t("Could not read the selected file"));
32534
32611
  });
32535
32612
  }
32536
32613
  }
32537
32614
  },
32538
- [validateFiles, onFiles, action, actionPayload, eventBus]
32615
+ [validateFiles, onFiles, action, actionPayload, eventBus, t]
32539
32616
  );
32540
32617
  const handleDragOver = (e) => {
32541
32618
  e.preventDefault();
@@ -42052,22 +42129,6 @@ var init_DataTable = __esm({
42052
42129
  DataTable.displayName = "DataTable";
42053
42130
  }
42054
42131
  });
42055
- function getFieldIcon(fieldName) {
42056
- const name = fieldName.toLowerCase();
42057
- if (name.includes("date") || name.includes("time")) return Calendar;
42058
- if (name.includes("status")) return Tag;
42059
- if (name.includes("priority")) return AlertCircle;
42060
- if (name.includes("progress") || name.includes("percent")) return TrendingUp;
42061
- if (name.includes("assignee") || name.includes("owner") || name.includes("user") || name.includes("member"))
42062
- return User;
42063
- if (name.includes("due")) return Clock;
42064
- if (name.includes("complete")) return CheckCircle2;
42065
- if (name.includes("budget") || name.includes("cost") || name.includes("price"))
42066
- return DollarSign;
42067
- if (name.includes("description") || name.includes("note") || name.includes("comment"))
42068
- return FileText;
42069
- return Package;
42070
- }
42071
42132
  function getBadgeVariant(fieldName, value) {
42072
42133
  const name = fieldName.toLowerCase();
42073
42134
  const val = String(value).toLowerCase();
@@ -42117,6 +42178,18 @@ function renderRichFieldValue(value, fieldName, fieldType, meta) {
42117
42178
  }
42118
42179
  ) });
42119
42180
  }
42181
+ if (fieldType === "url" && /^https?:\/\//i.test(str)) {
42182
+ return /* @__PURE__ */ jsx(
42183
+ "a",
42184
+ {
42185
+ href: str,
42186
+ target: "_blank",
42187
+ rel: "noreferrer",
42188
+ className: "text-primary hover:underline break-all",
42189
+ children: str
42190
+ }
42191
+ );
42192
+ }
42120
42193
  return str;
42121
42194
  }
42122
42195
  case "markdown":
@@ -42215,6 +42288,23 @@ function renderRichFieldValue(value, fieldName, fieldType, meta) {
42215
42288
  }
42216
42289
  return str;
42217
42290
  }
42291
+ case "boolean": {
42292
+ if (typeof value === "boolean") return value ? "Yes" : "No";
42293
+ if (str === "true") return "Yes";
42294
+ if (str === "false") return "No";
42295
+ return str;
42296
+ }
42297
+ case "array": {
42298
+ if (Array.isArray(value) && value.length > 0) {
42299
+ return /* @__PURE__ */ jsx(HStack, { gap: "xs", wrap: true, children: value.map((item, i) => /* @__PURE__ */ jsx(Badge, { variant: "default", children: String(item) }, i)) });
42300
+ }
42301
+ if (Array.isArray(value)) return "\u2014";
42302
+ return str;
42303
+ }
42304
+ case "email":
42305
+ return /* @__PURE__ */ jsx("a", { href: `mailto:${str}`, className: "text-primary hover:underline break-all", children: str });
42306
+ case "phone":
42307
+ return /* @__PURE__ */ jsx("a", { href: `tel:${str}`, className: "text-primary hover:underline", children: str });
42218
42308
  default:
42219
42309
  if (meta?.values && meta.values.length > 0 && meta.values.includes(str)) {
42220
42310
  return /* @__PURE__ */ jsx(Badge, { variant: getBadgeVariant(fieldName, str), children: humanizeEnumValue(str) });
@@ -42287,10 +42377,11 @@ var init_DetailPanel = __esm({
42287
42377
  avatar,
42288
42378
  sections: propSections,
42289
42379
  actions,
42290
- maxInlineActions,
42380
+ maxInlineActions = 2,
42291
42381
  backAction,
42292
42382
  footer,
42293
42383
  slideOver = false,
42384
+ showActions = true,
42294
42385
  className,
42295
42386
  entity,
42296
42387
  fields: propFields,
@@ -42338,9 +42429,6 @@ var init_DetailPanel = __esm({
42338
42429
  },
42339
42430
  [eventBus]
42340
42431
  );
42341
- const handleClose = useCallback(() => {
42342
- eventBus.emit("UI:CLOSE", {});
42343
- }, [eventBus]);
42344
42432
  const entityRecord = Array.isArray(entity) ? entity[0] : entity;
42345
42433
  const data = entityRecord ?? initialData;
42346
42434
  let title = propTitle;
@@ -42354,8 +42442,7 @@ var init_DetailPanel = __esm({
42354
42442
  const value = getNestedValue(normalizedData, field);
42355
42443
  return {
42356
42444
  label: labelFor(field),
42357
- value: formatFieldValue2(value, field),
42358
- icon: getFieldIcon(field)
42445
+ value: formatFieldValue2(value, field)
42359
42446
  };
42360
42447
  }
42361
42448
  return field;
@@ -42389,15 +42476,14 @@ var init_DetailPanel = __esm({
42389
42476
  (f3) => (!titleDerivedFromPrimary || f3 !== primaryField) && !statusFields.includes(f3) && !progressFields.includes(f3) && !metricFields.includes(f3) && !dateFields.includes(f3) && !descriptionFields.includes(f3)
42390
42477
  );
42391
42478
  sections = [];
42392
- if (statusFields.length > 0 || otherFields.length > 0) {
42479
+ if (otherFields.length > 0) {
42393
42480
  const overviewFields = [];
42394
- [...statusFields, ...otherFields].forEach((field) => {
42481
+ otherFields.forEach((field) => {
42395
42482
  const value = getNestedValue(normalizedData, field);
42396
42483
  if (value !== void 0 && value !== null) {
42397
42484
  overviewFields.push({
42398
42485
  label: labelFor(field),
42399
- value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field)),
42400
- icon: getFieldIcon(field)
42486
+ value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field))
42401
42487
  });
42402
42488
  }
42403
42489
  });
@@ -42412,8 +42498,7 @@ var init_DetailPanel = __esm({
42412
42498
  if (value !== void 0 && value !== null) {
42413
42499
  metricsFields.push({
42414
42500
  label: labelFor(field),
42415
- value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field)),
42416
- icon: getFieldIcon(field)
42501
+ value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field))
42417
42502
  });
42418
42503
  }
42419
42504
  });
@@ -42428,8 +42513,7 @@ var init_DetailPanel = __esm({
42428
42513
  if (value !== void 0 && value !== null) {
42429
42514
  timelineFields.push({
42430
42515
  label: labelFor(field),
42431
- value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field)),
42432
- icon: getFieldIcon(field)
42516
+ value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field))
42433
42517
  });
42434
42518
  }
42435
42519
  });
@@ -42444,8 +42528,7 @@ var init_DetailPanel = __esm({
42444
42528
  if (value !== void 0 && value !== null) {
42445
42529
  descFields.push({
42446
42530
  label: labelFor(field),
42447
- value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field)),
42448
- icon: getFieldIcon(field)
42531
+ value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field))
42449
42532
  });
42450
42533
  }
42451
42534
  });
@@ -42454,6 +42537,15 @@ var init_DetailPanel = __esm({
42454
42537
  }
42455
42538
  }
42456
42539
  }
42540
+ const renderSlot = useRenderSlot();
42541
+ const navStack = useNavStack();
42542
+ const { setCurrentLabel } = navStack;
42543
+ const resolvedTitle = normalizedData ? title : void 0;
42544
+ useEffect(() => {
42545
+ if (renderSlot === "main" && !slideOver && resolvedTitle) {
42546
+ setCurrentLabel(resolvedTitle);
42547
+ }
42548
+ }, [renderSlot, slideOver, resolvedTitle, setCurrentLabel]);
42457
42549
  if (isLoading) {
42458
42550
  return /* @__PURE__ */ jsx(
42459
42551
  LoadingState,
@@ -42492,8 +42584,7 @@ var init_DetailPanel = __esm({
42492
42584
  const value = normalizedData ? getNestedValue(normalizedData, field) : void 0;
42493
42585
  allFields.push({
42494
42586
  label: labelFor(field),
42495
- value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field)),
42496
- icon: getFieldIcon(field)
42587
+ value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field))
42497
42588
  });
42498
42589
  } else {
42499
42590
  allFields.push(field);
@@ -42505,24 +42596,50 @@ var init_DetailPanel = __esm({
42505
42596
  (a) => a.event === "CLOSE" || a.event === "CANCEL" || a.label?.toLowerCase() === "close"
42506
42597
  );
42507
42598
  const otherActions = actions?.filter((a) => a !== closeAction) ?? [];
42508
- const effectiveCloseAction = closeAction ?? { event: void 0};
42509
- const content = /* @__PURE__ */ jsx(Card, { variant: "elevated", children: /* @__PURE__ */ jsxs(VStack, { gap: "md", className: "p-6", children: [
42510
- /* @__PURE__ */ jsxs(HStack, { justify: "between", align: "center", gap: "xs", children: [
42511
- /* @__PURE__ */ jsx(HStack, { align: "center", gap: "xs", children: backAction && /* @__PURE__ */ jsx(
42512
- Button,
42599
+ const statusBadges = /* @__PURE__ */ jsxs(Fragment, { children: [
42600
+ normalizedData && effectiveFieldNames && effectiveFieldNames.filter(
42601
+ (f3) => f3.toLowerCase().includes("status") || f3.toLowerCase().includes("priority")
42602
+ ).map((field) => {
42603
+ const value = getNestedValue(normalizedData, field);
42604
+ if (!value) return null;
42605
+ return /* @__PURE__ */ jsx(
42606
+ Badge,
42513
42607
  {
42514
- variant: backAction.variant || "ghost",
42515
- size: "sm",
42516
- action: backAction.navigatesTo ? void 0 : backAction.event,
42517
- actionPayload: { row: normalizedData },
42518
- onClick: backAction.navigatesTo ? () => handleActionClick(backAction, normalizedData) : void 0,
42519
- icon: backAction.icon ?? ArrowLeft,
42520
- "data-testid": backAction.event ? `action-${backAction.event}` : "action-back",
42521
- children: backAction.label
42522
- }
42523
- ) }),
42524
- /* @__PURE__ */ jsxs(HStack, { justify: "end", align: "center", gap: "xs", children: [
42525
- (maxInlineActions != null ? otherActions.slice(0, maxInlineActions) : otherActions).map((action, idx) => /* @__PURE__ */ jsx(
42608
+ variant: getBadgeVariant(field, String(value)),
42609
+ children: humanizeEnumValue(String(value))
42610
+ },
42611
+ field
42612
+ );
42613
+ }),
42614
+ status && /* @__PURE__ */ jsx(Badge, { variant: status.variant ?? "default", children: status.label })
42615
+ ] });
42616
+ const content = /* @__PURE__ */ jsx(Card, { variant: "elevated", children: /* @__PURE__ */ jsxs(VStack, { gap: "md", className: "p-6", children: [
42617
+ /* @__PURE__ */ jsxs(HStack, { justify: "between", align: "start", gap: "md", children: [
42618
+ /* @__PURE__ */ jsxs(HStack, { align: "start", gap: "sm", className: "min-w-0", children: [
42619
+ backAction && /* @__PURE__ */ jsx(
42620
+ Button,
42621
+ {
42622
+ variant: backAction.variant || "ghost",
42623
+ size: "sm",
42624
+ action: backAction.navigatesTo ? void 0 : backAction.event,
42625
+ actionPayload: { row: normalizedData },
42626
+ onClick: backAction.navigatesTo ? () => handleActionClick(backAction, normalizedData) : void 0,
42627
+ icon: backAction.icon ?? ArrowLeft,
42628
+ "data-testid": backAction.event ? `action-${backAction.event}` : "action-back",
42629
+ children: backAction.label
42630
+ }
42631
+ ),
42632
+ avatar,
42633
+ /* @__PURE__ */ jsxs(VStack, { gap: "xs", className: "min-w-0", children: [
42634
+ /* @__PURE__ */ jsxs(HStack, { align: "center", gap: "sm", wrap: true, children: [
42635
+ /* @__PURE__ */ jsx(Typography, { variant: "h2", weight: "bold", children: title || "Details" }),
42636
+ statusBadges
42637
+ ] }),
42638
+ subtitle && /* @__PURE__ */ jsx(Typography, { variant: "body", color: "secondary", children: subtitle })
42639
+ ] })
42640
+ ] }),
42641
+ showActions && /* @__PURE__ */ jsxs(HStack, { justify: "end", align: "center", gap: "xs", className: "shrink-0", children: [
42642
+ otherActions.slice(0, maxInlineActions).map((action, idx) => /* @__PURE__ */ jsx(
42526
42643
  Button,
42527
42644
  {
42528
42645
  variant: action.variant || "secondary",
@@ -42537,7 +42654,7 @@ var init_DetailPanel = __esm({
42537
42654
  },
42538
42655
  idx
42539
42656
  )),
42540
- maxInlineActions != null && otherActions.length > maxInlineActions && /* @__PURE__ */ jsx(
42657
+ otherActions.length > maxInlineActions && /* @__PURE__ */ jsx(
42541
42658
  Menu,
42542
42659
  {
42543
42660
  position: "bottom-end",
@@ -42553,40 +42670,20 @@ var init_DetailPanel = __esm({
42553
42670
  }))
42554
42671
  }
42555
42672
  ),
42556
- /* @__PURE__ */ jsx(
42673
+ closeAction && /* @__PURE__ */ jsx(
42557
42674
  Button,
42558
42675
  {
42559
42676
  variant: "ghost",
42560
42677
  size: "sm",
42561
- action: effectiveCloseAction.event,
42678
+ action: closeAction.event,
42562
42679
  actionPayload: { row: normalizedData },
42563
- onClick: effectiveCloseAction.event ? void 0 : handleClose,
42680
+ onClick: closeAction.event ? void 0 : () => handleActionClick(closeAction, normalizedData),
42564
42681
  icon: X,
42565
- "data-testid": effectiveCloseAction.event ? `action-${effectiveCloseAction.event}` : "action-close"
42682
+ "data-testid": closeAction.event ? `action-${closeAction.event}` : "action-close"
42566
42683
  }
42567
42684
  )
42568
42685
  ] })
42569
42686
  ] }),
42570
- avatar,
42571
- /* @__PURE__ */ jsx(Typography, { variant: "h2", weight: "bold", children: title || "Details" }),
42572
- subtitle && /* @__PURE__ */ jsx(Typography, { variant: "body", color: "secondary", children: subtitle }),
42573
- /* @__PURE__ */ jsxs(HStack, { gap: "xs", wrap: true, children: [
42574
- normalizedData && effectiveFieldNames && effectiveFieldNames.filter(
42575
- (f3) => f3.toLowerCase().includes("status") || f3.toLowerCase().includes("priority")
42576
- ).map((field) => {
42577
- const value = getNestedValue(normalizedData, field);
42578
- if (!value) return null;
42579
- return /* @__PURE__ */ jsx(
42580
- Badge,
42581
- {
42582
- variant: getBadgeVariant(field, String(value)),
42583
- children: String(value)
42584
- },
42585
- field
42586
- );
42587
- }),
42588
- status && /* @__PURE__ */ jsx(Badge, { variant: status.variant ?? "default", children: status.label })
42589
- ] }),
42590
42687
  normalizedData && effectiveFieldNames && effectiveFieldNames.filter(
42591
42688
  (f3) => f3.toLowerCase().includes("progress") || f3.toLowerCase().includes("percent")
42592
42689
  ).map((field) => {
@@ -42619,9 +42716,10 @@ var init_DetailPanel = __esm({
42619
42716
  /* @__PURE__ */ jsx(
42620
42717
  Typography,
42621
42718
  {
42622
- variant: "small",
42623
- color: "secondary",
42719
+ variant: "caption",
42720
+ color: "muted",
42624
42721
  weight: "medium",
42722
+ className: "uppercase tracking-wider",
42625
42723
  children: field.label
42626
42724
  }
42627
42725
  ),
@@ -42690,7 +42788,7 @@ var init_DrawerSlot = __esm({
42690
42788
  position,
42691
42789
  width: size,
42692
42790
  className,
42693
- children
42791
+ children: /* @__PURE__ */ jsx(RenderSlotProvider, { slot: "drawer", children })
42694
42792
  }
42695
42793
  );
42696
42794
  };
@@ -44737,7 +44835,7 @@ var init_ModalSlot = __esm({
44737
44835
  title,
44738
44836
  size,
44739
44837
  className,
44740
- children
44838
+ children: /* @__PURE__ */ jsx(RenderSlotProvider, { slot: "modal", children })
44741
44839
  }
44742
44840
  );
44743
44841
  };
@@ -49140,7 +49238,10 @@ function MaybeTraitScope({
49140
49238
  }
49141
49239
  return /* @__PURE__ */ jsx(Fragment, { children });
49142
49240
  }
49143
- function UISlotComponent({
49241
+ function UISlotComponent(props) {
49242
+ return /* @__PURE__ */ jsx(RenderSlotProvider, { slot: props.slot, children: /* @__PURE__ */ jsx(UISlotComponentInner, { ...props }) });
49243
+ }
49244
+ function UISlotComponentInner({
49144
49245
  slot,
49145
49246
  portal = false,
49146
49247
  position,
@@ -50709,6 +50810,12 @@ var CurrentPagePathProvider = ({
50709
50810
  }) => /* @__PURE__ */ jsx(CurrentPagePathContext.Provider, { value, children });
50710
50811
  CurrentPagePathProvider.displayName = "CurrentPagePathProvider";
50711
50812
  var useCurrentPagePath2 = () => useContext(CurrentPagePathContext);
50813
+ var RenderSlotContext = createContext("main");
50814
+ var RenderSlotProvider4 = ({ slot, children }) => /* @__PURE__ */ jsx(RenderSlotContext.Provider, { value: slot, children });
50815
+ RenderSlotProvider4.displayName = "RenderSlotProvider";
50816
+ function useRenderSlot2() {
50817
+ return useContext(RenderSlotContext);
50818
+ }
50712
50819
  var EntitySchemaContext = createContext(null);
50713
50820
  function EntitySchemaProvider({
50714
50821
  entities,
@@ -51499,6 +51606,18 @@ function syncNavStack(state, pages, path, pending) {
51499
51606
  }
51500
51607
  return { state: { ...state, [page.orbital]: stack }, orbital: page.orbital };
51501
51608
  }
51609
+ function relabelCurrent(state, pages, path, label) {
51610
+ const normalized = normalizePath(path);
51611
+ const page = matchNavPage(pages, normalized);
51612
+ if (!page || !label) return state;
51613
+ const stack = state[page.orbital];
51614
+ if (!stack || stack.length === 0) return state;
51615
+ const idx = stack.findIndex((e) => e.href === normalized);
51616
+ if (idx < 0 || stack[idx].label === label) return state;
51617
+ const next = [...stack];
51618
+ next[idx] = { href: normalized, label };
51619
+ return { ...state, [page.orbital]: next };
51620
+ }
51502
51621
  function previousEntry(state, pages, path) {
51503
51622
  const page = matchNavPage(pages, normalizePath(path));
51504
51623
  if (!page) return null;
@@ -51515,10 +51634,11 @@ var INERT_API = {
51515
51634
  canGoBack: false,
51516
51635
  beginNavigate: () => void 0,
51517
51636
  back: () => void 0,
51518
- goTo: () => void 0
51637
+ goTo: () => void 0,
51638
+ setCurrentLabel: () => void 0
51519
51639
  };
51520
51640
  var NavStackContext = createContext(INERT_API);
51521
- function useNavStack2() {
51641
+ function useNavStack3() {
51522
51642
  return useContext(NavStackContext);
51523
51643
  }
51524
51644
  function loadStored(storageKey) {
@@ -51575,6 +51695,16 @@ var NavStackProvider = ({
51575
51695
  },
51576
51696
  [navigate]
51577
51697
  );
51698
+ const setCurrentLabel = useCallback(
51699
+ (label) => {
51700
+ setState((prev) => {
51701
+ const next = relabelCurrent(prev, pages, currentPath, label);
51702
+ if (next !== prev) persistStored(storageKey, next);
51703
+ return next;
51704
+ });
51705
+ },
51706
+ [pages, currentPath, storageKey]
51707
+ );
51578
51708
  const entries = useMemo(
51579
51709
  () => entriesFor(state, pages, currentPath),
51580
51710
  [state, pages, currentPath]
@@ -51585,9 +51715,10 @@ var NavStackProvider = ({
51585
51715
  canGoBack: previousEntry(state, pages, currentPath) !== null,
51586
51716
  beginNavigate,
51587
51717
  back,
51588
- goTo
51718
+ goTo,
51719
+ setCurrentLabel
51589
51720
  }),
51590
- [entries, state, pages, currentPath, beginNavigate, back, goTo]
51721
+ [entries, state, pages, currentPath, beginNavigate, back, goTo, setCurrentLabel]
51591
51722
  );
51592
51723
  return /* @__PURE__ */ jsx(NavStackContext.Provider, { value: api, children });
51593
51724
  };
@@ -51618,4 +51749,4 @@ var NavStackRouterBridge = ({
51618
51749
  };
51619
51750
  NavStackRouterBridge.displayName = "NavStackRouterBridge";
51620
51751
 
51621
- export { CurrentPagePathContext, CurrentPagePathProvider, EntityBindingContext, EntitySchemaProvider, EventBusContext2 as EventBusContext, EventBusProvider, GameAudioContext2 as GameAudioContext, GameAudioProvider2 as GameAudioProvider, NavStackProvider, NavStackRouterBridge, NavigationProvider2 as NavigationProvider, OfflineModeProvider, OrbitalProvider, OrbitalThemeProvider, SelectionContext, SelectionProvider, ServerBridgeProvider, TraitContext, TraitProvider, TraitScopeProvider3 as TraitScopeProvider, UserContext, UserProvider, VerificationProvider, comparePathSpecificity2 as comparePathSpecificity, extractRouteParams2 as extractRouteParams, findPageByName2 as findPageByName, findPageByPath2 as findPageByPath, getAllPages2 as getAllPages, getDefaultPage2 as getDefaultPage, matchPath2 as matchPath, matchPathAmong2 as matchPathAmong, pathMatches2 as pathMatches, useActivePage2 as useActivePage, useCurrentPagePath2 as useCurrentPagePath, useEntityBindingSnapshot2 as useEntityBindingSnapshot, useEntitySchema, useEntitySchemaOptional6 as useEntitySchemaOptional, useGameAudioContext2 as useGameAudioContext, useGameAudioContextOptional2 as useGameAudioContextOptional, useHasPermission, useHasRole, useInitPayload2 as useInitPayload, useNavStack2 as useNavStack, useNavigateTo2 as useNavigateTo, useNavigation2 as useNavigation, useNavigationId2 as useNavigationId, useNavigationState2 as useNavigationState, useOfflineMode, useOptionalOfflineMode, useSelection, useSelectionOptional, useServerBridge, useTrait, useTraitContext, useTraitScope2 as useTraitScope, useTraitScopeChain2 as useTraitScopeChain, useUser, useUserForEvaluation };
51752
+ export { CurrentPagePathContext, CurrentPagePathProvider, EntityBindingContext, EntitySchemaProvider, EventBusContext2 as EventBusContext, EventBusProvider, GameAudioContext2 as GameAudioContext, GameAudioProvider2 as GameAudioProvider, NavStackProvider, NavStackRouterBridge, NavigationProvider2 as NavigationProvider, OfflineModeProvider, OrbitalProvider, OrbitalThemeProvider, RenderSlotProvider4 as RenderSlotProvider, SelectionContext, SelectionProvider, ServerBridgeProvider, TraitContext, TraitProvider, TraitScopeProvider3 as TraitScopeProvider, UserContext, UserProvider, VerificationProvider, comparePathSpecificity2 as comparePathSpecificity, extractRouteParams2 as extractRouteParams, findPageByName2 as findPageByName, findPageByPath2 as findPageByPath, getAllPages2 as getAllPages, getDefaultPage2 as getDefaultPage, matchPath2 as matchPath, matchPathAmong2 as matchPathAmong, pathMatches2 as pathMatches, useActivePage2 as useActivePage, useCurrentPagePath2 as useCurrentPagePath, useEntityBindingSnapshot2 as useEntityBindingSnapshot, useEntitySchema, useEntitySchemaOptional6 as useEntitySchemaOptional, useGameAudioContext2 as useGameAudioContext, useGameAudioContextOptional2 as useGameAudioContextOptional, useHasPermission, useHasRole, useInitPayload2 as useInitPayload, useNavStack3 as useNavStack, useNavigateTo2 as useNavigateTo, useNavigation2 as useNavigation, useNavigationId2 as useNavigationId, useNavigationState2 as useNavigationState, useOfflineMode, useOptionalOfflineMode, useRenderSlot2 as useRenderSlot, useSelection, useSelectionOptional, useServerBridge, useTrait, useTraitContext, useTraitScope2 as useTraitScope, useTraitScopeChain2 as useTraitScopeChain, useUser, useUserForEvaluation };