@almadar/ui 5.155.0 → 5.157.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.
@@ -8308,10 +8308,21 @@ function ControlGrid({
8308
8308
  directionAssets,
8309
8309
  size = "md",
8310
8310
  disabled,
8311
+ visibility = "auto",
8311
8312
  className
8312
8313
  }) {
8313
8314
  const eventBus = useEventBus();
8314
8315
  const [active, setActive] = React85__namespace.useState(/* @__PURE__ */ new Set());
8316
+ const [coarse, setCoarse] = React85__namespace.useState(
8317
+ () => typeof window !== "undefined" && window.matchMedia("(pointer: coarse)").matches
8318
+ );
8319
+ React85__namespace.useEffect(() => {
8320
+ if (visibility !== "auto" || typeof window === "undefined") return;
8321
+ const mq = window.matchMedia("(pointer: coarse)");
8322
+ const onChange = (e) => setCoarse(e.matches);
8323
+ mq.addEventListener("change", onChange);
8324
+ return () => mq.removeEventListener("change", onChange);
8325
+ }, [visibility]);
8315
8326
  const handlePress = React85__namespace.useCallback(
8316
8327
  (id) => {
8317
8328
  setActive((prev) => new Set(prev).add(id));
@@ -8344,6 +8355,7 @@ function ControlGrid({
8344
8355
  },
8345
8356
  [kind, actionEvent, directionEvent, directionReleaseEvents, eventBus, onAction, onDirection]
8346
8357
  );
8358
+ if (visibility === "auto" && !coarse) return null;
8347
8359
  if (kind === "dpad") {
8348
8360
  const ds = dpadSizeMap[size];
8349
8361
  const dir = (d) => /* @__PURE__ */ jsxRuntime.jsx(
@@ -8493,7 +8505,7 @@ function StatBadge({
8493
8505
  assetUrl,
8494
8506
  iconUrl,
8495
8507
  label,
8496
- value = 0,
8508
+ value,
8497
8509
  max,
8498
8510
  format = "number",
8499
8511
  icon,
@@ -8504,6 +8516,7 @@ function StatBadge({
8504
8516
  source: _source,
8505
8517
  field: _field
8506
8518
  }) {
8519
+ const hasValue = value !== void 0 && value !== null;
8507
8520
  const numValue = typeof value === "number" ? value : parseInt(String(value), 10) || 0;
8508
8521
  const resolvedAsset = iconUrl ?? assetUrl;
8509
8522
  return /* @__PURE__ */ jsxRuntime.jsxs(
@@ -8517,8 +8530,8 @@ function StatBadge({
8517
8530
  ),
8518
8531
  children: [
8519
8532
  resolvedAsset ? /* @__PURE__ */ jsxRuntime.jsx(GameIcon, { assetUrl: resolvedAsset, icon: "image", size: 16, className: "flex-shrink-0" }) : icon ? /* @__PURE__ */ jsxRuntime.jsx(Box, { as: "span", className: "flex-shrink-0 text-lg", children: typeof icon === "string" ? /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: icon, className: "w-4 h-4" }) : /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon, className: "w-4 h-4" }) }) : null,
8520
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { as: "span", className: "text-muted-foreground font-medium", children: label }),
8521
- format === "hearts" && max && /* @__PURE__ */ jsxRuntime.jsx(
8533
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { as: "span", className: "text-muted-foreground font-medium text-xs", children: label }),
8534
+ hasValue && format === "hearts" && max && /* @__PURE__ */ jsxRuntime.jsx(
8522
8535
  HealthBar,
8523
8536
  {
8524
8537
  current: numValue,
@@ -8527,7 +8540,7 @@ function StatBadge({
8527
8540
  size: size === "lg" ? "md" : "sm"
8528
8541
  }
8529
8542
  ),
8530
- format === "bar" && max && /* @__PURE__ */ jsxRuntime.jsx(
8543
+ hasValue && format === "bar" && max && /* @__PURE__ */ jsxRuntime.jsx(
8531
8544
  HealthBar,
8532
8545
  {
8533
8546
  current: numValue,
@@ -8536,14 +8549,15 @@ function StatBadge({
8536
8549
  size: size === "lg" ? "md" : "sm"
8537
8550
  }
8538
8551
  ),
8539
- format === "number" && /* @__PURE__ */ jsxRuntime.jsx(
8552
+ hasValue && format === "number" && /* @__PURE__ */ jsxRuntime.jsx(
8540
8553
  ScoreDisplay,
8541
8554
  {
8542
8555
  value: numValue,
8543
- size: size === "lg" ? "md" : "sm"
8556
+ size: size === "lg" ? "md" : "sm",
8557
+ className: "font-display"
8544
8558
  }
8545
8559
  ),
8546
- format === "text" && /* @__PURE__ */ jsxRuntime.jsx(Typography, { as: "span", className: "font-bold text-foreground", children: value })
8560
+ hasValue && format === "text" && /* @__PURE__ */ jsxRuntime.jsx(Typography, { as: "span", className: "font-bold text-foreground", children: value })
8547
8561
  ]
8548
8562
  }
8549
8563
  );
@@ -8559,6 +8573,7 @@ var init_StatBadge = __esm({
8559
8573
  init_HealthBar();
8560
8574
  init_ScoreDisplay();
8561
8575
  sizeMap6 = {
8576
+ xs: "text-xs px-1.5 py-0.5",
8562
8577
  sm: "text-xs px-2 py-1",
8563
8578
  md: "text-sm px-3 py-1.5",
8564
8579
  lg: "text-base px-4 py-2"
@@ -8601,6 +8616,7 @@ function GameHud({
8601
8616
  items,
8602
8617
  elements,
8603
8618
  size = "md",
8619
+ variant = "floating",
8604
8620
  className,
8605
8621
  transparent = true
8606
8622
  }) {
@@ -8615,7 +8631,7 @@ function GameHud({
8615
8631
  /* @__PURE__ */ jsxRuntime.jsx(Box, { position: "absolute", className: "top-4 right-4 flex flex-col gap-2 items-end pointer-events-auto", children: rightStats.map((stat, i) => /* @__PURE__ */ jsxRuntime.jsx(StatBadge, { ...stat, size }, i)) })
8616
8632
  ] });
8617
8633
  }
8618
- if (position === "top" || position === "bottom") {
8634
+ if ((position === "top" || position === "bottom") && variant === "bar") {
8619
8635
  const mid = Math.ceil(stats.length / 2);
8620
8636
  const leftStats = stats.slice(0, mid);
8621
8637
  const rightStats = stats.slice(mid);
@@ -11729,7 +11745,7 @@ var init_StateJsonView = __esm({
11729
11745
  StateJsonView.displayName = "StateJsonView";
11730
11746
  }
11731
11747
  });
11732
- var FONT_BASE, GAME_FONTS, FONT_FACES, GameShell;
11748
+ var GAME_FONTS, GameShell;
11733
11749
  var init_GameShell = __esm({
11734
11750
  "components/game/templates/GameShell.tsx"() {
11735
11751
  init_cn();
@@ -11737,7 +11753,6 @@ var init_GameShell = __esm({
11737
11753
  init_Card();
11738
11754
  init_Typography();
11739
11755
  init_AtlasImage();
11740
- FONT_BASE = "https://almadar-kflow-assets.web.app/shared/_shared/kenney-fonts/fonts";
11741
11756
  GAME_FONTS = {
11742
11757
  future: "Kenney Future",
11743
11758
  "future-narrow": "Kenney Future Narrow",
@@ -11745,14 +11760,6 @@ var init_GameShell = __esm({
11745
11760
  blocks: "Kenney Blocks",
11746
11761
  mini: "Kenney Mini"
11747
11762
  };
11748
- FONT_FACES = `
11749
- @font-face { font-family: 'Kenney Future'; src: url('${FONT_BASE}/Kenney%20Future.ttf') format('truetype'); font-display: swap; }
11750
- @font-face { font-family: 'Kenney Future Narrow'; src: url('${FONT_BASE}/Kenney%20Future%20Narrow.ttf') format('truetype'); font-display: swap; }
11751
- @font-face { font-family: 'Kenney Pixel'; src: url('${FONT_BASE}/Kenney%20Pixel.ttf') format('truetype'); font-display: swap; }
11752
- @font-face { font-family: 'Kenney Blocks'; src: url('${FONT_BASE}/Kenney%20Blocks.ttf') format('truetype'); font-display: swap; }
11753
- @font-face { font-family: 'Kenney Mini'; src: url('${FONT_BASE}/Kenney%20Mini.ttf') format('truetype'); font-display: swap; }
11754
- .game-shell, .game-shell * { font-family: inherit; }
11755
- `;
11756
11763
  GameShell = ({
11757
11764
  appName = "Game",
11758
11765
  hud,
@@ -11779,10 +11786,12 @@ var init_GameShell = __esm({
11779
11786
  overflow: "hidden",
11780
11787
  background: "var(--color-background, #0a0a0f)",
11781
11788
  color: "var(--color-foreground, #e0e0e0)",
11782
- fontFamily: `'${font}', system-ui, sans-serif`
11789
+ // The fontFamily knob is a scoped override of the theme contract's
11790
+ // display slot: titles/numerics take the game face, body text keeps
11791
+ // the active theme's --font-family-body.
11792
+ "--font-family-display": `'${font}', ui-sans-serif, system-ui, sans-serif`
11783
11793
  },
11784
11794
  children: [
11785
- /* @__PURE__ */ jsxRuntime.jsx("style", { children: FONT_FACES }),
11786
11795
  backgroundAsset && /* @__PURE__ */ jsxRuntime.jsx(
11787
11796
  AtlasPanel,
11788
11797
  {
@@ -11820,6 +11829,7 @@ var init_GameShell = __esm({
11820
11829
  Typography,
11821
11830
  {
11822
11831
  as: "span",
11832
+ className: "font-display",
11823
11833
  style: {
11824
11834
  fontWeight: 700,
11825
11835
  fontSize: "1.05rem",
@@ -11900,6 +11910,11 @@ var init_molecules = __esm({
11900
11910
  init_puzzleObject();
11901
11911
  }
11902
11912
  });
11913
+ function themeBodyFont(el) {
11914
+ if (typeof getComputedStyle !== "function") return "system-ui, sans-serif";
11915
+ const v = getComputedStyle(el).getPropertyValue("--font-family-body").trim();
11916
+ return v || "system-ui, sans-serif";
11917
+ }
11903
11918
  function resolveColor2(color, ctx, fallback) {
11904
11919
  if (!color) return fallback;
11905
11920
  if (color.startsWith("var(")) {
@@ -12088,6 +12103,8 @@ function drawShape(ctx, shape, width, height, allShapes) {
12088
12103
  case "path": {
12089
12104
  if (!shape.path) break;
12090
12105
  const p = new Path2D(shape.path);
12106
+ ctx.lineJoin = "round";
12107
+ ctx.lineCap = "round";
12091
12108
  if (fill) {
12092
12109
  ctx.fillStyle = fill;
12093
12110
  ctx.fill(p);
@@ -12099,7 +12116,7 @@ function drawShape(ctx, shape, width, height, allShapes) {
12099
12116
  case "text": {
12100
12117
  if (shape.x == null || shape.y == null || !shape.text) break;
12101
12118
  ctx.fillStyle = stroke;
12102
- ctx.font = `${shape.fontSize ?? 14}px system-ui, sans-serif`;
12119
+ ctx.font = `${shape.fontSize ?? 14}px ${themeBodyFont(ctx.canvas)}`;
12103
12120
  ctx.textAlign = shape.align ?? "left";
12104
12121
  ctx.textBaseline = "middle";
12105
12122
  ctx.fillText(shape.text, shape.x, shape.y);
@@ -12296,7 +12313,7 @@ var init_LearningCanvas = __esm({
12296
12313
  ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
12297
12314
  ctx.clearRect(0, 0, width, height);
12298
12315
  if (backgroundColor) {
12299
- ctx.fillStyle = backgroundColor;
12316
+ ctx.fillStyle = resolveColor2(backgroundColor, ctx, backgroundColor);
12300
12317
  ctx.fillRect(0, 0, width, height);
12301
12318
  }
12302
12319
  for (const shape of derivedShapes) {
@@ -21333,7 +21350,22 @@ function eventStartDate(event, startField) {
21333
21350
  const raw = getNestedValue(event, startField);
21334
21351
  return new Date(raw ?? "");
21335
21352
  }
21336
- function generateDefaultTimeSlots(events2, startField) {
21353
+ function eventEndDate(event, endField) {
21354
+ const raw = getNestedValue(event, endField);
21355
+ if (raw === void 0 || raw === null || raw === "") return null;
21356
+ const end = new Date(raw);
21357
+ return Number.isNaN(end.getTime()) ? null : end;
21358
+ }
21359
+ function eventContinuesInSlot(event, day, slotTime, startField, endField) {
21360
+ const eventStart = eventStartDate(event, startField);
21361
+ const eventEnd = eventEndDate(event, endField);
21362
+ if (eventEnd === null || Number.isNaN(eventStart.getTime())) return false;
21363
+ const [slotHour] = slotTime.split(":").map(Number);
21364
+ const slotStart = new Date(day);
21365
+ slotStart.setHours(slotHour, 0, 0, 0);
21366
+ return slotStart.getTime() > eventStart.getTime() && slotStart.getTime() < eventEnd.getTime();
21367
+ }
21368
+ function generateDefaultTimeSlots(events2, startField, endField) {
21337
21369
  let first = DEFAULT_FIRST_HOUR;
21338
21370
  let last = DEFAULT_LAST_HOUR;
21339
21371
  for (const ev of events2) {
@@ -21342,6 +21374,11 @@ function generateDefaultTimeSlots(events2, startField) {
21342
21374
  const hour = start.getHours();
21343
21375
  if (hour < first) first = hour;
21344
21376
  if (hour > last) last = hour;
21377
+ const end = eventEndDate(ev, endField);
21378
+ if (end && end.toDateString() === start.toDateString()) {
21379
+ const endHour = end.getMinutes() === 0 && end.getSeconds() === 0 ? end.getHours() - 1 : end.getHours();
21380
+ if (endHour > last) last = endHour;
21381
+ }
21345
21382
  }
21346
21383
  const slots = [];
21347
21384
  for (let hour = first; hour <= last; hour++) {
@@ -21370,6 +21407,7 @@ function CalendarGrid({
21370
21407
  dayWindow = "auto",
21371
21408
  titleField = "title",
21372
21409
  startField = "startTime",
21410
+ endField = "endTime",
21373
21411
  colorField = "color",
21374
21412
  children,
21375
21413
  renderItem
@@ -21387,8 +21425,8 @@ function CalendarGrid({
21387
21425
  [resolvedWeekStart]
21388
21426
  );
21389
21427
  const resolvedTimeSlots = React85.useMemo(
21390
- () => timeSlots ?? generateDefaultTimeSlots(evs, startField),
21391
- [timeSlots, evs, startField]
21428
+ () => timeSlots ?? generateDefaultTimeSlots(evs, startField, endField),
21429
+ [timeSlots, evs, startField, endField]
21392
21430
  );
21393
21431
  const visibleCount = useDayWindow(dayWindow);
21394
21432
  const [dayOffset, setDayOffset] = React85.useState(0);
@@ -21548,12 +21586,15 @@ function CalendarGrid({
21548
21586
  const slotEvents = evs.filter(
21549
21587
  (ev) => eventInSlot(ev, day, time, startField)
21550
21588
  );
21589
+ const continuingEvents = evs.filter(
21590
+ (ev) => eventContinuesInSlot(ev, day, time, startField, endField)
21591
+ );
21551
21592
  const isToday = day.toDateString() === (/* @__PURE__ */ new Date()).toDateString();
21552
21593
  return /* @__PURE__ */ jsxRuntime.jsx(
21553
21594
  TimeSlotCell,
21554
21595
  {
21555
21596
  time,
21556
- isOccupied: slotEvents.length > 0,
21597
+ isOccupied: slotEvents.length > 0 || continuingEvents.length > 0,
21557
21598
  onClick: () => handleSlotClick(day, time),
21558
21599
  className: cn(
21559
21600
  "border-l border-border",
@@ -21564,7 +21605,25 @@ function CalendarGrid({
21564
21605
  onPointerUp: clearLongPress,
21565
21606
  onPointerCancel: clearLongPress
21566
21607
  } : {},
21567
- children: /* @__PURE__ */ jsxRuntime.jsx(VStack, { gap: "xs", children: slotEvents.map(renderEvent) })
21608
+ children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "xs", children: [
21609
+ slotEvents.map(renderEvent),
21610
+ continuingEvents.map((event) => {
21611
+ const color = getNestedValue(event, colorField);
21612
+ return /* @__PURE__ */ jsxRuntime.jsx(
21613
+ Box,
21614
+ {
21615
+ rounded: "sm",
21616
+ border: true,
21617
+ className: cn(
21618
+ "cursor-pointer h-2",
21619
+ color ? cn(color, "opacity-50") : "bg-primary/10 border-primary/20"
21620
+ ),
21621
+ onClick: (e) => handleEventClick(event, e)
21622
+ },
21623
+ `${event.id}-cont`
21624
+ );
21625
+ })
21626
+ ] })
21568
21627
  },
21569
21628
  `${day.toISOString()}-${time}`
21570
21629
  );
@@ -30012,6 +30071,9 @@ var init_MathCanvas = __esm({
30012
30071
  showAxes = true,
30013
30072
  showGrid = true,
30014
30073
  gridStep = 1,
30074
+ backgroundColor,
30075
+ gridColor = "var(--color-border, #9ca3af)",
30076
+ axisColor = "var(--color-muted-foreground, #374151)",
30015
30077
  showTickLabels = false,
30016
30078
  showCurveLabels = false,
30017
30079
  curves = [],
@@ -30066,11 +30128,11 @@ var init_MathCanvas = __esm({
30066
30128
  if (showGrid) {
30067
30129
  for (let x = Math.ceil(xMin / gridStep) * gridStep; x <= xMax; x += gridStep) {
30068
30130
  const px = mapX(x);
30069
- out.push({ type: "line", x1: px, y1: margin, x2: px, y2: height - margin, color: "#9ca3af", opacity: 0.35, lineWidth: 1 });
30131
+ out.push({ type: "line", x1: px, y1: margin, x2: px, y2: height - margin, color: gridColor, opacity: 0.35, lineWidth: 1 });
30070
30132
  }
30071
30133
  for (let y = Math.ceil(yMin / gridStep) * gridStep; y <= yMax; y += gridStep) {
30072
30134
  const py = mapY(y);
30073
- out.push({ type: "line", x1: margin, y1: py, x2: width - margin, y2: py, color: "#9ca3af", opacity: 0.35, lineWidth: 1 });
30135
+ out.push({ type: "line", x1: margin, y1: py, x2: width - margin, y2: py, color: gridColor, opacity: 0.35, lineWidth: 1 });
30074
30136
  }
30075
30137
  }
30076
30138
  if (showTickLabels) {
@@ -30141,8 +30203,8 @@ var init_MathCanvas = __esm({
30141
30203
  });
30142
30204
  }
30143
30205
  if (showAxes) {
30144
- out.push({ type: "line", x1: margin, y1: xAxisY, x2: width - margin, y2: xAxisY, color: "#374151", lineWidth: 2 });
30145
- out.push({ type: "line", x1: yAxisX, y1: margin, x2: yAxisX, y2: height - margin, color: "#374151", lineWidth: 2 });
30206
+ out.push({ type: "line", x1: margin, y1: xAxisY, x2: width - margin, y2: xAxisY, color: axisColor, lineWidth: 2 });
30207
+ out.push({ type: "line", x1: yAxisX, y1: margin, x2: yAxisX, y2: height - margin, color: axisColor, lineWidth: 2 });
30146
30208
  }
30147
30209
  for (const guide of guides) {
30148
30210
  const color = guide.color ?? "#9ca3af";
@@ -30165,23 +30227,36 @@ var init_MathCanvas = __esm({
30165
30227
  }
30166
30228
  for (const curve of curves) {
30167
30229
  if (!curve.samples || curve.samples.length < 2) continue;
30230
+ let d = "";
30231
+ let penDown = false;
30168
30232
  let lastInRange;
30169
30233
  for (let i = 1; i < curve.samples.length; i++) {
30170
30234
  const a = curve.samples[i - 1];
30171
30235
  const b = curve.samples[i];
30172
- if (a.x < xMin || a.x > xMax || b.x < xMin || b.x > xMax) continue;
30173
- out.push({
30174
- type: "line",
30175
- x1: mapX(a.x),
30176
- y1: mapY(a.y),
30177
- x2: mapX(b.x),
30178
- y2: mapY(b.y),
30179
- color: curve.color ?? "#2563eb",
30180
- lineWidth: 2,
30181
- dash: curve.dash
30182
- });
30183
- lastInRange = b;
30184
- }
30236
+ if (a.x < xMin && b.x < xMin || a.x > xMax && b.x > xMax) {
30237
+ penDown = false;
30238
+ continue;
30239
+ }
30240
+ const clip = (p, q, xLim) => {
30241
+ const t = q.x === p.x ? 0 : (xLim - p.x) / (q.x - p.x);
30242
+ return { x: xLim, y: p.y + t * (q.y - p.y) };
30243
+ };
30244
+ const ca = a.x < xMin ? clip(a, b, xMin) : a.x > xMax ? clip(a, b, xMax) : a;
30245
+ const cb = b.x < xMin ? clip(a, b, xMin) : b.x > xMax ? clip(a, b, xMax) : b;
30246
+ const pax = mapX(ca.x);
30247
+ const pay = mapY(ca.y);
30248
+ d += `${penDown ? "L" : "M"} ${pax} ${pay} L ${mapX(cb.x)} ${mapY(cb.y)} `;
30249
+ penDown = true;
30250
+ if (b.x >= xMin && b.x <= xMax) lastInRange = b;
30251
+ }
30252
+ if (!d) continue;
30253
+ out.push({
30254
+ type: "path",
30255
+ path: d,
30256
+ color: curve.color ?? "#2563eb",
30257
+ lineWidth: 2,
30258
+ dash: curve.dash
30259
+ });
30185
30260
  if (showCurveLabels && curve.label && lastInRange) {
30186
30261
  out.push({
30187
30262
  type: "text",
@@ -30296,6 +30371,8 @@ var init_MathCanvas = __esm({
30296
30371
  showAxes,
30297
30372
  showGrid,
30298
30373
  gridStep,
30374
+ gridColor,
30375
+ axisColor,
30299
30376
  showTickLabels,
30300
30377
  showCurveLabels,
30301
30378
  curves,
@@ -30315,6 +30392,7 @@ var init_MathCanvas = __esm({
30315
30392
  {
30316
30393
  width,
30317
30394
  height,
30395
+ backgroundColor,
30318
30396
  shapes: derivedShapes,
30319
30397
  readouts,
30320
30398
  traces,
@@ -31545,13 +31623,13 @@ var init_MapView = __esm({
31545
31623
  shadowSize: [41, 41]
31546
31624
  });
31547
31625
  L.Marker.prototype.options.icon = defaultIcon;
31548
- const { useEffect: useEffect61, useRef: useRef63, useCallback: useCallback90, useState: useState92 } = React85__namespace.default;
31626
+ const { useEffect: useEffect63, useRef: useRef63, useCallback: useCallback90, useState: useState92 } = React85__namespace.default;
31549
31627
  const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
31550
31628
  const { useEventBus: useEventBus2 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
31551
31629
  function MapUpdater({ centerLat, centerLng, zoom }) {
31552
31630
  const map = useMap();
31553
31631
  const prevRef = useRef63({ centerLat, centerLng, zoom });
31554
- useEffect61(() => {
31632
+ useEffect63(() => {
31555
31633
  const prev = prevRef.current;
31556
31634
  if (prev.centerLat !== centerLat || prev.centerLng !== centerLng || prev.zoom !== zoom) {
31557
31635
  map.setView([centerLat, centerLng], zoom);
@@ -31562,7 +31640,7 @@ var init_MapView = __esm({
31562
31640
  }
31563
31641
  function MapClickHandler({ onMapClick }) {
31564
31642
  const map = useMap();
31565
- useEffect61(() => {
31643
+ useEffect63(() => {
31566
31644
  if (!onMapClick) return;
31567
31645
  const handler = (e) => {
31568
31646
  onMapClick(e.latlng.lat, e.latlng.lng);
@@ -32091,14 +32169,34 @@ var init_UploadDropZone = __esm({
32091
32169
  if (valid.length > 0) {
32092
32170
  onFiles?.(valid);
32093
32171
  if (action) {
32094
- eventBus.emit(`UI:${action}`, {
32095
- ...actionPayload,
32096
- files: valid.map((f3) => ({ name: f3.name, size: f3.size, type: f3.type }))
32172
+ void Promise.all(
32173
+ valid.map(
32174
+ (f3) => new Promise(
32175
+ (resolvePayload, rejectPayload) => {
32176
+ const reader = new FileReader();
32177
+ reader.onload = () => resolvePayload({
32178
+ name: f3.name,
32179
+ size: f3.size,
32180
+ type: f3.type,
32181
+ content: String(reader.result ?? "")
32182
+ });
32183
+ reader.onerror = () => rejectPayload(reader.error);
32184
+ reader.readAsDataURL(f3);
32185
+ }
32186
+ )
32187
+ )
32188
+ ).then((payloadFiles) => {
32189
+ eventBus.emit(`UI:${action}`, {
32190
+ ...actionPayload,
32191
+ files: payloadFiles
32192
+ });
32193
+ }).catch(() => {
32194
+ setError(t("Could not read the selected file"));
32097
32195
  });
32098
32196
  }
32099
32197
  }
32100
32198
  },
32101
- [validateFiles, onFiles, action, actionPayload, eventBus]
32199
+ [validateFiles, onFiles, action, actionPayload, eventBus, t]
32102
32200
  );
32103
32201
  const handleDragOver = (e) => {
32104
32202
  e.preventDefault();
@@ -41477,22 +41575,6 @@ var init_DataTable = __esm({
41477
41575
  DataTable.displayName = "DataTable";
41478
41576
  }
41479
41577
  });
41480
- function getFieldIcon(fieldName) {
41481
- const name = fieldName.toLowerCase();
41482
- if (name.includes("date") || name.includes("time")) return LucideIcons2.Calendar;
41483
- if (name.includes("status")) return LucideIcons2.Tag;
41484
- if (name.includes("priority")) return LucideIcons2.AlertCircle;
41485
- if (name.includes("progress") || name.includes("percent")) return LucideIcons2.TrendingUp;
41486
- if (name.includes("assignee") || name.includes("owner") || name.includes("user") || name.includes("member"))
41487
- return LucideIcons2.User;
41488
- if (name.includes("due")) return LucideIcons2.Clock;
41489
- if (name.includes("complete")) return LucideIcons2.CheckCircle2;
41490
- if (name.includes("budget") || name.includes("cost") || name.includes("price"))
41491
- return LucideIcons2.DollarSign;
41492
- if (name.includes("description") || name.includes("note") || name.includes("comment"))
41493
- return LucideIcons2.FileText;
41494
- return LucideIcons2.Package;
41495
- }
41496
41578
  function getBadgeVariant(fieldName, value) {
41497
41579
  const name = fieldName.toLowerCase();
41498
41580
  const val = String(value).toLowerCase();
@@ -41542,6 +41624,18 @@ function renderRichFieldValue(value, fieldName, fieldType, meta) {
41542
41624
  }
41543
41625
  ) });
41544
41626
  }
41627
+ if (fieldType === "url" && /^https?:\/\//i.test(str)) {
41628
+ return /* @__PURE__ */ jsxRuntime.jsx(
41629
+ "a",
41630
+ {
41631
+ href: str,
41632
+ target: "_blank",
41633
+ rel: "noreferrer",
41634
+ className: "text-primary hover:underline break-all",
41635
+ children: str
41636
+ }
41637
+ );
41638
+ }
41545
41639
  return str;
41546
41640
  }
41547
41641
  case "markdown":
@@ -41640,6 +41734,23 @@ function renderRichFieldValue(value, fieldName, fieldType, meta) {
41640
41734
  }
41641
41735
  return str;
41642
41736
  }
41737
+ case "boolean": {
41738
+ if (typeof value === "boolean") return value ? "Yes" : "No";
41739
+ if (str === "true") return "Yes";
41740
+ if (str === "false") return "No";
41741
+ return str;
41742
+ }
41743
+ case "array": {
41744
+ if (Array.isArray(value) && value.length > 0) {
41745
+ return /* @__PURE__ */ jsxRuntime.jsx(HStack, { gap: "xs", wrap: true, children: value.map((item, i) => /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: "default", children: String(item) }, i)) });
41746
+ }
41747
+ if (Array.isArray(value)) return "\u2014";
41748
+ return str;
41749
+ }
41750
+ case "email":
41751
+ return /* @__PURE__ */ jsxRuntime.jsx("a", { href: `mailto:${str}`, className: "text-primary hover:underline break-all", children: str });
41752
+ case "phone":
41753
+ return /* @__PURE__ */ jsxRuntime.jsx("a", { href: `tel:${str}`, className: "text-primary hover:underline", children: str });
41643
41754
  default:
41644
41755
  if (meta?.values && meta.values.length > 0 && meta.values.includes(str)) {
41645
41756
  return /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: getBadgeVariant(fieldName, str), children: humanizeEnumValue(str) });
@@ -41712,10 +41823,11 @@ var init_DetailPanel = __esm({
41712
41823
  avatar,
41713
41824
  sections: propSections,
41714
41825
  actions,
41715
- maxInlineActions,
41826
+ maxInlineActions = 2,
41716
41827
  backAction,
41717
41828
  footer,
41718
41829
  slideOver = false,
41830
+ showActions = true,
41719
41831
  className,
41720
41832
  entity,
41721
41833
  fields: propFields,
@@ -41763,9 +41875,6 @@ var init_DetailPanel = __esm({
41763
41875
  },
41764
41876
  [eventBus]
41765
41877
  );
41766
- const handleClose = React85.useCallback(() => {
41767
- eventBus.emit("UI:CLOSE", {});
41768
- }, [eventBus]);
41769
41878
  const entityRecord = Array.isArray(entity) ? entity[0] : entity;
41770
41879
  const data = entityRecord ?? initialData;
41771
41880
  let title = propTitle;
@@ -41779,8 +41888,7 @@ var init_DetailPanel = __esm({
41779
41888
  const value = getNestedValue(normalizedData, field);
41780
41889
  return {
41781
41890
  label: labelFor(field),
41782
- value: formatFieldValue2(value, field),
41783
- icon: getFieldIcon(field)
41891
+ value: formatFieldValue2(value, field)
41784
41892
  };
41785
41893
  }
41786
41894
  return field;
@@ -41814,15 +41922,14 @@ var init_DetailPanel = __esm({
41814
41922
  (f3) => (!titleDerivedFromPrimary || f3 !== primaryField) && !statusFields.includes(f3) && !progressFields.includes(f3) && !metricFields.includes(f3) && !dateFields.includes(f3) && !descriptionFields.includes(f3)
41815
41923
  );
41816
41924
  sections = [];
41817
- if (statusFields.length > 0 || otherFields.length > 0) {
41925
+ if (otherFields.length > 0) {
41818
41926
  const overviewFields = [];
41819
- [...statusFields, ...otherFields].forEach((field) => {
41927
+ otherFields.forEach((field) => {
41820
41928
  const value = getNestedValue(normalizedData, field);
41821
41929
  if (value !== void 0 && value !== null) {
41822
41930
  overviewFields.push({
41823
41931
  label: labelFor(field),
41824
- value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field)),
41825
- icon: getFieldIcon(field)
41932
+ value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field))
41826
41933
  });
41827
41934
  }
41828
41935
  });
@@ -41837,8 +41944,7 @@ var init_DetailPanel = __esm({
41837
41944
  if (value !== void 0 && value !== null) {
41838
41945
  metricsFields.push({
41839
41946
  label: labelFor(field),
41840
- value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field)),
41841
- icon: getFieldIcon(field)
41947
+ value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field))
41842
41948
  });
41843
41949
  }
41844
41950
  });
@@ -41853,8 +41959,7 @@ var init_DetailPanel = __esm({
41853
41959
  if (value !== void 0 && value !== null) {
41854
41960
  timelineFields.push({
41855
41961
  label: labelFor(field),
41856
- value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field)),
41857
- icon: getFieldIcon(field)
41962
+ value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field))
41858
41963
  });
41859
41964
  }
41860
41965
  });
@@ -41869,8 +41974,7 @@ var init_DetailPanel = __esm({
41869
41974
  if (value !== void 0 && value !== null) {
41870
41975
  descFields.push({
41871
41976
  label: labelFor(field),
41872
- value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field)),
41873
- icon: getFieldIcon(field)
41977
+ value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field))
41874
41978
  });
41875
41979
  }
41876
41980
  });
@@ -41879,6 +41983,15 @@ var init_DetailPanel = __esm({
41879
41983
  }
41880
41984
  }
41881
41985
  }
41986
+ const renderSlot = providers.useRenderSlot();
41987
+ const navStack = providers.useNavStack();
41988
+ const { setCurrentLabel } = navStack;
41989
+ const resolvedTitle = normalizedData ? title : void 0;
41990
+ React85.useEffect(() => {
41991
+ if (renderSlot === "main" && !slideOver && resolvedTitle) {
41992
+ setCurrentLabel(resolvedTitle);
41993
+ }
41994
+ }, [renderSlot, slideOver, resolvedTitle, setCurrentLabel]);
41882
41995
  if (isLoading) {
41883
41996
  return /* @__PURE__ */ jsxRuntime.jsx(
41884
41997
  LoadingState,
@@ -41917,8 +42030,7 @@ var init_DetailPanel = __esm({
41917
42030
  const value = normalizedData ? getNestedValue(normalizedData, field) : void 0;
41918
42031
  allFields.push({
41919
42032
  label: labelFor(field),
41920
- value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field)),
41921
- icon: getFieldIcon(field)
42033
+ value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field))
41922
42034
  });
41923
42035
  } else {
41924
42036
  allFields.push(field);
@@ -41930,24 +42042,50 @@ var init_DetailPanel = __esm({
41930
42042
  (a) => a.event === "CLOSE" || a.event === "CANCEL" || a.label?.toLowerCase() === "close"
41931
42043
  );
41932
42044
  const otherActions = actions?.filter((a) => a !== closeAction) ?? [];
41933
- const effectiveCloseAction = closeAction ?? { event: void 0};
41934
- const content = /* @__PURE__ */ jsxRuntime.jsx(Card, { variant: "elevated", children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "md", className: "p-6", children: [
41935
- /* @__PURE__ */ jsxRuntime.jsxs(HStack, { justify: "between", align: "center", gap: "xs", children: [
41936
- /* @__PURE__ */ jsxRuntime.jsx(HStack, { align: "center", gap: "xs", children: backAction && /* @__PURE__ */ jsxRuntime.jsx(
41937
- Button,
42045
+ const statusBadges = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
42046
+ normalizedData && effectiveFieldNames && effectiveFieldNames.filter(
42047
+ (f3) => f3.toLowerCase().includes("status") || f3.toLowerCase().includes("priority")
42048
+ ).map((field) => {
42049
+ const value = getNestedValue(normalizedData, field);
42050
+ if (!value) return null;
42051
+ return /* @__PURE__ */ jsxRuntime.jsx(
42052
+ Badge,
41938
42053
  {
41939
- variant: backAction.variant || "ghost",
41940
- size: "sm",
41941
- action: backAction.navigatesTo ? void 0 : backAction.event,
41942
- actionPayload: { row: normalizedData },
41943
- onClick: backAction.navigatesTo ? () => handleActionClick(backAction, normalizedData) : void 0,
41944
- icon: backAction.icon ?? LucideIcons2.ArrowLeft,
41945
- "data-testid": backAction.event ? `action-${backAction.event}` : "action-back",
41946
- children: backAction.label
41947
- }
41948
- ) }),
41949
- /* @__PURE__ */ jsxRuntime.jsxs(HStack, { justify: "end", align: "center", gap: "xs", children: [
41950
- (maxInlineActions != null ? otherActions.slice(0, maxInlineActions) : otherActions).map((action, idx) => /* @__PURE__ */ jsxRuntime.jsx(
42054
+ variant: getBadgeVariant(field, String(value)),
42055
+ children: humanizeEnumValue(String(value))
42056
+ },
42057
+ field
42058
+ );
42059
+ }),
42060
+ status && /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: status.variant ?? "default", children: status.label })
42061
+ ] });
42062
+ const content = /* @__PURE__ */ jsxRuntime.jsx(Card, { variant: "elevated", children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "md", className: "p-6", children: [
42063
+ /* @__PURE__ */ jsxRuntime.jsxs(HStack, { justify: "between", align: "start", gap: "md", children: [
42064
+ /* @__PURE__ */ jsxRuntime.jsxs(HStack, { align: "start", gap: "sm", className: "min-w-0", children: [
42065
+ backAction && /* @__PURE__ */ jsxRuntime.jsx(
42066
+ Button,
42067
+ {
42068
+ variant: backAction.variant || "ghost",
42069
+ size: "sm",
42070
+ action: backAction.navigatesTo ? void 0 : backAction.event,
42071
+ actionPayload: { row: normalizedData },
42072
+ onClick: backAction.navigatesTo ? () => handleActionClick(backAction, normalizedData) : void 0,
42073
+ icon: backAction.icon ?? LucideIcons2.ArrowLeft,
42074
+ "data-testid": backAction.event ? `action-${backAction.event}` : "action-back",
42075
+ children: backAction.label
42076
+ }
42077
+ ),
42078
+ avatar,
42079
+ /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "xs", className: "min-w-0", children: [
42080
+ /* @__PURE__ */ jsxRuntime.jsxs(HStack, { align: "center", gap: "sm", wrap: true, children: [
42081
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h2", weight: "bold", children: title || "Details" }),
42082
+ statusBadges
42083
+ ] }),
42084
+ subtitle && /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", color: "secondary", children: subtitle })
42085
+ ] })
42086
+ ] }),
42087
+ showActions && /* @__PURE__ */ jsxRuntime.jsxs(HStack, { justify: "end", align: "center", gap: "xs", className: "shrink-0", children: [
42088
+ otherActions.slice(0, maxInlineActions).map((action, idx) => /* @__PURE__ */ jsxRuntime.jsx(
41951
42089
  Button,
41952
42090
  {
41953
42091
  variant: action.variant || "secondary",
@@ -41962,7 +42100,7 @@ var init_DetailPanel = __esm({
41962
42100
  },
41963
42101
  idx
41964
42102
  )),
41965
- maxInlineActions != null && otherActions.length > maxInlineActions && /* @__PURE__ */ jsxRuntime.jsx(
42103
+ otherActions.length > maxInlineActions && /* @__PURE__ */ jsxRuntime.jsx(
41966
42104
  Menu,
41967
42105
  {
41968
42106
  position: "bottom-end",
@@ -41978,40 +42116,20 @@ var init_DetailPanel = __esm({
41978
42116
  }))
41979
42117
  }
41980
42118
  ),
41981
- /* @__PURE__ */ jsxRuntime.jsx(
42119
+ closeAction && /* @__PURE__ */ jsxRuntime.jsx(
41982
42120
  Button,
41983
42121
  {
41984
42122
  variant: "ghost",
41985
42123
  size: "sm",
41986
- action: effectiveCloseAction.event,
42124
+ action: closeAction.event,
41987
42125
  actionPayload: { row: normalizedData },
41988
- onClick: effectiveCloseAction.event ? void 0 : handleClose,
42126
+ onClick: closeAction.event ? void 0 : () => handleActionClick(closeAction, normalizedData),
41989
42127
  icon: LucideIcons2.X,
41990
- "data-testid": effectiveCloseAction.event ? `action-${effectiveCloseAction.event}` : "action-close"
42128
+ "data-testid": closeAction.event ? `action-${closeAction.event}` : "action-close"
41991
42129
  }
41992
42130
  )
41993
42131
  ] })
41994
42132
  ] }),
41995
- avatar,
41996
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h2", weight: "bold", children: title || "Details" }),
41997
- subtitle && /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", color: "secondary", children: subtitle }),
41998
- /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "xs", wrap: true, children: [
41999
- normalizedData && effectiveFieldNames && effectiveFieldNames.filter(
42000
- (f3) => f3.toLowerCase().includes("status") || f3.toLowerCase().includes("priority")
42001
- ).map((field) => {
42002
- const value = getNestedValue(normalizedData, field);
42003
- if (!value) return null;
42004
- return /* @__PURE__ */ jsxRuntime.jsx(
42005
- Badge,
42006
- {
42007
- variant: getBadgeVariant(field, String(value)),
42008
- children: String(value)
42009
- },
42010
- field
42011
- );
42012
- }),
42013
- status && /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: status.variant ?? "default", children: status.label })
42014
- ] }),
42015
42133
  normalizedData && effectiveFieldNames && effectiveFieldNames.filter(
42016
42134
  (f3) => f3.toLowerCase().includes("progress") || f3.toLowerCase().includes("percent")
42017
42135
  ).map((field) => {
@@ -42044,9 +42162,10 @@ var init_DetailPanel = __esm({
42044
42162
  /* @__PURE__ */ jsxRuntime.jsx(
42045
42163
  Typography,
42046
42164
  {
42047
- variant: "small",
42048
- color: "secondary",
42165
+ variant: "caption",
42166
+ color: "muted",
42049
42167
  weight: "medium",
42168
+ className: "uppercase tracking-wider",
42050
42169
  children: field.label
42051
42170
  }
42052
42171
  ),
@@ -42115,7 +42234,7 @@ var init_DrawerSlot = __esm({
42115
42234
  position,
42116
42235
  width: size,
42117
42236
  className,
42118
- children
42237
+ children: /* @__PURE__ */ jsxRuntime.jsx(providers.RenderSlotProvider, { slot: "drawer", children })
42119
42238
  }
42120
42239
  );
42121
42240
  };
@@ -44162,7 +44281,7 @@ var init_ModalSlot = __esm({
44162
44281
  title,
44163
44282
  size,
44164
44283
  className,
44165
- children
44284
+ children: /* @__PURE__ */ jsxRuntime.jsx(providers.RenderSlotProvider, { slot: "modal", children })
44166
44285
  }
44167
44286
  );
44168
44287
  };
@@ -48584,7 +48703,10 @@ function MaybeTraitScope({
48584
48703
  }
48585
48704
  return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children });
48586
48705
  }
48587
- function UISlotComponent({
48706
+ function UISlotComponent(props) {
48707
+ return /* @__PURE__ */ jsxRuntime.jsx(providers.RenderSlotProvider, { slot: props.slot, children: /* @__PURE__ */ jsxRuntime.jsx(UISlotComponentInner, { ...props }) });
48708
+ }
48709
+ function UISlotComponentInner({
48588
48710
  slot,
48589
48711
  portal = false,
48590
48712
  position,