@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.
package/dist/avl/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
2
2
  import * as React94 from 'react';
3
3
  import React94__default, { createContext, useState, useMemo, useRef, useEffect, useContext, useCallback, Suspense, useLayoutEffect, Profiler, useReducer, useSyncExternalStore, lazy, useId } from 'react';
4
- import { getAllPages, matchPathAmong, CurrentPagePathProvider, NavStackProvider, OrbitalProvider, EventBusContext, useTraitScopeChain, useNavStack, ServerBridgeProvider, VerificationProvider, EntitySchemaProvider, OrbitalThemeProvider, useServerBridge, EntityBindingContext, useEntitySchema, useEntitySchemaOptional, useEntityBindingSnapshot, TraitScopeProvider, useTraitScope, useCurrentPagePath, useGameAudioContextOptional } from '@almadar/ui/providers';
4
+ import { getAllPages, matchPathAmong, CurrentPagePathProvider, NavStackProvider, OrbitalProvider, EventBusContext, useTraitScopeChain, useNavStack, ServerBridgeProvider, VerificationProvider, EntitySchemaProvider, OrbitalThemeProvider, useServerBridge, EntityBindingContext, useEntitySchema, RenderSlotProvider, useEntitySchemaOptional, useEntityBindingSnapshot, TraitScopeProvider, useTraitScope, useCurrentPagePath, useRenderSlot, useGameAudioContextOptional } from '@almadar/ui/providers';
5
5
  import { createLogger, setNamespaceLevel, isLogLevelEnabled } from '@almadar/logger';
6
6
  import ELK from 'elkjs/lib/elk.bundled.js';
7
7
  import { MarkerType, useReactFlow, Handle, Position, getBezierPath, EdgeLabelRenderer, useNodeId, ReactFlowProvider, BaseEdge, useNodesState, useEdgesState, ReactFlow, Controls, Background, BackgroundVariant } from '@xyflow/react';
@@ -9,7 +9,7 @@ import { useTranslate } from '@almadar/ui/hooks';
9
9
  import { InMemoryPersistence, StateMachineManager, collectDeclaredConfigDefaults, resolveCallSitePayloadCaptures, createServerEffectHandlers, EffectExecutor, createContextFromBindings, createTickScheduler, isValidCronExpression, parseDurationString, normalizeCallSiteConfigToValues, interpolateValue } from '@almadar/runtime';
10
10
  import { FieldTypeSchema, isInlineTrait, isPageReference, buildResolvedTraitConfigs, schemaToIR, getPage, mergeEntityFrame, isCircuitEvent, applyListenPayloadMapping, walkSExpr, isRenderBindingMarker, containsEntityBinding, isSExpr, isEventPayloadValue, RENDER_BINDING_MARKER, isFileValue, ANIMATION_NAMES } from '@almadar/core';
11
11
  import * as LucideIcons2 from 'lucide-react';
12
- import { Loader2, X, Code, FileText, WrapText, Check, Copy, Lightbulb, CheckCircle, List, Printer, ChevronRight, ChevronLeft, GitBranch, Pencil, Eye, Plus, ArrowRight, Trash, 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';
12
+ import { Loader2, X, Code, FileText, WrapText, Check, Copy, Lightbulb, CheckCircle, List, Printer, ChevronRight, ChevronLeft, GitBranch, Pencil, Eye, Plus, ArrowRight, Trash, 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';
13
13
  import { createPortal } from 'react-dom';
14
14
  import { UISlotProvider, useUISlots, useTheme } from '@almadar/ui/context';
15
15
  import { evaluateGuard, evaluateListenPayloadExpr, evaluate, createMinimalContext, executeEffects } from '@almadar/evaluator';
@@ -11533,10 +11533,21 @@ function ControlGrid({
11533
11533
  directionAssets,
11534
11534
  size = "md",
11535
11535
  disabled,
11536
+ visibility = "auto",
11536
11537
  className
11537
11538
  }) {
11538
11539
  const eventBus = useEventBus();
11539
11540
  const [active, setActive] = React94.useState(/* @__PURE__ */ new Set());
11541
+ const [coarse, setCoarse] = React94.useState(
11542
+ () => typeof window !== "undefined" && window.matchMedia("(pointer: coarse)").matches
11543
+ );
11544
+ React94.useEffect(() => {
11545
+ if (visibility !== "auto" || typeof window === "undefined") return;
11546
+ const mq = window.matchMedia("(pointer: coarse)");
11547
+ const onChange = (e) => setCoarse(e.matches);
11548
+ mq.addEventListener("change", onChange);
11549
+ return () => mq.removeEventListener("change", onChange);
11550
+ }, [visibility]);
11540
11551
  const handlePress = React94.useCallback(
11541
11552
  (id) => {
11542
11553
  setActive((prev) => new Set(prev).add(id));
@@ -11569,6 +11580,7 @@ function ControlGrid({
11569
11580
  },
11570
11581
  [kind, actionEvent, directionEvent, directionReleaseEvents, eventBus, onAction, onDirection]
11571
11582
  );
11583
+ if (visibility === "auto" && !coarse) return null;
11572
11584
  if (kind === "dpad") {
11573
11585
  const ds = dpadSizeMap[size];
11574
11586
  const dir = (d) => /* @__PURE__ */ jsx(
@@ -11718,7 +11730,7 @@ function StatBadge({
11718
11730
  assetUrl,
11719
11731
  iconUrl,
11720
11732
  label,
11721
- value = 0,
11733
+ value,
11722
11734
  max,
11723
11735
  format = "number",
11724
11736
  icon,
@@ -11729,6 +11741,7 @@ function StatBadge({
11729
11741
  source: _source,
11730
11742
  field: _field
11731
11743
  }) {
11744
+ const hasValue = value !== void 0 && value !== null;
11732
11745
  const numValue = typeof value === "number" ? value : parseInt(String(value), 10) || 0;
11733
11746
  const resolvedAsset = iconUrl ?? assetUrl;
11734
11747
  return /* @__PURE__ */ jsxs(
@@ -11742,8 +11755,8 @@ function StatBadge({
11742
11755
  ),
11743
11756
  children: [
11744
11757
  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,
11745
- /* @__PURE__ */ jsx(Typography, { as: "span", className: "text-muted-foreground font-medium", children: label }),
11746
- format === "hearts" && max && /* @__PURE__ */ jsx(
11758
+ /* @__PURE__ */ jsx(Typography, { as: "span", className: "text-muted-foreground font-medium text-xs", children: label }),
11759
+ hasValue && format === "hearts" && max && /* @__PURE__ */ jsx(
11747
11760
  HealthBar,
11748
11761
  {
11749
11762
  current: numValue,
@@ -11752,7 +11765,7 @@ function StatBadge({
11752
11765
  size: size === "lg" ? "md" : "sm"
11753
11766
  }
11754
11767
  ),
11755
- format === "bar" && max && /* @__PURE__ */ jsx(
11768
+ hasValue && format === "bar" && max && /* @__PURE__ */ jsx(
11756
11769
  HealthBar,
11757
11770
  {
11758
11771
  current: numValue,
@@ -11761,14 +11774,15 @@ function StatBadge({
11761
11774
  size: size === "lg" ? "md" : "sm"
11762
11775
  }
11763
11776
  ),
11764
- format === "number" && /* @__PURE__ */ jsx(
11777
+ hasValue && format === "number" && /* @__PURE__ */ jsx(
11765
11778
  ScoreDisplay,
11766
11779
  {
11767
11780
  value: numValue,
11768
- size: size === "lg" ? "md" : "sm"
11781
+ size: size === "lg" ? "md" : "sm",
11782
+ className: "font-display"
11769
11783
  }
11770
11784
  ),
11771
- format === "text" && /* @__PURE__ */ jsx(Typography, { as: "span", className: "font-bold text-foreground", children: value })
11785
+ hasValue && format === "text" && /* @__PURE__ */ jsx(Typography, { as: "span", className: "font-bold text-foreground", children: value })
11772
11786
  ]
11773
11787
  }
11774
11788
  );
@@ -11784,6 +11798,7 @@ var init_StatBadge = __esm({
11784
11798
  init_HealthBar();
11785
11799
  init_ScoreDisplay();
11786
11800
  sizeMap6 = {
11801
+ xs: "text-xs px-1.5 py-0.5",
11787
11802
  sm: "text-xs px-2 py-1",
11788
11803
  md: "text-sm px-3 py-1.5",
11789
11804
  lg: "text-base px-4 py-2"
@@ -11826,6 +11841,7 @@ function GameHud({
11826
11841
  items,
11827
11842
  elements,
11828
11843
  size = "md",
11844
+ variant = "floating",
11829
11845
  className,
11830
11846
  transparent = true
11831
11847
  }) {
@@ -11840,7 +11856,7 @@ function GameHud({
11840
11856
  /* @__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)) })
11841
11857
  ] });
11842
11858
  }
11843
- if (position === "top" || position === "bottom") {
11859
+ if ((position === "top" || position === "bottom") && variant === "bar") {
11844
11860
  const mid = Math.ceil(stats.length / 2);
11845
11861
  const leftStats = stats.slice(0, mid);
11846
11862
  const rightStats = stats.slice(mid);
@@ -15061,7 +15077,7 @@ var init_StateJsonView = __esm({
15061
15077
  StateJsonView.displayName = "StateJsonView";
15062
15078
  }
15063
15079
  });
15064
- var FONT_BASE, GAME_FONTS, FONT_FACES, GameShell;
15080
+ var GAME_FONTS, GameShell;
15065
15081
  var init_GameShell = __esm({
15066
15082
  "components/game/templates/GameShell.tsx"() {
15067
15083
  init_cn();
@@ -15069,7 +15085,6 @@ var init_GameShell = __esm({
15069
15085
  init_Card();
15070
15086
  init_Typography();
15071
15087
  init_AtlasImage();
15072
- FONT_BASE = "https://almadar-kflow-assets.web.app/shared/_shared/kenney-fonts/fonts";
15073
15088
  GAME_FONTS = {
15074
15089
  future: "Kenney Future",
15075
15090
  "future-narrow": "Kenney Future Narrow",
@@ -15077,14 +15092,6 @@ var init_GameShell = __esm({
15077
15092
  blocks: "Kenney Blocks",
15078
15093
  mini: "Kenney Mini"
15079
15094
  };
15080
- FONT_FACES = `
15081
- @font-face { font-family: 'Kenney Future'; src: url('${FONT_BASE}/Kenney%20Future.ttf') format('truetype'); font-display: swap; }
15082
- @font-face { font-family: 'Kenney Future Narrow'; src: url('${FONT_BASE}/Kenney%20Future%20Narrow.ttf') format('truetype'); font-display: swap; }
15083
- @font-face { font-family: 'Kenney Pixel'; src: url('${FONT_BASE}/Kenney%20Pixel.ttf') format('truetype'); font-display: swap; }
15084
- @font-face { font-family: 'Kenney Blocks'; src: url('${FONT_BASE}/Kenney%20Blocks.ttf') format('truetype'); font-display: swap; }
15085
- @font-face { font-family: 'Kenney Mini'; src: url('${FONT_BASE}/Kenney%20Mini.ttf') format('truetype'); font-display: swap; }
15086
- .game-shell, .game-shell * { font-family: inherit; }
15087
- `;
15088
15095
  GameShell = ({
15089
15096
  appName = "Game",
15090
15097
  hud,
@@ -15111,10 +15118,12 @@ var init_GameShell = __esm({
15111
15118
  overflow: "hidden",
15112
15119
  background: "var(--color-background, #0a0a0f)",
15113
15120
  color: "var(--color-foreground, #e0e0e0)",
15114
- fontFamily: `'${font}', system-ui, sans-serif`
15121
+ // The fontFamily knob is a scoped override of the theme contract's
15122
+ // display slot: titles/numerics take the game face, body text keeps
15123
+ // the active theme's --font-family-body.
15124
+ "--font-family-display": `'${font}', ui-sans-serif, system-ui, sans-serif`
15115
15125
  },
15116
15126
  children: [
15117
- /* @__PURE__ */ jsx("style", { children: FONT_FACES }),
15118
15127
  backgroundAsset && /* @__PURE__ */ jsx(
15119
15128
  AtlasPanel,
15120
15129
  {
@@ -15152,6 +15161,7 @@ var init_GameShell = __esm({
15152
15161
  Typography,
15153
15162
  {
15154
15163
  as: "span",
15164
+ className: "font-display",
15155
15165
  style: {
15156
15166
  fontWeight: 700,
15157
15167
  fontSize: "1.05rem",
@@ -15232,6 +15242,11 @@ var init_molecules = __esm({
15232
15242
  init_puzzleObject();
15233
15243
  }
15234
15244
  });
15245
+ function themeBodyFont(el) {
15246
+ if (typeof getComputedStyle !== "function") return "system-ui, sans-serif";
15247
+ const v = getComputedStyle(el).getPropertyValue("--font-family-body").trim();
15248
+ return v || "system-ui, sans-serif";
15249
+ }
15235
15250
  function resolveColor2(color, ctx, fallback) {
15236
15251
  if (!color) return fallback;
15237
15252
  if (color.startsWith("var(")) {
@@ -15420,6 +15435,8 @@ function drawShape(ctx, shape, width, height, allShapes) {
15420
15435
  case "path": {
15421
15436
  if (!shape.path) break;
15422
15437
  const p = new Path2D(shape.path);
15438
+ ctx.lineJoin = "round";
15439
+ ctx.lineCap = "round";
15423
15440
  if (fill) {
15424
15441
  ctx.fillStyle = fill;
15425
15442
  ctx.fill(p);
@@ -15431,7 +15448,7 @@ function drawShape(ctx, shape, width, height, allShapes) {
15431
15448
  case "text": {
15432
15449
  if (shape.x == null || shape.y == null || !shape.text) break;
15433
15450
  ctx.fillStyle = stroke;
15434
- ctx.font = `${shape.fontSize ?? 14}px system-ui, sans-serif`;
15451
+ ctx.font = `${shape.fontSize ?? 14}px ${themeBodyFont(ctx.canvas)}`;
15435
15452
  ctx.textAlign = shape.align ?? "left";
15436
15453
  ctx.textBaseline = "middle";
15437
15454
  ctx.fillText(shape.text, shape.x, shape.y);
@@ -15628,7 +15645,7 @@ var init_LearningCanvas = __esm({
15628
15645
  ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
15629
15646
  ctx.clearRect(0, 0, width, height);
15630
15647
  if (backgroundColor) {
15631
- ctx.fillStyle = backgroundColor;
15648
+ ctx.fillStyle = resolveColor2(backgroundColor, ctx, backgroundColor);
15632
15649
  ctx.fillRect(0, 0, width, height);
15633
15650
  }
15634
15651
  for (const shape of derivedShapes) {
@@ -23955,7 +23972,22 @@ function eventStartDate(event, startField) {
23955
23972
  const raw = getNestedValue(event, startField);
23956
23973
  return new Date(raw ?? "");
23957
23974
  }
23958
- function generateDefaultTimeSlots(events2, startField) {
23975
+ function eventEndDate(event, endField) {
23976
+ const raw = getNestedValue(event, endField);
23977
+ if (raw === void 0 || raw === null || raw === "") return null;
23978
+ const end = new Date(raw);
23979
+ return Number.isNaN(end.getTime()) ? null : end;
23980
+ }
23981
+ function eventContinuesInSlot(event, day, slotTime, startField, endField) {
23982
+ const eventStart = eventStartDate(event, startField);
23983
+ const eventEnd = eventEndDate(event, endField);
23984
+ if (eventEnd === null || Number.isNaN(eventStart.getTime())) return false;
23985
+ const [slotHour] = slotTime.split(":").map(Number);
23986
+ const slotStart = new Date(day);
23987
+ slotStart.setHours(slotHour, 0, 0, 0);
23988
+ return slotStart.getTime() > eventStart.getTime() && slotStart.getTime() < eventEnd.getTime();
23989
+ }
23990
+ function generateDefaultTimeSlots(events2, startField, endField) {
23959
23991
  let first = DEFAULT_FIRST_HOUR;
23960
23992
  let last = DEFAULT_LAST_HOUR;
23961
23993
  for (const ev of events2) {
@@ -23964,6 +23996,11 @@ function generateDefaultTimeSlots(events2, startField) {
23964
23996
  const hour = start.getHours();
23965
23997
  if (hour < first) first = hour;
23966
23998
  if (hour > last) last = hour;
23999
+ const end = eventEndDate(ev, endField);
24000
+ if (end && end.toDateString() === start.toDateString()) {
24001
+ const endHour = end.getMinutes() === 0 && end.getSeconds() === 0 ? end.getHours() - 1 : end.getHours();
24002
+ if (endHour > last) last = endHour;
24003
+ }
23967
24004
  }
23968
24005
  const slots = [];
23969
24006
  for (let hour = first; hour <= last; hour++) {
@@ -23992,6 +24029,7 @@ function CalendarGrid({
23992
24029
  dayWindow = "auto",
23993
24030
  titleField = "title",
23994
24031
  startField = "startTime",
24032
+ endField = "endTime",
23995
24033
  colorField = "color",
23996
24034
  children,
23997
24035
  renderItem
@@ -24009,8 +24047,8 @@ function CalendarGrid({
24009
24047
  [resolvedWeekStart]
24010
24048
  );
24011
24049
  const resolvedTimeSlots = useMemo(
24012
- () => timeSlots ?? generateDefaultTimeSlots(evs, startField),
24013
- [timeSlots, evs, startField]
24050
+ () => timeSlots ?? generateDefaultTimeSlots(evs, startField, endField),
24051
+ [timeSlots, evs, startField, endField]
24014
24052
  );
24015
24053
  const visibleCount = useDayWindow(dayWindow);
24016
24054
  const [dayOffset, setDayOffset] = useState(0);
@@ -24170,12 +24208,15 @@ function CalendarGrid({
24170
24208
  const slotEvents = evs.filter(
24171
24209
  (ev) => eventInSlot(ev, day, time, startField)
24172
24210
  );
24211
+ const continuingEvents = evs.filter(
24212
+ (ev) => eventContinuesInSlot(ev, day, time, startField, endField)
24213
+ );
24173
24214
  const isToday = day.toDateString() === (/* @__PURE__ */ new Date()).toDateString();
24174
24215
  return /* @__PURE__ */ jsx(
24175
24216
  TimeSlotCell,
24176
24217
  {
24177
24218
  time,
24178
- isOccupied: slotEvents.length > 0,
24219
+ isOccupied: slotEvents.length > 0 || continuingEvents.length > 0,
24179
24220
  onClick: () => handleSlotClick(day, time),
24180
24221
  className: cn(
24181
24222
  "border-l border-border",
@@ -24186,7 +24227,25 @@ function CalendarGrid({
24186
24227
  onPointerUp: clearLongPress,
24187
24228
  onPointerCancel: clearLongPress
24188
24229
  } : {},
24189
- children: /* @__PURE__ */ jsx(VStack, { gap: "xs", children: slotEvents.map(renderEvent) })
24230
+ children: /* @__PURE__ */ jsxs(VStack, { gap: "xs", children: [
24231
+ slotEvents.map(renderEvent),
24232
+ continuingEvents.map((event) => {
24233
+ const color = getNestedValue(event, colorField);
24234
+ return /* @__PURE__ */ jsx(
24235
+ Box,
24236
+ {
24237
+ rounded: "sm",
24238
+ border: true,
24239
+ className: cn(
24240
+ "cursor-pointer h-2",
24241
+ color ? cn(color, "opacity-50") : "bg-primary/10 border-primary/20"
24242
+ ),
24243
+ onClick: (e) => handleEventClick(event, e)
24244
+ },
24245
+ `${event.id}-cont`
24246
+ );
24247
+ })
24248
+ ] })
24190
24249
  },
24191
24250
  `${day.toISOString()}-${time}`
24192
24251
  );
@@ -32712,6 +32771,9 @@ var init_MathCanvas = __esm({
32712
32771
  showAxes = true,
32713
32772
  showGrid = true,
32714
32773
  gridStep = 1,
32774
+ backgroundColor,
32775
+ gridColor = "var(--color-border, #9ca3af)",
32776
+ axisColor = "var(--color-muted-foreground, #374151)",
32715
32777
  showTickLabels = false,
32716
32778
  showCurveLabels = false,
32717
32779
  curves = [],
@@ -32766,11 +32828,11 @@ var init_MathCanvas = __esm({
32766
32828
  if (showGrid) {
32767
32829
  for (let x = Math.ceil(xMin / gridStep) * gridStep; x <= xMax; x += gridStep) {
32768
32830
  const px = mapX(x);
32769
- out.push({ type: "line", x1: px, y1: margin, x2: px, y2: height - margin, color: "#9ca3af", opacity: 0.35, lineWidth: 1 });
32831
+ out.push({ type: "line", x1: px, y1: margin, x2: px, y2: height - margin, color: gridColor, opacity: 0.35, lineWidth: 1 });
32770
32832
  }
32771
32833
  for (let y = Math.ceil(yMin / gridStep) * gridStep; y <= yMax; y += gridStep) {
32772
32834
  const py = mapY(y);
32773
- out.push({ type: "line", x1: margin, y1: py, x2: width - margin, y2: py, color: "#9ca3af", opacity: 0.35, lineWidth: 1 });
32835
+ out.push({ type: "line", x1: margin, y1: py, x2: width - margin, y2: py, color: gridColor, opacity: 0.35, lineWidth: 1 });
32774
32836
  }
32775
32837
  }
32776
32838
  if (showTickLabels) {
@@ -32841,8 +32903,8 @@ var init_MathCanvas = __esm({
32841
32903
  });
32842
32904
  }
32843
32905
  if (showAxes) {
32844
- out.push({ type: "line", x1: margin, y1: xAxisY, x2: width - margin, y2: xAxisY, color: "#374151", lineWidth: 2 });
32845
- out.push({ type: "line", x1: yAxisX, y1: margin, x2: yAxisX, y2: height - margin, color: "#374151", lineWidth: 2 });
32906
+ out.push({ type: "line", x1: margin, y1: xAxisY, x2: width - margin, y2: xAxisY, color: axisColor, lineWidth: 2 });
32907
+ out.push({ type: "line", x1: yAxisX, y1: margin, x2: yAxisX, y2: height - margin, color: axisColor, lineWidth: 2 });
32846
32908
  }
32847
32909
  for (const guide of guides) {
32848
32910
  const color = guide.color ?? "#9ca3af";
@@ -32865,23 +32927,36 @@ var init_MathCanvas = __esm({
32865
32927
  }
32866
32928
  for (const curve of curves) {
32867
32929
  if (!curve.samples || curve.samples.length < 2) continue;
32930
+ let d = "";
32931
+ let penDown = false;
32868
32932
  let lastInRange;
32869
32933
  for (let i = 1; i < curve.samples.length; i++) {
32870
32934
  const a = curve.samples[i - 1];
32871
32935
  const b = curve.samples[i];
32872
- if (a.x < xMin || a.x > xMax || b.x < xMin || b.x > xMax) continue;
32873
- out.push({
32874
- type: "line",
32875
- x1: mapX(a.x),
32876
- y1: mapY(a.y),
32877
- x2: mapX(b.x),
32878
- y2: mapY(b.y),
32879
- color: curve.color ?? "#2563eb",
32880
- lineWidth: 2,
32881
- dash: curve.dash
32882
- });
32883
- lastInRange = b;
32884
- }
32936
+ if (a.x < xMin && b.x < xMin || a.x > xMax && b.x > xMax) {
32937
+ penDown = false;
32938
+ continue;
32939
+ }
32940
+ const clip = (p, q, xLim) => {
32941
+ const t = q.x === p.x ? 0 : (xLim - p.x) / (q.x - p.x);
32942
+ return { x: xLim, y: p.y + t * (q.y - p.y) };
32943
+ };
32944
+ const ca = a.x < xMin ? clip(a, b, xMin) : a.x > xMax ? clip(a, b, xMax) : a;
32945
+ const cb = b.x < xMin ? clip(a, b, xMin) : b.x > xMax ? clip(a, b, xMax) : b;
32946
+ const pax = mapX(ca.x);
32947
+ const pay = mapY(ca.y);
32948
+ d += `${penDown ? "L" : "M"} ${pax} ${pay} L ${mapX(cb.x)} ${mapY(cb.y)} `;
32949
+ penDown = true;
32950
+ if (b.x >= xMin && b.x <= xMax) lastInRange = b;
32951
+ }
32952
+ if (!d) continue;
32953
+ out.push({
32954
+ type: "path",
32955
+ path: d,
32956
+ color: curve.color ?? "#2563eb",
32957
+ lineWidth: 2,
32958
+ dash: curve.dash
32959
+ });
32885
32960
  if (showCurveLabels && curve.label && lastInRange) {
32886
32961
  out.push({
32887
32962
  type: "text",
@@ -32996,6 +33071,8 @@ var init_MathCanvas = __esm({
32996
33071
  showAxes,
32997
33072
  showGrid,
32998
33073
  gridStep,
33074
+ gridColor,
33075
+ axisColor,
32999
33076
  showTickLabels,
33000
33077
  showCurveLabels,
33001
33078
  curves,
@@ -33015,6 +33092,7 @@ var init_MathCanvas = __esm({
33015
33092
  {
33016
33093
  width,
33017
33094
  height,
33095
+ backgroundColor,
33018
33096
  shapes: derivedShapes,
33019
33097
  readouts,
33020
33098
  traces,
@@ -34245,13 +34323,13 @@ var init_MapView = __esm({
34245
34323
  shadowSize: [41, 41]
34246
34324
  });
34247
34325
  L.Marker.prototype.options.icon = defaultIcon;
34248
- const { useEffect: useEffect65, useRef: useRef65, useCallback: useCallback96, useState: useState100 } = React94__default;
34326
+ const { useEffect: useEffect67, useRef: useRef65, useCallback: useCallback96, useState: useState100 } = React94__default;
34249
34327
  const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
34250
34328
  const { useEventBus: useEventBus2 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
34251
34329
  function MapUpdater({ centerLat, centerLng, zoom }) {
34252
34330
  const map = useMap();
34253
34331
  const prevRef = useRef65({ centerLat, centerLng, zoom });
34254
- useEffect65(() => {
34332
+ useEffect67(() => {
34255
34333
  const prev = prevRef.current;
34256
34334
  if (prev.centerLat !== centerLat || prev.centerLng !== centerLng || prev.zoom !== zoom) {
34257
34335
  map.setView([centerLat, centerLng], zoom);
@@ -34262,7 +34340,7 @@ var init_MapView = __esm({
34262
34340
  }
34263
34341
  function MapClickHandler({ onMapClick }) {
34264
34342
  const map = useMap();
34265
- useEffect65(() => {
34343
+ useEffect67(() => {
34266
34344
  if (!onMapClick) return;
34267
34345
  const handler = (e) => {
34268
34346
  onMapClick(e.latlng.lat, e.latlng.lng);
@@ -34791,14 +34869,34 @@ var init_UploadDropZone = __esm({
34791
34869
  if (valid.length > 0) {
34792
34870
  onFiles?.(valid);
34793
34871
  if (action) {
34794
- eventBus.emit(`UI:${action}`, {
34795
- ...actionPayload,
34796
- files: valid.map((f3) => ({ name: f3.name, size: f3.size, type: f3.type }))
34872
+ void Promise.all(
34873
+ valid.map(
34874
+ (f3) => new Promise(
34875
+ (resolvePayload, rejectPayload) => {
34876
+ const reader = new FileReader();
34877
+ reader.onload = () => resolvePayload({
34878
+ name: f3.name,
34879
+ size: f3.size,
34880
+ type: f3.type,
34881
+ content: String(reader.result ?? "")
34882
+ });
34883
+ reader.onerror = () => rejectPayload(reader.error);
34884
+ reader.readAsDataURL(f3);
34885
+ }
34886
+ )
34887
+ )
34888
+ ).then((payloadFiles) => {
34889
+ eventBus.emit(`UI:${action}`, {
34890
+ ...actionPayload,
34891
+ files: payloadFiles
34892
+ });
34893
+ }).catch(() => {
34894
+ setError(t("Could not read the selected file"));
34797
34895
  });
34798
34896
  }
34799
34897
  }
34800
34898
  },
34801
- [validateFiles, onFiles, action, actionPayload, eventBus]
34899
+ [validateFiles, onFiles, action, actionPayload, eventBus, t]
34802
34900
  );
34803
34901
  const handleDragOver = (e) => {
34804
34902
  e.preventDefault();
@@ -43906,22 +44004,6 @@ var init_DataTable = __esm({
43906
44004
  DataTable.displayName = "DataTable";
43907
44005
  }
43908
44006
  });
43909
- function getFieldIcon(fieldName) {
43910
- const name = fieldName.toLowerCase();
43911
- if (name.includes("date") || name.includes("time")) return Calendar;
43912
- if (name.includes("status")) return Tag;
43913
- if (name.includes("priority")) return AlertCircle;
43914
- if (name.includes("progress") || name.includes("percent")) return TrendingUp;
43915
- if (name.includes("assignee") || name.includes("owner") || name.includes("user") || name.includes("member"))
43916
- return User;
43917
- if (name.includes("due")) return Clock;
43918
- if (name.includes("complete")) return CheckCircle2;
43919
- if (name.includes("budget") || name.includes("cost") || name.includes("price"))
43920
- return DollarSign;
43921
- if (name.includes("description") || name.includes("note") || name.includes("comment"))
43922
- return FileText;
43923
- return Package;
43924
- }
43925
44007
  function getBadgeVariant(fieldName, value) {
43926
44008
  const name = fieldName.toLowerCase();
43927
44009
  const val = String(value).toLowerCase();
@@ -43971,6 +44053,18 @@ function renderRichFieldValue(value, fieldName, fieldType, meta) {
43971
44053
  }
43972
44054
  ) });
43973
44055
  }
44056
+ if (fieldType === "url" && /^https?:\/\//i.test(str)) {
44057
+ return /* @__PURE__ */ jsx(
44058
+ "a",
44059
+ {
44060
+ href: str,
44061
+ target: "_blank",
44062
+ rel: "noreferrer",
44063
+ className: "text-primary hover:underline break-all",
44064
+ children: str
44065
+ }
44066
+ );
44067
+ }
43974
44068
  return str;
43975
44069
  }
43976
44070
  case "markdown":
@@ -44069,6 +44163,23 @@ function renderRichFieldValue(value, fieldName, fieldType, meta) {
44069
44163
  }
44070
44164
  return str;
44071
44165
  }
44166
+ case "boolean": {
44167
+ if (typeof value === "boolean") return value ? "Yes" : "No";
44168
+ if (str === "true") return "Yes";
44169
+ if (str === "false") return "No";
44170
+ return str;
44171
+ }
44172
+ case "array": {
44173
+ if (Array.isArray(value) && value.length > 0) {
44174
+ return /* @__PURE__ */ jsx(HStack, { gap: "xs", wrap: true, children: value.map((item, i) => /* @__PURE__ */ jsx(Badge, { variant: "default", children: String(item) }, i)) });
44175
+ }
44176
+ if (Array.isArray(value)) return "\u2014";
44177
+ return str;
44178
+ }
44179
+ case "email":
44180
+ return /* @__PURE__ */ jsx("a", { href: `mailto:${str}`, className: "text-primary hover:underline break-all", children: str });
44181
+ case "phone":
44182
+ return /* @__PURE__ */ jsx("a", { href: `tel:${str}`, className: "text-primary hover:underline", children: str });
44072
44183
  default:
44073
44184
  if (meta?.values && meta.values.length > 0 && meta.values.includes(str)) {
44074
44185
  return /* @__PURE__ */ jsx(Badge, { variant: getBadgeVariant(fieldName, str), children: humanizeEnumValue(str) });
@@ -44141,10 +44252,11 @@ var init_DetailPanel = __esm({
44141
44252
  avatar,
44142
44253
  sections: propSections,
44143
44254
  actions,
44144
- maxInlineActions,
44255
+ maxInlineActions = 2,
44145
44256
  backAction,
44146
44257
  footer,
44147
44258
  slideOver = false,
44259
+ showActions = true,
44148
44260
  className,
44149
44261
  entity,
44150
44262
  fields: propFields,
@@ -44192,9 +44304,6 @@ var init_DetailPanel = __esm({
44192
44304
  },
44193
44305
  [eventBus]
44194
44306
  );
44195
- const handleClose = useCallback(() => {
44196
- eventBus.emit("UI:CLOSE", {});
44197
- }, [eventBus]);
44198
44307
  const entityRecord = Array.isArray(entity) ? entity[0] : entity;
44199
44308
  const data = entityRecord ?? initialData;
44200
44309
  let title = propTitle;
@@ -44208,8 +44317,7 @@ var init_DetailPanel = __esm({
44208
44317
  const value = getNestedValue(normalizedData, field);
44209
44318
  return {
44210
44319
  label: labelFor(field),
44211
- value: formatFieldValue2(value, field),
44212
- icon: getFieldIcon(field)
44320
+ value: formatFieldValue2(value, field)
44213
44321
  };
44214
44322
  }
44215
44323
  return field;
@@ -44243,15 +44351,14 @@ var init_DetailPanel = __esm({
44243
44351
  (f3) => (!titleDerivedFromPrimary || f3 !== primaryField) && !statusFields.includes(f3) && !progressFields.includes(f3) && !metricFields.includes(f3) && !dateFields.includes(f3) && !descriptionFields.includes(f3)
44244
44352
  );
44245
44353
  sections = [];
44246
- if (statusFields.length > 0 || otherFields.length > 0) {
44354
+ if (otherFields.length > 0) {
44247
44355
  const overviewFields = [];
44248
- [...statusFields, ...otherFields].forEach((field) => {
44356
+ otherFields.forEach((field) => {
44249
44357
  const value = getNestedValue(normalizedData, field);
44250
44358
  if (value !== void 0 && value !== null) {
44251
44359
  overviewFields.push({
44252
44360
  label: labelFor(field),
44253
- value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field)),
44254
- icon: getFieldIcon(field)
44361
+ value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field))
44255
44362
  });
44256
44363
  }
44257
44364
  });
@@ -44266,8 +44373,7 @@ var init_DetailPanel = __esm({
44266
44373
  if (value !== void 0 && value !== null) {
44267
44374
  metricsFields.push({
44268
44375
  label: labelFor(field),
44269
- value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field)),
44270
- icon: getFieldIcon(field)
44376
+ value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field))
44271
44377
  });
44272
44378
  }
44273
44379
  });
@@ -44282,8 +44388,7 @@ var init_DetailPanel = __esm({
44282
44388
  if (value !== void 0 && value !== null) {
44283
44389
  timelineFields.push({
44284
44390
  label: labelFor(field),
44285
- value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field)),
44286
- icon: getFieldIcon(field)
44391
+ value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field))
44287
44392
  });
44288
44393
  }
44289
44394
  });
@@ -44298,8 +44403,7 @@ var init_DetailPanel = __esm({
44298
44403
  if (value !== void 0 && value !== null) {
44299
44404
  descFields.push({
44300
44405
  label: labelFor(field),
44301
- value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field)),
44302
- icon: getFieldIcon(field)
44406
+ value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field))
44303
44407
  });
44304
44408
  }
44305
44409
  });
@@ -44308,6 +44412,15 @@ var init_DetailPanel = __esm({
44308
44412
  }
44309
44413
  }
44310
44414
  }
44415
+ const renderSlot = useRenderSlot();
44416
+ const navStack = useNavStack();
44417
+ const { setCurrentLabel } = navStack;
44418
+ const resolvedTitle = normalizedData ? title : void 0;
44419
+ useEffect(() => {
44420
+ if (renderSlot === "main" && !slideOver && resolvedTitle) {
44421
+ setCurrentLabel(resolvedTitle);
44422
+ }
44423
+ }, [renderSlot, slideOver, resolvedTitle, setCurrentLabel]);
44311
44424
  if (isLoading) {
44312
44425
  return /* @__PURE__ */ jsx(
44313
44426
  LoadingState,
@@ -44346,8 +44459,7 @@ var init_DetailPanel = __esm({
44346
44459
  const value = normalizedData ? getNestedValue(normalizedData, field) : void 0;
44347
44460
  allFields.push({
44348
44461
  label: labelFor(field),
44349
- value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field)),
44350
- icon: getFieldIcon(field)
44462
+ value: renderRichFieldValue(value, field, fieldTypeMap[field], metaFor(field))
44351
44463
  });
44352
44464
  } else {
44353
44465
  allFields.push(field);
@@ -44359,24 +44471,50 @@ var init_DetailPanel = __esm({
44359
44471
  (a) => a.event === "CLOSE" || a.event === "CANCEL" || a.label?.toLowerCase() === "close"
44360
44472
  );
44361
44473
  const otherActions = actions?.filter((a) => a !== closeAction) ?? [];
44362
- const effectiveCloseAction = closeAction ?? { event: void 0};
44363
- const content = /* @__PURE__ */ jsx(Card, { variant: "elevated", children: /* @__PURE__ */ jsxs(VStack, { gap: "md", className: "p-6", children: [
44364
- /* @__PURE__ */ jsxs(HStack, { justify: "between", align: "center", gap: "xs", children: [
44365
- /* @__PURE__ */ jsx(HStack, { align: "center", gap: "xs", children: backAction && /* @__PURE__ */ jsx(
44366
- Button,
44474
+ const statusBadges = /* @__PURE__ */ jsxs(Fragment, { children: [
44475
+ normalizedData && effectiveFieldNames && effectiveFieldNames.filter(
44476
+ (f3) => f3.toLowerCase().includes("status") || f3.toLowerCase().includes("priority")
44477
+ ).map((field) => {
44478
+ const value = getNestedValue(normalizedData, field);
44479
+ if (!value) return null;
44480
+ return /* @__PURE__ */ jsx(
44481
+ Badge,
44367
44482
  {
44368
- variant: backAction.variant || "ghost",
44369
- size: "sm",
44370
- action: backAction.navigatesTo ? void 0 : backAction.event,
44371
- actionPayload: { row: normalizedData },
44372
- onClick: backAction.navigatesTo ? () => handleActionClick(backAction, normalizedData) : void 0,
44373
- icon: backAction.icon ?? ArrowLeft,
44374
- "data-testid": backAction.event ? `action-${backAction.event}` : "action-back",
44375
- children: backAction.label
44376
- }
44377
- ) }),
44378
- /* @__PURE__ */ jsxs(HStack, { justify: "end", align: "center", gap: "xs", children: [
44379
- (maxInlineActions != null ? otherActions.slice(0, maxInlineActions) : otherActions).map((action, idx) => /* @__PURE__ */ jsx(
44483
+ variant: getBadgeVariant(field, String(value)),
44484
+ children: humanizeEnumValue(String(value))
44485
+ },
44486
+ field
44487
+ );
44488
+ }),
44489
+ status && /* @__PURE__ */ jsx(Badge, { variant: status.variant ?? "default", children: status.label })
44490
+ ] });
44491
+ const content = /* @__PURE__ */ jsx(Card, { variant: "elevated", children: /* @__PURE__ */ jsxs(VStack, { gap: "md", className: "p-6", children: [
44492
+ /* @__PURE__ */ jsxs(HStack, { justify: "between", align: "start", gap: "md", children: [
44493
+ /* @__PURE__ */ jsxs(HStack, { align: "start", gap: "sm", className: "min-w-0", children: [
44494
+ backAction && /* @__PURE__ */ jsx(
44495
+ Button,
44496
+ {
44497
+ variant: backAction.variant || "ghost",
44498
+ size: "sm",
44499
+ action: backAction.navigatesTo ? void 0 : backAction.event,
44500
+ actionPayload: { row: normalizedData },
44501
+ onClick: backAction.navigatesTo ? () => handleActionClick(backAction, normalizedData) : void 0,
44502
+ icon: backAction.icon ?? ArrowLeft,
44503
+ "data-testid": backAction.event ? `action-${backAction.event}` : "action-back",
44504
+ children: backAction.label
44505
+ }
44506
+ ),
44507
+ avatar,
44508
+ /* @__PURE__ */ jsxs(VStack, { gap: "xs", className: "min-w-0", children: [
44509
+ /* @__PURE__ */ jsxs(HStack, { align: "center", gap: "sm", wrap: true, children: [
44510
+ /* @__PURE__ */ jsx(Typography, { variant: "h2", weight: "bold", children: title || "Details" }),
44511
+ statusBadges
44512
+ ] }),
44513
+ subtitle && /* @__PURE__ */ jsx(Typography, { variant: "body", color: "secondary", children: subtitle })
44514
+ ] })
44515
+ ] }),
44516
+ showActions && /* @__PURE__ */ jsxs(HStack, { justify: "end", align: "center", gap: "xs", className: "shrink-0", children: [
44517
+ otherActions.slice(0, maxInlineActions).map((action, idx) => /* @__PURE__ */ jsx(
44380
44518
  Button,
44381
44519
  {
44382
44520
  variant: action.variant || "secondary",
@@ -44391,7 +44529,7 @@ var init_DetailPanel = __esm({
44391
44529
  },
44392
44530
  idx
44393
44531
  )),
44394
- maxInlineActions != null && otherActions.length > maxInlineActions && /* @__PURE__ */ jsx(
44532
+ otherActions.length > maxInlineActions && /* @__PURE__ */ jsx(
44395
44533
  Menu,
44396
44534
  {
44397
44535
  position: "bottom-end",
@@ -44407,40 +44545,20 @@ var init_DetailPanel = __esm({
44407
44545
  }))
44408
44546
  }
44409
44547
  ),
44410
- /* @__PURE__ */ jsx(
44548
+ closeAction && /* @__PURE__ */ jsx(
44411
44549
  Button,
44412
44550
  {
44413
44551
  variant: "ghost",
44414
44552
  size: "sm",
44415
- action: effectiveCloseAction.event,
44553
+ action: closeAction.event,
44416
44554
  actionPayload: { row: normalizedData },
44417
- onClick: effectiveCloseAction.event ? void 0 : handleClose,
44555
+ onClick: closeAction.event ? void 0 : () => handleActionClick(closeAction, normalizedData),
44418
44556
  icon: X,
44419
- "data-testid": effectiveCloseAction.event ? `action-${effectiveCloseAction.event}` : "action-close"
44557
+ "data-testid": closeAction.event ? `action-${closeAction.event}` : "action-close"
44420
44558
  }
44421
44559
  )
44422
44560
  ] })
44423
44561
  ] }),
44424
- avatar,
44425
- /* @__PURE__ */ jsx(Typography, { variant: "h2", weight: "bold", children: title || "Details" }),
44426
- subtitle && /* @__PURE__ */ jsx(Typography, { variant: "body", color: "secondary", children: subtitle }),
44427
- /* @__PURE__ */ jsxs(HStack, { gap: "xs", wrap: true, children: [
44428
- normalizedData && effectiveFieldNames && effectiveFieldNames.filter(
44429
- (f3) => f3.toLowerCase().includes("status") || f3.toLowerCase().includes("priority")
44430
- ).map((field) => {
44431
- const value = getNestedValue(normalizedData, field);
44432
- if (!value) return null;
44433
- return /* @__PURE__ */ jsx(
44434
- Badge,
44435
- {
44436
- variant: getBadgeVariant(field, String(value)),
44437
- children: String(value)
44438
- },
44439
- field
44440
- );
44441
- }),
44442
- status && /* @__PURE__ */ jsx(Badge, { variant: status.variant ?? "default", children: status.label })
44443
- ] }),
44444
44562
  normalizedData && effectiveFieldNames && effectiveFieldNames.filter(
44445
44563
  (f3) => f3.toLowerCase().includes("progress") || f3.toLowerCase().includes("percent")
44446
44564
  ).map((field) => {
@@ -44473,9 +44591,10 @@ var init_DetailPanel = __esm({
44473
44591
  /* @__PURE__ */ jsx(
44474
44592
  Typography,
44475
44593
  {
44476
- variant: "small",
44477
- color: "secondary",
44594
+ variant: "caption",
44595
+ color: "muted",
44478
44596
  weight: "medium",
44597
+ className: "uppercase tracking-wider",
44479
44598
  children: field.label
44480
44599
  }
44481
44600
  ),
@@ -44544,7 +44663,7 @@ var init_DrawerSlot = __esm({
44544
44663
  position,
44545
44664
  width: size,
44546
44665
  className,
44547
- children
44666
+ children: /* @__PURE__ */ jsx(RenderSlotProvider, { slot: "drawer", children })
44548
44667
  }
44549
44668
  );
44550
44669
  };
@@ -46591,7 +46710,7 @@ var init_ModalSlot = __esm({
46591
46710
  title,
46592
46711
  size,
46593
46712
  className,
46594
- children
46713
+ children: /* @__PURE__ */ jsx(RenderSlotProvider, { slot: "modal", children })
46595
46714
  }
46596
46715
  );
46597
46716
  };
@@ -51013,7 +51132,10 @@ function MaybeTraitScope({
51013
51132
  }
51014
51133
  return /* @__PURE__ */ jsx(Fragment, { children });
51015
51134
  }
51016
- function UISlotComponent({
51135
+ function UISlotComponent(props) {
51136
+ return /* @__PURE__ */ jsx(RenderSlotProvider, { slot: props.slot, children: /* @__PURE__ */ jsx(UISlotComponentInner, { ...props }) });
51137
+ }
51138
+ function UISlotComponentInner({
51017
51139
  slot,
51018
51140
  portal = false,
51019
51141
  position,