@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.
@@ -3,11 +3,11 @@ import * as React77 from 'react';
3
3
  import React77__default, { createContext, useContext, useMemo, useRef, useEffect, useCallback, useState, useLayoutEffect, useId, Suspense, lazy, useSyncExternalStore } from 'react';
4
4
  import { clsx } from 'clsx';
5
5
  import { twMerge } from 'tailwind-merge';
6
- import { EventBusContext, useTraitScopeChain, useCurrentPagePath, useGameAudioContextOptional, useEntitySchemaOptional, useEntityBindingSnapshot, useTraitScope, TraitScopeProvider, useNavStack } from '@almadar/ui/providers';
6
+ import { EventBusContext, useTraitScopeChain, useCurrentPagePath, useGameAudioContextOptional, useRenderSlot, useNavStack, useEntitySchemaOptional, RenderSlotProvider, useEntityBindingSnapshot, useTraitScope, TraitScopeProvider } from '@almadar/ui/providers';
7
7
  export { GameAudioContext, GameAudioProvider, useGameAudioContext, useNavStack } from '@almadar/ui/providers';
8
8
  import { createLogger, isLogLevelEnabled } from '@almadar/logger';
9
9
  import * as LucideIcons2 from 'lucide-react';
10
- import { X, List, Printer, ChevronRight, ChevronLeft, Check, Copy, RotateCcw, Play, Terminal, CheckCircle, XCircle, ChevronDown, Search, ChevronUp, MoreHorizontal, ArrowLeft, DollarSign, Package, Calendar, Pencil, Eye, Image as Image$1, Upload, ZoomIn, TrendingUp, TrendingDown, Minus, AlertCircle, Circle, Clock, CheckCircle2, Loader2, Code, FileText, WrapText, HelpCircle, Type, Heading1, Heading2, Heading3, ListOrdered, Quote, GitBranch, Plus, ArrowRight, Trash, Menu as Menu$1, AlertTriangle, Trash2, Eraser, ZoomOut, Download, Lightbulb, PauseCircle, Link2, Tag, User } from 'lucide-react';
10
+ import { X, List, Printer, ChevronRight, ChevronLeft, Check, Copy, RotateCcw, Play, Terminal, CheckCircle, XCircle, ChevronDown, Search, ChevronUp, MoreHorizontal, ArrowLeft, DollarSign, Package, Calendar, Pencil, Eye, Image as Image$1, Upload, ZoomIn, TrendingUp, TrendingDown, Minus, AlertCircle, Circle, Clock, CheckCircle2, Loader2, Code, FileText, WrapText, HelpCircle, Type, Heading1, Heading2, Heading3, ListOrdered, Quote, GitBranch, Plus, ArrowRight, Trash, Menu as Menu$1, AlertTriangle, Trash2, Eraser, ZoomOut, Download, Lightbulb, PauseCircle, Link2 } from 'lucide-react';
11
11
  import { useTranslate } from '@almadar/ui/hooks';
12
12
  import { useUISlots, useTheme } from '@almadar/ui/context';
13
13
  import { evaluate, createMinimalContext } from '@almadar/evaluator';
@@ -8421,6 +8421,11 @@ var init_ComponentPatterns = __esm({
8421
8421
  AlertPattern.displayName = "AlertPattern";
8422
8422
  }
8423
8423
  });
8424
+ function themeBodyFont(el) {
8425
+ if (typeof getComputedStyle !== "function") return "system-ui, sans-serif";
8426
+ const v = getComputedStyle(el).getPropertyValue("--font-family-body").trim();
8427
+ return v || "system-ui, sans-serif";
8428
+ }
8424
8429
  function resolveColor2(color, ctx, fallback) {
8425
8430
  if (!color) return fallback;
8426
8431
  if (color.startsWith("var(")) {
@@ -8620,7 +8625,7 @@ function drawShape(ctx, shape, width, height, allShapes) {
8620
8625
  case "text": {
8621
8626
  if (shape.x == null || shape.y == null || !shape.text) break;
8622
8627
  ctx.fillStyle = stroke;
8623
- ctx.font = `${shape.fontSize ?? 14}px system-ui, sans-serif`;
8628
+ ctx.font = `${shape.fontSize ?? 14}px ${themeBodyFont(ctx.canvas)}`;
8624
8629
  ctx.textAlign = shape.align ?? "left";
8625
8630
  ctx.textBaseline = "middle";
8626
8631
  ctx.fillText(shape.text, shape.x, shape.y);
@@ -16492,7 +16497,22 @@ function eventStartDate(event, startField) {
16492
16497
  const raw = getNestedValue(event, startField);
16493
16498
  return new Date(raw ?? "");
16494
16499
  }
16495
- function generateDefaultTimeSlots(events2, startField) {
16500
+ function eventEndDate(event, endField) {
16501
+ const raw = getNestedValue(event, endField);
16502
+ if (raw === void 0 || raw === null || raw === "") return null;
16503
+ const end = new Date(raw);
16504
+ return Number.isNaN(end.getTime()) ? null : end;
16505
+ }
16506
+ function eventContinuesInSlot(event, day, slotTime, startField, endField) {
16507
+ const eventStart = eventStartDate(event, startField);
16508
+ const eventEnd = eventEndDate(event, endField);
16509
+ if (eventEnd === null || Number.isNaN(eventStart.getTime())) return false;
16510
+ const [slotHour] = slotTime.split(":").map(Number);
16511
+ const slotStart = new Date(day);
16512
+ slotStart.setHours(slotHour, 0, 0, 0);
16513
+ return slotStart.getTime() > eventStart.getTime() && slotStart.getTime() < eventEnd.getTime();
16514
+ }
16515
+ function generateDefaultTimeSlots(events2, startField, endField) {
16496
16516
  let first = DEFAULT_FIRST_HOUR;
16497
16517
  let last = DEFAULT_LAST_HOUR;
16498
16518
  for (const ev of events2) {
@@ -16501,6 +16521,11 @@ function generateDefaultTimeSlots(events2, startField) {
16501
16521
  const hour = start.getHours();
16502
16522
  if (hour < first) first = hour;
16503
16523
  if (hour > last) last = hour;
16524
+ const end = eventEndDate(ev, endField);
16525
+ if (end && end.toDateString() === start.toDateString()) {
16526
+ const endHour = end.getMinutes() === 0 && end.getSeconds() === 0 ? end.getHours() - 1 : end.getHours();
16527
+ if (endHour > last) last = endHour;
16528
+ }
16504
16529
  }
16505
16530
  const slots = [];
16506
16531
  for (let hour = first; hour <= last; hour++) {
@@ -16529,6 +16554,7 @@ function CalendarGrid({
16529
16554
  dayWindow = "auto",
16530
16555
  titleField = "title",
16531
16556
  startField = "startTime",
16557
+ endField = "endTime",
16532
16558
  colorField = "color",
16533
16559
  children,
16534
16560
  renderItem
@@ -16546,8 +16572,8 @@ function CalendarGrid({
16546
16572
  [resolvedWeekStart]
16547
16573
  );
16548
16574
  const resolvedTimeSlots = useMemo(
16549
- () => timeSlots ?? generateDefaultTimeSlots(evs, startField),
16550
- [timeSlots, evs, startField]
16575
+ () => timeSlots ?? generateDefaultTimeSlots(evs, startField, endField),
16576
+ [timeSlots, evs, startField, endField]
16551
16577
  );
16552
16578
  const visibleCount = useDayWindow(dayWindow);
16553
16579
  const [dayOffset, setDayOffset] = useState(0);
@@ -16707,12 +16733,15 @@ function CalendarGrid({
16707
16733
  const slotEvents = evs.filter(
16708
16734
  (ev) => eventInSlot(ev, day, time, startField)
16709
16735
  );
16736
+ const continuingEvents = evs.filter(
16737
+ (ev) => eventContinuesInSlot(ev, day, time, startField, endField)
16738
+ );
16710
16739
  const isToday = day.toDateString() === (/* @__PURE__ */ new Date()).toDateString();
16711
16740
  return /* @__PURE__ */ jsx(
16712
16741
  TimeSlotCell,
16713
16742
  {
16714
16743
  time,
16715
- isOccupied: slotEvents.length > 0,
16744
+ isOccupied: slotEvents.length > 0 || continuingEvents.length > 0,
16716
16745
  onClick: () => handleSlotClick(day, time),
16717
16746
  className: cn(
16718
16747
  "border-l border-border",
@@ -16723,7 +16752,25 @@ function CalendarGrid({
16723
16752
  onPointerUp: clearLongPress,
16724
16753
  onPointerCancel: clearLongPress
16725
16754
  } : {},
16726
- children: /* @__PURE__ */ jsx(VStack, { gap: "xs", children: slotEvents.map(renderEvent) })
16755
+ children: /* @__PURE__ */ jsxs(VStack, { gap: "xs", children: [
16756
+ slotEvents.map(renderEvent),
16757
+ continuingEvents.map((event) => {
16758
+ const color = getNestedValue(event, colorField);
16759
+ return /* @__PURE__ */ jsx(
16760
+ Box,
16761
+ {
16762
+ rounded: "sm",
16763
+ border: true,
16764
+ className: cn(
16765
+ "cursor-pointer h-2",
16766
+ color ? cn(color, "opacity-50") : "bg-primary/10 border-primary/20"
16767
+ ),
16768
+ onClick: (e) => handleEventClick(event, e)
16769
+ },
16770
+ `${event.id}-cont`
16771
+ );
16772
+ })
16773
+ ] })
16727
16774
  },
16728
16775
  `${day.toISOString()}-${time}`
16729
16776
  );
@@ -21930,10 +21977,21 @@ function ControlGrid({
21930
21977
  directionAssets,
21931
21978
  size = "md",
21932
21979
  disabled,
21980
+ visibility = "auto",
21933
21981
  className
21934
21982
  }) {
21935
21983
  const eventBus = useEventBus();
21936
21984
  const [active, setActive] = React77.useState(/* @__PURE__ */ new Set());
21985
+ const [coarse, setCoarse] = React77.useState(
21986
+ () => typeof window !== "undefined" && window.matchMedia("(pointer: coarse)").matches
21987
+ );
21988
+ React77.useEffect(() => {
21989
+ if (visibility !== "auto" || typeof window === "undefined") return;
21990
+ const mq = window.matchMedia("(pointer: coarse)");
21991
+ const onChange = (e) => setCoarse(e.matches);
21992
+ mq.addEventListener("change", onChange);
21993
+ return () => mq.removeEventListener("change", onChange);
21994
+ }, [visibility]);
21937
21995
  const handlePress = React77.useCallback(
21938
21996
  (id) => {
21939
21997
  setActive((prev) => new Set(prev).add(id));
@@ -21966,6 +22024,7 @@ function ControlGrid({
21966
22024
  },
21967
22025
  [kind, actionEvent, directionEvent, directionReleaseEvents, eventBus, onAction, onDirection]
21968
22026
  );
22027
+ if (visibility === "auto" && !coarse) return null;
21969
22028
  if (kind === "dpad") {
21970
22029
  const ds = dpadSizeMap[size];
21971
22030
  const dir = (d) => /* @__PURE__ */ jsx(
@@ -29481,7 +29540,7 @@ function StatBadge({
29481
29540
  assetUrl,
29482
29541
  iconUrl,
29483
29542
  label,
29484
- value = 0,
29543
+ value,
29485
29544
  max,
29486
29545
  format = "number",
29487
29546
  icon,
@@ -29492,6 +29551,7 @@ function StatBadge({
29492
29551
  source: _source,
29493
29552
  field: _field
29494
29553
  }) {
29554
+ const hasValue = value !== void 0 && value !== null;
29495
29555
  const numValue = typeof value === "number" ? value : parseInt(String(value), 10) || 0;
29496
29556
  const resolvedAsset = iconUrl ?? assetUrl;
29497
29557
  return /* @__PURE__ */ jsxs(
@@ -29505,8 +29565,8 @@ function StatBadge({
29505
29565
  ),
29506
29566
  children: [
29507
29567
  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,
29508
- /* @__PURE__ */ jsx(Typography, { as: "span", className: "text-muted-foreground font-medium", children: label }),
29509
- format === "hearts" && max && /* @__PURE__ */ jsx(
29568
+ /* @__PURE__ */ jsx(Typography, { as: "span", className: "text-muted-foreground font-medium text-xs", children: label }),
29569
+ hasValue && format === "hearts" && max && /* @__PURE__ */ jsx(
29510
29570
  HealthBar,
29511
29571
  {
29512
29572
  current: numValue,
@@ -29515,7 +29575,7 @@ function StatBadge({
29515
29575
  size: size === "lg" ? "md" : "sm"
29516
29576
  }
29517
29577
  ),
29518
- format === "bar" && max && /* @__PURE__ */ jsx(
29578
+ hasValue && format === "bar" && max && /* @__PURE__ */ jsx(
29519
29579
  HealthBar,
29520
29580
  {
29521
29581
  current: numValue,
@@ -29524,14 +29584,15 @@ function StatBadge({
29524
29584
  size: size === "lg" ? "md" : "sm"
29525
29585
  }
29526
29586
  ),
29527
- format === "number" && /* @__PURE__ */ jsx(
29587
+ hasValue && format === "number" && /* @__PURE__ */ jsx(
29528
29588
  ScoreDisplay,
29529
29589
  {
29530
29590
  value: numValue,
29531
- size: size === "lg" ? "md" : "sm"
29591
+ size: size === "lg" ? "md" : "sm",
29592
+ className: "font-display"
29532
29593
  }
29533
29594
  ),
29534
- format === "text" && /* @__PURE__ */ jsx(Typography, { as: "span", className: "font-bold text-foreground", children: value })
29595
+ hasValue && format === "text" && /* @__PURE__ */ jsx(Typography, { as: "span", className: "font-bold text-foreground", children: value })
29535
29596
  ]
29536
29597
  }
29537
29598
  );
@@ -29547,6 +29608,7 @@ var init_StatBadge = __esm({
29547
29608
  init_HealthBar();
29548
29609
  init_ScoreDisplay();
29549
29610
  sizeMap7 = {
29611
+ xs: "text-xs px-1.5 py-0.5",
29550
29612
  sm: "text-xs px-2 py-1",
29551
29613
  md: "text-sm px-3 py-1.5",
29552
29614
  lg: "text-base px-4 py-2"
@@ -29589,6 +29651,7 @@ function GameHud({
29589
29651
  items,
29590
29652
  elements,
29591
29653
  size = "md",
29654
+ variant = "floating",
29592
29655
  className,
29593
29656
  transparent = true
29594
29657
  }) {
@@ -29603,7 +29666,7 @@ function GameHud({
29603
29666
  /* @__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)) })
29604
29667
  ] });
29605
29668
  }
29606
- if (position === "top" || position === "bottom") {
29669
+ if ((position === "top" || position === "bottom") && variant === "bar") {
29607
29670
  const mid = Math.ceil(stats.length / 2);
29608
29671
  const leftStats = stats.slice(0, mid);
29609
29672
  const rightStats = stats.slice(mid);
@@ -31260,7 +31323,7 @@ var init_physicsPresets = __esm({
31260
31323
  ];
31261
31324
  }
31262
31325
  });
31263
- var FONT_BASE, GAME_FONTS, FONT_FACES, GameShell;
31326
+ var GAME_FONTS, GameShell;
31264
31327
  var init_GameShell = __esm({
31265
31328
  "components/game/templates/GameShell.tsx"() {
31266
31329
  init_cn();
@@ -31268,7 +31331,6 @@ var init_GameShell = __esm({
31268
31331
  init_Card();
31269
31332
  init_Typography();
31270
31333
  init_AtlasImage();
31271
- FONT_BASE = "https://almadar-kflow-assets.web.app/shared/_shared/kenney-fonts/fonts";
31272
31334
  GAME_FONTS = {
31273
31335
  future: "Kenney Future",
31274
31336
  "future-narrow": "Kenney Future Narrow",
@@ -31276,14 +31338,6 @@ var init_GameShell = __esm({
31276
31338
  blocks: "Kenney Blocks",
31277
31339
  mini: "Kenney Mini"
31278
31340
  };
31279
- FONT_FACES = `
31280
- @font-face { font-family: 'Kenney Future'; src: url('${FONT_BASE}/Kenney%20Future.ttf') format('truetype'); font-display: swap; }
31281
- @font-face { font-family: 'Kenney Future Narrow'; src: url('${FONT_BASE}/Kenney%20Future%20Narrow.ttf') format('truetype'); font-display: swap; }
31282
- @font-face { font-family: 'Kenney Pixel'; src: url('${FONT_BASE}/Kenney%20Pixel.ttf') format('truetype'); font-display: swap; }
31283
- @font-face { font-family: 'Kenney Blocks'; src: url('${FONT_BASE}/Kenney%20Blocks.ttf') format('truetype'); font-display: swap; }
31284
- @font-face { font-family: 'Kenney Mini'; src: url('${FONT_BASE}/Kenney%20Mini.ttf') format('truetype'); font-display: swap; }
31285
- .game-shell, .game-shell * { font-family: inherit; }
31286
- `;
31287
31341
  GameShell = ({
31288
31342
  appName = "Game",
31289
31343
  hud,
@@ -31310,10 +31364,12 @@ var init_GameShell = __esm({
31310
31364
  overflow: "hidden",
31311
31365
  background: "var(--color-background, #0a0a0f)",
31312
31366
  color: "var(--color-foreground, #e0e0e0)",
31313
- fontFamily: `'${font}', system-ui, sans-serif`
31367
+ // The fontFamily knob is a scoped override of the theme contract's
31368
+ // display slot: titles/numerics take the game face, body text keeps
31369
+ // the active theme's --font-family-body.
31370
+ "--font-family-display": `'${font}', ui-sans-serif, system-ui, sans-serif`
31314
31371
  },
31315
31372
  children: [
31316
- /* @__PURE__ */ jsx("style", { children: FONT_FACES }),
31317
31373
  backgroundAsset && /* @__PURE__ */ jsx(
31318
31374
  AtlasPanel,
31319
31375
  {
@@ -31351,6 +31407,7 @@ var init_GameShell = __esm({
31351
31407
  Typography,
31352
31408
  {
31353
31409
  as: "span",
31410
+ className: "font-display",
31354
31411
  style: {
31355
31412
  fontWeight: 700,
31356
31413
  fontSize: "1.05rem",
@@ -33016,13 +33073,13 @@ var init_MapView = __esm({
33016
33073
  shadowSize: [41, 41]
33017
33074
  });
33018
33075
  L.Marker.prototype.options.icon = defaultIcon;
33019
- const { useEffect: useEffect67, useRef: useRef65, useCallback: useCallback108, useState: useState104 } = React77__default;
33076
+ const { useEffect: useEffect69, useRef: useRef65, useCallback: useCallback108, useState: useState104 } = React77__default;
33020
33077
  const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
33021
33078
  const { useEventBus: useEventBus2 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
33022
33079
  function MapUpdater({ centerLat, centerLng, zoom }) {
33023
33080
  const map = useMap();
33024
33081
  const prevRef = useRef65({ centerLat, centerLng, zoom });
33025
- useEffect67(() => {
33082
+ useEffect69(() => {
33026
33083
  const prev = prevRef.current;
33027
33084
  if (prev.centerLat !== centerLat || prev.centerLng !== centerLng || prev.zoom !== zoom) {
33028
33085
  map.setView([centerLat, centerLng], zoom);
@@ -33033,7 +33090,7 @@ var init_MapView = __esm({
33033
33090
  }
33034
33091
  function MapClickHandler({ onMapClick }) {
33035
33092
  const map = useMap();
33036
- useEffect67(() => {
33093
+ useEffect69(() => {
33037
33094
  if (!onMapClick) return;
33038
33095
  const handler = (e) => {
33039
33096
  onMapClick(e.latlng.lat, e.latlng.lng);
@@ -33562,14 +33619,34 @@ var init_UploadDropZone = __esm({
33562
33619
  if (valid.length > 0) {
33563
33620
  onFiles?.(valid);
33564
33621
  if (action) {
33565
- eventBus.emit(`UI:${action}`, {
33566
- ...actionPayload,
33567
- files: valid.map((f3) => ({ name: f3.name, size: f3.size, type: f3.type }))
33622
+ void Promise.all(
33623
+ valid.map(
33624
+ (f3) => new Promise(
33625
+ (resolvePayload, rejectPayload) => {
33626
+ const reader = new FileReader();
33627
+ reader.onload = () => resolvePayload({
33628
+ name: f3.name,
33629
+ size: f3.size,
33630
+ type: f3.type,
33631
+ content: String(reader.result ?? "")
33632
+ });
33633
+ reader.onerror = () => rejectPayload(reader.error);
33634
+ reader.readAsDataURL(f3);
33635
+ }
33636
+ )
33637
+ )
33638
+ ).then((payloadFiles) => {
33639
+ eventBus.emit(`UI:${action}`, {
33640
+ ...actionPayload,
33641
+ files: payloadFiles
33642
+ });
33643
+ }).catch(() => {
33644
+ setError(t("Could not read the selected file"));
33568
33645
  });
33569
33646
  }
33570
33647
  }
33571
33648
  },
33572
- [validateFiles, onFiles, action, actionPayload, eventBus]
33649
+ [validateFiles, onFiles, action, actionPayload, eventBus, t]
33573
33650
  );
33574
33651
  const handleDragOver = (e) => {
33575
33652
  e.preventDefault();
@@ -43311,22 +43388,6 @@ var init_DataTable = __esm({
43311
43388
  DataTable.displayName = "DataTable";
43312
43389
  }
43313
43390
  });
43314
- function getFieldIcon(fieldName) {
43315
- const name = fieldName.toLowerCase();
43316
- if (name.includes("date") || name.includes("time")) return Calendar;
43317
- if (name.includes("status")) return Tag;
43318
- if (name.includes("priority")) return AlertCircle;
43319
- if (name.includes("progress") || name.includes("percent")) return TrendingUp;
43320
- if (name.includes("assignee") || name.includes("owner") || name.includes("user") || name.includes("member"))
43321
- return User;
43322
- if (name.includes("due")) return Clock;
43323
- if (name.includes("complete")) return CheckCircle2;
43324
- if (name.includes("budget") || name.includes("cost") || name.includes("price"))
43325
- return DollarSign;
43326
- if (name.includes("description") || name.includes("note") || name.includes("comment"))
43327
- return FileText;
43328
- return Package;
43329
- }
43330
43391
  function getBadgeVariant(fieldName, value) {
43331
43392
  const name = fieldName.toLowerCase();
43332
43393
  const val = String(value).toLowerCase();
@@ -43376,6 +43437,18 @@ function renderRichFieldValue(value, fieldName, fieldType, meta) {
43376
43437
  }
43377
43438
  ) });
43378
43439
  }
43440
+ if (fieldType === "url" && /^https?:\/\//i.test(str2)) {
43441
+ return /* @__PURE__ */ jsx(
43442
+ "a",
43443
+ {
43444
+ href: str2,
43445
+ target: "_blank",
43446
+ rel: "noreferrer",
43447
+ className: "text-primary hover:underline break-all",
43448
+ children: str2
43449
+ }
43450
+ );
43451
+ }
43379
43452
  return str2;
43380
43453
  }
43381
43454
  case "markdown":
@@ -43474,6 +43547,23 @@ function renderRichFieldValue(value, fieldName, fieldType, meta) {
43474
43547
  }
43475
43548
  return str2;
43476
43549
  }
43550
+ case "boolean": {
43551
+ if (typeof value === "boolean") return value ? "Yes" : "No";
43552
+ if (str2 === "true") return "Yes";
43553
+ if (str2 === "false") return "No";
43554
+ return str2;
43555
+ }
43556
+ case "array": {
43557
+ if (Array.isArray(value) && value.length > 0) {
43558
+ return /* @__PURE__ */ jsx(HStack, { gap: "xs", wrap: true, children: value.map((item, i) => /* @__PURE__ */ jsx(Badge, { variant: "default", children: String(item) }, i)) });
43559
+ }
43560
+ if (Array.isArray(value)) return "\u2014";
43561
+ return str2;
43562
+ }
43563
+ case "email":
43564
+ return /* @__PURE__ */ jsx("a", { href: `mailto:${str2}`, className: "text-primary hover:underline break-all", children: str2 });
43565
+ case "phone":
43566
+ return /* @__PURE__ */ jsx("a", { href: `tel:${str2}`, className: "text-primary hover:underline", children: str2 });
43477
43567
  default:
43478
43568
  if (meta?.values && meta.values.length > 0 && meta.values.includes(str2)) {
43479
43569
  return /* @__PURE__ */ jsx(Badge, { variant: getBadgeVariant(fieldName, str2), children: humanizeEnumValue(str2) });
@@ -43546,10 +43636,11 @@ var init_DetailPanel = __esm({
43546
43636
  avatar,
43547
43637
  sections: propSections,
43548
43638
  actions,
43549
- maxInlineActions,
43639
+ maxInlineActions = 2,
43550
43640
  backAction,
43551
43641
  footer,
43552
43642
  slideOver = false,
43643
+ showActions = true,
43553
43644
  className,
43554
43645
  entity,
43555
43646
  fields: propFields,
@@ -43597,9 +43688,6 @@ var init_DetailPanel = __esm({
43597
43688
  },
43598
43689
  [eventBus]
43599
43690
  );
43600
- const handleClose = useCallback(() => {
43601
- eventBus.emit("UI:CLOSE", {});
43602
- }, [eventBus]);
43603
43691
  const entityRecord = Array.isArray(entity) ? entity[0] : entity;
43604
43692
  const data = entityRecord ?? initialData;
43605
43693
  let title = propTitle;
@@ -43613,8 +43701,7 @@ var init_DetailPanel = __esm({
43613
43701
  const value = getNestedValue(normalizedData, field);
43614
43702
  return {
43615
43703
  label: labelFor(field),
43616
- value: formatFieldValue2(value, field),
43617
- icon: getFieldIcon(field)
43704
+ value: formatFieldValue2(value, field)
43618
43705
  };
43619
43706
  }
43620
43707
  return field;
@@ -43648,15 +43735,14 @@ var init_DetailPanel = __esm({
43648
43735
  (f3) => (!titleDerivedFromPrimary || f3 !== primaryField) && !statusFields.includes(f3) && !progressFields.includes(f3) && !metricFields.includes(f3) && !dateFields.includes(f3) && !descriptionFields.includes(f3)
43649
43736
  );
43650
43737
  sections = [];
43651
- if (statusFields.length > 0 || otherFields.length > 0) {
43738
+ if (otherFields.length > 0) {
43652
43739
  const overviewFields = [];
43653
- [...statusFields, ...otherFields].forEach((field) => {
43740
+ otherFields.forEach((field) => {
43654
43741
  const value = getNestedValue(normalizedData, field);
43655
43742
  if (value !== void 0 && value !== null) {
43656
43743
  overviewFields.push({
43657
43744
  label: labelFor(field),
43658
- value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field)),
43659
- icon: getFieldIcon(field)
43745
+ value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field))
43660
43746
  });
43661
43747
  }
43662
43748
  });
@@ -43671,8 +43757,7 @@ var init_DetailPanel = __esm({
43671
43757
  if (value !== void 0 && value !== null) {
43672
43758
  metricsFields.push({
43673
43759
  label: labelFor(field),
43674
- value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field)),
43675
- icon: getFieldIcon(field)
43760
+ value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field))
43676
43761
  });
43677
43762
  }
43678
43763
  });
@@ -43687,8 +43772,7 @@ var init_DetailPanel = __esm({
43687
43772
  if (value !== void 0 && value !== null) {
43688
43773
  timelineFields.push({
43689
43774
  label: labelFor(field),
43690
- value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field)),
43691
- icon: getFieldIcon(field)
43775
+ value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field))
43692
43776
  });
43693
43777
  }
43694
43778
  });
@@ -43703,8 +43787,7 @@ var init_DetailPanel = __esm({
43703
43787
  if (value !== void 0 && value !== null) {
43704
43788
  descFields.push({
43705
43789
  label: labelFor(field),
43706
- value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field)),
43707
- icon: getFieldIcon(field)
43790
+ value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field))
43708
43791
  });
43709
43792
  }
43710
43793
  });
@@ -43713,6 +43796,15 @@ var init_DetailPanel = __esm({
43713
43796
  }
43714
43797
  }
43715
43798
  }
43799
+ const renderSlot = useRenderSlot();
43800
+ const navStack = useNavStack();
43801
+ const { setCurrentLabel } = navStack;
43802
+ const resolvedTitle = normalizedData ? title : void 0;
43803
+ useEffect(() => {
43804
+ if (renderSlot === "main" && !slideOver && resolvedTitle) {
43805
+ setCurrentLabel(resolvedTitle);
43806
+ }
43807
+ }, [renderSlot, slideOver, resolvedTitle, setCurrentLabel]);
43716
43808
  if (isLoading) {
43717
43809
  return /* @__PURE__ */ jsx(
43718
43810
  LoadingState,
@@ -43751,8 +43843,7 @@ var init_DetailPanel = __esm({
43751
43843
  const value = normalizedData ? getNestedValue(normalizedData, field) : void 0;
43752
43844
  allFields.push({
43753
43845
  label: labelFor(field),
43754
- value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field)),
43755
- icon: getFieldIcon(field)
43846
+ value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field))
43756
43847
  });
43757
43848
  } else {
43758
43849
  allFields.push(field);
@@ -43764,24 +43855,50 @@ var init_DetailPanel = __esm({
43764
43855
  (a) => a.event === "CLOSE" || a.event === "CANCEL" || a.label?.toLowerCase() === "close"
43765
43856
  );
43766
43857
  const otherActions = actions?.filter((a) => a !== closeAction) ?? [];
43767
- const effectiveCloseAction = closeAction ?? { event: void 0};
43768
- const content = /* @__PURE__ */ jsx(Card, { variant: "elevated", children: /* @__PURE__ */ jsxs(VStack, { gap: "md", className: "p-6", children: [
43769
- /* @__PURE__ */ jsxs(HStack, { justify: "between", align: "center", gap: "xs", children: [
43770
- /* @__PURE__ */ jsx(HStack, { align: "center", gap: "xs", children: backAction && /* @__PURE__ */ jsx(
43771
- Button,
43858
+ const statusBadges = /* @__PURE__ */ jsxs(Fragment, { children: [
43859
+ normalizedData && effectiveFieldNames && effectiveFieldNames.filter(
43860
+ (f3) => f3.toLowerCase().includes("status") || f3.toLowerCase().includes("priority")
43861
+ ).map((field) => {
43862
+ const value = getNestedValue(normalizedData, field);
43863
+ if (!value) return null;
43864
+ return /* @__PURE__ */ jsx(
43865
+ Badge,
43772
43866
  {
43773
- variant: backAction.variant || "ghost",
43774
- size: "sm",
43775
- action: backAction.navigatesTo ? void 0 : backAction.event,
43776
- actionPayload: { row: normalizedData },
43777
- onClick: backAction.navigatesTo ? () => handleActionClick(backAction, normalizedData) : void 0,
43778
- icon: backAction.icon ?? ArrowLeft,
43779
- "data-testid": backAction.event ? `action-${backAction.event}` : "action-back",
43780
- children: backAction.label
43781
- }
43782
- ) }),
43783
- /* @__PURE__ */ jsxs(HStack, { justify: "end", align: "center", gap: "xs", children: [
43784
- (maxInlineActions != null ? otherActions.slice(0, maxInlineActions) : otherActions).map((action, idx) => /* @__PURE__ */ jsx(
43867
+ variant: getBadgeVariant(field, String(value)),
43868
+ children: humanizeEnumValue(String(value))
43869
+ },
43870
+ field
43871
+ );
43872
+ }),
43873
+ status && /* @__PURE__ */ jsx(Badge, { variant: status.variant ?? "default", children: status.label })
43874
+ ] });
43875
+ const content = /* @__PURE__ */ jsx(Card, { variant: "elevated", children: /* @__PURE__ */ jsxs(VStack, { gap: "md", className: "p-6", children: [
43876
+ /* @__PURE__ */ jsxs(HStack, { justify: "between", align: "start", gap: "md", children: [
43877
+ /* @__PURE__ */ jsxs(HStack, { align: "start", gap: "sm", className: "min-w-0", children: [
43878
+ backAction && /* @__PURE__ */ jsx(
43879
+ Button,
43880
+ {
43881
+ variant: backAction.variant || "ghost",
43882
+ size: "sm",
43883
+ action: backAction.navigatesTo ? void 0 : backAction.event,
43884
+ actionPayload: { row: normalizedData },
43885
+ onClick: backAction.navigatesTo ? () => handleActionClick(backAction, normalizedData) : void 0,
43886
+ icon: backAction.icon ?? ArrowLeft,
43887
+ "data-testid": backAction.event ? `action-${backAction.event}` : "action-back",
43888
+ children: backAction.label
43889
+ }
43890
+ ),
43891
+ avatar,
43892
+ /* @__PURE__ */ jsxs(VStack, { gap: "xs", className: "min-w-0", children: [
43893
+ /* @__PURE__ */ jsxs(HStack, { align: "center", gap: "sm", wrap: true, children: [
43894
+ /* @__PURE__ */ jsx(Typography, { variant: "h2", weight: "bold", children: title || "Details" }),
43895
+ statusBadges
43896
+ ] }),
43897
+ subtitle && /* @__PURE__ */ jsx(Typography, { variant: "body", color: "secondary", children: subtitle })
43898
+ ] })
43899
+ ] }),
43900
+ showActions && /* @__PURE__ */ jsxs(HStack, { justify: "end", align: "center", gap: "xs", className: "shrink-0", children: [
43901
+ otherActions.slice(0, maxInlineActions).map((action, idx) => /* @__PURE__ */ jsx(
43785
43902
  Button,
43786
43903
  {
43787
43904
  variant: action.variant || "secondary",
@@ -43796,7 +43913,7 @@ var init_DetailPanel = __esm({
43796
43913
  },
43797
43914
  idx
43798
43915
  )),
43799
- maxInlineActions != null && otherActions.length > maxInlineActions && /* @__PURE__ */ jsx(
43916
+ otherActions.length > maxInlineActions && /* @__PURE__ */ jsx(
43800
43917
  Menu,
43801
43918
  {
43802
43919
  position: "bottom-end",
@@ -43812,40 +43929,20 @@ var init_DetailPanel = __esm({
43812
43929
  }))
43813
43930
  }
43814
43931
  ),
43815
- /* @__PURE__ */ jsx(
43932
+ closeAction && /* @__PURE__ */ jsx(
43816
43933
  Button,
43817
43934
  {
43818
43935
  variant: "ghost",
43819
43936
  size: "sm",
43820
- action: effectiveCloseAction.event,
43937
+ action: closeAction.event,
43821
43938
  actionPayload: { row: normalizedData },
43822
- onClick: effectiveCloseAction.event ? void 0 : handleClose,
43939
+ onClick: closeAction.event ? void 0 : () => handleActionClick(closeAction, normalizedData),
43823
43940
  icon: X,
43824
- "data-testid": effectiveCloseAction.event ? `action-${effectiveCloseAction.event}` : "action-close"
43941
+ "data-testid": closeAction.event ? `action-${closeAction.event}` : "action-close"
43825
43942
  }
43826
43943
  )
43827
43944
  ] })
43828
43945
  ] }),
43829
- avatar,
43830
- /* @__PURE__ */ jsx(Typography, { variant: "h2", weight: "bold", children: title || "Details" }),
43831
- subtitle && /* @__PURE__ */ jsx(Typography, { variant: "body", color: "secondary", children: subtitle }),
43832
- /* @__PURE__ */ jsxs(HStack, { gap: "xs", wrap: true, children: [
43833
- normalizedData && effectiveFieldNames && effectiveFieldNames.filter(
43834
- (f3) => f3.toLowerCase().includes("status") || f3.toLowerCase().includes("priority")
43835
- ).map((field) => {
43836
- const value = getNestedValue(normalizedData, field);
43837
- if (!value) return null;
43838
- return /* @__PURE__ */ jsx(
43839
- Badge,
43840
- {
43841
- variant: getBadgeVariant(field, String(value)),
43842
- children: String(value)
43843
- },
43844
- field
43845
- );
43846
- }),
43847
- status && /* @__PURE__ */ jsx(Badge, { variant: status.variant ?? "default", children: status.label })
43848
- ] }),
43849
43946
  normalizedData && effectiveFieldNames && effectiveFieldNames.filter(
43850
43947
  (f3) => f3.toLowerCase().includes("progress") || f3.toLowerCase().includes("percent")
43851
43948
  ).map((field) => {
@@ -43878,9 +43975,10 @@ var init_DetailPanel = __esm({
43878
43975
  /* @__PURE__ */ jsx(
43879
43976
  Typography,
43880
43977
  {
43881
- variant: "small",
43882
- color: "secondary",
43978
+ variant: "caption",
43979
+ color: "muted",
43883
43980
  weight: "medium",
43981
+ className: "uppercase tracking-wider",
43884
43982
  children: field.label
43885
43983
  }
43886
43984
  ),
@@ -43949,7 +44047,7 @@ var init_DrawerSlot = __esm({
43949
44047
  position,
43950
44048
  width: size,
43951
44049
  className,
43952
- children
44050
+ children: /* @__PURE__ */ jsx(RenderSlotProvider, { slot: "drawer", children })
43953
44051
  }
43954
44052
  );
43955
44053
  };
@@ -45996,7 +46094,7 @@ var init_ModalSlot = __esm({
45996
46094
  title,
45997
46095
  size,
45998
46096
  className,
45999
- children
46097
+ children: /* @__PURE__ */ jsx(RenderSlotProvider, { slot: "modal", children })
46000
46098
  }
46001
46099
  );
46002
46100
  };
@@ -50399,7 +50497,10 @@ function MaybeTraitScope({
50399
50497
  }
50400
50498
  return /* @__PURE__ */ jsx(Fragment, { children });
50401
50499
  }
50402
- function UISlotComponent({
50500
+ function UISlotComponent(props) {
50501
+ return /* @__PURE__ */ jsx(RenderSlotProvider, { slot: props.slot, children: /* @__PURE__ */ jsx(UISlotComponentInner, { ...props }) });
50502
+ }
50503
+ function UISlotComponentInner({
50403
50504
  slot,
50404
50505
  portal = false,
50405
50506
  position,