@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 React85 from 'react';
2
2
  import React85__default, { createContext, useContext, useMemo, useRef, useEffect, useCallback, Suspense, useState, useSyncExternalStore, useLayoutEffect, lazy, useId } from 'react';
3
- import { EventBusContext, useTraitScopeChain, useEntitySchemaOptional, useEntityBindingSnapshot, useTraitScope, useEntitySchema, getAllPages, matchPathAmong, CurrentPagePathProvider, NavStackProvider, OrbitalProvider, TraitScopeProvider, useNavStack, ServerBridgeProvider, useCurrentPagePath, useGameAudioContextOptional, VerificationProvider, EntitySchemaProvider, OrbitalThemeProvider, useServerBridge, EntityBindingContext } from '@almadar/ui/providers';
3
+ import { EventBusContext, useTraitScopeChain, RenderSlotProvider, useEntitySchemaOptional, useEntityBindingSnapshot, useTraitScope, useEntitySchema, getAllPages, matchPathAmong, CurrentPagePathProvider, NavStackProvider, OrbitalProvider, TraitScopeProvider, useNavStack, ServerBridgeProvider, useCurrentPagePath, useRenderSlot, useGameAudioContextOptional, VerificationProvider, EntitySchemaProvider, OrbitalThemeProvider, useServerBridge, EntityBindingContext } from '@almadar/ui/providers';
4
4
  export { EntitySchemaProvider, ServerBridgeProvider, TraitContext, TraitProvider, useEntitySchema, useEntitySchemaOptional, useServerBridge, useTrait, useTraitContext } from '@almadar/ui/providers';
5
5
  import { createLogger, setNamespaceLevel, isLogLevelEnabled } from '@almadar/logger';
6
6
  import { StateMachineManager, collectDeclaredConfigDefaults, resolveCallSitePayloadCaptures, createServerEffectHandlers, EffectExecutor, createContextFromBindings, createTickScheduler, isValidCronExpression, parseDurationString, InMemoryPersistence, normalizeCallSiteConfigToValues, interpolateValue } from '@almadar/runtime';
@@ -8,7 +8,7 @@ import { mergeEntityFrame, isCircuitEvent, applyListenPayloadMapping, schemaToIR
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';
@@ -8234,10 +8234,21 @@ function ControlGrid({
8234
8234
  directionAssets,
8235
8235
  size = "md",
8236
8236
  disabled,
8237
+ visibility = "auto",
8237
8238
  className
8238
8239
  }) {
8239
8240
  const eventBus = useEventBus();
8240
8241
  const [active, setActive] = React85.useState(/* @__PURE__ */ new Set());
8242
+ const [coarse, setCoarse] = React85.useState(
8243
+ () => typeof window !== "undefined" && window.matchMedia("(pointer: coarse)").matches
8244
+ );
8245
+ React85.useEffect(() => {
8246
+ if (visibility !== "auto" || typeof window === "undefined") return;
8247
+ const mq = window.matchMedia("(pointer: coarse)");
8248
+ const onChange = (e) => setCoarse(e.matches);
8249
+ mq.addEventListener("change", onChange);
8250
+ return () => mq.removeEventListener("change", onChange);
8251
+ }, [visibility]);
8241
8252
  const handlePress = React85.useCallback(
8242
8253
  (id) => {
8243
8254
  setActive((prev) => new Set(prev).add(id));
@@ -8270,6 +8281,7 @@ function ControlGrid({
8270
8281
  },
8271
8282
  [kind, actionEvent, directionEvent, directionReleaseEvents, eventBus, onAction, onDirection]
8272
8283
  );
8284
+ if (visibility === "auto" && !coarse) return null;
8273
8285
  if (kind === "dpad") {
8274
8286
  const ds = dpadSizeMap[size];
8275
8287
  const dir = (d) => /* @__PURE__ */ jsx(
@@ -8419,7 +8431,7 @@ function StatBadge({
8419
8431
  assetUrl,
8420
8432
  iconUrl,
8421
8433
  label,
8422
- value = 0,
8434
+ value,
8423
8435
  max,
8424
8436
  format = "number",
8425
8437
  icon,
@@ -8430,6 +8442,7 @@ function StatBadge({
8430
8442
  source: _source,
8431
8443
  field: _field
8432
8444
  }) {
8445
+ const hasValue = value !== void 0 && value !== null;
8433
8446
  const numValue = typeof value === "number" ? value : parseInt(String(value), 10) || 0;
8434
8447
  const resolvedAsset = iconUrl ?? assetUrl;
8435
8448
  return /* @__PURE__ */ jsxs(
@@ -8443,8 +8456,8 @@ function StatBadge({
8443
8456
  ),
8444
8457
  children: [
8445
8458
  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,
8446
- /* @__PURE__ */ jsx(Typography, { as: "span", className: "text-muted-foreground font-medium", children: label }),
8447
- format === "hearts" && max && /* @__PURE__ */ jsx(
8459
+ /* @__PURE__ */ jsx(Typography, { as: "span", className: "text-muted-foreground font-medium text-xs", children: label }),
8460
+ hasValue && format === "hearts" && max && /* @__PURE__ */ jsx(
8448
8461
  HealthBar,
8449
8462
  {
8450
8463
  current: numValue,
@@ -8453,7 +8466,7 @@ function StatBadge({
8453
8466
  size: size === "lg" ? "md" : "sm"
8454
8467
  }
8455
8468
  ),
8456
- format === "bar" && max && /* @__PURE__ */ jsx(
8469
+ hasValue && format === "bar" && max && /* @__PURE__ */ jsx(
8457
8470
  HealthBar,
8458
8471
  {
8459
8472
  current: numValue,
@@ -8462,14 +8475,15 @@ function StatBadge({
8462
8475
  size: size === "lg" ? "md" : "sm"
8463
8476
  }
8464
8477
  ),
8465
- format === "number" && /* @__PURE__ */ jsx(
8478
+ hasValue && format === "number" && /* @__PURE__ */ jsx(
8466
8479
  ScoreDisplay,
8467
8480
  {
8468
8481
  value: numValue,
8469
- size: size === "lg" ? "md" : "sm"
8482
+ size: size === "lg" ? "md" : "sm",
8483
+ className: "font-display"
8470
8484
  }
8471
8485
  ),
8472
- format === "text" && /* @__PURE__ */ jsx(Typography, { as: "span", className: "font-bold text-foreground", children: value })
8486
+ hasValue && format === "text" && /* @__PURE__ */ jsx(Typography, { as: "span", className: "font-bold text-foreground", children: value })
8473
8487
  ]
8474
8488
  }
8475
8489
  );
@@ -8485,6 +8499,7 @@ var init_StatBadge = __esm({
8485
8499
  init_HealthBar();
8486
8500
  init_ScoreDisplay();
8487
8501
  sizeMap6 = {
8502
+ xs: "text-xs px-1.5 py-0.5",
8488
8503
  sm: "text-xs px-2 py-1",
8489
8504
  md: "text-sm px-3 py-1.5",
8490
8505
  lg: "text-base px-4 py-2"
@@ -8527,6 +8542,7 @@ function GameHud({
8527
8542
  items,
8528
8543
  elements,
8529
8544
  size = "md",
8545
+ variant = "floating",
8530
8546
  className,
8531
8547
  transparent = true
8532
8548
  }) {
@@ -8541,7 +8557,7 @@ function GameHud({
8541
8557
  /* @__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)) })
8542
8558
  ] });
8543
8559
  }
8544
- if (position === "top" || position === "bottom") {
8560
+ if ((position === "top" || position === "bottom") && variant === "bar") {
8545
8561
  const mid = Math.ceil(stats.length / 2);
8546
8562
  const leftStats = stats.slice(0, mid);
8547
8563
  const rightStats = stats.slice(mid);
@@ -11655,7 +11671,7 @@ var init_StateJsonView = __esm({
11655
11671
  StateJsonView.displayName = "StateJsonView";
11656
11672
  }
11657
11673
  });
11658
- var FONT_BASE, GAME_FONTS, FONT_FACES, GameShell;
11674
+ var GAME_FONTS, GameShell;
11659
11675
  var init_GameShell = __esm({
11660
11676
  "components/game/templates/GameShell.tsx"() {
11661
11677
  init_cn();
@@ -11663,7 +11679,6 @@ var init_GameShell = __esm({
11663
11679
  init_Card();
11664
11680
  init_Typography();
11665
11681
  init_AtlasImage();
11666
- FONT_BASE = "https://almadar-kflow-assets.web.app/shared/_shared/kenney-fonts/fonts";
11667
11682
  GAME_FONTS = {
11668
11683
  future: "Kenney Future",
11669
11684
  "future-narrow": "Kenney Future Narrow",
@@ -11671,14 +11686,6 @@ var init_GameShell = __esm({
11671
11686
  blocks: "Kenney Blocks",
11672
11687
  mini: "Kenney Mini"
11673
11688
  };
11674
- FONT_FACES = `
11675
- @font-face { font-family: 'Kenney Future'; src: url('${FONT_BASE}/Kenney%20Future.ttf') format('truetype'); font-display: swap; }
11676
- @font-face { font-family: 'Kenney Future Narrow'; src: url('${FONT_BASE}/Kenney%20Future%20Narrow.ttf') format('truetype'); font-display: swap; }
11677
- @font-face { font-family: 'Kenney Pixel'; src: url('${FONT_BASE}/Kenney%20Pixel.ttf') format('truetype'); font-display: swap; }
11678
- @font-face { font-family: 'Kenney Blocks'; src: url('${FONT_BASE}/Kenney%20Blocks.ttf') format('truetype'); font-display: swap; }
11679
- @font-face { font-family: 'Kenney Mini'; src: url('${FONT_BASE}/Kenney%20Mini.ttf') format('truetype'); font-display: swap; }
11680
- .game-shell, .game-shell * { font-family: inherit; }
11681
- `;
11682
11689
  GameShell = ({
11683
11690
  appName = "Game",
11684
11691
  hud,
@@ -11705,10 +11712,12 @@ var init_GameShell = __esm({
11705
11712
  overflow: "hidden",
11706
11713
  background: "var(--color-background, #0a0a0f)",
11707
11714
  color: "var(--color-foreground, #e0e0e0)",
11708
- fontFamily: `'${font}', system-ui, sans-serif`
11715
+ // The fontFamily knob is a scoped override of the theme contract's
11716
+ // display slot: titles/numerics take the game face, body text keeps
11717
+ // the active theme's --font-family-body.
11718
+ "--font-family-display": `'${font}', ui-sans-serif, system-ui, sans-serif`
11709
11719
  },
11710
11720
  children: [
11711
- /* @__PURE__ */ jsx("style", { children: FONT_FACES }),
11712
11721
  backgroundAsset && /* @__PURE__ */ jsx(
11713
11722
  AtlasPanel,
11714
11723
  {
@@ -11746,6 +11755,7 @@ var init_GameShell = __esm({
11746
11755
  Typography,
11747
11756
  {
11748
11757
  as: "span",
11758
+ className: "font-display",
11749
11759
  style: {
11750
11760
  fontWeight: 700,
11751
11761
  fontSize: "1.05rem",
@@ -11826,6 +11836,11 @@ var init_molecules = __esm({
11826
11836
  init_puzzleObject();
11827
11837
  }
11828
11838
  });
11839
+ function themeBodyFont(el) {
11840
+ if (typeof getComputedStyle !== "function") return "system-ui, sans-serif";
11841
+ const v = getComputedStyle(el).getPropertyValue("--font-family-body").trim();
11842
+ return v || "system-ui, sans-serif";
11843
+ }
11829
11844
  function resolveColor2(color, ctx, fallback) {
11830
11845
  if (!color) return fallback;
11831
11846
  if (color.startsWith("var(")) {
@@ -12025,7 +12040,7 @@ function drawShape(ctx, shape, width, height, allShapes) {
12025
12040
  case "text": {
12026
12041
  if (shape.x == null || shape.y == null || !shape.text) break;
12027
12042
  ctx.fillStyle = stroke;
12028
- ctx.font = `${shape.fontSize ?? 14}px system-ui, sans-serif`;
12043
+ ctx.font = `${shape.fontSize ?? 14}px ${themeBodyFont(ctx.canvas)}`;
12029
12044
  ctx.textAlign = shape.align ?? "left";
12030
12045
  ctx.textBaseline = "middle";
12031
12046
  ctx.fillText(shape.text, shape.x, shape.y);
@@ -21259,7 +21274,22 @@ function eventStartDate(event, startField) {
21259
21274
  const raw = getNestedValue(event, startField);
21260
21275
  return new Date(raw ?? "");
21261
21276
  }
21262
- function generateDefaultTimeSlots(events2, startField) {
21277
+ function eventEndDate(event, endField) {
21278
+ const raw = getNestedValue(event, endField);
21279
+ if (raw === void 0 || raw === null || raw === "") return null;
21280
+ const end = new Date(raw);
21281
+ return Number.isNaN(end.getTime()) ? null : end;
21282
+ }
21283
+ function eventContinuesInSlot(event, day, slotTime, startField, endField) {
21284
+ const eventStart = eventStartDate(event, startField);
21285
+ const eventEnd = eventEndDate(event, endField);
21286
+ if (eventEnd === null || Number.isNaN(eventStart.getTime())) return false;
21287
+ const [slotHour] = slotTime.split(":").map(Number);
21288
+ const slotStart = new Date(day);
21289
+ slotStart.setHours(slotHour, 0, 0, 0);
21290
+ return slotStart.getTime() > eventStart.getTime() && slotStart.getTime() < eventEnd.getTime();
21291
+ }
21292
+ function generateDefaultTimeSlots(events2, startField, endField) {
21263
21293
  let first = DEFAULT_FIRST_HOUR;
21264
21294
  let last = DEFAULT_LAST_HOUR;
21265
21295
  for (const ev of events2) {
@@ -21268,6 +21298,11 @@ function generateDefaultTimeSlots(events2, startField) {
21268
21298
  const hour = start.getHours();
21269
21299
  if (hour < first) first = hour;
21270
21300
  if (hour > last) last = hour;
21301
+ const end = eventEndDate(ev, endField);
21302
+ if (end && end.toDateString() === start.toDateString()) {
21303
+ const endHour = end.getMinutes() === 0 && end.getSeconds() === 0 ? end.getHours() - 1 : end.getHours();
21304
+ if (endHour > last) last = endHour;
21305
+ }
21271
21306
  }
21272
21307
  const slots = [];
21273
21308
  for (let hour = first; hour <= last; hour++) {
@@ -21296,6 +21331,7 @@ function CalendarGrid({
21296
21331
  dayWindow = "auto",
21297
21332
  titleField = "title",
21298
21333
  startField = "startTime",
21334
+ endField = "endTime",
21299
21335
  colorField = "color",
21300
21336
  children,
21301
21337
  renderItem
@@ -21313,8 +21349,8 @@ function CalendarGrid({
21313
21349
  [resolvedWeekStart]
21314
21350
  );
21315
21351
  const resolvedTimeSlots = useMemo(
21316
- () => timeSlots ?? generateDefaultTimeSlots(evs, startField),
21317
- [timeSlots, evs, startField]
21352
+ () => timeSlots ?? generateDefaultTimeSlots(evs, startField, endField),
21353
+ [timeSlots, evs, startField, endField]
21318
21354
  );
21319
21355
  const visibleCount = useDayWindow(dayWindow);
21320
21356
  const [dayOffset, setDayOffset] = useState(0);
@@ -21474,12 +21510,15 @@ function CalendarGrid({
21474
21510
  const slotEvents = evs.filter(
21475
21511
  (ev) => eventInSlot(ev, day, time, startField)
21476
21512
  );
21513
+ const continuingEvents = evs.filter(
21514
+ (ev) => eventContinuesInSlot(ev, day, time, startField, endField)
21515
+ );
21477
21516
  const isToday = day.toDateString() === (/* @__PURE__ */ new Date()).toDateString();
21478
21517
  return /* @__PURE__ */ jsx(
21479
21518
  TimeSlotCell,
21480
21519
  {
21481
21520
  time,
21482
- isOccupied: slotEvents.length > 0,
21521
+ isOccupied: slotEvents.length > 0 || continuingEvents.length > 0,
21483
21522
  onClick: () => handleSlotClick(day, time),
21484
21523
  className: cn(
21485
21524
  "border-l border-border",
@@ -21490,7 +21529,25 @@ function CalendarGrid({
21490
21529
  onPointerUp: clearLongPress,
21491
21530
  onPointerCancel: clearLongPress
21492
21531
  } : {},
21493
- children: /* @__PURE__ */ jsx(VStack, { gap: "xs", children: slotEvents.map(renderEvent) })
21532
+ children: /* @__PURE__ */ jsxs(VStack, { gap: "xs", children: [
21533
+ slotEvents.map(renderEvent),
21534
+ continuingEvents.map((event) => {
21535
+ const color = getNestedValue(event, colorField);
21536
+ return /* @__PURE__ */ jsx(
21537
+ Box,
21538
+ {
21539
+ rounded: "sm",
21540
+ border: true,
21541
+ className: cn(
21542
+ "cursor-pointer h-2",
21543
+ color ? cn(color, "opacity-50") : "bg-primary/10 border-primary/20"
21544
+ ),
21545
+ onClick: (e) => handleEventClick(event, e)
21546
+ },
21547
+ `${event.id}-cont`
21548
+ );
21549
+ })
21550
+ ] })
21494
21551
  },
21495
21552
  `${day.toISOString()}-${time}`
21496
21553
  );
@@ -31471,13 +31528,13 @@ var init_MapView = __esm({
31471
31528
  shadowSize: [41, 41]
31472
31529
  });
31473
31530
  L.Marker.prototype.options.icon = defaultIcon;
31474
- const { useEffect: useEffect61, useRef: useRef63, useCallback: useCallback90, useState: useState92 } = React85__default;
31531
+ const { useEffect: useEffect63, useRef: useRef63, useCallback: useCallback90, useState: useState92 } = React85__default;
31475
31532
  const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
31476
31533
  const { useEventBus: useEventBus2 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
31477
31534
  function MapUpdater({ centerLat, centerLng, zoom }) {
31478
31535
  const map = useMap();
31479
31536
  const prevRef = useRef63({ centerLat, centerLng, zoom });
31480
- useEffect61(() => {
31537
+ useEffect63(() => {
31481
31538
  const prev = prevRef.current;
31482
31539
  if (prev.centerLat !== centerLat || prev.centerLng !== centerLng || prev.zoom !== zoom) {
31483
31540
  map.setView([centerLat, centerLng], zoom);
@@ -31488,7 +31545,7 @@ var init_MapView = __esm({
31488
31545
  }
31489
31546
  function MapClickHandler({ onMapClick }) {
31490
31547
  const map = useMap();
31491
- useEffect61(() => {
31548
+ useEffect63(() => {
31492
31549
  if (!onMapClick) return;
31493
31550
  const handler = (e) => {
31494
31551
  onMapClick(e.latlng.lat, e.latlng.lng);
@@ -32017,14 +32074,34 @@ var init_UploadDropZone = __esm({
32017
32074
  if (valid.length > 0) {
32018
32075
  onFiles?.(valid);
32019
32076
  if (action) {
32020
- eventBus.emit(`UI:${action}`, {
32021
- ...actionPayload,
32022
- files: valid.map((f3) => ({ name: f3.name, size: f3.size, type: f3.type }))
32077
+ void Promise.all(
32078
+ valid.map(
32079
+ (f3) => new Promise(
32080
+ (resolvePayload, rejectPayload) => {
32081
+ const reader = new FileReader();
32082
+ reader.onload = () => resolvePayload({
32083
+ name: f3.name,
32084
+ size: f3.size,
32085
+ type: f3.type,
32086
+ content: String(reader.result ?? "")
32087
+ });
32088
+ reader.onerror = () => rejectPayload(reader.error);
32089
+ reader.readAsDataURL(f3);
32090
+ }
32091
+ )
32092
+ )
32093
+ ).then((payloadFiles) => {
32094
+ eventBus.emit(`UI:${action}`, {
32095
+ ...actionPayload,
32096
+ files: payloadFiles
32097
+ });
32098
+ }).catch(() => {
32099
+ setError(t("Could not read the selected file"));
32023
32100
  });
32024
32101
  }
32025
32102
  }
32026
32103
  },
32027
- [validateFiles, onFiles, action, actionPayload, eventBus]
32104
+ [validateFiles, onFiles, action, actionPayload, eventBus, t]
32028
32105
  );
32029
32106
  const handleDragOver = (e) => {
32030
32107
  e.preventDefault();
@@ -41403,22 +41480,6 @@ var init_DataTable = __esm({
41403
41480
  DataTable.displayName = "DataTable";
41404
41481
  }
41405
41482
  });
41406
- function getFieldIcon(fieldName) {
41407
- const name = fieldName.toLowerCase();
41408
- if (name.includes("date") || name.includes("time")) return Calendar;
41409
- if (name.includes("status")) return Tag;
41410
- if (name.includes("priority")) return AlertCircle;
41411
- if (name.includes("progress") || name.includes("percent")) return TrendingUp;
41412
- if (name.includes("assignee") || name.includes("owner") || name.includes("user") || name.includes("member"))
41413
- return User;
41414
- if (name.includes("due")) return Clock;
41415
- if (name.includes("complete")) return CheckCircle2;
41416
- if (name.includes("budget") || name.includes("cost") || name.includes("price"))
41417
- return DollarSign;
41418
- if (name.includes("description") || name.includes("note") || name.includes("comment"))
41419
- return FileText;
41420
- return Package;
41421
- }
41422
41483
  function getBadgeVariant(fieldName, value) {
41423
41484
  const name = fieldName.toLowerCase();
41424
41485
  const val = String(value).toLowerCase();
@@ -41468,6 +41529,18 @@ function renderRichFieldValue(value, fieldName, fieldType, meta) {
41468
41529
  }
41469
41530
  ) });
41470
41531
  }
41532
+ if (fieldType === "url" && /^https?:\/\//i.test(str)) {
41533
+ return /* @__PURE__ */ jsx(
41534
+ "a",
41535
+ {
41536
+ href: str,
41537
+ target: "_blank",
41538
+ rel: "noreferrer",
41539
+ className: "text-primary hover:underline break-all",
41540
+ children: str
41541
+ }
41542
+ );
41543
+ }
41471
41544
  return str;
41472
41545
  }
41473
41546
  case "markdown":
@@ -41566,6 +41639,23 @@ function renderRichFieldValue(value, fieldName, fieldType, meta) {
41566
41639
  }
41567
41640
  return str;
41568
41641
  }
41642
+ case "boolean": {
41643
+ if (typeof value === "boolean") return value ? "Yes" : "No";
41644
+ if (str === "true") return "Yes";
41645
+ if (str === "false") return "No";
41646
+ return str;
41647
+ }
41648
+ case "array": {
41649
+ if (Array.isArray(value) && value.length > 0) {
41650
+ return /* @__PURE__ */ jsx(HStack, { gap: "xs", wrap: true, children: value.map((item, i) => /* @__PURE__ */ jsx(Badge, { variant: "default", children: String(item) }, i)) });
41651
+ }
41652
+ if (Array.isArray(value)) return "\u2014";
41653
+ return str;
41654
+ }
41655
+ case "email":
41656
+ return /* @__PURE__ */ jsx("a", { href: `mailto:${str}`, className: "text-primary hover:underline break-all", children: str });
41657
+ case "phone":
41658
+ return /* @__PURE__ */ jsx("a", { href: `tel:${str}`, className: "text-primary hover:underline", children: str });
41569
41659
  default:
41570
41660
  if (meta?.values && meta.values.length > 0 && meta.values.includes(str)) {
41571
41661
  return /* @__PURE__ */ jsx(Badge, { variant: getBadgeVariant(fieldName, str), children: humanizeEnumValue(str) });
@@ -41638,10 +41728,11 @@ var init_DetailPanel = __esm({
41638
41728
  avatar,
41639
41729
  sections: propSections,
41640
41730
  actions,
41641
- maxInlineActions,
41731
+ maxInlineActions = 2,
41642
41732
  backAction,
41643
41733
  footer,
41644
41734
  slideOver = false,
41735
+ showActions = true,
41645
41736
  className,
41646
41737
  entity,
41647
41738
  fields: propFields,
@@ -41689,9 +41780,6 @@ var init_DetailPanel = __esm({
41689
41780
  },
41690
41781
  [eventBus]
41691
41782
  );
41692
- const handleClose = useCallback(() => {
41693
- eventBus.emit("UI:CLOSE", {});
41694
- }, [eventBus]);
41695
41783
  const entityRecord = Array.isArray(entity) ? entity[0] : entity;
41696
41784
  const data = entityRecord ?? initialData;
41697
41785
  let title = propTitle;
@@ -41705,8 +41793,7 @@ var init_DetailPanel = __esm({
41705
41793
  const value = getNestedValue(normalizedData, field);
41706
41794
  return {
41707
41795
  label: labelFor(field),
41708
- value: formatFieldValue2(value, field),
41709
- icon: getFieldIcon(field)
41796
+ value: formatFieldValue2(value, field)
41710
41797
  };
41711
41798
  }
41712
41799
  return field;
@@ -41740,15 +41827,14 @@ var init_DetailPanel = __esm({
41740
41827
  (f3) => (!titleDerivedFromPrimary || f3 !== primaryField) && !statusFields.includes(f3) && !progressFields.includes(f3) && !metricFields.includes(f3) && !dateFields.includes(f3) && !descriptionFields.includes(f3)
41741
41828
  );
41742
41829
  sections = [];
41743
- if (statusFields.length > 0 || otherFields.length > 0) {
41830
+ if (otherFields.length > 0) {
41744
41831
  const overviewFields = [];
41745
- [...statusFields, ...otherFields].forEach((field) => {
41832
+ otherFields.forEach((field) => {
41746
41833
  const value = getNestedValue(normalizedData, field);
41747
41834
  if (value !== void 0 && value !== null) {
41748
41835
  overviewFields.push({
41749
41836
  label: labelFor(field),
41750
- value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field)),
41751
- icon: getFieldIcon(field)
41837
+ value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field))
41752
41838
  });
41753
41839
  }
41754
41840
  });
@@ -41763,8 +41849,7 @@ var init_DetailPanel = __esm({
41763
41849
  if (value !== void 0 && value !== null) {
41764
41850
  metricsFields.push({
41765
41851
  label: labelFor(field),
41766
- value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field)),
41767
- icon: getFieldIcon(field)
41852
+ value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field))
41768
41853
  });
41769
41854
  }
41770
41855
  });
@@ -41779,8 +41864,7 @@ var init_DetailPanel = __esm({
41779
41864
  if (value !== void 0 && value !== null) {
41780
41865
  timelineFields.push({
41781
41866
  label: labelFor(field),
41782
- value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field)),
41783
- icon: getFieldIcon(field)
41867
+ value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field))
41784
41868
  });
41785
41869
  }
41786
41870
  });
@@ -41795,8 +41879,7 @@ var init_DetailPanel = __esm({
41795
41879
  if (value !== void 0 && value !== null) {
41796
41880
  descFields.push({
41797
41881
  label: labelFor(field),
41798
- value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field)),
41799
- icon: getFieldIcon(field)
41882
+ value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field))
41800
41883
  });
41801
41884
  }
41802
41885
  });
@@ -41805,6 +41888,15 @@ var init_DetailPanel = __esm({
41805
41888
  }
41806
41889
  }
41807
41890
  }
41891
+ const renderSlot = useRenderSlot();
41892
+ const navStack = useNavStack();
41893
+ const { setCurrentLabel } = navStack;
41894
+ const resolvedTitle = normalizedData ? title : void 0;
41895
+ useEffect(() => {
41896
+ if (renderSlot === "main" && !slideOver && resolvedTitle) {
41897
+ setCurrentLabel(resolvedTitle);
41898
+ }
41899
+ }, [renderSlot, slideOver, resolvedTitle, setCurrentLabel]);
41808
41900
  if (isLoading) {
41809
41901
  return /* @__PURE__ */ jsx(
41810
41902
  LoadingState,
@@ -41843,8 +41935,7 @@ var init_DetailPanel = __esm({
41843
41935
  const value = normalizedData ? getNestedValue(normalizedData, field) : void 0;
41844
41936
  allFields.push({
41845
41937
  label: labelFor(field),
41846
- value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field)),
41847
- icon: getFieldIcon(field)
41938
+ value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field))
41848
41939
  });
41849
41940
  } else {
41850
41941
  allFields.push(field);
@@ -41856,24 +41947,50 @@ var init_DetailPanel = __esm({
41856
41947
  (a) => a.event === "CLOSE" || a.event === "CANCEL" || a.label?.toLowerCase() === "close"
41857
41948
  );
41858
41949
  const otherActions = actions?.filter((a) => a !== closeAction) ?? [];
41859
- const effectiveCloseAction = closeAction ?? { event: void 0};
41860
- const content = /* @__PURE__ */ jsx(Card, { variant: "elevated", children: /* @__PURE__ */ jsxs(VStack, { gap: "md", className: "p-6", children: [
41861
- /* @__PURE__ */ jsxs(HStack, { justify: "between", align: "center", gap: "xs", children: [
41862
- /* @__PURE__ */ jsx(HStack, { align: "center", gap: "xs", children: backAction && /* @__PURE__ */ jsx(
41863
- Button,
41950
+ const statusBadges = /* @__PURE__ */ jsxs(Fragment, { children: [
41951
+ normalizedData && effectiveFieldNames && effectiveFieldNames.filter(
41952
+ (f3) => f3.toLowerCase().includes("status") || f3.toLowerCase().includes("priority")
41953
+ ).map((field) => {
41954
+ const value = getNestedValue(normalizedData, field);
41955
+ if (!value) return null;
41956
+ return /* @__PURE__ */ jsx(
41957
+ Badge,
41864
41958
  {
41865
- variant: backAction.variant || "ghost",
41866
- size: "sm",
41867
- action: backAction.navigatesTo ? void 0 : backAction.event,
41868
- actionPayload: { row: normalizedData },
41869
- onClick: backAction.navigatesTo ? () => handleActionClick(backAction, normalizedData) : void 0,
41870
- icon: backAction.icon ?? ArrowLeft,
41871
- "data-testid": backAction.event ? `action-${backAction.event}` : "action-back",
41872
- children: backAction.label
41873
- }
41874
- ) }),
41875
- /* @__PURE__ */ jsxs(HStack, { justify: "end", align: "center", gap: "xs", children: [
41876
- (maxInlineActions != null ? otherActions.slice(0, maxInlineActions) : otherActions).map((action, idx) => /* @__PURE__ */ jsx(
41959
+ variant: getBadgeVariant(field, String(value)),
41960
+ children: humanizeEnumValue(String(value))
41961
+ },
41962
+ field
41963
+ );
41964
+ }),
41965
+ status && /* @__PURE__ */ jsx(Badge, { variant: status.variant ?? "default", children: status.label })
41966
+ ] });
41967
+ const content = /* @__PURE__ */ jsx(Card, { variant: "elevated", children: /* @__PURE__ */ jsxs(VStack, { gap: "md", className: "p-6", children: [
41968
+ /* @__PURE__ */ jsxs(HStack, { justify: "between", align: "start", gap: "md", children: [
41969
+ /* @__PURE__ */ jsxs(HStack, { align: "start", gap: "sm", className: "min-w-0", children: [
41970
+ backAction && /* @__PURE__ */ jsx(
41971
+ Button,
41972
+ {
41973
+ variant: backAction.variant || "ghost",
41974
+ size: "sm",
41975
+ action: backAction.navigatesTo ? void 0 : backAction.event,
41976
+ actionPayload: { row: normalizedData },
41977
+ onClick: backAction.navigatesTo ? () => handleActionClick(backAction, normalizedData) : void 0,
41978
+ icon: backAction.icon ?? ArrowLeft,
41979
+ "data-testid": backAction.event ? `action-${backAction.event}` : "action-back",
41980
+ children: backAction.label
41981
+ }
41982
+ ),
41983
+ avatar,
41984
+ /* @__PURE__ */ jsxs(VStack, { gap: "xs", className: "min-w-0", children: [
41985
+ /* @__PURE__ */ jsxs(HStack, { align: "center", gap: "sm", wrap: true, children: [
41986
+ /* @__PURE__ */ jsx(Typography, { variant: "h2", weight: "bold", children: title || "Details" }),
41987
+ statusBadges
41988
+ ] }),
41989
+ subtitle && /* @__PURE__ */ jsx(Typography, { variant: "body", color: "secondary", children: subtitle })
41990
+ ] })
41991
+ ] }),
41992
+ showActions && /* @__PURE__ */ jsxs(HStack, { justify: "end", align: "center", gap: "xs", className: "shrink-0", children: [
41993
+ otherActions.slice(0, maxInlineActions).map((action, idx) => /* @__PURE__ */ jsx(
41877
41994
  Button,
41878
41995
  {
41879
41996
  variant: action.variant || "secondary",
@@ -41888,7 +42005,7 @@ var init_DetailPanel = __esm({
41888
42005
  },
41889
42006
  idx
41890
42007
  )),
41891
- maxInlineActions != null && otherActions.length > maxInlineActions && /* @__PURE__ */ jsx(
42008
+ otherActions.length > maxInlineActions && /* @__PURE__ */ jsx(
41892
42009
  Menu,
41893
42010
  {
41894
42011
  position: "bottom-end",
@@ -41904,40 +42021,20 @@ var init_DetailPanel = __esm({
41904
42021
  }))
41905
42022
  }
41906
42023
  ),
41907
- /* @__PURE__ */ jsx(
42024
+ closeAction && /* @__PURE__ */ jsx(
41908
42025
  Button,
41909
42026
  {
41910
42027
  variant: "ghost",
41911
42028
  size: "sm",
41912
- action: effectiveCloseAction.event,
42029
+ action: closeAction.event,
41913
42030
  actionPayload: { row: normalizedData },
41914
- onClick: effectiveCloseAction.event ? void 0 : handleClose,
42031
+ onClick: closeAction.event ? void 0 : () => handleActionClick(closeAction, normalizedData),
41915
42032
  icon: X,
41916
- "data-testid": effectiveCloseAction.event ? `action-${effectiveCloseAction.event}` : "action-close"
42033
+ "data-testid": closeAction.event ? `action-${closeAction.event}` : "action-close"
41917
42034
  }
41918
42035
  )
41919
42036
  ] })
41920
42037
  ] }),
41921
- avatar,
41922
- /* @__PURE__ */ jsx(Typography, { variant: "h2", weight: "bold", children: title || "Details" }),
41923
- subtitle && /* @__PURE__ */ jsx(Typography, { variant: "body", color: "secondary", children: subtitle }),
41924
- /* @__PURE__ */ jsxs(HStack, { gap: "xs", wrap: true, children: [
41925
- normalizedData && effectiveFieldNames && effectiveFieldNames.filter(
41926
- (f3) => f3.toLowerCase().includes("status") || f3.toLowerCase().includes("priority")
41927
- ).map((field) => {
41928
- const value = getNestedValue(normalizedData, field);
41929
- if (!value) return null;
41930
- return /* @__PURE__ */ jsx(
41931
- Badge,
41932
- {
41933
- variant: getBadgeVariant(field, String(value)),
41934
- children: String(value)
41935
- },
41936
- field
41937
- );
41938
- }),
41939
- status && /* @__PURE__ */ jsx(Badge, { variant: status.variant ?? "default", children: status.label })
41940
- ] }),
41941
42038
  normalizedData && effectiveFieldNames && effectiveFieldNames.filter(
41942
42039
  (f3) => f3.toLowerCase().includes("progress") || f3.toLowerCase().includes("percent")
41943
42040
  ).map((field) => {
@@ -41970,9 +42067,10 @@ var init_DetailPanel = __esm({
41970
42067
  /* @__PURE__ */ jsx(
41971
42068
  Typography,
41972
42069
  {
41973
- variant: "small",
41974
- color: "secondary",
42070
+ variant: "caption",
42071
+ color: "muted",
41975
42072
  weight: "medium",
42073
+ className: "uppercase tracking-wider",
41976
42074
  children: field.label
41977
42075
  }
41978
42076
  ),
@@ -42041,7 +42139,7 @@ var init_DrawerSlot = __esm({
42041
42139
  position,
42042
42140
  width: size,
42043
42141
  className,
42044
- children
42142
+ children: /* @__PURE__ */ jsx(RenderSlotProvider, { slot: "drawer", children })
42045
42143
  }
42046
42144
  );
42047
42145
  };
@@ -44088,7 +44186,7 @@ var init_ModalSlot = __esm({
44088
44186
  title,
44089
44187
  size,
44090
44188
  className,
44091
- children
44189
+ children: /* @__PURE__ */ jsx(RenderSlotProvider, { slot: "modal", children })
44092
44190
  }
44093
44191
  );
44094
44192
  };
@@ -48510,7 +48608,10 @@ function MaybeTraitScope({
48510
48608
  }
48511
48609
  return /* @__PURE__ */ jsx(Fragment, { children });
48512
48610
  }
48513
- function UISlotComponent({
48611
+ function UISlotComponent(props) {
48612
+ return /* @__PURE__ */ jsx(RenderSlotProvider, { slot: props.slot, children: /* @__PURE__ */ jsx(UISlotComponentInner, { ...props }) });
48613
+ }
48614
+ function UISlotComponentInner({
48514
48615
  slot,
48515
48616
  portal = false,
48516
48617
  position,