@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.
@@ -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(")) {
@@ -12014,6 +12029,8 @@ function drawShape(ctx, shape, width, height, allShapes) {
12014
12029
  case "path": {
12015
12030
  if (!shape.path) break;
12016
12031
  const p = new Path2D(shape.path);
12032
+ ctx.lineJoin = "round";
12033
+ ctx.lineCap = "round";
12017
12034
  if (fill) {
12018
12035
  ctx.fillStyle = fill;
12019
12036
  ctx.fill(p);
@@ -12025,7 +12042,7 @@ function drawShape(ctx, shape, width, height, allShapes) {
12025
12042
  case "text": {
12026
12043
  if (shape.x == null || shape.y == null || !shape.text) break;
12027
12044
  ctx.fillStyle = stroke;
12028
- ctx.font = `${shape.fontSize ?? 14}px system-ui, sans-serif`;
12045
+ ctx.font = `${shape.fontSize ?? 14}px ${themeBodyFont(ctx.canvas)}`;
12029
12046
  ctx.textAlign = shape.align ?? "left";
12030
12047
  ctx.textBaseline = "middle";
12031
12048
  ctx.fillText(shape.text, shape.x, shape.y);
@@ -12222,7 +12239,7 @@ var init_LearningCanvas = __esm({
12222
12239
  ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
12223
12240
  ctx.clearRect(0, 0, width, height);
12224
12241
  if (backgroundColor) {
12225
- ctx.fillStyle = backgroundColor;
12242
+ ctx.fillStyle = resolveColor2(backgroundColor, ctx, backgroundColor);
12226
12243
  ctx.fillRect(0, 0, width, height);
12227
12244
  }
12228
12245
  for (const shape of derivedShapes) {
@@ -21259,7 +21276,22 @@ function eventStartDate(event, startField) {
21259
21276
  const raw = getNestedValue(event, startField);
21260
21277
  return new Date(raw ?? "");
21261
21278
  }
21262
- function generateDefaultTimeSlots(events2, startField) {
21279
+ function eventEndDate(event, endField) {
21280
+ const raw = getNestedValue(event, endField);
21281
+ if (raw === void 0 || raw === null || raw === "") return null;
21282
+ const end = new Date(raw);
21283
+ return Number.isNaN(end.getTime()) ? null : end;
21284
+ }
21285
+ function eventContinuesInSlot(event, day, slotTime, startField, endField) {
21286
+ const eventStart = eventStartDate(event, startField);
21287
+ const eventEnd = eventEndDate(event, endField);
21288
+ if (eventEnd === null || Number.isNaN(eventStart.getTime())) return false;
21289
+ const [slotHour] = slotTime.split(":").map(Number);
21290
+ const slotStart = new Date(day);
21291
+ slotStart.setHours(slotHour, 0, 0, 0);
21292
+ return slotStart.getTime() > eventStart.getTime() && slotStart.getTime() < eventEnd.getTime();
21293
+ }
21294
+ function generateDefaultTimeSlots(events2, startField, endField) {
21263
21295
  let first = DEFAULT_FIRST_HOUR;
21264
21296
  let last = DEFAULT_LAST_HOUR;
21265
21297
  for (const ev of events2) {
@@ -21268,6 +21300,11 @@ function generateDefaultTimeSlots(events2, startField) {
21268
21300
  const hour = start.getHours();
21269
21301
  if (hour < first) first = hour;
21270
21302
  if (hour > last) last = hour;
21303
+ const end = eventEndDate(ev, endField);
21304
+ if (end && end.toDateString() === start.toDateString()) {
21305
+ const endHour = end.getMinutes() === 0 && end.getSeconds() === 0 ? end.getHours() - 1 : end.getHours();
21306
+ if (endHour > last) last = endHour;
21307
+ }
21271
21308
  }
21272
21309
  const slots = [];
21273
21310
  for (let hour = first; hour <= last; hour++) {
@@ -21296,6 +21333,7 @@ function CalendarGrid({
21296
21333
  dayWindow = "auto",
21297
21334
  titleField = "title",
21298
21335
  startField = "startTime",
21336
+ endField = "endTime",
21299
21337
  colorField = "color",
21300
21338
  children,
21301
21339
  renderItem
@@ -21313,8 +21351,8 @@ function CalendarGrid({
21313
21351
  [resolvedWeekStart]
21314
21352
  );
21315
21353
  const resolvedTimeSlots = useMemo(
21316
- () => timeSlots ?? generateDefaultTimeSlots(evs, startField),
21317
- [timeSlots, evs, startField]
21354
+ () => timeSlots ?? generateDefaultTimeSlots(evs, startField, endField),
21355
+ [timeSlots, evs, startField, endField]
21318
21356
  );
21319
21357
  const visibleCount = useDayWindow(dayWindow);
21320
21358
  const [dayOffset, setDayOffset] = useState(0);
@@ -21474,12 +21512,15 @@ function CalendarGrid({
21474
21512
  const slotEvents = evs.filter(
21475
21513
  (ev) => eventInSlot(ev, day, time, startField)
21476
21514
  );
21515
+ const continuingEvents = evs.filter(
21516
+ (ev) => eventContinuesInSlot(ev, day, time, startField, endField)
21517
+ );
21477
21518
  const isToday = day.toDateString() === (/* @__PURE__ */ new Date()).toDateString();
21478
21519
  return /* @__PURE__ */ jsx(
21479
21520
  TimeSlotCell,
21480
21521
  {
21481
21522
  time,
21482
- isOccupied: slotEvents.length > 0,
21523
+ isOccupied: slotEvents.length > 0 || continuingEvents.length > 0,
21483
21524
  onClick: () => handleSlotClick(day, time),
21484
21525
  className: cn(
21485
21526
  "border-l border-border",
@@ -21490,7 +21531,25 @@ function CalendarGrid({
21490
21531
  onPointerUp: clearLongPress,
21491
21532
  onPointerCancel: clearLongPress
21492
21533
  } : {},
21493
- children: /* @__PURE__ */ jsx(VStack, { gap: "xs", children: slotEvents.map(renderEvent) })
21534
+ children: /* @__PURE__ */ jsxs(VStack, { gap: "xs", children: [
21535
+ slotEvents.map(renderEvent),
21536
+ continuingEvents.map((event) => {
21537
+ const color = getNestedValue(event, colorField);
21538
+ return /* @__PURE__ */ jsx(
21539
+ Box,
21540
+ {
21541
+ rounded: "sm",
21542
+ border: true,
21543
+ className: cn(
21544
+ "cursor-pointer h-2",
21545
+ color ? cn(color, "opacity-50") : "bg-primary/10 border-primary/20"
21546
+ ),
21547
+ onClick: (e) => handleEventClick(event, e)
21548
+ },
21549
+ `${event.id}-cont`
21550
+ );
21551
+ })
21552
+ ] })
21494
21553
  },
21495
21554
  `${day.toISOString()}-${time}`
21496
21555
  );
@@ -29938,6 +29997,9 @@ var init_MathCanvas = __esm({
29938
29997
  showAxes = true,
29939
29998
  showGrid = true,
29940
29999
  gridStep = 1,
30000
+ backgroundColor,
30001
+ gridColor = "var(--color-border, #9ca3af)",
30002
+ axisColor = "var(--color-muted-foreground, #374151)",
29941
30003
  showTickLabels = false,
29942
30004
  showCurveLabels = false,
29943
30005
  curves = [],
@@ -29992,11 +30054,11 @@ var init_MathCanvas = __esm({
29992
30054
  if (showGrid) {
29993
30055
  for (let x = Math.ceil(xMin / gridStep) * gridStep; x <= xMax; x += gridStep) {
29994
30056
  const px = mapX(x);
29995
- out.push({ type: "line", x1: px, y1: margin, x2: px, y2: height - margin, color: "#9ca3af", opacity: 0.35, lineWidth: 1 });
30057
+ out.push({ type: "line", x1: px, y1: margin, x2: px, y2: height - margin, color: gridColor, opacity: 0.35, lineWidth: 1 });
29996
30058
  }
29997
30059
  for (let y = Math.ceil(yMin / gridStep) * gridStep; y <= yMax; y += gridStep) {
29998
30060
  const py = mapY(y);
29999
- out.push({ type: "line", x1: margin, y1: py, x2: width - margin, y2: py, color: "#9ca3af", opacity: 0.35, lineWidth: 1 });
30061
+ out.push({ type: "line", x1: margin, y1: py, x2: width - margin, y2: py, color: gridColor, opacity: 0.35, lineWidth: 1 });
30000
30062
  }
30001
30063
  }
30002
30064
  if (showTickLabels) {
@@ -30067,8 +30129,8 @@ var init_MathCanvas = __esm({
30067
30129
  });
30068
30130
  }
30069
30131
  if (showAxes) {
30070
- out.push({ type: "line", x1: margin, y1: xAxisY, x2: width - margin, y2: xAxisY, color: "#374151", lineWidth: 2 });
30071
- out.push({ type: "line", x1: yAxisX, y1: margin, x2: yAxisX, y2: height - margin, color: "#374151", lineWidth: 2 });
30132
+ out.push({ type: "line", x1: margin, y1: xAxisY, x2: width - margin, y2: xAxisY, color: axisColor, lineWidth: 2 });
30133
+ out.push({ type: "line", x1: yAxisX, y1: margin, x2: yAxisX, y2: height - margin, color: axisColor, lineWidth: 2 });
30072
30134
  }
30073
30135
  for (const guide of guides) {
30074
30136
  const color = guide.color ?? "#9ca3af";
@@ -30091,23 +30153,36 @@ var init_MathCanvas = __esm({
30091
30153
  }
30092
30154
  for (const curve of curves) {
30093
30155
  if (!curve.samples || curve.samples.length < 2) continue;
30156
+ let d = "";
30157
+ let penDown = false;
30094
30158
  let lastInRange;
30095
30159
  for (let i = 1; i < curve.samples.length; i++) {
30096
30160
  const a = curve.samples[i - 1];
30097
30161
  const b = curve.samples[i];
30098
- if (a.x < xMin || a.x > xMax || b.x < xMin || b.x > xMax) continue;
30099
- out.push({
30100
- type: "line",
30101
- x1: mapX(a.x),
30102
- y1: mapY(a.y),
30103
- x2: mapX(b.x),
30104
- y2: mapY(b.y),
30105
- color: curve.color ?? "#2563eb",
30106
- lineWidth: 2,
30107
- dash: curve.dash
30108
- });
30109
- lastInRange = b;
30110
- }
30162
+ if (a.x < xMin && b.x < xMin || a.x > xMax && b.x > xMax) {
30163
+ penDown = false;
30164
+ continue;
30165
+ }
30166
+ const clip = (p, q, xLim) => {
30167
+ const t = q.x === p.x ? 0 : (xLim - p.x) / (q.x - p.x);
30168
+ return { x: xLim, y: p.y + t * (q.y - p.y) };
30169
+ };
30170
+ const ca = a.x < xMin ? clip(a, b, xMin) : a.x > xMax ? clip(a, b, xMax) : a;
30171
+ const cb = b.x < xMin ? clip(a, b, xMin) : b.x > xMax ? clip(a, b, xMax) : b;
30172
+ const pax = mapX(ca.x);
30173
+ const pay = mapY(ca.y);
30174
+ d += `${penDown ? "L" : "M"} ${pax} ${pay} L ${mapX(cb.x)} ${mapY(cb.y)} `;
30175
+ penDown = true;
30176
+ if (b.x >= xMin && b.x <= xMax) lastInRange = b;
30177
+ }
30178
+ if (!d) continue;
30179
+ out.push({
30180
+ type: "path",
30181
+ path: d,
30182
+ color: curve.color ?? "#2563eb",
30183
+ lineWidth: 2,
30184
+ dash: curve.dash
30185
+ });
30111
30186
  if (showCurveLabels && curve.label && lastInRange) {
30112
30187
  out.push({
30113
30188
  type: "text",
@@ -30222,6 +30297,8 @@ var init_MathCanvas = __esm({
30222
30297
  showAxes,
30223
30298
  showGrid,
30224
30299
  gridStep,
30300
+ gridColor,
30301
+ axisColor,
30225
30302
  showTickLabels,
30226
30303
  showCurveLabels,
30227
30304
  curves,
@@ -30241,6 +30318,7 @@ var init_MathCanvas = __esm({
30241
30318
  {
30242
30319
  width,
30243
30320
  height,
30321
+ backgroundColor,
30244
30322
  shapes: derivedShapes,
30245
30323
  readouts,
30246
30324
  traces,
@@ -31471,13 +31549,13 @@ var init_MapView = __esm({
31471
31549
  shadowSize: [41, 41]
31472
31550
  });
31473
31551
  L.Marker.prototype.options.icon = defaultIcon;
31474
- const { useEffect: useEffect61, useRef: useRef63, useCallback: useCallback90, useState: useState92 } = React85__default;
31552
+ const { useEffect: useEffect63, useRef: useRef63, useCallback: useCallback90, useState: useState92 } = React85__default;
31475
31553
  const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
31476
31554
  const { useEventBus: useEventBus2 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
31477
31555
  function MapUpdater({ centerLat, centerLng, zoom }) {
31478
31556
  const map = useMap();
31479
31557
  const prevRef = useRef63({ centerLat, centerLng, zoom });
31480
- useEffect61(() => {
31558
+ useEffect63(() => {
31481
31559
  const prev = prevRef.current;
31482
31560
  if (prev.centerLat !== centerLat || prev.centerLng !== centerLng || prev.zoom !== zoom) {
31483
31561
  map.setView([centerLat, centerLng], zoom);
@@ -31488,7 +31566,7 @@ var init_MapView = __esm({
31488
31566
  }
31489
31567
  function MapClickHandler({ onMapClick }) {
31490
31568
  const map = useMap();
31491
- useEffect61(() => {
31569
+ useEffect63(() => {
31492
31570
  if (!onMapClick) return;
31493
31571
  const handler = (e) => {
31494
31572
  onMapClick(e.latlng.lat, e.latlng.lng);
@@ -32017,14 +32095,34 @@ var init_UploadDropZone = __esm({
32017
32095
  if (valid.length > 0) {
32018
32096
  onFiles?.(valid);
32019
32097
  if (action) {
32020
- eventBus.emit(`UI:${action}`, {
32021
- ...actionPayload,
32022
- files: valid.map((f3) => ({ name: f3.name, size: f3.size, type: f3.type }))
32098
+ void Promise.all(
32099
+ valid.map(
32100
+ (f3) => new Promise(
32101
+ (resolvePayload, rejectPayload) => {
32102
+ const reader = new FileReader();
32103
+ reader.onload = () => resolvePayload({
32104
+ name: f3.name,
32105
+ size: f3.size,
32106
+ type: f3.type,
32107
+ content: String(reader.result ?? "")
32108
+ });
32109
+ reader.onerror = () => rejectPayload(reader.error);
32110
+ reader.readAsDataURL(f3);
32111
+ }
32112
+ )
32113
+ )
32114
+ ).then((payloadFiles) => {
32115
+ eventBus.emit(`UI:${action}`, {
32116
+ ...actionPayload,
32117
+ files: payloadFiles
32118
+ });
32119
+ }).catch(() => {
32120
+ setError(t("Could not read the selected file"));
32023
32121
  });
32024
32122
  }
32025
32123
  }
32026
32124
  },
32027
- [validateFiles, onFiles, action, actionPayload, eventBus]
32125
+ [validateFiles, onFiles, action, actionPayload, eventBus, t]
32028
32126
  );
32029
32127
  const handleDragOver = (e) => {
32030
32128
  e.preventDefault();
@@ -41403,22 +41501,6 @@ var init_DataTable = __esm({
41403
41501
  DataTable.displayName = "DataTable";
41404
41502
  }
41405
41503
  });
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
41504
  function getBadgeVariant(fieldName, value) {
41423
41505
  const name = fieldName.toLowerCase();
41424
41506
  const val = String(value).toLowerCase();
@@ -41468,6 +41550,18 @@ function renderRichFieldValue(value, fieldName, fieldType, meta) {
41468
41550
  }
41469
41551
  ) });
41470
41552
  }
41553
+ if (fieldType === "url" && /^https?:\/\//i.test(str)) {
41554
+ return /* @__PURE__ */ jsx(
41555
+ "a",
41556
+ {
41557
+ href: str,
41558
+ target: "_blank",
41559
+ rel: "noreferrer",
41560
+ className: "text-primary hover:underline break-all",
41561
+ children: str
41562
+ }
41563
+ );
41564
+ }
41471
41565
  return str;
41472
41566
  }
41473
41567
  case "markdown":
@@ -41566,6 +41660,23 @@ function renderRichFieldValue(value, fieldName, fieldType, meta) {
41566
41660
  }
41567
41661
  return str;
41568
41662
  }
41663
+ case "boolean": {
41664
+ if (typeof value === "boolean") return value ? "Yes" : "No";
41665
+ if (str === "true") return "Yes";
41666
+ if (str === "false") return "No";
41667
+ return str;
41668
+ }
41669
+ case "array": {
41670
+ if (Array.isArray(value) && value.length > 0) {
41671
+ return /* @__PURE__ */ jsx(HStack, { gap: "xs", wrap: true, children: value.map((item, i) => /* @__PURE__ */ jsx(Badge, { variant: "default", children: String(item) }, i)) });
41672
+ }
41673
+ if (Array.isArray(value)) return "\u2014";
41674
+ return str;
41675
+ }
41676
+ case "email":
41677
+ return /* @__PURE__ */ jsx("a", { href: `mailto:${str}`, className: "text-primary hover:underline break-all", children: str });
41678
+ case "phone":
41679
+ return /* @__PURE__ */ jsx("a", { href: `tel:${str}`, className: "text-primary hover:underline", children: str });
41569
41680
  default:
41570
41681
  if (meta?.values && meta.values.length > 0 && meta.values.includes(str)) {
41571
41682
  return /* @__PURE__ */ jsx(Badge, { variant: getBadgeVariant(fieldName, str), children: humanizeEnumValue(str) });
@@ -41638,10 +41749,11 @@ var init_DetailPanel = __esm({
41638
41749
  avatar,
41639
41750
  sections: propSections,
41640
41751
  actions,
41641
- maxInlineActions,
41752
+ maxInlineActions = 2,
41642
41753
  backAction,
41643
41754
  footer,
41644
41755
  slideOver = false,
41756
+ showActions = true,
41645
41757
  className,
41646
41758
  entity,
41647
41759
  fields: propFields,
@@ -41689,9 +41801,6 @@ var init_DetailPanel = __esm({
41689
41801
  },
41690
41802
  [eventBus]
41691
41803
  );
41692
- const handleClose = useCallback(() => {
41693
- eventBus.emit("UI:CLOSE", {});
41694
- }, [eventBus]);
41695
41804
  const entityRecord = Array.isArray(entity) ? entity[0] : entity;
41696
41805
  const data = entityRecord ?? initialData;
41697
41806
  let title = propTitle;
@@ -41705,8 +41814,7 @@ var init_DetailPanel = __esm({
41705
41814
  const value = getNestedValue(normalizedData, field);
41706
41815
  return {
41707
41816
  label: labelFor(field),
41708
- value: formatFieldValue2(value, field),
41709
- icon: getFieldIcon(field)
41817
+ value: formatFieldValue2(value, field)
41710
41818
  };
41711
41819
  }
41712
41820
  return field;
@@ -41740,15 +41848,14 @@ var init_DetailPanel = __esm({
41740
41848
  (f3) => (!titleDerivedFromPrimary || f3 !== primaryField) && !statusFields.includes(f3) && !progressFields.includes(f3) && !metricFields.includes(f3) && !dateFields.includes(f3) && !descriptionFields.includes(f3)
41741
41849
  );
41742
41850
  sections = [];
41743
- if (statusFields.length > 0 || otherFields.length > 0) {
41851
+ if (otherFields.length > 0) {
41744
41852
  const overviewFields = [];
41745
- [...statusFields, ...otherFields].forEach((field) => {
41853
+ otherFields.forEach((field) => {
41746
41854
  const value = getNestedValue(normalizedData, field);
41747
41855
  if (value !== void 0 && value !== null) {
41748
41856
  overviewFields.push({
41749
41857
  label: labelFor(field),
41750
- value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field)),
41751
- icon: getFieldIcon(field)
41858
+ value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field))
41752
41859
  });
41753
41860
  }
41754
41861
  });
@@ -41763,8 +41870,7 @@ var init_DetailPanel = __esm({
41763
41870
  if (value !== void 0 && value !== null) {
41764
41871
  metricsFields.push({
41765
41872
  label: labelFor(field),
41766
- value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field)),
41767
- icon: getFieldIcon(field)
41873
+ value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field))
41768
41874
  });
41769
41875
  }
41770
41876
  });
@@ -41779,8 +41885,7 @@ var init_DetailPanel = __esm({
41779
41885
  if (value !== void 0 && value !== null) {
41780
41886
  timelineFields.push({
41781
41887
  label: labelFor(field),
41782
- value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field)),
41783
- icon: getFieldIcon(field)
41888
+ value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field))
41784
41889
  });
41785
41890
  }
41786
41891
  });
@@ -41795,8 +41900,7 @@ var init_DetailPanel = __esm({
41795
41900
  if (value !== void 0 && value !== null) {
41796
41901
  descFields.push({
41797
41902
  label: labelFor(field),
41798
- value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field)),
41799
- icon: getFieldIcon(field)
41903
+ value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field))
41800
41904
  });
41801
41905
  }
41802
41906
  });
@@ -41805,6 +41909,15 @@ var init_DetailPanel = __esm({
41805
41909
  }
41806
41910
  }
41807
41911
  }
41912
+ const renderSlot = useRenderSlot();
41913
+ const navStack = useNavStack();
41914
+ const { setCurrentLabel } = navStack;
41915
+ const resolvedTitle = normalizedData ? title : void 0;
41916
+ useEffect(() => {
41917
+ if (renderSlot === "main" && !slideOver && resolvedTitle) {
41918
+ setCurrentLabel(resolvedTitle);
41919
+ }
41920
+ }, [renderSlot, slideOver, resolvedTitle, setCurrentLabel]);
41808
41921
  if (isLoading) {
41809
41922
  return /* @__PURE__ */ jsx(
41810
41923
  LoadingState,
@@ -41843,8 +41956,7 @@ var init_DetailPanel = __esm({
41843
41956
  const value = normalizedData ? getNestedValue(normalizedData, field) : void 0;
41844
41957
  allFields.push({
41845
41958
  label: labelFor(field),
41846
- value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field)),
41847
- icon: getFieldIcon(field)
41959
+ value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field))
41848
41960
  });
41849
41961
  } else {
41850
41962
  allFields.push(field);
@@ -41856,24 +41968,50 @@ var init_DetailPanel = __esm({
41856
41968
  (a) => a.event === "CLOSE" || a.event === "CANCEL" || a.label?.toLowerCase() === "close"
41857
41969
  );
41858
41970
  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,
41971
+ const statusBadges = /* @__PURE__ */ jsxs(Fragment, { children: [
41972
+ normalizedData && effectiveFieldNames && effectiveFieldNames.filter(
41973
+ (f3) => f3.toLowerCase().includes("status") || f3.toLowerCase().includes("priority")
41974
+ ).map((field) => {
41975
+ const value = getNestedValue(normalizedData, field);
41976
+ if (!value) return null;
41977
+ return /* @__PURE__ */ jsx(
41978
+ Badge,
41864
41979
  {
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(
41980
+ variant: getBadgeVariant(field, String(value)),
41981
+ children: humanizeEnumValue(String(value))
41982
+ },
41983
+ field
41984
+ );
41985
+ }),
41986
+ status && /* @__PURE__ */ jsx(Badge, { variant: status.variant ?? "default", children: status.label })
41987
+ ] });
41988
+ const content = /* @__PURE__ */ jsx(Card, { variant: "elevated", children: /* @__PURE__ */ jsxs(VStack, { gap: "md", className: "p-6", children: [
41989
+ /* @__PURE__ */ jsxs(HStack, { justify: "between", align: "start", gap: "md", children: [
41990
+ /* @__PURE__ */ jsxs(HStack, { align: "start", gap: "sm", className: "min-w-0", children: [
41991
+ backAction && /* @__PURE__ */ jsx(
41992
+ Button,
41993
+ {
41994
+ variant: backAction.variant || "ghost",
41995
+ size: "sm",
41996
+ action: backAction.navigatesTo ? void 0 : backAction.event,
41997
+ actionPayload: { row: normalizedData },
41998
+ onClick: backAction.navigatesTo ? () => handleActionClick(backAction, normalizedData) : void 0,
41999
+ icon: backAction.icon ?? ArrowLeft,
42000
+ "data-testid": backAction.event ? `action-${backAction.event}` : "action-back",
42001
+ children: backAction.label
42002
+ }
42003
+ ),
42004
+ avatar,
42005
+ /* @__PURE__ */ jsxs(VStack, { gap: "xs", className: "min-w-0", children: [
42006
+ /* @__PURE__ */ jsxs(HStack, { align: "center", gap: "sm", wrap: true, children: [
42007
+ /* @__PURE__ */ jsx(Typography, { variant: "h2", weight: "bold", children: title || "Details" }),
42008
+ statusBadges
42009
+ ] }),
42010
+ subtitle && /* @__PURE__ */ jsx(Typography, { variant: "body", color: "secondary", children: subtitle })
42011
+ ] })
42012
+ ] }),
42013
+ showActions && /* @__PURE__ */ jsxs(HStack, { justify: "end", align: "center", gap: "xs", className: "shrink-0", children: [
42014
+ otherActions.slice(0, maxInlineActions).map((action, idx) => /* @__PURE__ */ jsx(
41877
42015
  Button,
41878
42016
  {
41879
42017
  variant: action.variant || "secondary",
@@ -41888,7 +42026,7 @@ var init_DetailPanel = __esm({
41888
42026
  },
41889
42027
  idx
41890
42028
  )),
41891
- maxInlineActions != null && otherActions.length > maxInlineActions && /* @__PURE__ */ jsx(
42029
+ otherActions.length > maxInlineActions && /* @__PURE__ */ jsx(
41892
42030
  Menu,
41893
42031
  {
41894
42032
  position: "bottom-end",
@@ -41904,40 +42042,20 @@ var init_DetailPanel = __esm({
41904
42042
  }))
41905
42043
  }
41906
42044
  ),
41907
- /* @__PURE__ */ jsx(
42045
+ closeAction && /* @__PURE__ */ jsx(
41908
42046
  Button,
41909
42047
  {
41910
42048
  variant: "ghost",
41911
42049
  size: "sm",
41912
- action: effectiveCloseAction.event,
42050
+ action: closeAction.event,
41913
42051
  actionPayload: { row: normalizedData },
41914
- onClick: effectiveCloseAction.event ? void 0 : handleClose,
42052
+ onClick: closeAction.event ? void 0 : () => handleActionClick(closeAction, normalizedData),
41915
42053
  icon: X,
41916
- "data-testid": effectiveCloseAction.event ? `action-${effectiveCloseAction.event}` : "action-close"
42054
+ "data-testid": closeAction.event ? `action-${closeAction.event}` : "action-close"
41917
42055
  }
41918
42056
  )
41919
42057
  ] })
41920
42058
  ] }),
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
42059
  normalizedData && effectiveFieldNames && effectiveFieldNames.filter(
41942
42060
  (f3) => f3.toLowerCase().includes("progress") || f3.toLowerCase().includes("percent")
41943
42061
  ).map((field) => {
@@ -41970,9 +42088,10 @@ var init_DetailPanel = __esm({
41970
42088
  /* @__PURE__ */ jsx(
41971
42089
  Typography,
41972
42090
  {
41973
- variant: "small",
41974
- color: "secondary",
42091
+ variant: "caption",
42092
+ color: "muted",
41975
42093
  weight: "medium",
42094
+ className: "uppercase tracking-wider",
41976
42095
  children: field.label
41977
42096
  }
41978
42097
  ),
@@ -42041,7 +42160,7 @@ var init_DrawerSlot = __esm({
42041
42160
  position,
42042
42161
  width: size,
42043
42162
  className,
42044
- children
42163
+ children: /* @__PURE__ */ jsx(RenderSlotProvider, { slot: "drawer", children })
42045
42164
  }
42046
42165
  );
42047
42166
  };
@@ -44088,7 +44207,7 @@ var init_ModalSlot = __esm({
44088
44207
  title,
44089
44208
  size,
44090
44209
  className,
44091
- children
44210
+ children: /* @__PURE__ */ jsx(RenderSlotProvider, { slot: "modal", children })
44092
44211
  }
44093
44212
  );
44094
44213
  };
@@ -48510,7 +48629,10 @@ function MaybeTraitScope({
48510
48629
  }
48511
48630
  return /* @__PURE__ */ jsx(Fragment, { children });
48512
48631
  }
48513
- function UISlotComponent({
48632
+ function UISlotComponent(props) {
48633
+ return /* @__PURE__ */ jsx(RenderSlotProvider, { slot: props.slot, children: /* @__PURE__ */ jsx(UISlotComponentInner, { ...props }) });
48634
+ }
48635
+ function UISlotComponentInner({
48514
48636
  slot,
48515
48637
  portal = false,
48516
48638
  position,