@almadar/ui 5.160.0 → 5.162.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.
@@ -7,7 +7,7 @@ import { EventBusContext, useTraitScopeChain, useCurrentPagePath, useGameAudioCo
7
7
  export { GameAudioContext, GameAudioProvider, useGameAudioContext, useNavStack } from '@almadar/ui/providers';
8
8
  import { createLogger, isLogLevelEnabled } from '@almadar/logger';
9
9
  import * as LucideIcons2 from 'lucide-react';
10
- import { X, List, Printer, ChevronRight, ChevronLeft, Check, Copy, RotateCcw, Play, Terminal, CheckCircle, XCircle, ChevronDown, Search, ChevronUp, MoreHorizontal, ArrowLeft, DollarSign, Package, Calendar, Pencil, Eye, Image as Image$1, Upload, ZoomIn, TrendingUp, TrendingDown, Minus, AlertCircle, Circle, Clock, CheckCircle2, Loader2, Code, FileText, WrapText, HelpCircle, Type, Heading1, Heading2, Heading3, ListOrdered, Quote, GitBranch, Plus, ArrowRight, Trash, Menu as Menu$1, AlertTriangle, Trash2, Eraser, ZoomOut, Download, Lightbulb, PauseCircle, Link2 } from 'lucide-react';
10
+ import { X, List, Printer, ChevronRight, ChevronLeft, Check, Copy, RotateCcw, Play, Terminal, CheckCircle, XCircle, ChevronDown, Search, ChevronUp, MoreHorizontal, ArrowLeft, DollarSign, Package, Calendar, Pencil, Eye, Image as Image$1, Upload, ZoomIn, TrendingUp, TrendingDown, Minus, AlertCircle, Circle, Clock, CheckCircle2, Loader2, Code, FileText, WrapText, HelpCircle, Bold, Italic, Underline, Strikethrough, Heading1, Heading2, Heading3, ListOrdered, Quote, Link as Link$1, GitBranch, Plus, ArrowRight, Trash, Menu as Menu$1, AlertTriangle, Trash2, Eraser, ZoomOut, Download, Lightbulb, PauseCircle, Link2 } from 'lucide-react';
11
11
  import { useTranslate } from '@almadar/ui/hooks';
12
12
  import { useUISlots, useTheme } from '@almadar/ui/context';
13
13
  import { evaluate, createMinimalContext } from '@almadar/evaluator';
@@ -23950,8 +23950,9 @@ function DataGrid({
23950
23950
  const titleField = fieldDefs.find((f3) => f3.variant === "h3" || f3.variant === "h4") ?? fieldDefs[0];
23951
23951
  const badgeFields = fieldDefs.filter((f3) => f3.variant === "badge" && f3 !== titleField);
23952
23952
  const bodyFields = fieldDefs.filter((f3) => f3 !== titleField && !badgeFields.includes(f3));
23953
- const primaryActions = actionDefs.filter((a) => a.variant !== "danger");
23954
- const dangerActions = actionDefs.filter((a) => a.variant === "danger");
23953
+ const inlineCap = Math.min(maxInlineActions ?? 1, 1);
23954
+ const inlineCardActions = actionDefs.filter((a) => a.variant === "primary").slice(0, inlineCap);
23955
+ const menuCardActions = actionDefs.filter((a) => !inlineCardActions.includes(a));
23955
23956
  const fireAction = (action, itemData) => {
23956
23957
  if (action.navigatesTo) {
23957
23958
  const url = action.navigatesTo.replace(
@@ -23971,6 +23972,9 @@ function DataGrid({
23971
23972
  e.stopPropagation();
23972
23973
  fireAction(action, itemData);
23973
23974
  };
23975
+ const cardClickAction = actionDefs.find((a) => a.variant !== "danger");
23976
+ const handleCardClick = cardClickAction ? (itemData) => () => fireAction(cardClickAction, itemData) : void 0;
23977
+ const stopCardClick = handleCardClick ? (e) => e.stopPropagation() : void 0;
23974
23978
  const hasRenderProp = typeof children === "function";
23975
23979
  useEffect(() => {
23976
23980
  if (data.length > 0 && !hasRenderProp && fieldDefs.length === 0) {
@@ -24042,55 +24046,40 @@ function DataGrid({
24042
24046
  {
24043
24047
  "data-entity-row": true,
24044
24048
  "data-entity-id": id,
24045
- className: cn("relative group/rowactions", isSelected && "ring-2 ring-primary rounded-lg"),
24049
+ onClick: handleCardClick?.(itemData),
24050
+ className: cn("relative group/rowactions", handleCardClick && "cursor-pointer", isSelected && "ring-2 ring-primary rounded-lg"),
24046
24051
  children: [
24047
24052
  children(itemData, index),
24048
- actionDefs.length > 0 && /* @__PURE__ */ jsxs(Box, { className: "absolute top-2 right-2 z-10 opacity-0 group-hover/rowactions:opacity-100 focus-within:opacity-100 [@media(pointer:coarse)]:opacity-100 transition-opacity duration-fast", children: [
24049
- /* @__PURE__ */ jsxs(HStack, { gap: "xs", className: "rounded-md border border-border bg-card/95 backdrop-blur-sm shadow-sm p-0.5 [@media(pointer:coarse)]:hidden", children: [
24050
- (maxInlineActions != null ? actionDefs.slice(0, maxInlineActions) : actionDefs).map((action, idx) => /* @__PURE__ */ jsxs(
24051
- Button,
24052
- {
24053
- variant: action.variant === "primary" ? "primary" : "ghost",
24054
- size: "sm",
24055
- onClick: handleActionClick(action, itemData),
24056
- "data-testid": `action-${action.event}`,
24057
- "data-row-id": String(itemData.id),
24058
- className: action.variant === "danger" ? "text-error hover:text-error hover:bg-error/10" : void 0,
24059
- children: [
24060
- action.icon && renderIconInput(action.icon, { size: "xs", className: "mr-1" }),
24061
- action.label
24062
- ]
24063
- },
24064
- idx
24065
- )),
24066
- maxInlineActions != null && actionDefs.length > maxInlineActions && /* @__PURE__ */ jsx(
24067
- Menu,
24068
- {
24069
- position: "bottom-end",
24070
- trigger: /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", "aria-label": t("common.actions"), "data-testid": "action-overflow", children: /* @__PURE__ */ jsx(Icon, { name: "more-horizontal", size: "xs" }) }),
24071
- items: actionDefs.slice(maxInlineActions).map((action) => ({
24072
- label: action.label,
24073
- icon: action.icon,
24074
- event: action.event,
24075
- onClick: () => fireAction(action, itemData)
24076
- }))
24077
- }
24078
- )
24079
- ] }),
24080
- /* @__PURE__ */ jsx(Box, { className: "hidden [@media(pointer:coarse)]:block rounded-md border border-border bg-card/95 backdrop-blur-sm shadow-sm p-0.5", children: /* @__PURE__ */ jsx(
24053
+ actionDefs.length > 0 && /* @__PURE__ */ jsx(Box, { onClick: stopCardClick, className: "absolute top-2 right-2 z-10 opacity-0 group-hover/rowactions:opacity-100 focus-within:opacity-100 [@media(pointer:coarse)]:opacity-100 transition-opacity duration-fast", children: /* @__PURE__ */ jsxs(HStack, { gap: "xs", className: "rounded-md border border-border bg-card/95 backdrop-blur-sm shadow-sm p-0.5", children: [
24054
+ inlineCardActions.map((action, idx) => /* @__PURE__ */ jsxs(
24055
+ Button,
24056
+ {
24057
+ variant: "primary",
24058
+ size: "sm",
24059
+ onClick: handleActionClick(action, itemData),
24060
+ "data-testid": `action-${action.event}`,
24061
+ "data-row-id": String(itemData.id),
24062
+ children: [
24063
+ action.icon && renderIconInput(action.icon, { size: "xs", className: "mr-1" }),
24064
+ action.label
24065
+ ]
24066
+ },
24067
+ idx
24068
+ )),
24069
+ menuCardActions.length > 0 && /* @__PURE__ */ jsx(
24081
24070
  Menu,
24082
24071
  {
24083
24072
  position: "bottom-end",
24084
24073
  trigger: /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", "aria-label": t("common.actions"), "data-testid": "action-overflow", children: /* @__PURE__ */ jsx(Icon, { name: "more-horizontal", size: "xs" }) }),
24085
- items: actionDefs.map((action) => ({
24074
+ items: menuCardActions.map((action) => ({
24086
24075
  label: action.label,
24087
24076
  icon: action.icon,
24088
- event: action.event,
24077
+ variant: action.variant === "danger" ? "danger" : "default",
24089
24078
  onClick: () => fireAction(action, itemData)
24090
24079
  }))
24091
24080
  }
24092
- ) })
24093
- ] })
24081
+ )
24082
+ ] }) })
24094
24083
  ]
24095
24084
  },
24096
24085
  id
@@ -24104,12 +24093,14 @@ function DataGrid({
24104
24093
  {
24105
24094
  "data-entity-row": true,
24106
24095
  "data-entity-id": id,
24096
+ onClick: handleCardClick?.(itemData),
24107
24097
  className: cn(
24108
24098
  "bg-card rounded-lg",
24109
24099
  "border border-border",
24110
24100
  "shadow-elevation-card hover:shadow-elevation-dialog",
24111
24101
  "hover:border-primary transition-all",
24112
24102
  "flex flex-col",
24103
+ handleCardClick && "cursor-pointer",
24113
24104
  isSelected && "ring-2 ring-primary border-primary"
24114
24105
  ),
24115
24106
  children: [
@@ -24159,53 +24150,35 @@ function DataGrid({
24159
24150
  ] }, field.name);
24160
24151
  }) })
24161
24152
  ] }),
24162
- (primaryActions.length > 0 || dangerActions.length > 0) && /* @__PURE__ */ jsxs(HStack, { gap: "xs", className: "flex-shrink-0", children: [
24163
- (maxInlineActions != null ? primaryActions.slice(0, maxInlineActions) : primaryActions).map((action, idx) => /* @__PURE__ */ jsx(
24153
+ actionDefs.length > 0 && /* @__PURE__ */ jsxs(HStack, { gap: "xs", onClick: stopCardClick, className: "flex-shrink-0", children: [
24154
+ inlineCardActions.map((action, idx) => /* @__PURE__ */ jsx(
24164
24155
  Button,
24165
24156
  {
24166
- variant: action.variant === "primary" ? "primary" : "ghost",
24157
+ variant: "primary",
24167
24158
  size: "sm",
24168
24159
  onClick: handleActionClick(action, itemData),
24169
24160
  "data-testid": `action-${action.event}`,
24170
24161
  "data-row-id": String(itemData.id),
24171
24162
  "aria-label": action.label,
24172
24163
  title: action.label,
24173
- className: cn(
24174
- action.variant === "primary" ? void 0 : "text-muted-foreground hover:text-foreground",
24175
- action.icon && "px-2"
24176
- ),
24164
+ className: cn(action.icon && "px-2"),
24177
24165
  children: action.icon ? renderIconInput(action.icon, { size: "xs" }) : action.label
24178
24166
  },
24179
24167
  idx
24180
24168
  )),
24181
- maxInlineActions != null && primaryActions.length > maxInlineActions && /* @__PURE__ */ jsx(
24169
+ menuCardActions.length > 0 && /* @__PURE__ */ jsx(
24182
24170
  Menu,
24183
24171
  {
24184
24172
  position: "bottom-end",
24185
24173
  trigger: /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", "aria-label": t("common.actions"), "data-testid": "action-overflow", children: /* @__PURE__ */ jsx(Icon, { name: "more-horizontal", size: "xs" }) }),
24186
- items: primaryActions.slice(maxInlineActions).map((action) => ({
24174
+ items: menuCardActions.map((action) => ({
24187
24175
  label: action.label,
24188
24176
  icon: action.icon,
24189
- event: action.event,
24177
+ variant: action.variant === "danger" ? "danger" : "default",
24190
24178
  onClick: () => fireAction(action, itemData)
24191
24179
  }))
24192
24180
  }
24193
- ),
24194
- dangerActions.map((action, idx) => /* @__PURE__ */ jsx(
24195
- Button,
24196
- {
24197
- variant: "ghost",
24198
- size: "sm",
24199
- onClick: handleActionClick(action, itemData),
24200
- "data-testid": `action-${action.event}`,
24201
- "data-row-id": String(itemData.id),
24202
- "aria-label": action.label,
24203
- title: action.label,
24204
- className: "text-error hover:text-error hover:bg-error/10 px-2",
24205
- children: action.icon ? renderIconInput(action.icon, { size: "xs" }) : action.label
24206
- },
24207
- `danger-${idx}`
24208
- ))
24181
+ )
24209
24182
  ] })
24210
24183
  ] }) }),
24211
24184
  bodyFields.length > 0 && /* @__PURE__ */ jsx(Box, { className: "px-4 pt-2 pb-4 flex-1", children: /* @__PURE__ */ jsxs(VStack, { gap: "xs", children: [
@@ -24447,54 +24420,59 @@ function DataList({
24447
24420
  if (!itemActions || itemActions.length === 0) return null;
24448
24421
  const inline = maxInlineActions != null ? itemActions.slice(0, maxInlineActions) : itemActions;
24449
24422
  const overflow = maxInlineActions != null ? itemActions.slice(maxInlineActions) : [];
24450
- return /* @__PURE__ */ jsxs(HStack, { gap: "xs", className: "flex-shrink-0", children: [
24451
- inline.map((action, idx) => /* @__PURE__ */ jsxs(
24452
- Button,
24453
- {
24454
- variant: action.variant ?? "ghost",
24455
- size: "sm",
24456
- onClick: handleActionClick(action, itemData),
24457
- "data-testid": `action-${action.event}`,
24458
- "data-row-id": String(itemData.id),
24459
- className: cn(
24460
- action.variant === "danger" && "text-error hover:bg-error/10",
24461
- // Must sit on the Button itself: the variant's own text colour
24462
- // beats an inherited one from the row wrapper.
24463
- onPrimary && "!text-primary-foreground hover:bg-primary-foreground/15"
24464
- ),
24465
- children: [
24466
- action.icon && renderIconInput2(action.icon, { size: "xs", className: "mr-1" }),
24467
- action.label
24468
- ]
24469
- },
24470
- idx
24471
- )),
24472
- overflow.length > 0 && /* @__PURE__ */ jsx(
24473
- Menu,
24474
- {
24475
- position: "bottom-end",
24476
- trigger: /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", "aria-label": t("common.actions"), "data-testid": "action-overflow", children: /* @__PURE__ */ jsx(Icon, { name: "more-horizontal", size: "xs" }) }),
24477
- items: overflow.map((action) => ({
24478
- label: action.label,
24479
- icon: action.icon,
24480
- event: action.event,
24481
- variant: action.variant === "danger" ? "danger" : "default",
24482
- onClick: () => eventBus.emit(`UI:${action.event}`, {
24483
- id: itemData.id,
24484
- row: itemData
24485
- })
24486
- }))
24487
- }
24488
- )
24489
- ] });
24423
+ return (
24424
+ // stopPropagation at the cluster: the "⋯" trigger opens the overflow
24425
+ // menu without also firing the row's default click (inline buttons
24426
+ // already stop it in handleActionClick; the Menu panel is portaled).
24427
+ /* @__PURE__ */ jsxs(HStack, { gap: "xs", onClick: rowClickEvent ? (e) => e.stopPropagation() : void 0, className: "flex-shrink-0", children: [
24428
+ inline.map((action, idx) => /* @__PURE__ */ jsxs(
24429
+ Button,
24430
+ {
24431
+ variant: action.variant ?? "ghost",
24432
+ size: "sm",
24433
+ onClick: handleActionClick(action, itemData),
24434
+ "data-testid": `action-${action.event}`,
24435
+ "data-row-id": String(itemData.id),
24436
+ className: cn(
24437
+ action.variant === "danger" && "text-error hover:bg-error/10",
24438
+ // Must sit on the Button itself: the variant's own text colour
24439
+ // beats an inherited one from the row wrapper.
24440
+ onPrimary && "!text-primary-foreground hover:bg-primary-foreground/15"
24441
+ ),
24442
+ children: [
24443
+ action.icon && renderIconInput2(action.icon, { size: "xs", className: "mr-1" }),
24444
+ action.label
24445
+ ]
24446
+ },
24447
+ idx
24448
+ )),
24449
+ overflow.length > 0 && /* @__PURE__ */ jsx(
24450
+ Menu,
24451
+ {
24452
+ position: "bottom-end",
24453
+ trigger: /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", "aria-label": t("common.actions"), "data-testid": "action-overflow", children: /* @__PURE__ */ jsx(Icon, { name: "more-horizontal", size: "xs" }) }),
24454
+ items: overflow.map((action) => ({
24455
+ label: action.label,
24456
+ icon: action.icon,
24457
+ variant: action.variant === "danger" ? "danger" : "default",
24458
+ onClick: () => eventBus.emit(`UI:${action.event}`, {
24459
+ id: itemData.id,
24460
+ row: itemData
24461
+ })
24462
+ }))
24463
+ }
24464
+ )
24465
+ ] })
24466
+ );
24490
24467
  };
24468
+ const rowClickEvent = itemClickEvent || itemActions?.find((a) => a.variant !== "danger")?.event;
24491
24469
  const handleRowClick = (itemData) => () => {
24492
- if (!itemClickEvent) return;
24470
+ if (!rowClickEvent) return;
24493
24471
  const payload = {
24494
24472
  id: itemData.id,
24495
24473
  row: itemData
24496
24474
  };
24497
- eventBus.emit(`UI:${itemClickEvent}`, payload);
24475
+ eventBus.emit(`UI:${rowClickEvent}`, payload);
24498
24476
  };
24499
24477
  if (isLoading) {
24500
24478
  return /* @__PURE__ */ jsx(Box, { className: "text-center py-8", children: /* @__PURE__ */ jsx(Typography, { variant: "body", color: "secondary", children: t("loading.items") }) });
@@ -24542,10 +24520,10 @@ function DataList({
24542
24520
  {
24543
24521
  "data-entity-row": true,
24544
24522
  "data-entity-id": id,
24545
- onClick: itemClickEvent ? handleRowClick(itemData) : void 0,
24523
+ onClick: rowClickEvent ? handleRowClick(itemData) : void 0,
24546
24524
  className: cn(
24547
24525
  "flex px-4 group/rowactions",
24548
- itemClickEvent && "cursor-pointer",
24526
+ rowClickEvent && "cursor-pointer",
24549
24527
  isSent ? "justify-end" : "justify-start"
24550
24528
  ),
24551
24529
  children: /* @__PURE__ */ jsxs(
@@ -24621,7 +24599,7 @@ function DataList({
24621
24599
  const id2 = itemData.id || String(index);
24622
24600
  const actions = renderItemActions(itemData);
24623
24601
  return wrapDnd(
24624
- /* @__PURE__ */ jsxs(Box, { "data-entity-row": true, "data-entity-id": id2, onClick: itemClickEvent ? handleRowClick(itemData) : void 0, className: cn("relative group/rowactions", itemClickEvent && "cursor-pointer"), children: [
24602
+ /* @__PURE__ */ jsxs(Box, { "data-entity-row": true, "data-entity-id": id2, onClick: rowClickEvent ? handleRowClick(itemData) : void 0, className: cn("relative group/rowactions", rowClickEvent && "cursor-pointer"), children: [
24625
24603
  children(itemData, index),
24626
24604
  actions && /* @__PURE__ */ jsxs(Box, { className: "absolute top-2 right-2 z-10 opacity-0 group-hover/rowactions:opacity-100 focus-within:opacity-100 [@media(pointer:coarse)]:opacity-100 transition-opacity duration-fast", children: [
24627
24605
  /* @__PURE__ */ jsx(Box, { className: "rounded-md border border-border bg-card/95 backdrop-blur-sm shadow-sm p-0.5 [@media(pointer:coarse)]:hidden", children: actions }),
@@ -24633,7 +24611,6 @@ function DataList({
24633
24611
  items: (itemActions ?? []).map((action) => ({
24634
24612
  label: action.label,
24635
24613
  icon: action.icon,
24636
- event: action.event,
24637
24614
  variant: action.variant === "danger" ? "danger" : "default",
24638
24615
  onClick: () => eventBus.emit(`UI:${action.event}`, {
24639
24616
  id: itemData.id,
@@ -24650,7 +24627,7 @@ function DataList({
24650
24627
  const id = itemData.id || String(index);
24651
24628
  const titleValue = getNestedValue(itemData, titleField?.name ?? "");
24652
24629
  return wrapDnd(
24653
- /* @__PURE__ */ jsxs(Box, { "data-entity-row": true, "data-entity-id": id, onClick: itemClickEvent ? handleRowClick(itemData) : void 0, className: cn(itemClickEvent && "cursor-pointer"), children: [
24630
+ /* @__PURE__ */ jsxs(Box, { "data-entity-row": true, "data-entity-id": id, onClick: rowClickEvent ? handleRowClick(itemData) : void 0, className: cn(rowClickEvent && "cursor-pointer"), children: [
24654
24631
  /* @__PURE__ */ jsxs(
24655
24632
  Box,
24656
24633
  {
@@ -33277,7 +33254,7 @@ var init_MapView = __esm({
33277
33254
  shadowSize: [41, 41]
33278
33255
  });
33279
33256
  L.Marker.prototype.options.icon = defaultIcon;
33280
- const { useEffect: useEffect69, useRef: useRef65, useCallback: useCallback108, useState: useState104 } = React77__default;
33257
+ const { useEffect: useEffect69, useRef: useRef65, useCallback: useCallback108, useState: useState106 } = React77__default;
33281
33258
  const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
33282
33259
  const { useEventBus: useEventBus2 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
33283
33260
  function MapUpdater({ centerLat, centerLng, zoom }) {
@@ -33322,7 +33299,7 @@ var init_MapView = __esm({
33322
33299
  showAttribution = true
33323
33300
  }) {
33324
33301
  const eventBus = useEventBus2();
33325
- const [clickedPosition, setClickedPosition] = useState104(null);
33302
+ const [clickedPosition, setClickedPosition] = useState106(null);
33326
33303
  const handleMapClick = useCallback108((lat, lng) => {
33327
33304
  if (showClickedPin) {
33328
33305
  setClickedPosition({ lat, lng });
@@ -37684,741 +37661,775 @@ var init_PositionedCanvas = __esm({
37684
37661
  PositionedCanvas.displayName = "PositionedCanvas";
37685
37662
  }
37686
37663
  });
37687
- function nextBlockId(prefix = "blk") {
37688
- _idSeq += 1;
37689
- const random = Math.random().toString(36).slice(2, 8);
37690
- return `${prefix}-${Date.now().toString(36)}-${_idSeq}-${random}`;
37691
- }
37692
- function normalizeBlocks(raw) {
37693
- if (!Array.isArray(raw) || raw.length === 0) return [createBlock("paragraph")];
37694
- return raw.map((row) => {
37695
- const entity = row;
37696
- const rawType = entity.type;
37697
- const type = typeof rawType === "string" && BLOCK_TYPES.has(rawType) ? rawType : "paragraph";
37698
- const id = typeof entity.id === "string" && entity.id ? entity.id : nextBlockId(type);
37699
- return { ...entity, id, type };
37700
- });
37701
- }
37702
- function createBlock(type) {
37703
- switch (type) {
37704
- case "bullet-list":
37705
- case "numbered-list":
37706
- return {
37707
- id: nextBlockId(type),
37708
- type,
37709
- children: [
37710
- { id: nextBlockId("li"), type: "paragraph", content: "" }
37711
- ]
37712
- };
37713
- case "image":
37714
- return {
37715
- id: nextBlockId(type),
37716
- type,
37717
- content: "",
37718
- metadata: { url: "", caption: "" }
37719
- };
37720
- case "code":
37721
- return {
37722
- id: nextBlockId(type),
37723
- type,
37724
- content: "",
37725
- metadata: { language: "plaintext" }
37726
- };
37727
- case "divider":
37728
- return { id: nextBlockId(type), type };
37729
- default:
37730
- return { id: nextBlockId(type), type, content: "" };
37664
+ function safeUrl(raw, kinds) {
37665
+ const url = raw.trim();
37666
+ if (url.startsWith("#") || url.startsWith("/")) return url;
37667
+ const lower = url.toLowerCase();
37668
+ for (const kind of kinds) {
37669
+ if (lower.startsWith(kind)) return url;
37731
37670
  }
37671
+ return null;
37732
37672
  }
37733
- function replaceBlock(blocks, id, updater) {
37734
- return blocks.map((block) => block.id === id ? updater(block) : block);
37735
- }
37736
- function removeBlock(blocks, id) {
37737
- return blocks.filter((block) => block.id !== id);
37738
- }
37739
- function duplicateBlock(block) {
37740
- return {
37741
- ...block,
37742
- id: nextBlockId(block.type),
37743
- children: block.children?.map((child) => ({
37744
- ...child,
37745
- id: nextBlockId("li")
37746
- })),
37747
- metadata: block.metadata ? { ...block.metadata } : void 0
37748
- };
37749
- }
37750
- function insertAfter(blocks, targetId, inserted) {
37751
- const idx = blocks.findIndex((b) => b.id === targetId);
37752
- if (idx === -1) return [...blocks, inserted];
37753
- const next = blocks.slice();
37754
- next.splice(idx + 1, 0, inserted);
37755
- return next;
37756
- }
37757
- function changeBlockType(block, type) {
37758
- if (block.type === type) return block;
37759
- if (type === "bullet-list" || type === "numbered-list") {
37760
- if (block.children && block.children.length > 0) {
37761
- return { ...block, type };
37673
+ function sanitizeNode(el) {
37674
+ const children = Array.from(el.children);
37675
+ for (const child of children) {
37676
+ const tag = child.tagName.toLowerCase();
37677
+ if (DROP_TAGS.has(tag)) {
37678
+ child.remove();
37679
+ continue;
37762
37680
  }
37763
- const seed2 = block.content ?? "";
37764
- return {
37765
- id: block.id,
37766
- type,
37767
- children: [
37768
- { id: nextBlockId("li"), type: "paragraph", content: seed2 }
37769
- ]
37770
- };
37771
- }
37772
- if (type === "divider") {
37773
- return { id: block.id, type };
37681
+ const allowed = ALLOWED_ATTRS[tag];
37682
+ if (!allowed) {
37683
+ sanitizeNode(child);
37684
+ child.replaceWith(...Array.from(child.childNodes));
37685
+ continue;
37686
+ }
37687
+ for (const attr of Array.from(child.attributes)) {
37688
+ const name = attr.name.toLowerCase();
37689
+ if (!allowed.includes(name)) {
37690
+ child.removeAttribute(attr.name);
37691
+ continue;
37692
+ }
37693
+ if (name === "href") {
37694
+ const url = safeUrl(attr.value, ["http://", "https://", "mailto:"]);
37695
+ if (url === null) child.removeAttribute(attr.name);
37696
+ else child.setAttribute("href", url);
37697
+ }
37698
+ if (name === "src") {
37699
+ const url = safeUrl(attr.value, ["http://", "https://", "data:image/"]);
37700
+ if (url === null) child.remove();
37701
+ else child.setAttribute("src", url);
37702
+ }
37703
+ }
37704
+ sanitizeNode(child);
37774
37705
  }
37775
- if (type === "image") {
37776
- return {
37777
- id: block.id,
37778
- type,
37779
- content: "",
37780
- metadata: { url: "", caption: block.content ?? "" }
37781
- };
37706
+ }
37707
+ function sanitizeRichHtml(html) {
37708
+ if (!html || typeof html !== "string") return "";
37709
+ if (typeof window === "undefined" || typeof DOMParser === "undefined") return "";
37710
+ const doc = new DOMParser().parseFromString(html, "text/html");
37711
+ sanitizeNode(doc.body);
37712
+ if (!doc.body.querySelector("img, hr") && (doc.body.textContent ?? "").trim().length === 0) {
37713
+ return "";
37782
37714
  }
37783
- if (type === "code") {
37784
- return {
37785
- id: block.id,
37786
- type,
37787
- content: block.content ?? "",
37788
- metadata: { language: "plaintext" }
37789
- };
37715
+ return doc.body.innerHTML.replace(/​/g, "");
37716
+ }
37717
+ function htmlIsEmpty(el) {
37718
+ if (el.querySelector("img, hr")) return false;
37719
+ return (el.textContent ?? "").trim().length === 0;
37720
+ }
37721
+ function closestBlock(node, root) {
37722
+ let cur = node;
37723
+ while (cur && cur !== root) {
37724
+ if (cur instanceof Element && BLOCK_TAGS.has(cur.tagName)) return cur;
37725
+ cur = cur.parentNode;
37790
37726
  }
37791
- const seed = block.children?.[0]?.content ?? block.content ?? "";
37792
- return { id: block.id, type, content: seed };
37727
+ return null;
37793
37728
  }
37794
- function BlockMenu({ block, readOnly, onDelete, onDuplicate, onChangeType }) {
37795
- const { t } = useTranslate();
37796
- const [open, setOpen] = useState(false);
37797
- const ref = useRef(null);
37798
- useEffect(() => {
37799
- if (!open) return;
37800
- function onDocClick(e) {
37801
- if (ref.current && !ref.current.contains(e.target)) {
37802
- setOpen(false);
37729
+ function RichTextStyles() {
37730
+ return /* @__PURE__ */ jsx("style", { children: RICH_TEXT_CSS });
37731
+ }
37732
+ function readToolbarState(root) {
37733
+ const sel = window.getSelection();
37734
+ if (!sel || sel.rangeCount === 0) return IDLE_TOOLBAR;
37735
+ const anchor = sel.anchorNode;
37736
+ if (!anchor || !root.contains(anchor)) return IDLE_TOOLBAR;
37737
+ let block = null;
37738
+ let link = false;
37739
+ let node = anchor;
37740
+ while (node && node !== root) {
37741
+ if (node instanceof Element) {
37742
+ const tag = node.tagName.toLowerCase();
37743
+ if (!block && (tag === "h1" || tag === "h2" || tag === "h3" || tag === "blockquote" || tag === "pre")) {
37744
+ block = tag;
37803
37745
  }
37746
+ if (tag === "a") link = true;
37804
37747
  }
37805
- document.addEventListener("mousedown", onDocClick);
37806
- return () => document.removeEventListener("mousedown", onDocClick);
37807
- }, [open]);
37808
- if (readOnly) return null;
37809
- return /* @__PURE__ */ jsxs(Box, { ref, className: "relative", children: [
37810
- /* @__PURE__ */ jsx(
37811
- Button,
37812
- {
37813
- type: "button",
37814
- variant: "ghost",
37815
- "aria-label": t("richBlockEditor.blockActions"),
37816
- className: cn(
37817
- "inline-flex items-center justify-center",
37818
- "h-6 w-6 rounded-sm p-0 gap-0",
37819
- "text-muted-foreground hover:bg-muted hover:text-foreground",
37820
- "opacity-0 group-hover:opacity-100 focus-visible:opacity-100",
37821
- "transition-opacity"
37822
- ),
37823
- onClick: () => setOpen((v) => !v),
37824
- children: /* @__PURE__ */ jsx(Icon, { name: "more-horizontal", className: "w-3.5 h-3.5" })
37825
- }
37826
- ),
37827
- open && /* @__PURE__ */ jsxs(
37828
- Box,
37829
- {
37830
- role: "menu",
37831
- className: cn(
37832
- "absolute right-0 z-10 mt-1 w-44",
37833
- "rounded-container border border-border bg-popover shadow-elevation-popover",
37834
- "py-1 text-sm"
37835
- ),
37836
- children: [
37837
- /* @__PURE__ */ jsx(Box, { className: "px-2 py-1 text-xs uppercase tracking-wide text-muted-foreground", children: t(BLOCK_TYPE_LABEL_KEY[block.type]) }),
37838
- /* @__PURE__ */ jsxs(
37839
- Button,
37840
- {
37841
- type: "button",
37842
- variant: "ghost",
37843
- role: "menuitem",
37844
- className: "flex w-full items-center gap-2 px-2 py-1.5 text-left justify-start rounded-none",
37845
- onClick: () => {
37846
- onDuplicate();
37847
- setOpen(false);
37848
- },
37849
- children: [
37850
- /* @__PURE__ */ jsx(Icon, { name: "plus", className: "w-3.5 h-3.5" }),
37851
- " ",
37852
- t("richBlockEditor.duplicate")
37853
- ]
37854
- }
37855
- ),
37856
- /* @__PURE__ */ jsxs(
37857
- Button,
37858
- {
37859
- type: "button",
37860
- variant: "ghost",
37861
- role: "menuitem",
37862
- className: "flex w-full items-center gap-2 px-2 py-1.5 text-left text-destructive hover:bg-muted justify-start rounded-none",
37863
- onClick: () => {
37864
- onDelete();
37865
- setOpen(false);
37866
- },
37867
- children: [
37868
- /* @__PURE__ */ jsx(Icon, { name: "trash", className: "w-3.5 h-3.5" }),
37869
- " ",
37870
- t("common.delete")
37871
- ]
37872
- }
37873
- ),
37874
- CHANGEABLE_TYPES.includes(block.type) && /* @__PURE__ */ jsxs(Fragment, { children: [
37875
- /* @__PURE__ */ jsx(Box, { className: "my-1 border-t border-border" }),
37876
- /* @__PURE__ */ jsx(Box, { className: "px-2 py-1 text-xs uppercase tracking-wide text-muted-foreground", children: t("richBlockEditor.turnInto") }),
37877
- CHANGEABLE_TYPES.filter((bt) => bt !== block.type).map((bt) => /* @__PURE__ */ jsx(
37878
- Button,
37879
- {
37880
- type: "button",
37881
- variant: "ghost",
37882
- role: "menuitem",
37883
- className: "flex w-full items-center gap-2 px-2 py-1.5 text-left justify-start rounded-none",
37884
- onClick: () => {
37885
- onChangeType(bt);
37886
- setOpen(false);
37887
- },
37888
- children: t(BLOCK_TYPE_LABEL_KEY[bt])
37889
- },
37890
- bt
37891
- ))
37892
- ] })
37893
- ]
37894
- }
37895
- )
37896
- ] });
37748
+ node = node.parentNode;
37749
+ }
37750
+ return {
37751
+ bold: document.queryCommandState("bold"),
37752
+ italic: document.queryCommandState("italic"),
37753
+ underline: document.queryCommandState("underline"),
37754
+ strikeThrough: document.queryCommandState("strikeThrough"),
37755
+ block,
37756
+ bullets: document.queryCommandState("insertUnorderedList"),
37757
+ numbers: document.queryCommandState("insertOrderedList"),
37758
+ link
37759
+ };
37897
37760
  }
37898
- function Editable({
37899
- tag,
37900
- value,
37901
- readOnly,
37902
- placeholder,
37903
- className,
37904
- ariaLabel,
37905
- onValueChange
37906
- }) {
37907
- const ref = useRef(null);
37908
- useEffect(() => {
37909
- const el = ref.current;
37910
- if (!el) return;
37911
- const isFocused = document.activeElement === el;
37912
- if (!isFocused && el.textContent !== value) {
37913
- el.textContent = value;
37914
- }
37915
- }, [value]);
37916
- const handleInput = useCallback(
37917
- (e) => {
37918
- onValueChange(e.currentTarget.textContent ?? "");
37919
- },
37920
- [onValueChange]
37921
- );
37761
+ function ToolbarButton({ icon: IconCmp, label, active, onExec }) {
37922
37762
  return /* @__PURE__ */ jsx(
37923
- Box,
37763
+ Button,
37924
37764
  {
37925
- as: tag,
37926
- ref,
37927
- contentEditable: !readOnly,
37928
- suppressContentEditableWarning: true,
37929
- role: readOnly ? void 0 : "textbox",
37930
- "aria-label": ariaLabel,
37931
- "aria-multiline": "true",
37932
- "data-placeholder": placeholder,
37765
+ type: "button",
37766
+ variant: "ghost",
37767
+ size: "sm",
37768
+ "aria-label": label,
37769
+ "aria-pressed": active,
37770
+ title: label,
37933
37771
  className: cn(
37934
- "outline-none focus-visible:ring-1 focus-visible:ring-ring rounded-sm",
37935
- "empty:before:content-[attr(data-placeholder)] empty:before:text-muted-foreground/60",
37936
- className
37772
+ "h-8 w-8 p-0 gap-0 justify-center",
37773
+ active && "bg-muted text-foreground"
37937
37774
  ),
37938
- onInput: handleInput
37775
+ onMouseDown: (e) => {
37776
+ e.preventDefault();
37777
+ onExec();
37778
+ },
37779
+ children: /* @__PURE__ */ jsx(IconCmp, { size: 15 })
37939
37780
  }
37940
37781
  );
37941
37782
  }
37942
- function BlockRow({
37943
- block,
37944
- readOnly,
37945
- showAffordances,
37946
- placeholder,
37947
- onUpdate,
37948
- onDelete,
37949
- onDuplicate,
37950
- onChangeType
37951
- }) {
37952
- const { t } = useTranslate();
37953
- const setContent = useCallback(
37954
- (next) => onUpdate((b) => ({ ...b, content: next })),
37955
- [onUpdate]
37956
- );
37957
- const setMetadata = useCallback(
37958
- (key, value) => onUpdate((b) => ({
37959
- ...b,
37960
- metadata: { ...b.metadata ?? {}, [key]: value }
37961
- })),
37962
- [onUpdate]
37963
- );
37964
- const setChildContent = useCallback(
37965
- (childId, next) => onUpdate((b) => ({
37966
- ...b,
37967
- children: (b.children ?? []).map(
37968
- (c) => c.id === childId ? { ...c, content: next } : c
37969
- )
37970
- })),
37971
- [onUpdate]
37972
- );
37973
- const addListItem = useCallback(
37974
- () => onUpdate((b) => ({
37975
- ...b,
37976
- children: [
37977
- ...b.children ?? [],
37978
- { id: nextBlockId("li"), type: "paragraph", content: "" }
37979
- ]
37980
- })),
37981
- [onUpdate]
37982
- );
37983
- const removeListItem = useCallback(
37984
- (childId) => onUpdate((b) => {
37985
- const remaining = (b.children ?? []).filter((c) => c.id !== childId);
37986
- return {
37987
- ...b,
37988
- children: remaining.length === 0 ? [{ id: nextBlockId("li"), type: "paragraph", content: "" }] : remaining
37989
- };
37990
- }),
37991
- [onUpdate]
37992
- );
37993
- const renderBody = () => {
37994
- switch (block.type) {
37995
- case "heading-1":
37996
- return /* @__PURE__ */ jsx(
37997
- Editable,
37998
- {
37999
- tag: "h1",
38000
- value: block.content ?? "",
38001
- readOnly,
38002
- placeholder: placeholder ?? t("richBlockEditor.placeholder.heading1"),
38003
- ariaLabel: t("richBlockEditor.aria.heading1Block"),
38004
- className: "text-3xl font-bold leading-tight",
38005
- onValueChange: setContent
38006
- }
38007
- );
38008
- case "heading-2":
38009
- return /* @__PURE__ */ jsx(
38010
- Editable,
38011
- {
38012
- tag: "h2",
38013
- value: block.content ?? "",
38014
- readOnly,
38015
- placeholder: placeholder ?? t("richBlockEditor.placeholder.heading2"),
38016
- ariaLabel: t("richBlockEditor.aria.heading2Block"),
38017
- className: "text-2xl font-semibold leading-tight",
38018
- onValueChange: setContent
38019
- }
38020
- );
38021
- case "heading-3":
38022
- return /* @__PURE__ */ jsx(
38023
- Editable,
38024
- {
38025
- tag: "h3",
38026
- value: block.content ?? "",
38027
- readOnly,
38028
- placeholder: placeholder ?? t("richBlockEditor.placeholder.heading3"),
38029
- ariaLabel: t("richBlockEditor.aria.heading3Block"),
38030
- className: "text-xl font-semibold leading-tight",
38031
- onValueChange: setContent
38032
- }
38033
- );
38034
- case "quote":
38035
- return /* @__PURE__ */ jsx(
38036
- Editable,
38037
- {
38038
- tag: "blockquote",
38039
- value: block.content ?? "",
38040
- readOnly,
38041
- placeholder: placeholder ?? t("richBlockEditor.placeholder.quote"),
38042
- ariaLabel: t("richBlockEditor.aria.quoteBlock"),
38043
- className: "border-l-4 border-primary/60 pl-4 italic text-muted-foreground",
38044
- onValueChange: setContent
38045
- }
38046
- );
38047
- case "code":
38048
- return /* @__PURE__ */ jsxs(Box, { className: "rounded-md border border-border bg-muted/40", children: [
38049
- /* @__PURE__ */ jsxs(Box, { className: "flex items-center justify-between border-b border-border px-3 py-1 text-xs text-muted-foreground", children: [
38050
- /* @__PURE__ */ jsx(Typography, { as: "span", variant: "caption", className: "uppercase tracking-wide", children: t("richBlockEditor.blockType.code") }),
38051
- !readOnly && /* @__PURE__ */ jsx(
38052
- Input,
38053
- {
38054
- inputType: "text",
38055
- value: String(block.metadata?.language ?? "plaintext"),
38056
- "aria-label": t("richBlockEditor.aria.codeLanguage"),
38057
- className: cn(
38058
- "h-6 w-32 rounded-sm border border-border bg-background",
38059
- "px-2 text-xs outline-none focus:ring-1 focus:ring-ring"
38060
- ),
38061
- onChange: (e) => setMetadata("language", e.target.value)
38062
- }
38063
- ),
38064
- readOnly && /* @__PURE__ */ jsx(Typography, { as: "span", variant: "caption", className: "text-xs", children: String(block.metadata?.language ?? "plaintext") })
38065
- ] }),
38066
- /* @__PURE__ */ jsx(
38067
- Editable,
38068
- {
38069
- tag: "pre",
38070
- value: block.content ?? "",
38071
- readOnly,
38072
- placeholder: placeholder ?? t("richBlockEditor.placeholder.code"),
38073
- ariaLabel: t("richBlockEditor.aria.codeBlock"),
38074
- className: "block whitespace-pre-wrap p-3 font-mono text-sm leading-relaxed",
38075
- onValueChange: setContent
38076
- }
38077
- )
38078
- ] });
38079
- case "divider":
38080
- return /* @__PURE__ */ jsx(Divider, { className: "my-2" });
38081
- case "image": {
38082
- const url = String(block.metadata?.url ?? "");
38083
- const caption = String(block.metadata?.caption ?? "");
38084
- const imgProps = {
38085
- src: url,
38086
- alt: caption || t("richBlockEditor.embeddedImage"),
38087
- className: "max-h-96 w-full rounded-md border border-border object-contain"
37783
+ var CHANGE_DEBOUNCE_MS, ALLOWED_ATTRS, DROP_TAGS, MD_BLOCK_PREFIXES, BLOCK_TAGS, MD_INLINE_RULES, RICH_TEXT_CSS, IDLE_TOOLBAR, RichTextEditor;
37784
+ var init_RichTextEditor = __esm({
37785
+ "components/core/molecules/RichTextEditor.tsx"() {
37786
+ "use client";
37787
+ init_cn();
37788
+ init_Box();
37789
+ init_Button();
37790
+ init_useEventBus();
37791
+ CHANGE_DEBOUNCE_MS = 400;
37792
+ ALLOWED_ATTRS = {
37793
+ p: [],
37794
+ h1: [],
37795
+ h2: [],
37796
+ h3: [],
37797
+ h4: [],
37798
+ ul: [],
37799
+ ol: [],
37800
+ li: [],
37801
+ blockquote: [],
37802
+ pre: [],
37803
+ code: [],
37804
+ b: [],
37805
+ strong: [],
37806
+ i: [],
37807
+ em: [],
37808
+ u: [],
37809
+ s: [],
37810
+ strike: [],
37811
+ br: [],
37812
+ hr: [],
37813
+ div: [],
37814
+ span: [],
37815
+ a: ["href"],
37816
+ img: ["src", "alt"]
37817
+ };
37818
+ DROP_TAGS = /* @__PURE__ */ new Set(["script", "style", "iframe", "object", "embed", "link", "meta", "form", "input", "button", "textarea", "select"]);
37819
+ MD_BLOCK_PREFIXES = {
37820
+ "#": { kind: "block", tag: "h1" },
37821
+ "##": { kind: "block", tag: "h2" },
37822
+ "###": { kind: "block", tag: "h3" },
37823
+ ">": { kind: "block", tag: "blockquote" },
37824
+ "```": { kind: "block", tag: "pre" },
37825
+ "-": { kind: "list", command: "insertUnorderedList" },
37826
+ "*": { kind: "list", command: "insertUnorderedList" },
37827
+ "1.": { kind: "list", command: "insertOrderedList" }
37828
+ };
37829
+ BLOCK_TAGS = /* @__PURE__ */ new Set(["P", "DIV", "LI", "H1", "H2", "H3", "H4", "BLOCKQUOTE", "PRE"]);
37830
+ MD_INLINE_RULES = [
37831
+ { pattern: /\*\*([^*\n]+)\*\*$/, wrap: "strong" },
37832
+ { pattern: /(^|[^*])\*([^*\n]+)\*$/, wrap: "em" },
37833
+ { pattern: /`([^`\n]+)`$/, wrap: "code" }
37834
+ ];
37835
+ RICH_TEXT_CSS = `
37836
+ .almadar-rich-text { line-height: 1.7; color: var(--color-foreground); }
37837
+ .almadar-rich-text h1 { font-size: 1.875rem; font-weight: 700; line-height: 1.25; margin: 1.25em 0 0.4em; }
37838
+ .almadar-rich-text h2 { font-size: 1.5rem; font-weight: 650; line-height: 1.3; margin: 1.1em 0 0.4em; }
37839
+ .almadar-rich-text h3 { font-size: 1.25rem; font-weight: 600; line-height: 1.35; margin: 1em 0 0.35em; }
37840
+ .almadar-rich-text h1:first-child, .almadar-rich-text h2:first-child, .almadar-rich-text h3:first-child { margin-top: 0; }
37841
+ .almadar-rich-text p { margin: 0.5em 0; }
37842
+ .almadar-rich-text ul { list-style: disc; padding-inline-start: 1.5rem; margin: 0.5em 0; }
37843
+ .almadar-rich-text ol { list-style: decimal; padding-inline-start: 1.5rem; margin: 0.5em 0; }
37844
+ .almadar-rich-text li { margin: 0.25em 0; }
37845
+ .almadar-rich-text blockquote { border-inline-start: 3px solid var(--color-primary); padding-inline-start: 1rem; font-style: italic; color: var(--color-muted-foreground); margin: 0.75em 0; }
37846
+ .almadar-rich-text pre { background: var(--color-muted); border: 1px solid var(--color-border); border-radius: 0.375rem; padding: 0.75rem 1rem; font-family: ui-monospace, monospace; font-size: 0.875em; white-space: pre-wrap; margin: 0.75em 0; }
37847
+ .almadar-rich-text code { font-family: ui-monospace, monospace; font-size: 0.875em; background: var(--color-muted); border-radius: 0.25rem; padding: 0.1em 0.35em; }
37848
+ .almadar-rich-text pre code { background: transparent; padding: 0; }
37849
+ .almadar-rich-text a { color: var(--color-primary); text-decoration: underline; }
37850
+ .almadar-rich-text hr { border: none; border-top: 1px solid var(--color-border); margin: 1.25em 0; }
37851
+ .almadar-rich-text img { max-width: 100%; border-radius: 0.375rem; }
37852
+ `;
37853
+ IDLE_TOOLBAR = {
37854
+ bold: false,
37855
+ italic: false,
37856
+ underline: false,
37857
+ strikeThrough: false,
37858
+ block: null,
37859
+ bullets: false,
37860
+ numbers: false,
37861
+ link: false
37862
+ };
37863
+ RichTextEditor = ({
37864
+ value,
37865
+ onChange,
37866
+ changeEvent,
37867
+ readOnly = false,
37868
+ placeholder,
37869
+ showToolbar = true,
37870
+ className
37871
+ }) => {
37872
+ const { t } = useTranslate();
37873
+ const ref = useRef(null);
37874
+ const [toolbar, setToolbar] = useState(IDLE_TOOLBAR);
37875
+ const [empty, setEmpty] = useState(() => !value || !sanitizeRichHtml(value).trim());
37876
+ const eventBus = useEventBus();
37877
+ const onChangeRef = useRef(onChange);
37878
+ const changeEventRef = useRef(changeEvent);
37879
+ useEffect(() => {
37880
+ onChangeRef.current = onChange;
37881
+ changeEventRef.current = changeEvent;
37882
+ }, [onChange, changeEvent]);
37883
+ const emitTimerRef = useRef(null);
37884
+ const emitNow = useCallback(() => {
37885
+ const el = ref.current;
37886
+ if (!el) return;
37887
+ const html = sanitizeRichHtml(el.innerHTML);
37888
+ onChangeRef.current?.(html);
37889
+ const evt = changeEventRef.current;
37890
+ if (evt) eventBus.emit(`UI:${evt}`, { value: html });
37891
+ }, [eventBus]);
37892
+ const scheduleEmit = useCallback(() => {
37893
+ if (emitTimerRef.current) clearTimeout(emitTimerRef.current);
37894
+ emitTimerRef.current = setTimeout(() => {
37895
+ emitTimerRef.current = null;
37896
+ emitNow();
37897
+ }, CHANGE_DEBOUNCE_MS);
37898
+ }, [emitNow]);
37899
+ const flushEmit = useCallback(() => {
37900
+ if (!emitTimerRef.current) return;
37901
+ clearTimeout(emitTimerRef.current);
37902
+ emitTimerRef.current = null;
37903
+ emitNow();
37904
+ }, [emitNow]);
37905
+ useEffect(() => () => {
37906
+ if (emitTimerRef.current) {
37907
+ clearTimeout(emitTimerRef.current);
37908
+ emitTimerRef.current = null;
37909
+ emitNow();
37910
+ }
37911
+ }, [emitNow]);
37912
+ useEffect(() => {
37913
+ if (readOnly) return;
37914
+ const el = ref.current;
37915
+ if (!el) return;
37916
+ if (document.activeElement === el) return;
37917
+ const next = sanitizeRichHtml(value ?? "") || "<p><br></p>";
37918
+ if (el.innerHTML !== next) {
37919
+ el.innerHTML = next;
37920
+ setEmpty(htmlIsEmpty(el));
37921
+ }
37922
+ }, [readOnly, value]);
37923
+ useEffect(() => {
37924
+ if (readOnly) return;
37925
+ document.execCommand("defaultParagraphSeparator", false, "p");
37926
+ }, [readOnly]);
37927
+ useEffect(() => {
37928
+ if (readOnly || !showToolbar) return;
37929
+ const onSelectionChange = () => {
37930
+ const el = ref.current;
37931
+ if (!el) return;
37932
+ setToolbar(readToolbarState(el));
38088
37933
  };
38089
- return /* @__PURE__ */ jsxs(Box, { className: "space-y-2", children: [
38090
- url ? /* @__PURE__ */ jsx(Box, { as: "img", ...imgProps }) : /* @__PURE__ */ jsxs(
38091
- Box,
38092
- {
38093
- className: cn(
38094
- "flex h-32 items-center justify-center",
38095
- "rounded-md border border-dashed border-border",
38096
- "text-sm text-muted-foreground"
38097
- ),
38098
- children: [
38099
- /* @__PURE__ */ jsx(Icon, { name: "image", className: "mr-2 w-4 h-4" }),
38100
- " ",
38101
- t("richBlockEditor.noImageUrl")
38102
- ]
38103
- }
38104
- ),
38105
- !readOnly && /* @__PURE__ */ jsxs(Box, { className: "flex flex-col gap-2 sm:flex-row", children: [
38106
- /* @__PURE__ */ jsx(
38107
- Input,
38108
- {
38109
- inputType: "url",
38110
- value: url,
38111
- placeholder: "https://example.com/image.png",
38112
- "aria-label": t("richBlockEditor.aria.imageUrl"),
38113
- className: cn(
38114
- "h-8 flex-1 rounded-sm border border-border bg-background",
38115
- "px-2 text-sm outline-none focus:ring-1 focus:ring-ring"
38116
- ),
38117
- onChange: (e) => setMetadata("url", e.target.value)
38118
- }
38119
- ),
38120
- /* @__PURE__ */ jsx(
38121
- Input,
38122
- {
38123
- inputType: "text",
38124
- value: caption,
38125
- placeholder: t("richBlockEditor.placeholder.caption"),
38126
- "aria-label": t("richBlockEditor.aria.imageCaption"),
38127
- className: cn(
38128
- "h-8 flex-1 rounded-sm border border-border bg-background",
38129
- "px-2 text-sm outline-none focus:ring-1 focus:ring-ring"
38130
- ),
38131
- onChange: (e) => setMetadata("caption", e.target.value)
38132
- }
38133
- )
38134
- ] }),
38135
- readOnly && caption && /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-center text-muted-foreground", children: caption })
37934
+ document.addEventListener("selectionchange", onSelectionChange);
37935
+ return () => document.removeEventListener("selectionchange", onSelectionChange);
37936
+ }, [readOnly, showToolbar]);
37937
+ const tryInlineMarkdown = useCallback(() => {
37938
+ const root = ref.current;
37939
+ if (!root) return false;
37940
+ const sel = window.getSelection();
37941
+ if (!sel || !sel.isCollapsed || sel.rangeCount === 0) return false;
37942
+ const node = sel.anchorNode;
37943
+ if (!node || node.nodeType !== Node.TEXT_NODE || !root.contains(node)) return false;
37944
+ if (node.parentElement?.closest("code, pre")) return false;
37945
+ const text = (node.textContent ?? "").slice(0, sel.anchorOffset);
37946
+ for (const rule of MD_INLINE_RULES) {
37947
+ const m = rule.pattern.exec(text);
37948
+ if (!m) continue;
37949
+ const inner = rule.wrap === "em" ? m[2] : m[1];
37950
+ if (!inner || !inner.trim()) continue;
37951
+ const lead = rule.wrap === "em" ? m[1]?.length ?? 0 : 0;
37952
+ const range = document.createRange();
37953
+ range.setStart(node, m.index + lead);
37954
+ range.setEnd(node, sel.anchorOffset);
37955
+ const el = document.createElement(rule.wrap);
37956
+ el.textContent = inner;
37957
+ range.deleteContents();
37958
+ range.insertNode(el);
37959
+ const after = document.createTextNode("\u200B");
37960
+ el.parentNode?.insertBefore(after, el.nextSibling);
37961
+ const caret = document.createRange();
37962
+ caret.setStart(after, 1);
37963
+ caret.collapse(true);
37964
+ sel.removeAllRanges();
37965
+ sel.addRange(caret);
37966
+ return true;
37967
+ }
37968
+ return false;
37969
+ }, []);
37970
+ const tryBlockMarkdown = useCallback(() => {
37971
+ const root = ref.current;
37972
+ if (!root) return false;
37973
+ const sel = window.getSelection();
37974
+ if (!sel || !sel.isCollapsed || sel.rangeCount === 0) return false;
37975
+ const node = sel.anchorNode;
37976
+ if (!node || node.nodeType !== Node.TEXT_NODE || !root.contains(node)) return false;
37977
+ const block = closestBlock(node, root);
37978
+ if (block ? block.firstChild !== node : root.firstChild !== node) return false;
37979
+ const prefix = (node.textContent ?? "").slice(0, sel.anchorOffset);
37980
+ const rule = MD_BLOCK_PREFIXES[prefix];
37981
+ if (!rule) return false;
37982
+ if (node.parentElement?.closest("pre, code")) return false;
37983
+ if (rule.kind === "list" && node.parentElement?.closest("li")) return false;
37984
+ node.deleteData(0, prefix.length);
37985
+ const caret = document.createRange();
37986
+ caret.setStart(node, 0);
37987
+ caret.collapse(true);
37988
+ sel.removeAllRanges();
37989
+ sel.addRange(caret);
37990
+ if (rule.kind === "list") {
37991
+ const container = block ?? node.parentElement;
37992
+ const list = document.createElement(rule.command === "insertOrderedList" ? "ol" : "ul");
37993
+ const li = document.createElement("li");
37994
+ if (block) {
37995
+ while (block.firstChild) li.appendChild(block.firstChild);
37996
+ list.appendChild(li);
37997
+ block.replaceWith(list);
37998
+ } else if (container) {
37999
+ li.appendChild(node);
38000
+ list.appendChild(li);
38001
+ container.appendChild(list);
38002
+ }
38003
+ if (!li.firstChild) li.appendChild(document.createElement("br"));
38004
+ const liCaret = document.createRange();
38005
+ liCaret.selectNodeContents(li);
38006
+ liCaret.collapse(true);
38007
+ sel.removeAllRanges();
38008
+ sel.addRange(liCaret);
38009
+ } else {
38010
+ document.execCommand("formatBlock", false, rule.tag);
38011
+ }
38012
+ return true;
38013
+ }, []);
38014
+ const afterEdit = useCallback(() => {
38015
+ const el = ref.current;
38016
+ if (!el) return;
38017
+ tryInlineMarkdown();
38018
+ setEmpty(htmlIsEmpty(el));
38019
+ setToolbar(readToolbarState(el));
38020
+ scheduleEmit();
38021
+ }, [scheduleEmit, tryInlineMarkdown]);
38022
+ const handleKeyDown = useCallback((e) => {
38023
+ if (e.key === " " && tryBlockMarkdown()) {
38024
+ e.preventDefault();
38025
+ afterEdit();
38026
+ }
38027
+ }, [afterEdit, tryBlockMarkdown]);
38028
+ const execInline = useCallback((command) => {
38029
+ document.execCommand(command, false);
38030
+ afterEdit();
38031
+ }, [afterEdit]);
38032
+ const execBlock = useCallback((tag) => {
38033
+ const next = toolbar.block === tag ? "p" : tag;
38034
+ document.execCommand("formatBlock", false, next);
38035
+ afterEdit();
38036
+ }, [afterEdit, toolbar.block]);
38037
+ const execList = useCallback((command) => {
38038
+ document.execCommand(command, false);
38039
+ afterEdit();
38040
+ }, [afterEdit]);
38041
+ const execLink = useCallback(() => {
38042
+ if (toolbar.link) {
38043
+ document.execCommand("unlink", false);
38044
+ afterEdit();
38045
+ return;
38046
+ }
38047
+ const url = window.prompt(t("richTextEditor.linkPrompt"));
38048
+ if (!url) return;
38049
+ const safe = safeUrl(url, ["http://", "https://", "mailto:"]) ?? `https://${url}`;
38050
+ document.execCommand("createLink", false, safe);
38051
+ afterEdit();
38052
+ }, [afterEdit, t, toolbar.link]);
38053
+ const execRule = useCallback(() => {
38054
+ document.execCommand("insertHorizontalRule", false);
38055
+ afterEdit();
38056
+ }, [afterEdit]);
38057
+ if (readOnly) {
38058
+ return /* @__PURE__ */ jsxs(Box, { className: cn("almadar-rich-text max-w-none", className), children: [
38059
+ /* @__PURE__ */ jsx(RichTextStyles, {}),
38060
+ /* @__PURE__ */ jsx(Box, { dangerouslySetInnerHTML: { __html: sanitizeRichHtml(value ?? "") } })
38136
38061
  ] });
38137
38062
  }
38138
- case "bullet-list":
38139
- case "numbered-list": {
38140
- const items = block.children ?? [];
38141
- return /* @__PURE__ */ jsxs(
38063
+ return /* @__PURE__ */ jsxs(Box, { className: cn("flex flex-col gap-2", className), children: [
38064
+ /* @__PURE__ */ jsx(RichTextStyles, {}),
38065
+ showToolbar && /* @__PURE__ */ jsxs(
38142
38066
  Box,
38143
38067
  {
38144
- as: block.type === "bullet-list" ? "ul" : "ol",
38068
+ role: "toolbar",
38069
+ "aria-label": t("richTextEditor.editorToolbar"),
38145
38070
  className: cn(
38146
- "space-y-1 pl-6",
38147
- block.type === "bullet-list" ? "list-disc" : "list-decimal"
38071
+ "sticky top-0 z-10 flex flex-wrap items-center gap-0.5 self-start",
38072
+ "rounded-md border border-border bg-background/95 px-1 py-0.5 shadow-sm"
38148
38073
  ),
38149
38074
  children: [
38150
- items.map((child) => /* @__PURE__ */ jsxs(Box, { as: "li", className: "group/item flex items-start gap-2", children: [
38151
- /* @__PURE__ */ jsx(
38152
- Editable,
38153
- {
38154
- tag: "span",
38155
- value: child.content ?? "",
38156
- readOnly,
38157
- placeholder: t("richBlockEditor.placeholder.listItem"),
38158
- ariaLabel: t("richBlockEditor.aria.listItem"),
38159
- className: "inline-block min-w-[1ch] flex-1",
38160
- onValueChange: (next) => setChildContent(child.id, next)
38161
- }
38162
- ),
38163
- !readOnly && showAffordances && /* @__PURE__ */ jsx(
38164
- Button,
38165
- {
38166
- type: "button",
38167
- variant: "ghost",
38168
- "aria-label": t("richBlockEditor.aria.removeListItem"),
38169
- className: cn(
38170
- "h-5 w-5 shrink-0 rounded-sm text-muted-foreground p-0 gap-0",
38171
- "opacity-0 group-hover/item:opacity-100 hover:bg-muted hover:text-foreground"
38172
- ),
38173
- onClick: () => removeListItem(child.id),
38174
- children: /* @__PURE__ */ jsx(Icon, { name: "trash", className: "w-3 h-3" })
38175
- }
38176
- )
38177
- ] }, child.id)),
38178
- !readOnly && showAffordances && /* @__PURE__ */ jsx(Box, { as: "li", className: "list-none pl-0", children: /* @__PURE__ */ jsxs(
38179
- Button,
38180
- {
38181
- type: "button",
38182
- variant: "ghost",
38183
- className: cn(
38184
- "inline-flex items-center gap-1 text-xs text-muted-foreground",
38185
- "hover:text-foreground p-0 h-auto"
38186
- ),
38187
- onClick: addListItem,
38188
- children: [
38189
- /* @__PURE__ */ jsx(Icon, { name: "plus", className: "w-3 h-3" }),
38190
- " ",
38191
- t("richBlockEditor.addItem")
38192
- ]
38193
- }
38194
- ) })
38075
+ /* @__PURE__ */ jsx(ToolbarButton, { icon: Bold, label: t("richTextEditor.bold"), active: toolbar.bold, onExec: () => execInline("bold") }),
38076
+ /* @__PURE__ */ jsx(ToolbarButton, { icon: Italic, label: t("richTextEditor.italic"), active: toolbar.italic, onExec: () => execInline("italic") }),
38077
+ /* @__PURE__ */ jsx(ToolbarButton, { icon: Underline, label: t("richTextEditor.underline"), active: toolbar.underline, onExec: () => execInline("underline") }),
38078
+ /* @__PURE__ */ jsx(ToolbarButton, { icon: Strikethrough, label: t("richTextEditor.strikethrough"), active: toolbar.strikeThrough, onExec: () => execInline("strikeThrough") }),
38079
+ /* @__PURE__ */ jsx(Box, { className: "mx-1 h-5 w-px bg-border" }),
38080
+ /* @__PURE__ */ jsx(ToolbarButton, { icon: Heading1, label: t("richTextEditor.heading1"), active: toolbar.block === "h1", onExec: () => execBlock("h1") }),
38081
+ /* @__PURE__ */ jsx(ToolbarButton, { icon: Heading2, label: t("richTextEditor.heading2"), active: toolbar.block === "h2", onExec: () => execBlock("h2") }),
38082
+ /* @__PURE__ */ jsx(ToolbarButton, { icon: Heading3, label: t("richTextEditor.heading3"), active: toolbar.block === "h3", onExec: () => execBlock("h3") }),
38083
+ /* @__PURE__ */ jsx(Box, { className: "mx-1 h-5 w-px bg-border" }),
38084
+ /* @__PURE__ */ jsx(ToolbarButton, { icon: List, label: t("richTextEditor.bulletList"), active: toolbar.bullets, onExec: () => execList("insertUnorderedList") }),
38085
+ /* @__PURE__ */ jsx(ToolbarButton, { icon: ListOrdered, label: t("richTextEditor.numberedList"), active: toolbar.numbers, onExec: () => execList("insertOrderedList") }),
38086
+ /* @__PURE__ */ jsx(Box, { className: "mx-1 h-5 w-px bg-border" }),
38087
+ /* @__PURE__ */ jsx(ToolbarButton, { icon: Quote, label: t("richTextEditor.quote"), active: toolbar.block === "blockquote", onExec: () => execBlock("blockquote") }),
38088
+ /* @__PURE__ */ jsx(ToolbarButton, { icon: Code, label: t("richTextEditor.code"), active: toolbar.block === "pre", onExec: () => execBlock("pre") }),
38089
+ /* @__PURE__ */ jsx(ToolbarButton, { icon: Link$1, label: toolbar.link ? t("richTextEditor.removeLink") : t("richTextEditor.link"), active: toolbar.link, onExec: execLink }),
38090
+ /* @__PURE__ */ jsx(ToolbarButton, { icon: Minus, label: t("richTextEditor.divider"), onExec: execRule })
38195
38091
  ]
38196
38092
  }
38197
- );
38198
- }
38199
- case "paragraph":
38200
- default:
38201
- return /* @__PURE__ */ jsx(
38202
- Editable,
38093
+ ),
38094
+ /* @__PURE__ */ jsx(
38095
+ Box,
38203
38096
  {
38204
- tag: "p",
38205
- value: block.content ?? "",
38206
- readOnly,
38207
- placeholder: placeholder ?? t("richBlockEditor.placeholder.paragraph"),
38208
- ariaLabel: t("richBlockEditor.aria.paragraphBlock"),
38209
- className: "leading-7",
38210
- onValueChange: setContent
38097
+ ref,
38098
+ contentEditable: true,
38099
+ suppressContentEditableWarning: true,
38100
+ role: "textbox",
38101
+ "aria-multiline": "true",
38102
+ "aria-label": t("richTextEditor.editorSurface"),
38103
+ "data-placeholder": placeholder ?? t("richTextEditor.placeholder"),
38104
+ "data-empty": empty ? "true" : "false",
38105
+ className: cn(
38106
+ "almadar-rich-text max-w-none min-h-[8rem] outline-none",
38107
+ "data-[empty=true]:before:content-[attr(data-placeholder)]",
38108
+ "data-[empty=true]:before:text-muted-foreground/60",
38109
+ "data-[empty=true]:before:pointer-events-none",
38110
+ "data-[empty=true]:before:absolute",
38111
+ "relative cursor-text"
38112
+ ),
38113
+ onInput: afterEdit,
38114
+ onKeyDown: handleKeyDown,
38115
+ onBlur: flushEmit
38211
38116
  }
38212
- );
38213
- }
38117
+ )
38118
+ ] });
38119
+ };
38120
+ RichTextEditor.displayName = "RichTextEditor";
38121
+ }
38122
+ });
38123
+ function renderIconInput4(icon, props) {
38124
+ return typeof icon === "string" ? /* @__PURE__ */ jsx(Icon, { name: icon, ...props }) : /* @__PURE__ */ jsx(Icon, { icon, ...props });
38125
+ }
38126
+ function DocumentPanel({
38127
+ id,
38128
+ title,
38129
+ subtitle,
38130
+ value,
38131
+ actions,
38132
+ maxInlineActions,
38133
+ editing = false,
38134
+ titleCommitEvent,
38135
+ contentChangeEvent,
38136
+ editEvent,
38137
+ doneEvent,
38138
+ editLabel,
38139
+ doneLabel,
38140
+ autosaveHint,
38141
+ placeholder,
38142
+ className
38143
+ }) {
38144
+ const eventBus = useEventBus();
38145
+ const { t } = useTranslate();
38146
+ const [titleDraft, setTitleDraft] = useState(null);
38147
+ const recordId = id ?? "";
38148
+ const actionDefs = actions ?? [];
38149
+ const inlineCap = Math.min(maxInlineActions ?? 1, 1);
38150
+ const inlineActions = actionDefs.filter((a) => a.variant === "primary").slice(0, inlineCap);
38151
+ const menuActions = actionDefs.filter((a) => !inlineActions.includes(a));
38152
+ const fireAction = (action) => {
38153
+ if (!action.event) return;
38154
+ eventBus.emit(`UI:${action.event}`, { id: recordId, title: title ?? "" });
38214
38155
  };
38215
- return /* @__PURE__ */ jsxs(
38156
+ const commitTitle = () => {
38157
+ if (!titleCommitEvent) return;
38158
+ const next = (titleDraft ?? "").trim();
38159
+ setTitleDraft(null);
38160
+ if (!next || next === title) return;
38161
+ eventBus.emit(`UI:${titleCommitEvent}`, { title: next, id: recordId });
38162
+ };
38163
+ const untitled = t("documentPanel.untitled") || "Untitled";
38164
+ const titleNode = titleCommitEvent && titleDraft !== null ? /* @__PURE__ */ jsx(
38165
+ Input,
38166
+ {
38167
+ value: titleDraft,
38168
+ autoFocus: true,
38169
+ "aria-label": t("common.title"),
38170
+ className: "h-auto py-1 text-3xl font-bold tracking-tight",
38171
+ onFocus: (e) => e.currentTarget.select(),
38172
+ onChange: (e) => setTitleDraft(e.target.value),
38173
+ onBlur: commitTitle,
38174
+ onKeyDown: (e) => {
38175
+ if (e.key === "Enter") {
38176
+ e.preventDefault();
38177
+ commitTitle();
38178
+ } else if (e.key === "Escape") {
38179
+ e.preventDefault();
38180
+ setTitleDraft(null);
38181
+ }
38182
+ },
38183
+ "data-testid": "document-title-input"
38184
+ }
38185
+ ) : titleCommitEvent ? /* @__PURE__ */ jsx(
38216
38186
  Box,
38217
38187
  {
38218
- className: cn(
38219
- "group relative flex items-start gap-2 rounded-sm",
38220
- "px-2 py-1 hover:bg-muted/30"
38221
- ),
38222
- "data-block-id": block.id,
38223
- "data-block-type": block.type,
38224
- children: [
38225
- !readOnly && showAffordances && /* @__PURE__ */ jsx(Box, { className: "flex w-8 shrink-0 items-center pt-1", children: /* @__PURE__ */ jsx(
38226
- BlockMenu,
38188
+ role: "button",
38189
+ tabIndex: 0,
38190
+ className: "cursor-text rounded px-1 -mx-1 transition-colors hover:bg-muted/40",
38191
+ onClick: () => setTitleDraft(title ?? ""),
38192
+ onKeyDown: (e) => {
38193
+ if (e.key === "Enter" || e.key === " ") {
38194
+ e.preventDefault();
38195
+ setTitleDraft(title ?? "");
38196
+ }
38197
+ },
38198
+ "data-testid": "document-title-editable",
38199
+ children: /* @__PURE__ */ jsx(Typography, { variant: "h2", weight: "bold", className: cn(!title && "text-muted-foreground"), children: title || untitled })
38200
+ }
38201
+ ) : /* @__PURE__ */ jsx(Typography, { variant: "h2", weight: "bold", children: title || untitled });
38202
+ const emitWithId = (event) => () => {
38203
+ if (event) eventBus.emit(`UI:${event}`, { id: recordId });
38204
+ };
38205
+ return /* @__PURE__ */ jsx(Card, { variant: "elevated", className: cn("w-full", className), children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", className: "p-6 sm:p-8", children: [
38206
+ /* @__PURE__ */ jsxs(HStack, { gap: "sm", className: "justify-between items-start", children: [
38207
+ /* @__PURE__ */ jsxs(VStack, { gap: "xs", className: "flex-1 min-w-0", children: [
38208
+ titleNode,
38209
+ subtitle ? /* @__PURE__ */ jsx(Typography, { variant: "small", color: "secondary", children: subtitle }) : null
38210
+ ] }),
38211
+ /* @__PURE__ */ jsx(HStack, { gap: "xs", className: "flex-shrink-0 pt-1 items-center", children: editing ? /* @__PURE__ */ jsxs(Fragment, { children: [
38212
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "muted", className: "hidden sm:block", children: autosaveHint ?? (t("documentPanel.autosaveHint") || "") }),
38213
+ /* @__PURE__ */ jsx(Button, { variant: "primary", size: "sm", onClick: emitWithId(doneEvent), "data-testid": "document-done", children: doneLabel ?? (t("documentPanel.done") || "Done") })
38214
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
38215
+ editEvent && /* @__PURE__ */ jsxs(Button, { variant: "ghost", size: "sm", onClick: emitWithId(editEvent), "data-testid": "document-edit", children: [
38216
+ /* @__PURE__ */ jsx(Icon, { name: "edit", size: "xs", className: "mr-1" }),
38217
+ editLabel ?? (t("documentPanel.edit") || "Edit")
38218
+ ] }),
38219
+ inlineActions.map((action, idx) => /* @__PURE__ */ jsxs(
38220
+ Button,
38227
38221
  {
38228
- block,
38229
- readOnly,
38230
- onDelete,
38231
- onDuplicate,
38232
- onChangeType
38222
+ variant: "primary",
38223
+ size: "sm",
38224
+ onClick: () => fireAction(action),
38225
+ "data-testid": `action-${action.event}`,
38226
+ children: [
38227
+ action.icon && renderIconInput4(action.icon, { size: "xs", className: "mr-1" }),
38228
+ action.label
38229
+ ]
38230
+ },
38231
+ idx
38232
+ )),
38233
+ menuActions.length > 0 && /* @__PURE__ */ jsx(
38234
+ Menu,
38235
+ {
38236
+ position: "bottom-end",
38237
+ trigger: /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", "aria-label": t("common.actions"), "data-testid": "action-overflow", children: /* @__PURE__ */ jsx(Icon, { name: "more-horizontal", size: "xs" }) }),
38238
+ items: menuActions.map((action) => ({
38239
+ label: action.label,
38240
+ icon: action.icon,
38241
+ variant: action.variant === "danger" ? "danger" : "default",
38242
+ onClick: () => fireAction(action)
38243
+ }))
38233
38244
  }
38234
- ) }),
38235
- /* @__PURE__ */ jsx(Box, { className: "min-w-0 flex-1", children: renderBody() })
38236
- ]
38237
- }
38238
- );
38245
+ )
38246
+ ] }) })
38247
+ ] }),
38248
+ editing ? /* @__PURE__ */ jsx(RichTextEditor, { value, changeEvent: contentChangeEvent, placeholder }) : /* @__PURE__ */ jsx(
38249
+ Box,
38250
+ {
38251
+ onClick: emitWithId(editEvent),
38252
+ className: cn(
38253
+ "rounded-md px-1 -mx-1 min-h-[16rem]",
38254
+ editEvent && "cursor-text transition-colors hover:bg-muted/30"
38255
+ ),
38256
+ "data-testid": "document-body",
38257
+ children: value && value !== "" ? /* @__PURE__ */ jsx(RichTextEditor, { value, readOnly: true }) : /* @__PURE__ */ jsx(Typography, { variant: "body", color: "muted", children: placeholder ?? (t("documentPanel.placeholder") || "") })
38258
+ }
38259
+ )
38260
+ ] }) });
38239
38261
  }
38240
- var TOOLBAR_ENTRIES, BLOCK_TYPE_LABEL_KEY, CHANGEABLE_TYPES, _idSeq, BLOCK_TYPES, RichBlockEditor;
38241
- var init_RichBlockEditor = __esm({
38242
- "components/core/molecules/RichBlockEditor.tsx"() {
38262
+ var init_DocumentPanel = __esm({
38263
+ "components/core/molecules/DocumentPanel.tsx"() {
38243
38264
  "use client";
38244
38265
  init_cn();
38245
- init_Card();
38266
+ init_useEventBus();
38267
+ init_Box();
38268
+ init_Stack();
38246
38269
  init_Typography();
38247
38270
  init_Button();
38248
- init_Box();
38249
- init_Divider();
38250
- init_Input();
38271
+ init_Card();
38251
38272
  init_Icon();
38252
- init_useEventBus();
38253
- TOOLBAR_ENTRIES = [
38254
- { type: "paragraph", labelKey: "richBlockEditor.toolbar.text", icon: Type },
38255
- { type: "heading-1", labelKey: "richBlockEditor.toolbar.h1", icon: Heading1 },
38256
- { type: "heading-2", labelKey: "richBlockEditor.toolbar.h2", icon: Heading2 },
38257
- { type: "heading-3", labelKey: "richBlockEditor.toolbar.h3", icon: Heading3 },
38258
- { type: "bullet-list", labelKey: "richBlockEditor.toolbar.bulletList", icon: List },
38259
- { type: "numbered-list", labelKey: "richBlockEditor.toolbar.numbered", icon: ListOrdered },
38260
- { type: "quote", labelKey: "richBlockEditor.toolbar.quote", icon: Quote },
38261
- { type: "code", labelKey: "richBlockEditor.toolbar.code", icon: Code },
38262
- { type: "divider", labelKey: "richBlockEditor.toolbar.divider", icon: Minus },
38263
- { type: "image", labelKey: "richBlockEditor.toolbar.image", icon: Image$1 }
38264
- ];
38265
- BLOCK_TYPE_LABEL_KEY = {
38266
- paragraph: "richBlockEditor.blockType.paragraph",
38267
- "heading-1": "richBlockEditor.blockType.heading1",
38268
- "heading-2": "richBlockEditor.blockType.heading2",
38269
- "heading-3": "richBlockEditor.blockType.heading3",
38270
- "bullet-list": "richBlockEditor.blockType.bulletList",
38271
- "numbered-list": "richBlockEditor.blockType.numberedList",
38272
- quote: "richBlockEditor.blockType.quote",
38273
- code: "richBlockEditor.blockType.code",
38274
- divider: "richBlockEditor.blockType.divider",
38275
- image: "richBlockEditor.blockType.image"
38276
- };
38277
- CHANGEABLE_TYPES = [
38278
- "paragraph",
38279
- "heading-1",
38280
- "heading-2",
38281
- "heading-3",
38282
- "bullet-list",
38283
- "numbered-list",
38284
- "quote",
38285
- "code"
38286
- ];
38287
- _idSeq = 0;
38288
- BLOCK_TYPES = /* @__PURE__ */ new Set([
38289
- "paragraph",
38290
- "heading-1",
38291
- "heading-2",
38292
- "heading-3",
38293
- "bullet-list",
38294
- "numbered-list",
38295
- "quote",
38296
- "code",
38297
- "divider",
38298
- "image"
38299
- ]);
38300
- RichBlockEditor = ({
38301
- initialBlocks,
38302
- onChange,
38303
- changeEvent,
38304
- readOnly = false,
38305
- placeholder,
38306
- enableBlocks = true,
38307
- showToolbar = true,
38308
- className
38309
- }) => {
38310
- const { t } = useTranslate();
38311
- const [blocks, setBlocks] = useState(
38312
- () => normalizeBlocks(initialBlocks)
38313
- );
38314
- const onChangeRef = useRef(onChange);
38315
- useEffect(() => {
38316
- onChangeRef.current = onChange;
38317
- }, [onChange]);
38318
- const eventBus = useEventBus();
38319
- const changeEventRef = useRef(changeEvent);
38320
- useEffect(() => {
38321
- changeEventRef.current = changeEvent;
38322
- }, [changeEvent]);
38323
- const commit = useCallback((next) => {
38324
- setBlocks(next);
38325
- onChangeRef.current?.(next);
38326
- const evt = changeEventRef.current;
38327
- if (evt) eventBus.emit(`UI:${evt}`, { blocks: next });
38328
- }, [eventBus]);
38329
- const handleAppend = useCallback(
38330
- (type) => {
38331
- if (readOnly) return;
38332
- commit([...blocks, createBlock(type)]);
38333
- },
38334
- [blocks, commit, readOnly]
38335
- );
38336
- const handleUpdate = useCallback(
38337
- (id, updater) => {
38338
- commit(replaceBlock(blocks, id, updater));
38339
- },
38340
- [blocks, commit]
38341
- );
38342
- const handleDelete = useCallback(
38343
- (id) => {
38344
- const next = removeBlock(blocks, id);
38345
- commit(next.length > 0 ? next : [createBlock("paragraph")]);
38346
- },
38347
- [blocks, commit]
38348
- );
38349
- const handleDuplicate = useCallback(
38350
- (id) => {
38351
- const target = blocks.find((b) => b.id === id);
38352
- if (!target) return;
38353
- commit(insertAfter(blocks, id, duplicateBlock(target)));
38354
- },
38355
- [blocks, commit]
38273
+ init_Input();
38274
+ init_Menu();
38275
+ init_RichTextEditor();
38276
+ }
38277
+ });
38278
+ function renderIconInput5(icon, props) {
38279
+ return typeof icon === "string" ? /* @__PURE__ */ jsx(Icon, { name: icon, ...props }) : /* @__PURE__ */ jsx(Icon, { icon, ...props });
38280
+ }
38281
+ function fieldName(field) {
38282
+ return field.name ?? field.key ?? "";
38283
+ }
38284
+ function relationLabel(value) {
38285
+ if (value === null || typeof value !== "object" || Array.isArray(value) || value instanceof Date) return null;
38286
+ for (const key of ["name", "title", "label"]) {
38287
+ const candidate = value[key];
38288
+ if (typeof candidate === "string" && candidate !== "") return candidate;
38289
+ }
38290
+ const id = value.id;
38291
+ return id !== void 0 && id !== null ? String(id) : null;
38292
+ }
38293
+ function fieldKind(field, value) {
38294
+ if (field.kind) return field.kind;
38295
+ if (field.options && field.options.length > 0) return "select";
38296
+ if (typeof value === "boolean" || field.format === "boolean") return "boolean";
38297
+ if (Array.isArray(value) || relationLabel(value ?? null) !== null) return "readonly";
38298
+ return "text";
38299
+ }
38300
+ function DocumentDetails({
38301
+ entity,
38302
+ fields,
38303
+ metaCommitEvent,
38304
+ title,
38305
+ className
38306
+ }) {
38307
+ const eventBus = useEventBus();
38308
+ const { t } = useTranslate();
38309
+ const [fieldDraft, setFieldDraft] = useState(null);
38310
+ const recordId = entity?.id !== void 0 && entity?.id !== null ? String(entity.id) : "";
38311
+ const fieldDefs = (fields ?? []).filter((f3) => fieldName(f3) !== "");
38312
+ if (fieldDefs.length === 0) return null;
38313
+ const commitField = (name, next, current) => {
38314
+ setFieldDraft(null);
38315
+ if (!metaCommitEvent || next === current) return;
38316
+ eventBus.emit(`UI:${metaCommitEvent}`, { id: recordId, patch: { id: recordId, [name]: next } });
38317
+ };
38318
+ const renderValue = (field) => {
38319
+ const name = fieldName(field);
38320
+ const raw = getNestedValue(entity ?? {}, name);
38321
+ const kind = fieldKind(field, raw);
38322
+ const label = field.label ?? field.header ?? humanizeFieldName(name);
38323
+ if (kind === "boolean") {
38324
+ return /* @__PURE__ */ jsx(
38325
+ Switch,
38326
+ {
38327
+ checked: Boolean(raw),
38328
+ disabled: !metaCommitEvent,
38329
+ "aria-label": label,
38330
+ onChange: (checked) => commitField(name, checked, Boolean(raw))
38331
+ }
38356
38332
  );
38357
- const handleChangeType = useCallback(
38358
- (id, type) => {
38359
- commit(
38360
- replaceBlock(blocks, id, (b) => changeBlockType(b, type))
38361
- );
38362
- },
38363
- [blocks, commit]
38333
+ }
38334
+ if (kind === "select") {
38335
+ return /* @__PURE__ */ jsx(
38336
+ Select,
38337
+ {
38338
+ value: raw !== void 0 && raw !== null ? String(raw) : "",
38339
+ disabled: !metaCommitEvent,
38340
+ "aria-label": label,
38341
+ className: "h-8 w-full",
38342
+ options: (field.options ?? []).map((opt) => ({ value: opt, label: humanizeEnumValue(opt) })),
38343
+ onChange: (e) => commitField(name, e.target.value, raw)
38344
+ }
38364
38345
  );
38365
- return /* @__PURE__ */ jsxs(
38366
- Card,
38346
+ }
38347
+ if (kind === "readonly" || !metaCommitEvent) {
38348
+ if (Array.isArray(raw)) {
38349
+ if (raw.length === 0) {
38350
+ return /* @__PURE__ */ jsx(Typography, { variant: "small", color: "secondary", children: "\u2014" });
38351
+ }
38352
+ return /* @__PURE__ */ jsx(HStack, { gap: "xs", className: "flex-wrap", children: raw.map((item, i) => /* @__PURE__ */ jsx(Badge, { variant: "default", children: relationLabel(item) ?? humanizeEnumValue(String(item)) }, i)) });
38353
+ }
38354
+ const shown2 = raw === void 0 || raw === null || raw === "" ? "\u2014" : relationLabel(raw) ?? formatValue(raw, field.format);
38355
+ return /* @__PURE__ */ jsx(Typography, { variant: "small", className: "break-words", children: shown2 });
38356
+ }
38357
+ if (fieldDraft?.name === name) {
38358
+ return /* @__PURE__ */ jsx(
38359
+ Input,
38367
38360
  {
38368
- variant: "bordered",
38369
- padding: "none",
38370
- className: cn("flex flex-col text-card-foreground", className),
38371
- children: [
38372
- enableBlocks && showToolbar && !readOnly && /* @__PURE__ */ jsx(
38373
- Box,
38374
- {
38375
- role: "toolbar",
38376
- "aria-label": t("richBlockEditor.editorToolbar"),
38377
- className: cn(
38378
- "flex flex-wrap items-center gap-1",
38379
- "border-b border-border bg-muted/30 px-2 py-2"
38380
- ),
38381
- children: TOOLBAR_ENTRIES.map((entry) => {
38382
- const Icon2 = entry.icon;
38383
- const entryLabel = t(entry.labelKey);
38384
- return /* @__PURE__ */ jsxs(
38385
- Button,
38386
- {
38387
- type: "button",
38388
- variant: "ghost",
38389
- size: "sm",
38390
- "aria-label": t("richBlockEditor.insertEntry", { label: entryLabel }),
38391
- title: entryLabel,
38392
- onClick: () => handleAppend(entry.type),
38393
- children: [
38394
- /* @__PURE__ */ jsx(Icon2, { size: 14 }),
38395
- /* @__PURE__ */ jsx(Typography, { as: "span", variant: "caption", className: "ml-1 hidden text-xs sm:inline", children: entryLabel })
38396
- ]
38397
- },
38398
- entry.type
38399
- );
38400
- })
38401
- }
38402
- ),
38403
- /* @__PURE__ */ jsx(Box, { className: "flex flex-col gap-1 px-3 py-3", children: blocks.map((block) => /* @__PURE__ */ jsx(
38404
- BlockRow,
38405
- {
38406
- block,
38407
- readOnly,
38408
- showAffordances: enableBlocks,
38409
- placeholder,
38410
- onUpdate: (updater) => handleUpdate(block.id, updater),
38411
- onDelete: () => handleDelete(block.id),
38412
- onDuplicate: () => handleDuplicate(block.id),
38413
- onChangeType: (type) => handleChangeType(block.id, type)
38414
- },
38415
- block.id
38416
- )) })
38417
- ]
38361
+ value: fieldDraft.value,
38362
+ autoFocus: true,
38363
+ "aria-label": label,
38364
+ className: "h-8 w-full",
38365
+ onChange: (e) => setFieldDraft({ name, value: e.target.value }),
38366
+ onBlur: () => commitField(name, fieldDraft.value.trim(), raw),
38367
+ onKeyDown: (e) => {
38368
+ if (e.key === "Enter") {
38369
+ e.preventDefault();
38370
+ commitField(name, fieldDraft.value.trim(), raw);
38371
+ } else if (e.key === "Escape") {
38372
+ e.preventDefault();
38373
+ setFieldDraft(null);
38374
+ }
38375
+ },
38376
+ "data-testid": `document-property-input-${name}`
38418
38377
  }
38419
38378
  );
38420
- };
38421
- RichBlockEditor.displayName = "RichBlockEditor";
38379
+ }
38380
+ const shown = raw === void 0 || raw === null || raw === "" ? "\u2014" : relationLabel(raw) ?? formatValue(raw, field.format);
38381
+ return /* @__PURE__ */ jsx(
38382
+ Box,
38383
+ {
38384
+ role: "button",
38385
+ tabIndex: 0,
38386
+ className: cn(
38387
+ "cursor-text rounded px-1 -mx-1 transition-colors hover:bg-muted/40",
38388
+ (raw === void 0 || raw === null || raw === "") && "text-muted-foreground"
38389
+ ),
38390
+ onClick: () => setFieldDraft({ name, value: raw !== void 0 && raw !== null ? String(raw) : "" }),
38391
+ onKeyDown: (e) => {
38392
+ if (e.key === "Enter" || e.key === " ") {
38393
+ e.preventDefault();
38394
+ setFieldDraft({ name, value: raw !== void 0 && raw !== null ? String(raw) : "" });
38395
+ }
38396
+ },
38397
+ "data-testid": `document-property-${name}`,
38398
+ children: /* @__PURE__ */ jsx(Typography, { variant: "small", className: "break-words", children: shown })
38399
+ }
38400
+ );
38401
+ };
38402
+ return /* @__PURE__ */ jsx(Card, { variant: "bordered", className: cn("w-full", className), children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", className: "p-4", children: [
38403
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "secondary", weight: "medium", className: "uppercase tracking-wide", children: title ?? (t("documentDetails.title") || "Details") }),
38404
+ /* @__PURE__ */ jsx(VStack, { gap: "sm", children: fieldDefs.map((field) => {
38405
+ const name = fieldName(field);
38406
+ const label = field.label ?? field.header ?? humanizeFieldName(name);
38407
+ return /* @__PURE__ */ jsxs(VStack, { gap: "xs", children: [
38408
+ /* @__PURE__ */ jsxs(HStack, { gap: "xs", className: "items-center", children: [
38409
+ field.icon && renderIconInput5(field.icon, { size: "xs", className: "text-muted-foreground" }),
38410
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "muted", children: label })
38411
+ ] }),
38412
+ /* @__PURE__ */ jsx(Box, { className: "min-w-0", children: renderValue(field) })
38413
+ ] }, name);
38414
+ }) })
38415
+ ] }) });
38416
+ }
38417
+ var init_DocumentDetails = __esm({
38418
+ "components/core/molecules/DocumentDetails.tsx"() {
38419
+ "use client";
38420
+ init_cn();
38421
+ init_format();
38422
+ init_getNestedValue();
38423
+ init_useEventBus();
38424
+ init_Box();
38425
+ init_Stack();
38426
+ init_Typography();
38427
+ init_Badge();
38428
+ init_Card();
38429
+ init_Icon();
38430
+ init_Input();
38431
+ init_Select();
38432
+ init_Switch();
38422
38433
  }
38423
38434
  });
38424
38435
  function collectInitiallyCollapsed(nodes, acc) {
@@ -43092,7 +43103,9 @@ var init_molecules2 = __esm({
43092
43103
  init_QrScanner();
43093
43104
  init_OptionConstraintGroup();
43094
43105
  init_PositionedCanvas();
43095
- init_RichBlockEditor();
43106
+ init_RichTextEditor();
43107
+ init_DocumentPanel();
43108
+ init_DocumentDetails();
43096
43109
  init_ReplyTree();
43097
43110
  init_BranchingLogicBuilder();
43098
43111
  init_VersionDiff();
@@ -43592,8 +43605,8 @@ var init_DataTable = __esm({
43592
43605
  DataTable.displayName = "DataTable";
43593
43606
  }
43594
43607
  });
43595
- function getBadgeVariant(fieldName, value) {
43596
- const name = fieldName.toLowerCase();
43608
+ function getBadgeVariant(fieldName2, value) {
43609
+ const name = fieldName2.toLowerCase();
43597
43610
  const val = String(value).toLowerCase();
43598
43611
  if (name.includes("status")) {
43599
43612
  if (val.includes("complete") || val.includes("done") || val.includes("active"))
@@ -43609,12 +43622,12 @@ function getBadgeVariant(fieldName, value) {
43609
43622
  }
43610
43623
  return "default";
43611
43624
  }
43612
- function formatFieldValue2(value, fieldName) {
43625
+ function formatFieldValue2(value, fieldName2) {
43613
43626
  if (typeof value === "number") {
43614
- if (fieldName.toLowerCase().includes("progress") || fieldName.toLowerCase().includes("percent")) {
43627
+ if (fieldName2.toLowerCase().includes("progress") || fieldName2.toLowerCase().includes("percent")) {
43615
43628
  return `${value}%`;
43616
43629
  }
43617
- if (fieldName.toLowerCase().includes("budget") || fieldName.toLowerCase().includes("cost")) {
43630
+ if (fieldName2.toLowerCase().includes("budget") || fieldName2.toLowerCase().includes("cost")) {
43618
43631
  return `$${value.toLocaleString()}`;
43619
43632
  }
43620
43633
  return value.toLocaleString();
@@ -43624,7 +43637,7 @@ function formatFieldValue2(value, fieldName) {
43624
43637
  }
43625
43638
  return String(value);
43626
43639
  }
43627
- function renderRichFieldValue(value, fieldName, fieldType, meta) {
43640
+ function renderRichFieldValue(value, fieldName2, fieldType, meta) {
43628
43641
  if (value === void 0 || value === null) return "\u2014";
43629
43642
  const str2 = String(value);
43630
43643
  switch (fieldType) {
@@ -43635,7 +43648,7 @@ function renderRichFieldValue(value, fieldName, fieldType, meta) {
43635
43648
  "img",
43636
43649
  {
43637
43650
  src: str2,
43638
- alt: formatFieldLabel(fieldName),
43651
+ alt: formatFieldLabel(fieldName2),
43639
43652
  className: "max-w-full max-h-64 rounded-md object-contain",
43640
43653
  loading: "lazy"
43641
43654
  }
@@ -43770,9 +43783,9 @@ function renderRichFieldValue(value, fieldName, fieldType, meta) {
43770
43783
  return /* @__PURE__ */ jsx("a", { href: `tel:${str2}`, className: "text-primary hover:underline", children: str2 });
43771
43784
  default:
43772
43785
  if (meta?.values && meta.values.length > 0 && meta.values.includes(str2)) {
43773
- return /* @__PURE__ */ jsx(Badge, { variant: getBadgeVariant(fieldName, str2), children: humanizeEnumValue(str2) });
43786
+ return /* @__PURE__ */ jsx(Badge, { variant: getBadgeVariant(fieldName2, str2), children: humanizeEnumValue(str2) });
43774
43787
  }
43775
- return formatFieldValue2(value, fieldName);
43788
+ return formatFieldValue2(value, fieldName2);
43776
43789
  }
43777
43790
  }
43778
43791
  function normalizeFieldDefs(fields) {
@@ -44615,7 +44628,21 @@ var init_Form = __esm({
44615
44628
  const normalizedInitialData = React77__default.useMemo(() => {
44616
44629
  const entityRowAsInitial = isPlainEntityRow(entity) ? entity : void 0;
44617
44630
  const callerInitial = initialData !== null && typeof initialData === "object" && !Array.isArray(initialData) ? initialData : {};
44618
- return entityRowAsInitial !== void 0 ? { ...entityRowAsInitial, ...callerInitial } : callerInitial;
44631
+ const merged = entityRowAsInitial !== void 0 ? { ...entityRowAsInitial, ...callerInitial } : callerInitial;
44632
+ const toId = (value) => {
44633
+ if (value === null || value === void 0 || typeof value !== "object" || value instanceof Date) return value;
44634
+ if (Array.isArray(value)) {
44635
+ return value.map(
44636
+ (item) => item !== null && typeof item === "object" && !Array.isArray(item) && !(item instanceof Date) && item.id !== void 0 && item.id !== null ? String(item.id) : item
44637
+ );
44638
+ }
44639
+ return value.id !== void 0 && value.id !== null ? String(value.id) : value;
44640
+ };
44641
+ const normalized = {};
44642
+ for (const [key, value] of Object.entries(merged)) {
44643
+ normalized[key] = key === "id" ? value : toId(value);
44644
+ }
44645
+ return normalized;
44619
44646
  }, [entity, initialData]);
44620
44647
  const entityDerivedFields = React77__default.useMemo(() => {
44621
44648
  if (fields && fields.length > 0) return void 0;
@@ -44744,8 +44771,8 @@ var init_Form = __esm({
44744
44771
  checkViolations(name, newFormData);
44745
44772
  };
44746
44773
  const isFieldVisible = React77__default.useCallback(
44747
- (fieldName) => {
44748
- const condition = conditionalFields[fieldName];
44774
+ (fieldName2) => {
44775
+ const condition = conditionalFields[fieldName2];
44749
44776
  if (!condition) return true;
44750
44777
  return Boolean(evaluateFormExpression(condition, evalContext));
44751
44778
  },
@@ -44791,9 +44818,9 @@ var init_Form = __esm({
44791
44818
  };
44792
44819
  const handleInvalid = (e) => {
44793
44820
  const target = e.target;
44794
- const fieldName = target.getAttribute("data-field-name") ?? target.name ?? "";
44821
+ const fieldName2 = target.getAttribute("data-field-name") ?? target.name ?? "";
44795
44822
  const fieldMessage = target.validationMessage || "Invalid value";
44796
- debug("forms", "invalid", { mode: formMode, fieldName, fieldMessage });
44823
+ debug("forms", "invalid", { mode: formMode, fieldName: fieldName2, fieldMessage });
44797
44824
  queueMicrotask(() => {
44798
44825
  const form = formRef.current;
44799
44826
  if (!form) return;
@@ -44829,22 +44856,22 @@ var init_Form = __esm({
44829
44856
  };
44830
44857
  const renderField = React77__default.useCallback(
44831
44858
  (field) => {
44832
- const fieldName = field.name || field.field;
44833
- if (!fieldName) return null;
44834
- if (!isFieldVisible(fieldName)) {
44859
+ const fieldName2 = field.name || field.field;
44860
+ if (!fieldName2) return null;
44861
+ if (!isFieldVisible(fieldName2)) {
44835
44862
  return null;
44836
44863
  }
44837
44864
  const inputType = determineInputType(field);
44838
- const label = field.label || fieldName.charAt(0).toUpperCase() + fieldName.slice(1).replace(/([A-Z])/g, " $1");
44839
- const currentValue2 = formData[fieldName] ?? field.defaultValue ?? "";
44840
- return /* @__PURE__ */ jsxs(VStack, { gap: "xs", "data-field": fieldName, children: [
44865
+ const label = field.label || fieldName2.charAt(0).toUpperCase() + fieldName2.slice(1).replace(/([A-Z])/g, " $1");
44866
+ const currentValue2 = formData[fieldName2] ?? field.defaultValue ?? "";
44867
+ return /* @__PURE__ */ jsxs(VStack, { gap: "xs", "data-field": fieldName2, children: [
44841
44868
  inputType !== "checkbox" && /* @__PURE__ */ jsxs(Typography, { as: "label", variant: "label", weight: "bold", children: [
44842
44869
  label,
44843
44870
  field.required && /* @__PURE__ */ jsx(Typography, { as: "span", color: "error", className: "ml-1", children: "*" })
44844
44871
  ] }),
44845
- renderFieldInput(field, fieldName, inputType, currentValue2, label),
44872
+ renderFieldInput(field, fieldName2, inputType, currentValue2, label),
44846
44873
  field.hint && /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "muted", children: field.hint })
44847
- ] }, fieldName);
44874
+ ] }, fieldName2);
44848
44875
  },
44849
44876
  [formData, isFieldVisible, relationsData, relationsLoading, isLoading]
44850
44877
  );
@@ -44873,8 +44900,8 @@ var init_Form = __esm({
44873
44900
  }
44874
44901
  return field;
44875
44902
  }).map((field) => {
44876
- const fieldName = field.name || field.field;
44877
- const override = fieldOverrides?.find((o) => o.name === fieldName);
44903
+ const fieldName2 = field.name || field.field;
44904
+ const override = fieldOverrides?.find((o) => o.name === fieldName2);
44878
44905
  if (!override) return field;
44879
44906
  return {
44880
44907
  ...field,
@@ -44930,11 +44957,11 @@ var init_Form = __esm({
44930
44957
  ] }, section.id);
44931
44958
  }).filter(Boolean);
44932
44959
  }, [sections, isSectionVisible, collapsedSections, renderField, gap]);
44933
- function renderFieldInput(field, fieldName, inputType, currentValue2, label) {
44960
+ function renderFieldInput(field, fieldName2, inputType, currentValue2, label) {
44934
44961
  const commonProps = {
44935
- id: fieldName,
44936
- name: fieldName,
44937
- "data-field-name": fieldName,
44962
+ id: fieldName2,
44963
+ name: fieldName2,
44964
+ "data-field-name": fieldName2,
44938
44965
  required: field.required,
44939
44966
  disabled: isLoading,
44940
44967
  placeholder: field.placeholder,
@@ -44948,7 +44975,7 @@ var init_Form = __esm({
44948
44975
  ...commonProps,
44949
44976
  label: label + (field.required ? " *" : ""),
44950
44977
  checked: Boolean(currentValue2),
44951
- onChange: (e) => handleChange(fieldName, e.target.checked)
44978
+ onChange: (e) => handleChange(fieldName2, e.target.checked)
44952
44979
  }
44953
44980
  );
44954
44981
  case "textarea":
@@ -44957,7 +44984,7 @@ var init_Form = __esm({
44957
44984
  {
44958
44985
  ...commonProps,
44959
44986
  value: String(currentValue2),
44960
- onChange: (e) => handleChange(fieldName, e.target.value),
44987
+ onChange: (e) => handleChange(fieldName2, e.target.value),
44961
44988
  minLength: field.min,
44962
44989
  maxLength: field.max
44963
44990
  }
@@ -44970,14 +44997,14 @@ var init_Form = __esm({
44970
44997
  ...commonProps,
44971
44998
  options,
44972
44999
  value: String(currentValue2),
44973
- onValueChange: (v) => handleChange(fieldName, v),
45000
+ onValueChange: (v) => handleChange(fieldName2, v),
44974
45001
  placeholder: field.placeholder || `Select ${label}...`
44975
45002
  }
44976
45003
  );
44977
45004
  }
44978
45005
  case "relation": {
44979
- const relationOptions = relationsData[fieldName] || [];
44980
- const relationLoading = relationsLoading[fieldName] || false;
45006
+ const relationOptions = relationsData[fieldName2] || [];
45007
+ const relationLoading = relationsLoading[fieldName2] || false;
44981
45008
  if (field.relation?.cardinality !== void 0 && MANY_CARDINALITIES.includes(field.relation.cardinality)) {
44982
45009
  const selectedValues = Array.isArray(currentValue2) ? currentValue2.map((v) => String(v)) : [];
44983
45010
  return /* @__PURE__ */ jsx(
@@ -44989,7 +45016,7 @@ var init_Form = __esm({
44989
45016
  clearable: true,
44990
45017
  options: [...relationOptions],
44991
45018
  value: selectedValues,
44992
- onValueChange: (value) => handleChange(fieldName, Array.isArray(value) ? value : [value]),
45019
+ onValueChange: (value) => handleChange(fieldName2, Array.isArray(value) ? value : [value]),
44993
45020
  placeholder: field.placeholder || `Select ${label}...`
44994
45021
  }
44995
45022
  );
@@ -44999,7 +45026,7 @@ var init_Form = __esm({
44999
45026
  {
45000
45027
  ...commonProps,
45001
45028
  value: currentValue2 ? String(currentValue2) : void 0,
45002
- onChange: (value) => handleChange(fieldName, value),
45029
+ onChange: (value) => handleChange(fieldName2, value),
45003
45030
  options: relationOptions,
45004
45031
  isLoading: relationLoading,
45005
45032
  placeholder: field.placeholder || `Select ${label}...`,
@@ -45016,7 +45043,7 @@ var init_Form = __esm({
45016
45043
  placeholder: field.placeholder,
45017
45044
  disabled: isLoading,
45018
45045
  value: arrayValue,
45019
- onChange: (next) => handleChange(fieldName, [...next])
45046
+ onChange: (next) => handleChange(fieldName2, [...next])
45020
45047
  }
45021
45048
  );
45022
45049
  }
@@ -45028,7 +45055,7 @@ var init_Form = __esm({
45028
45055
  type: "number",
45029
45056
  value: currentValue2 !== void 0 && currentValue2 !== "" ? String(currentValue2) : "",
45030
45057
  onChange: (e) => handleChange(
45031
- fieldName,
45058
+ fieldName2,
45032
45059
  e.target.value ? Number(e.target.value) : void 0
45033
45060
  ),
45034
45061
  min: field.min,
@@ -45045,7 +45072,7 @@ var init_Form = __esm({
45045
45072
  icon: DollarSign,
45046
45073
  value: currentValue2 !== void 0 && currentValue2 !== "" ? String(currentValue2) : "",
45047
45074
  onChange: (e) => handleChange(
45048
- fieldName,
45075
+ fieldName2,
45049
45076
  e.target.value ? Number(e.target.value) : void 0
45050
45077
  ),
45051
45078
  min: field.min,
@@ -45063,7 +45090,7 @@ var init_Form = __esm({
45063
45090
  const f3 = files[0];
45064
45091
  if (!f3) return;
45065
45092
  const reader = new FileReader();
45066
- reader.onload = () => handleChange(fieldName, {
45093
+ reader.onload = () => handleChange(fieldName2, {
45067
45094
  name: f3.name,
45068
45095
  mimeType: f3.type,
45069
45096
  sizeBytes: f3.size,
@@ -45080,7 +45107,7 @@ var init_Form = __esm({
45080
45107
  ...commonProps,
45081
45108
  type: "date",
45082
45109
  value: formatDateValue(currentValue2),
45083
- onChange: (e) => handleChange(fieldName, e.target.value)
45110
+ onChange: (e) => handleChange(fieldName2, e.target.value)
45084
45111
  }
45085
45112
  );
45086
45113
  case "datetime-local":
@@ -45090,7 +45117,7 @@ var init_Form = __esm({
45090
45117
  ...commonProps,
45091
45118
  type: "datetime-local",
45092
45119
  value: formatDateTimeValue(currentValue2),
45093
- onChange: (e) => handleChange(fieldName, e.target.value)
45120
+ onChange: (e) => handleChange(fieldName2, e.target.value)
45094
45121
  }
45095
45122
  );
45096
45123
  case "email":
@@ -45100,7 +45127,7 @@ var init_Form = __esm({
45100
45127
  ...commonProps,
45101
45128
  type: "email",
45102
45129
  value: String(currentValue2),
45103
- onChange: (e) => handleChange(fieldName, e.target.value),
45130
+ onChange: (e) => handleChange(fieldName2, e.target.value),
45104
45131
  minLength: field.min,
45105
45132
  maxLength: field.max
45106
45133
  }
@@ -45112,7 +45139,7 @@ var init_Form = __esm({
45112
45139
  ...commonProps,
45113
45140
  type: "url",
45114
45141
  value: String(currentValue2),
45115
- onChange: (e) => handleChange(fieldName, e.target.value),
45142
+ onChange: (e) => handleChange(fieldName2, e.target.value),
45116
45143
  minLength: field.min,
45117
45144
  maxLength: field.max
45118
45145
  }
@@ -45124,7 +45151,7 @@ var init_Form = __esm({
45124
45151
  ...commonProps,
45125
45152
  type: "password",
45126
45153
  value: String(currentValue2),
45127
- onChange: (e) => handleChange(fieldName, e.target.value),
45154
+ onChange: (e) => handleChange(fieldName2, e.target.value),
45128
45155
  minLength: field.min,
45129
45156
  maxLength: field.max
45130
45157
  }
@@ -45137,7 +45164,7 @@ var init_Form = __esm({
45137
45164
  ...commonProps,
45138
45165
  type: "text",
45139
45166
  value: String(currentValue2),
45140
- onChange: (e) => handleChange(fieldName, e.target.value),
45167
+ onChange: (e) => handleChange(fieldName2, e.target.value),
45141
45168
  minLength: field.min,
45142
45169
  maxLength: field.max
45143
45170
  }
@@ -45494,7 +45521,7 @@ function entityFieldsFromListItem(item) {
45494
45521
  }
45495
45522
  return result;
45496
45523
  }
45497
- function getStatusStyle(fieldName, value) {
45524
+ function getStatusStyle(fieldName2, value) {
45498
45525
  const val = String(value).toLowerCase();
45499
45526
  if (val.includes("complete") || val.includes("done"))
45500
45527
  return STATUS_STYLES.complete;
@@ -45510,12 +45537,12 @@ function getStatusStyle(fieldName, value) {
45510
45537
  if (val.includes("low")) return STATUS_STYLES.low;
45511
45538
  return STATUS_STYLES.default;
45512
45539
  }
45513
- function formatValue3(value, fieldName) {
45540
+ function formatValue3(value, fieldName2) {
45514
45541
  if (typeof value === "number") {
45515
- if (fieldName.toLowerCase().includes("progress") || fieldName.toLowerCase().includes("percent")) {
45542
+ if (fieldName2.toLowerCase().includes("progress") || fieldName2.toLowerCase().includes("percent")) {
45516
45543
  return `${value}%`;
45517
45544
  }
45518
- if (fieldName.toLowerCase().includes("budget") || fieldName.toLowerCase().includes("cost")) {
45545
+ if (fieldName2.toLowerCase().includes("budget") || fieldName2.toLowerCase().includes("cost")) {
45519
45546
  return new Intl.NumberFormat("en-US", {
45520
45547
  style: "currency",
45521
45548
  currency: "USD",
@@ -45538,8 +45565,8 @@ function formatValue3(value, fieldName) {
45538
45565
  }
45539
45566
  return String(value);
45540
45567
  }
45541
- function formatFieldLabel2(fieldName) {
45542
- return humanizeFieldName(fieldName).replace(/\sId$/, "").trim();
45568
+ function formatFieldLabel2(fieldName2) {
45569
+ return humanizeFieldName(fieldName2).replace(/\sId$/, "").trim();
45543
45570
  }
45544
45571
  var STATUS_STYLES, StatusBadge, ProgressIndicator, List3;
45545
45572
  var init_List = __esm({
@@ -45607,9 +45634,9 @@ var init_List = __esm({
45607
45634
  };
45608
45635
  StatusBadge = ({
45609
45636
  value,
45610
- fieldName
45637
+ fieldName: fieldName2
45611
45638
  }) => {
45612
- const style = getStatusStyle(fieldName, value);
45639
+ const style = getStatusStyle(fieldName2, value);
45613
45640
  return /* @__PURE__ */ jsxs(
45614
45641
  Typography,
45615
45642
  {
@@ -48522,8 +48549,8 @@ var init_StatCard = __esm({
48522
48549
  return items.length;
48523
48550
  }
48524
48551
  if (field.includes(":")) {
48525
- const [fieldName, fieldValue] = field.split(":");
48526
- return items.filter((item) => item[fieldName] === fieldValue).length;
48552
+ const [fieldName2, fieldValue] = field.split(":");
48553
+ return items.filter((item) => item[fieldName2] === fieldValue).length;
48527
48554
  }
48528
48555
  const fieldExistsOnItems = items.some((item) => field in item);
48529
48556
  if (fieldExistsOnItems) {
@@ -50043,6 +50070,8 @@ var init_component_registry_generated = __esm({
50043
50070
  init_DocSearch();
50044
50071
  init_DocSidebar();
50045
50072
  init_DocTOC();
50073
+ init_DocumentDetails();
50074
+ init_DocumentPanel();
50046
50075
  init_DocumentViewer();
50047
50076
  init_DrawFxLayer();
50048
50077
  init_DrawGroup();
@@ -50152,7 +50181,7 @@ var init_component_registry_generated = __esm({
50152
50181
  init_RelationSelect();
50153
50182
  init_RepeatableFormSection();
50154
50183
  init_ReplyTree();
50155
- init_RichBlockEditor();
50184
+ init_RichTextEditor();
50156
50185
  init_RuntimeDebugger2();
50157
50186
  init_ScaledDiagram();
50158
50187
  init_ScoreDisplay();
@@ -50315,6 +50344,8 @@ var init_component_registry_generated = __esm({
50315
50344
  "DocSearch": DocSearch,
50316
50345
  "DocSidebar": DocSidebar,
50317
50346
  "DocTOC": DocTOC,
50347
+ "DocumentDetails": DocumentDetails,
50348
+ "DocumentPanel": DocumentPanel,
50318
50349
  "DocumentViewer": DocumentViewer,
50319
50350
  "DrawFxLayer": DrawFxLayer,
50320
50351
  "DrawGroup": DrawGroup,
@@ -50426,7 +50457,7 @@ var init_component_registry_generated = __esm({
50426
50457
  "RelationSelect": RelationSelect,
50427
50458
  "RepeatableFormSection": RepeatableFormSection,
50428
50459
  "ReplyTree": ReplyTree,
50429
- "RichBlockEditor": RichBlockEditor,
50460
+ "RichTextEditor": RichTextEditor,
50430
50461
  "RuntimeDebugger": RuntimeDebugger,
50431
50462
  "ScaledDiagram": ScaledDiagram,
50432
50463
  "ScoreDisplay": ScoreDisplay,
@@ -50580,10 +50611,10 @@ function enrichFormFields(fields, entityDef) {
50580
50611
  }
50581
50612
  if (field && typeof field === "object" && !Array.isArray(field) && !React77__default.isValidElement(field) && !(field instanceof Date)) {
50582
50613
  const obj = field;
50583
- const fieldName = typeof obj.name === "string" ? obj.name : typeof obj.field === "string" ? obj.field : void 0;
50584
- if (!fieldName) return field;
50614
+ const fieldName2 = typeof obj.name === "string" ? obj.name : typeof obj.field === "string" ? obj.field : void 0;
50615
+ if (!fieldName2) return field;
50585
50616
  if (obj.type || obj.inputType) return field;
50586
- const entityField = fieldMap.get(fieldName);
50617
+ const entityField = fieldMap.get(fieldName2);
50587
50618
  if (!entityField) return field;
50588
50619
  const enriched = { ...obj, type: entityField.type };
50589
50620
  if (entityField.required && !("required" in obj)) {
@@ -50633,9 +50664,9 @@ function enrichDetailFields(fields, entityDef) {
50633
50664
  }
50634
50665
  if (field && typeof field === "object" && !Array.isArray(field) && !React77__default.isValidElement(field) && !(field instanceof Date)) {
50635
50666
  const obj = field;
50636
- const fieldName = typeof obj.key === "string" ? obj.key : typeof obj.name === "string" ? obj.name : void 0;
50637
- if (!fieldName || obj.type) return field;
50638
- const meta = metaFor(fieldName);
50667
+ const fieldName2 = typeof obj.key === "string" ? obj.key : typeof obj.name === "string" ? obj.name : void 0;
50668
+ if (!fieldName2 || obj.type) return field;
50669
+ const meta = metaFor(fieldName2);
50639
50670
  return meta ? { ...obj, ...meta } : field;
50640
50671
  }
50641
50672
  return field;
@@ -51474,9 +51505,9 @@ function UISlotRenderer({
51474
51505
  "ui-slot-renderer relative min-h-full",
51475
51506
  className
51476
51507
  ), children: [
51477
- /* @__PURE__ */ jsxs(Box, { className: "flex min-h-full", children: [
51478
- /* @__PURE__ */ jsx(UISlotComponent, { slot: "sidebar", className: "ui-slot-sidebar min-w-0 shrink-0" }),
51479
- /* @__PURE__ */ jsx(UISlotComponent, { slot: "main", className: "ui-slot-main flex-1 min-h-[200px]" })
51508
+ /* @__PURE__ */ jsxs(Box, { className: "flex min-h-full flex-col lg:flex-row", children: [
51509
+ /* @__PURE__ */ jsx(UISlotComponent, { slot: "sidebar", className: "ui-slot-sidebar min-w-0 lg:shrink-0" }),
51510
+ /* @__PURE__ */ jsx(UISlotComponent, { slot: "main", className: "ui-slot-main flex-1 min-w-0 min-h-[200px]" })
51480
51511
  ] }),
51481
51512
  /* @__PURE__ */ jsx(UISlotComponent, { slot: "modal", portal: true }),
51482
51513
  /* @__PURE__ */ jsx(UISlotComponent, { slot: "drawer", portal: true }),
@@ -53464,54 +53495,6 @@ var en_default = {
53464
53495
  "template.faq": "Frequently Asked Questions",
53465
53496
  "template.ourTeam": "Our Team",
53466
53497
  "template.caseStudies": "Case Studies",
53467
- "richBlockEditor.toolbar.text": "Text",
53468
- "richBlockEditor.toolbar.h1": "H1",
53469
- "richBlockEditor.toolbar.h2": "H2",
53470
- "richBlockEditor.toolbar.h3": "H3",
53471
- "richBlockEditor.toolbar.bulletList": "Bullet list",
53472
- "richBlockEditor.toolbar.numbered": "Numbered",
53473
- "richBlockEditor.toolbar.quote": "Quote",
53474
- "richBlockEditor.toolbar.code": "Code",
53475
- "richBlockEditor.toolbar.divider": "Divider",
53476
- "richBlockEditor.toolbar.image": "Image",
53477
- "richBlockEditor.blockType.paragraph": "Text",
53478
- "richBlockEditor.blockType.heading1": "Heading 1",
53479
- "richBlockEditor.blockType.heading2": "Heading 2",
53480
- "richBlockEditor.blockType.heading3": "Heading 3",
53481
- "richBlockEditor.blockType.bulletList": "Bullet list",
53482
- "richBlockEditor.blockType.numberedList": "Numbered list",
53483
- "richBlockEditor.blockType.quote": "Quote",
53484
- "richBlockEditor.blockType.code": "Code",
53485
- "richBlockEditor.blockType.divider": "Divider",
53486
- "richBlockEditor.blockType.image": "Image",
53487
- "richBlockEditor.blockActions": "Block actions",
53488
- "richBlockEditor.duplicate": "Duplicate",
53489
- "richBlockEditor.turnInto": "Turn into",
53490
- "richBlockEditor.placeholder.heading1": "Heading 1",
53491
- "richBlockEditor.placeholder.heading2": "Heading 2",
53492
- "richBlockEditor.placeholder.heading3": "Heading 3",
53493
- "richBlockEditor.placeholder.quote": "Quote",
53494
- "richBlockEditor.placeholder.code": "Enter code",
53495
- "richBlockEditor.placeholder.paragraph": "Start writing...",
53496
- "richBlockEditor.placeholder.listItem": "List item",
53497
- "richBlockEditor.placeholder.caption": "Caption (optional)",
53498
- "richBlockEditor.aria.heading1Block": "Heading 1 block",
53499
- "richBlockEditor.aria.heading2Block": "Heading 2 block",
53500
- "richBlockEditor.aria.heading3Block": "Heading 3 block",
53501
- "richBlockEditor.aria.quoteBlock": "Quote block",
53502
- "richBlockEditor.aria.codeBlock": "Code block",
53503
- "richBlockEditor.aria.codeLanguage": "Code language",
53504
- "richBlockEditor.aria.imageUrl": "Image URL",
53505
- "richBlockEditor.aria.imageCaption": "Image caption",
53506
- "richBlockEditor.aria.listItem": "List item",
53507
- "richBlockEditor.aria.removeListItem": "Remove list item",
53508
- "richBlockEditor.aria.paragraphBlock": "Paragraph block",
53509
- "richBlockEditor.embeddedImage": "Embedded image",
53510
- "richBlockEditor.noImageUrl": "No image URL set",
53511
- "richBlockEditor.addItem": "Add item",
53512
- "richBlockEditor.insertParagraphBelow": "Insert paragraph below",
53513
- "richBlockEditor.editorToolbar": "Block editor toolbar",
53514
- "richBlockEditor.insertEntry": "Insert {{label}}",
53515
53498
  "versionDiff.compare": "Compare",
53516
53499
  "versionDiff.to": "to",
53517
53500
  "versionDiff.beforeRevision": "Before revision",
@@ -53859,7 +53842,31 @@ var en_default = {
53859
53842
  "td.survivedAllWaves": "Survived all waves!",
53860
53843
  "td.victory": "Victory!",
53861
53844
  "td.wave": "Wave",
53862
- "td.waveInProgress": "Wave in progress\u2026"
53845
+ "td.waveInProgress": "Wave in progress\u2026",
53846
+ "documentDetails.title": "Details",
53847
+ "documentPanel.untitled": "Untitled",
53848
+ "documentPanel.edit": "Edit",
53849
+ "documentPanel.done": "Done",
53850
+ "documentPanel.autosaveHint": "Saves as you type",
53851
+ "documentPanel.placeholder": "Start writing\u2026",
53852
+ "richTextEditor.editorToolbar": "Formatting toolbar",
53853
+ "richTextEditor.editorSurface": "Rich text editor",
53854
+ "richTextEditor.bold": "Bold",
53855
+ "richTextEditor.italic": "Italic",
53856
+ "richTextEditor.underline": "Underline",
53857
+ "richTextEditor.strikethrough": "Strikethrough",
53858
+ "richTextEditor.heading1": "Heading 1",
53859
+ "richTextEditor.heading2": "Heading 2",
53860
+ "richTextEditor.heading3": "Heading 3",
53861
+ "richTextEditor.bulletList": "Bullet list",
53862
+ "richTextEditor.numberedList": "Numbered list",
53863
+ "richTextEditor.quote": "Quote",
53864
+ "richTextEditor.code": "Code",
53865
+ "richTextEditor.link": "Add link",
53866
+ "richTextEditor.removeLink": "Remove link",
53867
+ "richTextEditor.divider": "Divider",
53868
+ "richTextEditor.linkPrompt": "Link address",
53869
+ "richTextEditor.placeholder": "Start writing\u2026"
53863
53870
  };
53864
53871
 
53865
53872
  // hooks/useTranslate.ts
@@ -53880,7 +53887,7 @@ var I18nContext = createContext({
53880
53887
  });
53881
53888
  I18nContext.displayName = "I18nContext";
53882
53889
  var I18nProvider = I18nContext.Provider;
53883
- function useTranslate114() {
53890
+ function useTranslate116() {
53884
53891
  return useContext(I18nContext);
53885
53892
  }
53886
53893
  function createTranslate(messages) {
@@ -54162,4 +54169,4 @@ function useGitHubBranches(owner, repo, enabled = true) {
54162
54169
  });
54163
54170
  }
54164
54171
 
54165
- export { ALL_PRESETS, ALMADAR_DND_MIME, AR_BOOK_FIELDS, AboutPageTemplate, Accordion, Card2 as ActionCard, ActionPalette, ActionTile, ActivationBlock, Alert, AlgoGraphCanvas, AlgorithmCanvas, AnimatedCounter, AnimatedGraphic, AnimatedReveal, ArticleSection, Aside, AssetPicker, AtlasImage, AtlasPanel, AuthLayout, Avatar, Badge, BehaviorView, BiologyCanvas, BloomQuizBlock, BookChapterView, BookCoverPage, BookNavBar, BookTableOfContents, BookViewer, Box, BranchingLogicBuilder, Breadcrumb, Button, ButtonGroup, CTABanner, CalendarGrid, Canvas, Canvas2D, Card, CardBody, CardContent, CardFooter, CardGrid, CardHeader, CardTitle, Carousel, CaseStudyCard, CaseStudyOrganism, Center, Chart, ChartLegend, ChatBar, Checkbox, ChemistryCanvas, ChoiceButton, Coachmark, CodeBlock, CodeRunnerPanel, CollapsibleSection, CommunityLinks, ConditionalWrapper, ConfettiEffect, ConfirmDialog, ConnectionBlock, Container, ContentRenderer, ContentSection, ControlButton, ControlGrid, CounterTemplate, DEFAULT_LIKERT_OPTIONS, DEFAULT_MATRIX_COLUMNS, DEFAULT_SLOTS, DIAMOND_TOP_Y, DashboardGrid, DashboardLayout, DataGrid, DataList, DataTable, DateRangePicker, DateRangeSelector, DayCell, DetailPanel, Dialog, DialogueBubble, Divider, DocBreadcrumb, DocPagination, DocSearch, DocSidebar, DocTOC, DocumentViewer, StateMachineView as DomStateMachineVisualizer, Drawer, DrawerSlot, ELEMENT_SELECTED_EVENT, EdgeDecoration, EditorCheckbox, EditorSelect, EditorSlider, EditorTextInput, EditorToolbar, EmojiPicker, EmptyState, EntityDisplayEvents, ErrorBoundary, ErrorState, FEATURE_COLORS, FEATURE_TYPES, FLOOR_HEIGHT, FeatureCard, FeatureDetailPageTemplate, FeatureGrid, FeatureGridOrganism, FileTree, FilterGroup, FilterPill, Flex, FlipCard, FlipContainer, FloatingActionButton, Form, FormActions, FormField, FormLayout, FormSection, FormSectionHeader, FxOverlay, GameAudioToggle, GameHud, GameIcon, GameMenu, GameShell, GenericAppTemplate, GeometricPattern, GradientDivider, GraphCanvas, GraphView, Grid, GridPicker, HStack, Header, HealthBar, HeroOrganism, HeroSection, I18nProvider, IDENTITY_BOOK_FIELDS, Icon, IconPicker, ImportPreviewTree, ImportProgress, ImportSourcePicker, InfiniteScrollSentinel, Input, InputGroup, InstallBox, JazariStateMachine, JsonTreeEditor, Label, LandingPageTemplate, LawReferenceTooltip, LearningCanvas, LearningScene3D, Lightbox, LikertScale, LineChart2 as LineChart, List3 as List, LoadingState, MapView, MarkdownContent, MarketingFooter, MarketingStatCard, MasterDetail, MasterDetailLayout, MathCanvas, MatrixQuestion, MediaGallery, Menu, Meter, Modal, ModalSlot, ModuleCard, Navigation, NodeSlotEditor, NotifyListener, NumberStepper, OnboardingSpotlight, OptionConstraintGroup, StateMachineView as OrbitalStateMachineView, OrbitalVisualization, Overlay, PageHeader, PageTransition, Pagination, PatternTile, PhysicsCanvas, Popover, PositionedCanvas, Presence, PricingCard, PricingGrid, PricingOrganism, PricingPageTemplate, ProgressBar, ProgressDots, PropertyInspector, PullQuote, PullToRefresh, QrScanner, QuizBlock, Radio, RangeSlider, ReflectionBlock, RelationSelect, RepeatableFormSection, ReplyTree, RichBlockEditor, RuntimeDebugger, SHEET_COLUMNS, SPRITE_SHEET_LAYOUT, ScaledDiagram, ScoreDisplay, SearchInput, Section, SectionHeader, SegmentRenderer, Select, SequenceBar, ServiceCatalog, SharedEntityStoreContext, ShowcaseCard, ShowcaseOrganism, SidePanel, Sidebar, SignaturePad, SimpleGrid, Skeleton, SlotContentRenderer, SocialProof, SortableList, Spacer, Sparkline, Spinner, Split, SplitPane, SplitSection, Stack, StarRating, StatBadge, StatCard, StatDisplay, StateGraph, StateJsonView, StateMachineView, StateNode2 as StateNode, StatsGrid, StatsOrganism, StatusBar, StatusDot, StepFlow, StepFlowOrganism, SubagentTracePanel, SvgBranch, SvgConnection, SvgFlow, SvgGrid, SvgLobe, SvgMesh, SvgMorph, SvgNode, SvgPulse, SvgRing, SvgShield, SvgStack, SwipeableRow, Switch, TERRAIN_COLORS, TILE_HEIGHT, TILE_WIDTH, TabbedContainer, TableView, Tabs, TagCloud, TagInput, TeamCard, TeamOrganism, TerrainPalette, TextHighlight, Textarea, ThemeToggle, TimeSlotCell, Timeline, TimerDisplay, Toast, ToastSlot, Tooltip, TraitFrame, TraitSlot, TraitStateViewer, TransitionArrow, TrendIndicator, TypewriterText, Typography, UISlotComponent, UISlotRenderer, UploadDropZone, VStack, VersionDiff, ViolationAlert, VoteStack, WizardContainer, WizardNavigation, WizardProgress, arrowBetween, billboardLabel, boardEntity, bool, calculateAttackTargets, calculateValidMoves, cn, createInitialGameState, createSharedEntityStore, createTranslate, createUnitAnimationState, cylinderBetween, get3DClickPayload, getCurrentFrame, getTileDimensions, inferDirection, isoToScreen, makeAsset, makeAssetMap, mapBookData, meshSphere, num, objAvailableActions, objAvailableEvents, objCurrentState, objIcon, objId, objMaxRules, objName, objRules, objStates, parseEditFocus, parseLessonSegments, parseMarkdownWithCodeBlocks, parseQueryBinding, pendulum, projectileMotion, registerCodeLanguageLoader, renderPatternValue, resolveFieldMap, resolveFrame, resolveSheetDirection, rows, runTickFrame, screenToIso, springOscillator, str, tickAnimationState, toCodeLanguage, transitionAnimation, unitHealth, unitPosition, unitTeam, useAgentChat, useAnchorRect, useAtlasSliceDataUrl, useAuthContext, useCamera, useCanvasGestures, useCompile, useConnectGitHub, useDeepAgentGeneration, useDisconnectGitHub, useDragReorder, useDraggable, useDropZone, useEmitEvent, useEventBus, useEventListener, useExtensions, useFileEditor, useFileSystem, useGameAudio, useGitHubBranches, useGitHubRepo, useGitHubRepos, useGitHubStatus, useImageCache, useInfiniteScroll, useLongPress, useMediaQuery, useOrbitalHistory, usePresence, usePreview, usePullToRefresh, useQuerySingleton, useRenderInterpolation, useSharedEntitySnapshot, useSharedEntityStore, useSharedEntityStoreContext, useSwipeGesture, useTapReveal, useTraitListens, useTranslate114 as useTranslate, useUIEvents, useUISlotManager, useUnitSpriteAtlas, useValidation, vec2 };
54172
+ export { ALL_PRESETS, ALMADAR_DND_MIME, AR_BOOK_FIELDS, AboutPageTemplate, Accordion, Card2 as ActionCard, ActionPalette, ActionTile, ActivationBlock, Alert, AlgoGraphCanvas, AlgorithmCanvas, AnimatedCounter, AnimatedGraphic, AnimatedReveal, ArticleSection, Aside, AssetPicker, AtlasImage, AtlasPanel, AuthLayout, Avatar, Badge, BehaviorView, BiologyCanvas, BloomQuizBlock, BookChapterView, BookCoverPage, BookNavBar, BookTableOfContents, BookViewer, Box, BranchingLogicBuilder, Breadcrumb, Button, ButtonGroup, CTABanner, CalendarGrid, Canvas, Canvas2D, Card, CardBody, CardContent, CardFooter, CardGrid, CardHeader, CardTitle, Carousel, CaseStudyCard, CaseStudyOrganism, Center, Chart, ChartLegend, ChatBar, Checkbox, ChemistryCanvas, ChoiceButton, Coachmark, CodeBlock, CodeRunnerPanel, CollapsibleSection, CommunityLinks, ConditionalWrapper, ConfettiEffect, ConfirmDialog, ConnectionBlock, Container, ContentRenderer, ContentSection, ControlButton, ControlGrid, CounterTemplate, DEFAULT_LIKERT_OPTIONS, DEFAULT_MATRIX_COLUMNS, DEFAULT_SLOTS, DIAMOND_TOP_Y, DashboardGrid, DashboardLayout, DataGrid, DataList, DataTable, DateRangePicker, DateRangeSelector, DayCell, DetailPanel, Dialog, DialogueBubble, Divider, DocBreadcrumb, DocPagination, DocSearch, DocSidebar, DocTOC, DocumentDetails, DocumentPanel, DocumentViewer, StateMachineView as DomStateMachineVisualizer, Drawer, DrawerSlot, ELEMENT_SELECTED_EVENT, EdgeDecoration, EditorCheckbox, EditorSelect, EditorSlider, EditorTextInput, EditorToolbar, EmojiPicker, EmptyState, EntityDisplayEvents, ErrorBoundary, ErrorState, FEATURE_COLORS, FEATURE_TYPES, FLOOR_HEIGHT, FeatureCard, FeatureDetailPageTemplate, FeatureGrid, FeatureGridOrganism, FileTree, FilterGroup, FilterPill, Flex, FlipCard, FlipContainer, FloatingActionButton, Form, FormActions, FormField, FormLayout, FormSection, FormSectionHeader, FxOverlay, GameAudioToggle, GameHud, GameIcon, GameMenu, GameShell, GenericAppTemplate, GeometricPattern, GradientDivider, GraphCanvas, GraphView, Grid, GridPicker, HStack, Header, HealthBar, HeroOrganism, HeroSection, I18nProvider, IDENTITY_BOOK_FIELDS, Icon, IconPicker, ImportPreviewTree, ImportProgress, ImportSourcePicker, InfiniteScrollSentinel, Input, InputGroup, InstallBox, JazariStateMachine, JsonTreeEditor, Label, LandingPageTemplate, LawReferenceTooltip, LearningCanvas, LearningScene3D, Lightbox, LikertScale, LineChart2 as LineChart, List3 as List, LoadingState, MapView, MarkdownContent, MarketingFooter, MarketingStatCard, MasterDetail, MasterDetailLayout, MathCanvas, MatrixQuestion, MediaGallery, Menu, Meter, Modal, ModalSlot, ModuleCard, Navigation, NodeSlotEditor, NotifyListener, NumberStepper, OnboardingSpotlight, OptionConstraintGroup, StateMachineView as OrbitalStateMachineView, OrbitalVisualization, Overlay, PageHeader, PageTransition, Pagination, PatternTile, PhysicsCanvas, Popover, PositionedCanvas, Presence, PricingCard, PricingGrid, PricingOrganism, PricingPageTemplate, ProgressBar, ProgressDots, PropertyInspector, PullQuote, PullToRefresh, QrScanner, QuizBlock, Radio, RangeSlider, ReflectionBlock, RelationSelect, RepeatableFormSection, ReplyTree, RichTextEditor, RuntimeDebugger, SHEET_COLUMNS, SPRITE_SHEET_LAYOUT, ScaledDiagram, ScoreDisplay, SearchInput, Section, SectionHeader, SegmentRenderer, Select, SequenceBar, ServiceCatalog, SharedEntityStoreContext, ShowcaseCard, ShowcaseOrganism, SidePanel, Sidebar, SignaturePad, SimpleGrid, Skeleton, SlotContentRenderer, SocialProof, SortableList, Spacer, Sparkline, Spinner, Split, SplitPane, SplitSection, Stack, StarRating, StatBadge, StatCard, StatDisplay, StateGraph, StateJsonView, StateMachineView, StateNode2 as StateNode, StatsGrid, StatsOrganism, StatusBar, StatusDot, StepFlow, StepFlowOrganism, SubagentTracePanel, SvgBranch, SvgConnection, SvgFlow, SvgGrid, SvgLobe, SvgMesh, SvgMorph, SvgNode, SvgPulse, SvgRing, SvgShield, SvgStack, SwipeableRow, Switch, TERRAIN_COLORS, TILE_HEIGHT, TILE_WIDTH, TabbedContainer, TableView, Tabs, TagCloud, TagInput, TeamCard, TeamOrganism, TerrainPalette, TextHighlight, Textarea, ThemeToggle, TimeSlotCell, Timeline, TimerDisplay, Toast, ToastSlot, Tooltip, TraitFrame, TraitSlot, TraitStateViewer, TransitionArrow, TrendIndicator, TypewriterText, Typography, UISlotComponent, UISlotRenderer, UploadDropZone, VStack, VersionDiff, ViolationAlert, VoteStack, WizardContainer, WizardNavigation, WizardProgress, arrowBetween, billboardLabel, boardEntity, bool, calculateAttackTargets, calculateValidMoves, cn, createInitialGameState, createSharedEntityStore, createTranslate, createUnitAnimationState, cylinderBetween, get3DClickPayload, getCurrentFrame, getTileDimensions, inferDirection, isoToScreen, makeAsset, makeAssetMap, mapBookData, meshSphere, num, objAvailableActions, objAvailableEvents, objCurrentState, objIcon, objId, objMaxRules, objName, objRules, objStates, parseEditFocus, parseLessonSegments, parseMarkdownWithCodeBlocks, parseQueryBinding, pendulum, projectileMotion, registerCodeLanguageLoader, renderPatternValue, resolveFieldMap, resolveFrame, resolveSheetDirection, rows, runTickFrame, sanitizeRichHtml, screenToIso, springOscillator, str, tickAnimationState, toCodeLanguage, transitionAnimation, unitHealth, unitPosition, unitTeam, useAgentChat, useAnchorRect, useAtlasSliceDataUrl, useAuthContext, useCamera, useCanvasGestures, useCompile, useConnectGitHub, useDeepAgentGeneration, useDisconnectGitHub, useDragReorder, useDraggable, useDropZone, useEmitEvent, useEventBus, useEventListener, useExtensions, useFileEditor, useFileSystem, useGameAudio, useGitHubBranches, useGitHubRepo, useGitHubRepos, useGitHubStatus, useImageCache, useInfiniteScroll, useLongPress, useMediaQuery, useOrbitalHistory, usePresence, usePreview, usePullToRefresh, useQuerySingleton, useRenderInterpolation, useSharedEntitySnapshot, useSharedEntityStore, useSharedEntityStoreContext, useSwipeGesture, useTapReveal, useTraitListens, useTranslate116 as useTranslate, useUIEvents, useUISlotManager, useUnitSpriteAtlas, useValidation, vec2 };