@almadar/ui 5.159.0 → 5.161.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.
@@ -26338,8 +26338,9 @@ function DataGrid({
26338
26338
  const titleField = fieldDefs.find((f3) => f3.variant === "h3" || f3.variant === "h4") ?? fieldDefs[0];
26339
26339
  const badgeFields = fieldDefs.filter((f3) => f3.variant === "badge" && f3 !== titleField);
26340
26340
  const bodyFields = fieldDefs.filter((f3) => f3 !== titleField && !badgeFields.includes(f3));
26341
- const primaryActions = actionDefs.filter((a) => a.variant !== "danger");
26342
- const dangerActions = actionDefs.filter((a) => a.variant === "danger");
26341
+ const inlineCap = Math.min(maxInlineActions ?? 1, 1);
26342
+ const inlineCardActions = actionDefs.filter((a) => a.variant === "primary").slice(0, inlineCap);
26343
+ const menuCardActions = actionDefs.filter((a) => !inlineCardActions.includes(a));
26343
26344
  const fireAction = (action, itemData) => {
26344
26345
  if (action.navigatesTo) {
26345
26346
  const url = action.navigatesTo.replace(
@@ -26359,6 +26360,9 @@ function DataGrid({
26359
26360
  e.stopPropagation();
26360
26361
  fireAction(action, itemData);
26361
26362
  };
26363
+ const cardClickAction = actionDefs.find((a) => a.variant !== "danger");
26364
+ const handleCardClick = cardClickAction ? (itemData) => () => fireAction(cardClickAction, itemData) : void 0;
26365
+ const stopCardClick = handleCardClick ? (e) => e.stopPropagation() : void 0;
26362
26366
  const hasRenderProp = typeof children === "function";
26363
26367
  React87.useEffect(() => {
26364
26368
  if (data.length > 0 && !hasRenderProp && fieldDefs.length === 0) {
@@ -26430,55 +26434,40 @@ function DataGrid({
26430
26434
  {
26431
26435
  "data-entity-row": true,
26432
26436
  "data-entity-id": id,
26433
- className: cn("relative group/rowactions", isSelected && "ring-2 ring-primary rounded-lg"),
26437
+ onClick: handleCardClick?.(itemData),
26438
+ className: cn("relative group/rowactions", handleCardClick && "cursor-pointer", isSelected && "ring-2 ring-primary rounded-lg"),
26434
26439
  children: [
26435
26440
  children(itemData, index),
26436
- actionDefs.length > 0 && /* @__PURE__ */ jsxRuntime.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: [
26437
- /* @__PURE__ */ jsxRuntime.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: [
26438
- (maxInlineActions != null ? actionDefs.slice(0, maxInlineActions) : actionDefs).map((action, idx) => /* @__PURE__ */ jsxRuntime.jsxs(
26439
- Button,
26440
- {
26441
- variant: action.variant === "primary" ? "primary" : "ghost",
26442
- size: "sm",
26443
- onClick: handleActionClick(action, itemData),
26444
- "data-testid": `action-${action.event}`,
26445
- "data-row-id": String(itemData.id),
26446
- className: action.variant === "danger" ? "text-error hover:text-error hover:bg-error/10" : void 0,
26447
- children: [
26448
- action.icon && renderIconInput(action.icon, { size: "xs", className: "mr-1" }),
26449
- action.label
26450
- ]
26451
- },
26452
- idx
26453
- )),
26454
- maxInlineActions != null && actionDefs.length > maxInlineActions && /* @__PURE__ */ jsxRuntime.jsx(
26455
- Menu,
26456
- {
26457
- position: "bottom-end",
26458
- trigger: /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "ghost", size: "sm", "aria-label": t("common.actions"), "data-testid": "action-overflow", children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "more-horizontal", size: "xs" }) }),
26459
- items: actionDefs.slice(maxInlineActions).map((action) => ({
26460
- label: action.label,
26461
- icon: action.icon,
26462
- event: action.event,
26463
- onClick: () => fireAction(action, itemData)
26464
- }))
26465
- }
26466
- )
26467
- ] }),
26468
- /* @__PURE__ */ jsxRuntime.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__ */ jsxRuntime.jsx(
26441
+ actionDefs.length > 0 && /* @__PURE__ */ jsxRuntime.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__ */ jsxRuntime.jsxs(HStack, { gap: "xs", className: "rounded-md border border-border bg-card/95 backdrop-blur-sm shadow-sm p-0.5", children: [
26442
+ inlineCardActions.map((action, idx) => /* @__PURE__ */ jsxRuntime.jsxs(
26443
+ Button,
26444
+ {
26445
+ variant: "primary",
26446
+ size: "sm",
26447
+ onClick: handleActionClick(action, itemData),
26448
+ "data-testid": `action-${action.event}`,
26449
+ "data-row-id": String(itemData.id),
26450
+ children: [
26451
+ action.icon && renderIconInput(action.icon, { size: "xs", className: "mr-1" }),
26452
+ action.label
26453
+ ]
26454
+ },
26455
+ idx
26456
+ )),
26457
+ menuCardActions.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
26469
26458
  Menu,
26470
26459
  {
26471
26460
  position: "bottom-end",
26472
26461
  trigger: /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "ghost", size: "sm", "aria-label": t("common.actions"), "data-testid": "action-overflow", children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "more-horizontal", size: "xs" }) }),
26473
- items: actionDefs.map((action) => ({
26462
+ items: menuCardActions.map((action) => ({
26474
26463
  label: action.label,
26475
26464
  icon: action.icon,
26476
- event: action.event,
26465
+ variant: action.variant === "danger" ? "danger" : "default",
26477
26466
  onClick: () => fireAction(action, itemData)
26478
26467
  }))
26479
26468
  }
26480
- ) })
26481
- ] })
26469
+ )
26470
+ ] }) })
26482
26471
  ]
26483
26472
  },
26484
26473
  id
@@ -26492,12 +26481,14 @@ function DataGrid({
26492
26481
  {
26493
26482
  "data-entity-row": true,
26494
26483
  "data-entity-id": id,
26484
+ onClick: handleCardClick?.(itemData),
26495
26485
  className: cn(
26496
26486
  "bg-card rounded-lg",
26497
26487
  "border border-border",
26498
26488
  "shadow-elevation-card hover:shadow-elevation-dialog",
26499
26489
  "hover:border-primary transition-all",
26500
26490
  "flex flex-col",
26491
+ handleCardClick && "cursor-pointer",
26501
26492
  isSelected && "ring-2 ring-primary border-primary"
26502
26493
  ),
26503
26494
  children: [
@@ -26547,53 +26538,35 @@ function DataGrid({
26547
26538
  ] }, field.name);
26548
26539
  }) })
26549
26540
  ] }),
26550
- (primaryActions.length > 0 || dangerActions.length > 0) && /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "xs", className: "flex-shrink-0", children: [
26551
- (maxInlineActions != null ? primaryActions.slice(0, maxInlineActions) : primaryActions).map((action, idx) => /* @__PURE__ */ jsxRuntime.jsx(
26541
+ actionDefs.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "xs", onClick: stopCardClick, className: "flex-shrink-0", children: [
26542
+ inlineCardActions.map((action, idx) => /* @__PURE__ */ jsxRuntime.jsx(
26552
26543
  Button,
26553
26544
  {
26554
- variant: action.variant === "primary" ? "primary" : "ghost",
26545
+ variant: "primary",
26555
26546
  size: "sm",
26556
26547
  onClick: handleActionClick(action, itemData),
26557
26548
  "data-testid": `action-${action.event}`,
26558
26549
  "data-row-id": String(itemData.id),
26559
26550
  "aria-label": action.label,
26560
26551
  title: action.label,
26561
- className: cn(
26562
- action.variant === "primary" ? void 0 : "text-muted-foreground hover:text-foreground",
26563
- action.icon && "px-2"
26564
- ),
26552
+ className: cn(action.icon && "px-2"),
26565
26553
  children: action.icon ? renderIconInput(action.icon, { size: "xs" }) : action.label
26566
26554
  },
26567
26555
  idx
26568
26556
  )),
26569
- maxInlineActions != null && primaryActions.length > maxInlineActions && /* @__PURE__ */ jsxRuntime.jsx(
26557
+ menuCardActions.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
26570
26558
  Menu,
26571
26559
  {
26572
26560
  position: "bottom-end",
26573
26561
  trigger: /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "ghost", size: "sm", "aria-label": t("common.actions"), "data-testid": "action-overflow", children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "more-horizontal", size: "xs" }) }),
26574
- items: primaryActions.slice(maxInlineActions).map((action) => ({
26562
+ items: menuCardActions.map((action) => ({
26575
26563
  label: action.label,
26576
26564
  icon: action.icon,
26577
- event: action.event,
26565
+ variant: action.variant === "danger" ? "danger" : "default",
26578
26566
  onClick: () => fireAction(action, itemData)
26579
26567
  }))
26580
26568
  }
26581
- ),
26582
- dangerActions.map((action, idx) => /* @__PURE__ */ jsxRuntime.jsx(
26583
- Button,
26584
- {
26585
- variant: "ghost",
26586
- size: "sm",
26587
- onClick: handleActionClick(action, itemData),
26588
- "data-testid": `action-${action.event}`,
26589
- "data-row-id": String(itemData.id),
26590
- "aria-label": action.label,
26591
- title: action.label,
26592
- className: "text-error hover:text-error hover:bg-error/10 px-2",
26593
- children: action.icon ? renderIconInput(action.icon, { size: "xs" }) : action.label
26594
- },
26595
- `danger-${idx}`
26596
- ))
26569
+ )
26597
26570
  ] })
26598
26571
  ] }) }),
26599
26572
  bodyFields.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "px-4 pt-2 pb-4 flex-1", children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "xs", children: [
@@ -26614,8 +26587,8 @@ function DataGrid({
26614
26587
  }
26615
26588
  return /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "xs", className: "items-center", children: [
26616
26589
  field.icon && renderIconInput(field.icon, { size: "xs", className: "text-muted-foreground" }),
26617
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", color: "secondary", children: (field.label ?? fieldLabel2(field.name)) + ":" }),
26618
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", children: formatValue(value, field.format) })
26590
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", color: "secondary", className: "sr-only", children: (field.label ?? fieldLabel2(field.name)) + ":" }),
26591
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", color: "secondary", children: formatValue(value, field.format) })
26619
26592
  ] }, field.name);
26620
26593
  }) })
26621
26594
  ] }) })
@@ -26835,54 +26808,59 @@ function DataList({
26835
26808
  if (!itemActions || itemActions.length === 0) return null;
26836
26809
  const inline = maxInlineActions != null ? itemActions.slice(0, maxInlineActions) : itemActions;
26837
26810
  const overflow = maxInlineActions != null ? itemActions.slice(maxInlineActions) : [];
26838
- return /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "xs", className: "flex-shrink-0", children: [
26839
- inline.map((action, idx) => /* @__PURE__ */ jsxRuntime.jsxs(
26840
- Button,
26841
- {
26842
- variant: action.variant ?? "ghost",
26843
- size: "sm",
26844
- onClick: handleActionClick(action, itemData),
26845
- "data-testid": `action-${action.event}`,
26846
- "data-row-id": String(itemData.id),
26847
- className: cn(
26848
- action.variant === "danger" && "text-error hover:bg-error/10",
26849
- // Must sit on the Button itself: the variant's own text colour
26850
- // beats an inherited one from the row wrapper.
26851
- onPrimary && "!text-primary-foreground hover:bg-primary-foreground/15"
26852
- ),
26853
- children: [
26854
- action.icon && renderIconInput2(action.icon, { size: "xs", className: "mr-1" }),
26855
- action.label
26856
- ]
26857
- },
26858
- idx
26859
- )),
26860
- overflow.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
26861
- Menu,
26862
- {
26863
- position: "bottom-end",
26864
- trigger: /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "ghost", size: "sm", "aria-label": t("common.actions"), "data-testid": "action-overflow", children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "more-horizontal", size: "xs" }) }),
26865
- items: overflow.map((action) => ({
26866
- label: action.label,
26867
- icon: action.icon,
26868
- event: action.event,
26869
- variant: action.variant === "danger" ? "danger" : "default",
26870
- onClick: () => eventBus.emit(`UI:${action.event}`, {
26871
- id: itemData.id,
26872
- row: itemData
26873
- })
26874
- }))
26875
- }
26876
- )
26877
- ] });
26811
+ return (
26812
+ // stopPropagation at the cluster: the "⋯" trigger opens the overflow
26813
+ // menu without also firing the row's default click (inline buttons
26814
+ // already stop it in handleActionClick; the Menu panel is portaled).
26815
+ /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "xs", onClick: rowClickEvent ? (e) => e.stopPropagation() : void 0, className: "flex-shrink-0", children: [
26816
+ inline.map((action, idx) => /* @__PURE__ */ jsxRuntime.jsxs(
26817
+ Button,
26818
+ {
26819
+ variant: action.variant ?? "ghost",
26820
+ size: "sm",
26821
+ onClick: handleActionClick(action, itemData),
26822
+ "data-testid": `action-${action.event}`,
26823
+ "data-row-id": String(itemData.id),
26824
+ className: cn(
26825
+ action.variant === "danger" && "text-error hover:bg-error/10",
26826
+ // Must sit on the Button itself: the variant's own text colour
26827
+ // beats an inherited one from the row wrapper.
26828
+ onPrimary && "!text-primary-foreground hover:bg-primary-foreground/15"
26829
+ ),
26830
+ children: [
26831
+ action.icon && renderIconInput2(action.icon, { size: "xs", className: "mr-1" }),
26832
+ action.label
26833
+ ]
26834
+ },
26835
+ idx
26836
+ )),
26837
+ overflow.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
26838
+ Menu,
26839
+ {
26840
+ position: "bottom-end",
26841
+ trigger: /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "ghost", size: "sm", "aria-label": t("common.actions"), "data-testid": "action-overflow", children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "more-horizontal", size: "xs" }) }),
26842
+ items: overflow.map((action) => ({
26843
+ label: action.label,
26844
+ icon: action.icon,
26845
+ variant: action.variant === "danger" ? "danger" : "default",
26846
+ onClick: () => eventBus.emit(`UI:${action.event}`, {
26847
+ id: itemData.id,
26848
+ row: itemData
26849
+ })
26850
+ }))
26851
+ }
26852
+ )
26853
+ ] })
26854
+ );
26878
26855
  };
26856
+ const rowClickEvent = itemClickEvent || itemActions?.find((a) => a.variant !== "danger")?.event;
26879
26857
  const handleRowClick = (itemData) => () => {
26880
- if (!itemClickEvent) return;
26858
+ if (!rowClickEvent) return;
26881
26859
  const payload = {
26882
26860
  id: itemData.id,
26883
26861
  row: itemData
26884
26862
  };
26885
- eventBus.emit(`UI:${itemClickEvent}`, payload);
26863
+ eventBus.emit(`UI:${rowClickEvent}`, payload);
26886
26864
  };
26887
26865
  if (isLoading) {
26888
26866
  return /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "text-center py-8", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", color: "secondary", children: t("loading.items") }) });
@@ -26930,10 +26908,10 @@ function DataList({
26930
26908
  {
26931
26909
  "data-entity-row": true,
26932
26910
  "data-entity-id": id,
26933
- onClick: itemClickEvent ? handleRowClick(itemData) : void 0,
26911
+ onClick: rowClickEvent ? handleRowClick(itemData) : void 0,
26934
26912
  className: cn(
26935
26913
  "flex px-4 group/rowactions",
26936
- itemClickEvent && "cursor-pointer",
26914
+ rowClickEvent && "cursor-pointer",
26937
26915
  isSent ? "justify-end" : "justify-start"
26938
26916
  ),
26939
26917
  children: /* @__PURE__ */ jsxRuntime.jsxs(
@@ -27009,7 +26987,7 @@ function DataList({
27009
26987
  const id2 = itemData.id || String(index);
27010
26988
  const actions = renderItemActions(itemData);
27011
26989
  return wrapDnd(
27012
- /* @__PURE__ */ jsxRuntime.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: [
26990
+ /* @__PURE__ */ jsxRuntime.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: [
27013
26991
  children(itemData, index),
27014
26992
  actions && /* @__PURE__ */ jsxRuntime.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: [
27015
26993
  /* @__PURE__ */ jsxRuntime.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 }),
@@ -27021,7 +26999,6 @@ function DataList({
27021
26999
  items: (itemActions ?? []).map((action) => ({
27022
27000
  label: action.label,
27023
27001
  icon: action.icon,
27024
- event: action.event,
27025
27002
  variant: action.variant === "danger" ? "danger" : "default",
27026
27003
  onClick: () => eventBus.emit(`UI:${action.event}`, {
27027
27004
  id: itemData.id,
@@ -27038,12 +27015,16 @@ function DataList({
27038
27015
  const id = itemData.id || String(index);
27039
27016
  const titleValue = getNestedValue(itemData, titleField?.name ?? "");
27040
27017
  return wrapDnd(
27041
- /* @__PURE__ */ jsxRuntime.jsxs(Box, { "data-entity-row": true, "data-entity-id": id, onClick: itemClickEvent ? handleRowClick(itemData) : void 0, className: cn(itemClickEvent && "cursor-pointer"), children: [
27018
+ /* @__PURE__ */ jsxRuntime.jsxs(Box, { "data-entity-row": true, "data-entity-id": id, onClick: rowClickEvent ? handleRowClick(itemData) : void 0, className: cn(rowClickEvent && "cursor-pointer"), children: [
27042
27019
  /* @__PURE__ */ jsxRuntime.jsxs(
27043
27020
  Box,
27044
27021
  {
27045
27022
  className: cn(
27046
- "group flex items-center gap-4 transition-all duration-fast",
27023
+ // items-start, not items-center: a multi-line row (title + meta +
27024
+ // progress) centred its action cluster in the vertical middle, so
27025
+ // the buttons floated between the meta fields instead of anchoring
27026
+ // to the title they act on (U-DATALIST-ACTIONS-FLOAT-MID-ROW).
27027
+ "group flex items-start gap-4 transition-all duration-fast",
27047
27028
  isCompact ? "px-4 py-2" : "px-6 py-4",
27048
27029
  "hover:bg-muted/80",
27049
27030
  !isCard && !isCompact && "rounded-lg border border-transparent hover:border-border"
@@ -27074,11 +27055,19 @@ function DataList({
27074
27055
  if (value === void 0 || value === null || value === "") return null;
27075
27056
  return /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "xs", className: "items-center", children: [
27076
27057
  field.icon && renderIconInput2(field.icon, { size: "xs", className: "text-muted-foreground" }),
27077
- /* @__PURE__ */ jsxRuntime.jsxs(Typography, { variant: "caption", color: "secondary", children: [
27078
- field.label ?? fieldLabel3(field.name),
27079
- ":"
27080
- ] }),
27081
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", children: formatValue2(value, field.format, { yes: t("common.yes"), no: t("common.no") }) })
27058
+ /* @__PURE__ */ jsxRuntime.jsxs(
27059
+ Typography,
27060
+ {
27061
+ variant: "caption",
27062
+ color: "secondary",
27063
+ className: cn(field.format !== "boolean" && "sr-only"),
27064
+ children: [
27065
+ field.label ?? fieldLabel3(field.name),
27066
+ ":"
27067
+ ]
27068
+ }
27069
+ ),
27070
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", color: "secondary", children: formatValue2(value, field.format, { yes: t("common.yes"), no: t("common.no") }) })
27082
27071
  ] }, field.name);
27083
27072
  }) }),
27084
27073
  progressFields.map((field) => {
@@ -27097,7 +27086,7 @@ function DataList({
27097
27086
  ]
27098
27087
  }
27099
27088
  ),
27100
- isCard && !isLast && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "mx-6 border-b border-border/40" })
27089
+ (isCard || isCompact) && !isLast && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: cn("border-b border-border/40", isCompact ? "mx-4" : "mx-6") })
27101
27090
  ] }, id)
27102
27091
  );
27103
27092
  };
@@ -27107,7 +27096,13 @@ function DataList({
27107
27096
  {
27108
27097
  className: cn(
27109
27098
  isCard && "bg-card rounded-xl border border-border shadow-elevation-dialog overflow-hidden",
27110
- !isCard && gapClass,
27099
+ // `gap-*` is inert on a block container, and Box only emits a display
27100
+ // class when its `display` prop is set — so every non-card list had
27101
+ // been asking for a gap that CSS silently dropped. flex-col makes it
27102
+ // real. `compact` keeps gap-0 on purpose: it separates with the row
27103
+ // divider above instead, never both (Almadar_UI_Beauty.md 6).
27104
+ !isCard && "flex flex-col",
27105
+ !isCard && !isCompact && gapClass,
27111
27106
  listLookStyles[look],
27112
27107
  className
27113
27108
  ),
@@ -32296,7 +32291,7 @@ var init_MapView = __esm({
32296
32291
  shadowSize: [41, 41]
32297
32292
  });
32298
32293
  L.Marker.prototype.options.icon = defaultIcon;
32299
- const { useEffect: useEffect67, useRef: useRef65, useCallback: useCallback97, useState: useState95 } = React87__namespace.default;
32294
+ const { useEffect: useEffect67, useRef: useRef65, useCallback: useCallback97, useState: useState97 } = React87__namespace.default;
32300
32295
  const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
32301
32296
  const { useEventBus: useEventBus2 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
32302
32297
  function MapUpdater({ centerLat, centerLng, zoom }) {
@@ -32341,7 +32336,7 @@ var init_MapView = __esm({
32341
32336
  showAttribution = true
32342
32337
  }) {
32343
32338
  const eventBus = useEventBus2();
32344
- const [clickedPosition, setClickedPosition] = useState95(null);
32339
+ const [clickedPosition, setClickedPosition] = useState97(null);
32345
32340
  const handleMapClick = useCallback97((lat, lng) => {
32346
32341
  if (showClickedPin) {
32347
32342
  setClickedPosition({ lat, lng });
@@ -36703,741 +36698,775 @@ var init_PositionedCanvas = __esm({
36703
36698
  PositionedCanvas.displayName = "PositionedCanvas";
36704
36699
  }
36705
36700
  });
36706
- function nextBlockId(prefix = "blk") {
36707
- _idSeq += 1;
36708
- const random = Math.random().toString(36).slice(2, 8);
36709
- return `${prefix}-${Date.now().toString(36)}-${_idSeq}-${random}`;
36710
- }
36711
- function normalizeBlocks(raw) {
36712
- if (!Array.isArray(raw) || raw.length === 0) return [createBlock("paragraph")];
36713
- return raw.map((row) => {
36714
- const entity = row;
36715
- const rawType = entity.type;
36716
- const type = typeof rawType === "string" && BLOCK_TYPES.has(rawType) ? rawType : "paragraph";
36717
- const id = typeof entity.id === "string" && entity.id ? entity.id : nextBlockId(type);
36718
- return { ...entity, id, type };
36719
- });
36720
- }
36721
- function createBlock(type) {
36722
- switch (type) {
36723
- case "bullet-list":
36724
- case "numbered-list":
36725
- return {
36726
- id: nextBlockId(type),
36727
- type,
36728
- children: [
36729
- { id: nextBlockId("li"), type: "paragraph", content: "" }
36730
- ]
36731
- };
36732
- case "image":
36733
- return {
36734
- id: nextBlockId(type),
36735
- type,
36736
- content: "",
36737
- metadata: { url: "", caption: "" }
36738
- };
36739
- case "code":
36740
- return {
36741
- id: nextBlockId(type),
36742
- type,
36743
- content: "",
36744
- metadata: { language: "plaintext" }
36745
- };
36746
- case "divider":
36747
- return { id: nextBlockId(type), type };
36748
- default:
36749
- return { id: nextBlockId(type), type, content: "" };
36701
+ function safeUrl(raw, kinds) {
36702
+ const url = raw.trim();
36703
+ if (url.startsWith("#") || url.startsWith("/")) return url;
36704
+ const lower = url.toLowerCase();
36705
+ for (const kind of kinds) {
36706
+ if (lower.startsWith(kind)) return url;
36750
36707
  }
36708
+ return null;
36751
36709
  }
36752
- function replaceBlock(blocks, id, updater) {
36753
- return blocks.map((block) => block.id === id ? updater(block) : block);
36754
- }
36755
- function removeBlock(blocks, id) {
36756
- return blocks.filter((block) => block.id !== id);
36757
- }
36758
- function duplicateBlock(block) {
36759
- return {
36760
- ...block,
36761
- id: nextBlockId(block.type),
36762
- children: block.children?.map((child) => ({
36763
- ...child,
36764
- id: nextBlockId("li")
36765
- })),
36766
- metadata: block.metadata ? { ...block.metadata } : void 0
36767
- };
36768
- }
36769
- function insertAfter(blocks, targetId, inserted) {
36770
- const idx = blocks.findIndex((b) => b.id === targetId);
36771
- if (idx === -1) return [...blocks, inserted];
36772
- const next = blocks.slice();
36773
- next.splice(idx + 1, 0, inserted);
36774
- return next;
36775
- }
36776
- function changeBlockType(block, type) {
36777
- if (block.type === type) return block;
36778
- if (type === "bullet-list" || type === "numbered-list") {
36779
- if (block.children && block.children.length > 0) {
36780
- return { ...block, type };
36710
+ function sanitizeNode(el) {
36711
+ const children = Array.from(el.children);
36712
+ for (const child of children) {
36713
+ const tag = child.tagName.toLowerCase();
36714
+ if (DROP_TAGS.has(tag)) {
36715
+ child.remove();
36716
+ continue;
36781
36717
  }
36782
- const seed2 = block.content ?? "";
36783
- return {
36784
- id: block.id,
36785
- type,
36786
- children: [
36787
- { id: nextBlockId("li"), type: "paragraph", content: seed2 }
36788
- ]
36789
- };
36790
- }
36791
- if (type === "divider") {
36792
- return { id: block.id, type };
36718
+ const allowed = ALLOWED_ATTRS[tag];
36719
+ if (!allowed) {
36720
+ sanitizeNode(child);
36721
+ child.replaceWith(...Array.from(child.childNodes));
36722
+ continue;
36723
+ }
36724
+ for (const attr of Array.from(child.attributes)) {
36725
+ const name = attr.name.toLowerCase();
36726
+ if (!allowed.includes(name)) {
36727
+ child.removeAttribute(attr.name);
36728
+ continue;
36729
+ }
36730
+ if (name === "href") {
36731
+ const url = safeUrl(attr.value, ["http://", "https://", "mailto:"]);
36732
+ if (url === null) child.removeAttribute(attr.name);
36733
+ else child.setAttribute("href", url);
36734
+ }
36735
+ if (name === "src") {
36736
+ const url = safeUrl(attr.value, ["http://", "https://", "data:image/"]);
36737
+ if (url === null) child.remove();
36738
+ else child.setAttribute("src", url);
36739
+ }
36740
+ }
36741
+ sanitizeNode(child);
36793
36742
  }
36794
- if (type === "image") {
36795
- return {
36796
- id: block.id,
36797
- type,
36798
- content: "",
36799
- metadata: { url: "", caption: block.content ?? "" }
36800
- };
36743
+ }
36744
+ function sanitizeRichHtml(html) {
36745
+ if (!html || typeof html !== "string") return "";
36746
+ if (typeof window === "undefined" || typeof DOMParser === "undefined") return "";
36747
+ const doc = new DOMParser().parseFromString(html, "text/html");
36748
+ sanitizeNode(doc.body);
36749
+ if (!doc.body.querySelector("img, hr") && (doc.body.textContent ?? "").trim().length === 0) {
36750
+ return "";
36801
36751
  }
36802
- if (type === "code") {
36803
- return {
36804
- id: block.id,
36805
- type,
36806
- content: block.content ?? "",
36807
- metadata: { language: "plaintext" }
36808
- };
36752
+ return doc.body.innerHTML.replace(/​/g, "");
36753
+ }
36754
+ function htmlIsEmpty(el) {
36755
+ if (el.querySelector("img, hr")) return false;
36756
+ return (el.textContent ?? "").trim().length === 0;
36757
+ }
36758
+ function closestBlock(node, root) {
36759
+ let cur = node;
36760
+ while (cur && cur !== root) {
36761
+ if (cur instanceof Element && BLOCK_TAGS.has(cur.tagName)) return cur;
36762
+ cur = cur.parentNode;
36809
36763
  }
36810
- const seed = block.children?.[0]?.content ?? block.content ?? "";
36811
- return { id: block.id, type, content: seed };
36764
+ return null;
36812
36765
  }
36813
- function BlockMenu({ block, readOnly, onDelete, onDuplicate, onChangeType }) {
36814
- const { t } = hooks.useTranslate();
36815
- const [open, setOpen] = React87.useState(false);
36816
- const ref = React87.useRef(null);
36817
- React87.useEffect(() => {
36818
- if (!open) return;
36819
- function onDocClick(e) {
36820
- if (ref.current && !ref.current.contains(e.target)) {
36821
- setOpen(false);
36766
+ function RichTextStyles() {
36767
+ return /* @__PURE__ */ jsxRuntime.jsx("style", { children: RICH_TEXT_CSS });
36768
+ }
36769
+ function readToolbarState(root) {
36770
+ const sel = window.getSelection();
36771
+ if (!sel || sel.rangeCount === 0) return IDLE_TOOLBAR;
36772
+ const anchor = sel.anchorNode;
36773
+ if (!anchor || !root.contains(anchor)) return IDLE_TOOLBAR;
36774
+ let block = null;
36775
+ let link = false;
36776
+ let node = anchor;
36777
+ while (node && node !== root) {
36778
+ if (node instanceof Element) {
36779
+ const tag = node.tagName.toLowerCase();
36780
+ if (!block && (tag === "h1" || tag === "h2" || tag === "h3" || tag === "blockquote" || tag === "pre")) {
36781
+ block = tag;
36822
36782
  }
36783
+ if (tag === "a") link = true;
36823
36784
  }
36824
- document.addEventListener("mousedown", onDocClick);
36825
- return () => document.removeEventListener("mousedown", onDocClick);
36826
- }, [open]);
36827
- if (readOnly) return null;
36828
- return /* @__PURE__ */ jsxRuntime.jsxs(Box, { ref, className: "relative", children: [
36829
- /* @__PURE__ */ jsxRuntime.jsx(
36830
- Button,
36831
- {
36832
- type: "button",
36833
- variant: "ghost",
36834
- "aria-label": t("richBlockEditor.blockActions"),
36835
- className: cn(
36836
- "inline-flex items-center justify-center",
36837
- "h-6 w-6 rounded-sm p-0 gap-0",
36838
- "text-muted-foreground hover:bg-muted hover:text-foreground",
36839
- "opacity-0 group-hover:opacity-100 focus-visible:opacity-100",
36840
- "transition-opacity"
36841
- ),
36842
- onClick: () => setOpen((v) => !v),
36843
- children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "more-horizontal", className: "w-3.5 h-3.5" })
36844
- }
36845
- ),
36846
- open && /* @__PURE__ */ jsxRuntime.jsxs(
36847
- Box,
36848
- {
36849
- role: "menu",
36850
- className: cn(
36851
- "absolute right-0 z-10 mt-1 w-44",
36852
- "rounded-container border border-border bg-popover shadow-elevation-popover",
36853
- "py-1 text-sm"
36854
- ),
36855
- children: [
36856
- /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "px-2 py-1 text-xs uppercase tracking-wide text-muted-foreground", children: t(BLOCK_TYPE_LABEL_KEY[block.type]) }),
36857
- /* @__PURE__ */ jsxRuntime.jsxs(
36858
- Button,
36859
- {
36860
- type: "button",
36861
- variant: "ghost",
36862
- role: "menuitem",
36863
- className: "flex w-full items-center gap-2 px-2 py-1.5 text-left justify-start rounded-none",
36864
- onClick: () => {
36865
- onDuplicate();
36866
- setOpen(false);
36867
- },
36868
- children: [
36869
- /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "plus", className: "w-3.5 h-3.5" }),
36870
- " ",
36871
- t("richBlockEditor.duplicate")
36872
- ]
36873
- }
36874
- ),
36875
- /* @__PURE__ */ jsxRuntime.jsxs(
36876
- Button,
36877
- {
36878
- type: "button",
36879
- variant: "ghost",
36880
- role: "menuitem",
36881
- className: "flex w-full items-center gap-2 px-2 py-1.5 text-left text-destructive hover:bg-muted justify-start rounded-none",
36882
- onClick: () => {
36883
- onDelete();
36884
- setOpen(false);
36885
- },
36886
- children: [
36887
- /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "trash", className: "w-3.5 h-3.5" }),
36888
- " ",
36889
- t("common.delete")
36890
- ]
36891
- }
36892
- ),
36893
- CHANGEABLE_TYPES.includes(block.type) && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
36894
- /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "my-1 border-t border-border" }),
36895
- /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "px-2 py-1 text-xs uppercase tracking-wide text-muted-foreground", children: t("richBlockEditor.turnInto") }),
36896
- CHANGEABLE_TYPES.filter((bt) => bt !== block.type).map((bt) => /* @__PURE__ */ jsxRuntime.jsx(
36897
- Button,
36898
- {
36899
- type: "button",
36900
- variant: "ghost",
36901
- role: "menuitem",
36902
- className: "flex w-full items-center gap-2 px-2 py-1.5 text-left justify-start rounded-none",
36903
- onClick: () => {
36904
- onChangeType(bt);
36905
- setOpen(false);
36906
- },
36907
- children: t(BLOCK_TYPE_LABEL_KEY[bt])
36908
- },
36909
- bt
36910
- ))
36911
- ] })
36912
- ]
36913
- }
36914
- )
36915
- ] });
36785
+ node = node.parentNode;
36786
+ }
36787
+ return {
36788
+ bold: document.queryCommandState("bold"),
36789
+ italic: document.queryCommandState("italic"),
36790
+ underline: document.queryCommandState("underline"),
36791
+ strikeThrough: document.queryCommandState("strikeThrough"),
36792
+ block,
36793
+ bullets: document.queryCommandState("insertUnorderedList"),
36794
+ numbers: document.queryCommandState("insertOrderedList"),
36795
+ link
36796
+ };
36916
36797
  }
36917
- function Editable({
36918
- tag,
36919
- value,
36920
- readOnly,
36921
- placeholder,
36922
- className,
36923
- ariaLabel,
36924
- onValueChange
36925
- }) {
36926
- const ref = React87.useRef(null);
36927
- React87.useEffect(() => {
36928
- const el = ref.current;
36929
- if (!el) return;
36930
- const isFocused = document.activeElement === el;
36931
- if (!isFocused && el.textContent !== value) {
36932
- el.textContent = value;
36933
- }
36934
- }, [value]);
36935
- const handleInput = React87.useCallback(
36936
- (e) => {
36937
- onValueChange(e.currentTarget.textContent ?? "");
36938
- },
36939
- [onValueChange]
36940
- );
36798
+ function ToolbarButton({ icon: IconCmp, label, active, onExec }) {
36941
36799
  return /* @__PURE__ */ jsxRuntime.jsx(
36942
- Box,
36800
+ Button,
36943
36801
  {
36944
- as: tag,
36945
- ref,
36946
- contentEditable: !readOnly,
36947
- suppressContentEditableWarning: true,
36948
- role: readOnly ? void 0 : "textbox",
36949
- "aria-label": ariaLabel,
36950
- "aria-multiline": "true",
36951
- "data-placeholder": placeholder,
36802
+ type: "button",
36803
+ variant: "ghost",
36804
+ size: "sm",
36805
+ "aria-label": label,
36806
+ "aria-pressed": active,
36807
+ title: label,
36952
36808
  className: cn(
36953
- "outline-none focus-visible:ring-1 focus-visible:ring-ring rounded-sm",
36954
- "empty:before:content-[attr(data-placeholder)] empty:before:text-muted-foreground/60",
36955
- className
36809
+ "h-8 w-8 p-0 gap-0 justify-center",
36810
+ active && "bg-muted text-foreground"
36956
36811
  ),
36957
- onInput: handleInput
36812
+ onMouseDown: (e) => {
36813
+ e.preventDefault();
36814
+ onExec();
36815
+ },
36816
+ children: /* @__PURE__ */ jsxRuntime.jsx(IconCmp, { size: 15 })
36958
36817
  }
36959
36818
  );
36960
36819
  }
36961
- function BlockRow({
36962
- block,
36963
- readOnly,
36964
- showAffordances,
36965
- placeholder,
36966
- onUpdate,
36967
- onDelete,
36968
- onDuplicate,
36969
- onChangeType
36970
- }) {
36971
- const { t } = hooks.useTranslate();
36972
- const setContent = React87.useCallback(
36973
- (next) => onUpdate((b) => ({ ...b, content: next })),
36974
- [onUpdate]
36975
- );
36976
- const setMetadata = React87.useCallback(
36977
- (key, value) => onUpdate((b) => ({
36978
- ...b,
36979
- metadata: { ...b.metadata ?? {}, [key]: value }
36980
- })),
36981
- [onUpdate]
36982
- );
36983
- const setChildContent = React87.useCallback(
36984
- (childId, next) => onUpdate((b) => ({
36985
- ...b,
36986
- children: (b.children ?? []).map(
36987
- (c) => c.id === childId ? { ...c, content: next } : c
36988
- )
36989
- })),
36990
- [onUpdate]
36991
- );
36992
- const addListItem = React87.useCallback(
36993
- () => onUpdate((b) => ({
36994
- ...b,
36995
- children: [
36996
- ...b.children ?? [],
36997
- { id: nextBlockId("li"), type: "paragraph", content: "" }
36998
- ]
36999
- })),
37000
- [onUpdate]
37001
- );
37002
- const removeListItem = React87.useCallback(
37003
- (childId) => onUpdate((b) => {
37004
- const remaining = (b.children ?? []).filter((c) => c.id !== childId);
37005
- return {
37006
- ...b,
37007
- children: remaining.length === 0 ? [{ id: nextBlockId("li"), type: "paragraph", content: "" }] : remaining
37008
- };
37009
- }),
37010
- [onUpdate]
37011
- );
37012
- const renderBody = () => {
37013
- switch (block.type) {
37014
- case "heading-1":
37015
- return /* @__PURE__ */ jsxRuntime.jsx(
37016
- Editable,
37017
- {
37018
- tag: "h1",
37019
- value: block.content ?? "",
37020
- readOnly,
37021
- placeholder: placeholder ?? t("richBlockEditor.placeholder.heading1"),
37022
- ariaLabel: t("richBlockEditor.aria.heading1Block"),
37023
- className: "text-3xl font-bold leading-tight",
37024
- onValueChange: setContent
37025
- }
37026
- );
37027
- case "heading-2":
37028
- return /* @__PURE__ */ jsxRuntime.jsx(
37029
- Editable,
37030
- {
37031
- tag: "h2",
37032
- value: block.content ?? "",
37033
- readOnly,
37034
- placeholder: placeholder ?? t("richBlockEditor.placeholder.heading2"),
37035
- ariaLabel: t("richBlockEditor.aria.heading2Block"),
37036
- className: "text-2xl font-semibold leading-tight",
37037
- onValueChange: setContent
37038
- }
37039
- );
37040
- case "heading-3":
37041
- return /* @__PURE__ */ jsxRuntime.jsx(
37042
- Editable,
37043
- {
37044
- tag: "h3",
37045
- value: block.content ?? "",
37046
- readOnly,
37047
- placeholder: placeholder ?? t("richBlockEditor.placeholder.heading3"),
37048
- ariaLabel: t("richBlockEditor.aria.heading3Block"),
37049
- className: "text-xl font-semibold leading-tight",
37050
- onValueChange: setContent
37051
- }
37052
- );
37053
- case "quote":
37054
- return /* @__PURE__ */ jsxRuntime.jsx(
37055
- Editable,
37056
- {
37057
- tag: "blockquote",
37058
- value: block.content ?? "",
37059
- readOnly,
37060
- placeholder: placeholder ?? t("richBlockEditor.placeholder.quote"),
37061
- ariaLabel: t("richBlockEditor.aria.quoteBlock"),
37062
- className: "border-l-4 border-primary/60 pl-4 italic text-muted-foreground",
37063
- onValueChange: setContent
37064
- }
37065
- );
37066
- case "code":
37067
- return /* @__PURE__ */ jsxRuntime.jsxs(Box, { className: "rounded-md border border-border bg-muted/40", children: [
37068
- /* @__PURE__ */ jsxRuntime.jsxs(Box, { className: "flex items-center justify-between border-b border-border px-3 py-1 text-xs text-muted-foreground", children: [
37069
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { as: "span", variant: "caption", className: "uppercase tracking-wide", children: t("richBlockEditor.blockType.code") }),
37070
- !readOnly && /* @__PURE__ */ jsxRuntime.jsx(
37071
- Input,
37072
- {
37073
- inputType: "text",
37074
- value: String(block.metadata?.language ?? "plaintext"),
37075
- "aria-label": t("richBlockEditor.aria.codeLanguage"),
37076
- className: cn(
37077
- "h-6 w-32 rounded-sm border border-border bg-background",
37078
- "px-2 text-xs outline-none focus:ring-1 focus:ring-ring"
37079
- ),
37080
- onChange: (e) => setMetadata("language", e.target.value)
37081
- }
37082
- ),
37083
- readOnly && /* @__PURE__ */ jsxRuntime.jsx(Typography, { as: "span", variant: "caption", className: "text-xs", children: String(block.metadata?.language ?? "plaintext") })
37084
- ] }),
37085
- /* @__PURE__ */ jsxRuntime.jsx(
37086
- Editable,
37087
- {
37088
- tag: "pre",
37089
- value: block.content ?? "",
37090
- readOnly,
37091
- placeholder: placeholder ?? t("richBlockEditor.placeholder.code"),
37092
- ariaLabel: t("richBlockEditor.aria.codeBlock"),
37093
- className: "block whitespace-pre-wrap p-3 font-mono text-sm leading-relaxed",
37094
- onValueChange: setContent
37095
- }
37096
- )
37097
- ] });
37098
- case "divider":
37099
- return /* @__PURE__ */ jsxRuntime.jsx(Divider, { className: "my-2" });
37100
- case "image": {
37101
- const url = String(block.metadata?.url ?? "");
37102
- const caption = String(block.metadata?.caption ?? "");
37103
- const imgProps = {
37104
- src: url,
37105
- alt: caption || t("richBlockEditor.embeddedImage"),
37106
- className: "max-h-96 w-full rounded-md border border-border object-contain"
37107
- };
37108
- return /* @__PURE__ */ jsxRuntime.jsxs(Box, { className: "space-y-2", children: [
37109
- url ? /* @__PURE__ */ jsxRuntime.jsx(Box, { as: "img", ...imgProps }) : /* @__PURE__ */ jsxRuntime.jsxs(
37110
- Box,
37111
- {
37112
- className: cn(
37113
- "flex h-32 items-center justify-center",
37114
- "rounded-md border border-dashed border-border",
37115
- "text-sm text-muted-foreground"
37116
- ),
37117
- children: [
37118
- /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "image", className: "mr-2 w-4 h-4" }),
37119
- " ",
37120
- t("richBlockEditor.noImageUrl")
37121
- ]
37122
- }
37123
- ),
37124
- !readOnly && /* @__PURE__ */ jsxRuntime.jsxs(Box, { className: "flex flex-col gap-2 sm:flex-row", children: [
37125
- /* @__PURE__ */ jsxRuntime.jsx(
37126
- Input,
37127
- {
37128
- inputType: "url",
37129
- value: url,
37130
- placeholder: "https://example.com/image.png",
37131
- "aria-label": t("richBlockEditor.aria.imageUrl"),
37132
- className: cn(
37133
- "h-8 flex-1 rounded-sm border border-border bg-background",
37134
- "px-2 text-sm outline-none focus:ring-1 focus:ring-ring"
37135
- ),
37136
- onChange: (e) => setMetadata("url", e.target.value)
37137
- }
37138
- ),
37139
- /* @__PURE__ */ jsxRuntime.jsx(
37140
- Input,
37141
- {
37142
- inputType: "text",
37143
- value: caption,
37144
- placeholder: t("richBlockEditor.placeholder.caption"),
37145
- "aria-label": t("richBlockEditor.aria.imageCaption"),
37146
- className: cn(
37147
- "h-8 flex-1 rounded-sm border border-border bg-background",
37148
- "px-2 text-sm outline-none focus:ring-1 focus:ring-ring"
37149
- ),
37150
- onChange: (e) => setMetadata("caption", e.target.value)
37151
- }
37152
- )
37153
- ] }),
37154
- readOnly && caption && /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", className: "text-center text-muted-foreground", children: caption })
36820
+ var CHANGE_DEBOUNCE_MS, ALLOWED_ATTRS, DROP_TAGS, MD_BLOCK_PREFIXES, BLOCK_TAGS, MD_INLINE_RULES, RICH_TEXT_CSS, IDLE_TOOLBAR, RichTextEditor;
36821
+ var init_RichTextEditor = __esm({
36822
+ "components/core/molecules/RichTextEditor.tsx"() {
36823
+ "use client";
36824
+ init_cn();
36825
+ init_Box();
36826
+ init_Button();
36827
+ init_useEventBus();
36828
+ CHANGE_DEBOUNCE_MS = 400;
36829
+ ALLOWED_ATTRS = {
36830
+ p: [],
36831
+ h1: [],
36832
+ h2: [],
36833
+ h3: [],
36834
+ h4: [],
36835
+ ul: [],
36836
+ ol: [],
36837
+ li: [],
36838
+ blockquote: [],
36839
+ pre: [],
36840
+ code: [],
36841
+ b: [],
36842
+ strong: [],
36843
+ i: [],
36844
+ em: [],
36845
+ u: [],
36846
+ s: [],
36847
+ strike: [],
36848
+ br: [],
36849
+ hr: [],
36850
+ div: [],
36851
+ span: [],
36852
+ a: ["href"],
36853
+ img: ["src", "alt"]
36854
+ };
36855
+ DROP_TAGS = /* @__PURE__ */ new Set(["script", "style", "iframe", "object", "embed", "link", "meta", "form", "input", "button", "textarea", "select"]);
36856
+ MD_BLOCK_PREFIXES = {
36857
+ "#": { kind: "block", tag: "h1" },
36858
+ "##": { kind: "block", tag: "h2" },
36859
+ "###": { kind: "block", tag: "h3" },
36860
+ ">": { kind: "block", tag: "blockquote" },
36861
+ "```": { kind: "block", tag: "pre" },
36862
+ "-": { kind: "list", command: "insertUnorderedList" },
36863
+ "*": { kind: "list", command: "insertUnorderedList" },
36864
+ "1.": { kind: "list", command: "insertOrderedList" }
36865
+ };
36866
+ BLOCK_TAGS = /* @__PURE__ */ new Set(["P", "DIV", "LI", "H1", "H2", "H3", "H4", "BLOCKQUOTE", "PRE"]);
36867
+ MD_INLINE_RULES = [
36868
+ { pattern: /\*\*([^*\n]+)\*\*$/, wrap: "strong" },
36869
+ { pattern: /(^|[^*])\*([^*\n]+)\*$/, wrap: "em" },
36870
+ { pattern: /`([^`\n]+)`$/, wrap: "code" }
36871
+ ];
36872
+ RICH_TEXT_CSS = `
36873
+ .almadar-rich-text { line-height: 1.7; color: var(--color-foreground); }
36874
+ .almadar-rich-text h1 { font-size: 1.875rem; font-weight: 700; line-height: 1.25; margin: 1.25em 0 0.4em; }
36875
+ .almadar-rich-text h2 { font-size: 1.5rem; font-weight: 650; line-height: 1.3; margin: 1.1em 0 0.4em; }
36876
+ .almadar-rich-text h3 { font-size: 1.25rem; font-weight: 600; line-height: 1.35; margin: 1em 0 0.35em; }
36877
+ .almadar-rich-text h1:first-child, .almadar-rich-text h2:first-child, .almadar-rich-text h3:first-child { margin-top: 0; }
36878
+ .almadar-rich-text p { margin: 0.5em 0; }
36879
+ .almadar-rich-text ul { list-style: disc; padding-inline-start: 1.5rem; margin: 0.5em 0; }
36880
+ .almadar-rich-text ol { list-style: decimal; padding-inline-start: 1.5rem; margin: 0.5em 0; }
36881
+ .almadar-rich-text li { margin: 0.25em 0; }
36882
+ .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; }
36883
+ .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; }
36884
+ .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; }
36885
+ .almadar-rich-text pre code { background: transparent; padding: 0; }
36886
+ .almadar-rich-text a { color: var(--color-primary); text-decoration: underline; }
36887
+ .almadar-rich-text hr { border: none; border-top: 1px solid var(--color-border); margin: 1.25em 0; }
36888
+ .almadar-rich-text img { max-width: 100%; border-radius: 0.375rem; }
36889
+ `;
36890
+ IDLE_TOOLBAR = {
36891
+ bold: false,
36892
+ italic: false,
36893
+ underline: false,
36894
+ strikeThrough: false,
36895
+ block: null,
36896
+ bullets: false,
36897
+ numbers: false,
36898
+ link: false
36899
+ };
36900
+ RichTextEditor = ({
36901
+ value,
36902
+ onChange,
36903
+ changeEvent,
36904
+ readOnly = false,
36905
+ placeholder,
36906
+ showToolbar = true,
36907
+ className
36908
+ }) => {
36909
+ const { t } = hooks.useTranslate();
36910
+ const ref = React87.useRef(null);
36911
+ const [toolbar, setToolbar] = React87.useState(IDLE_TOOLBAR);
36912
+ const [empty, setEmpty] = React87.useState(() => !value || !sanitizeRichHtml(value).trim());
36913
+ const eventBus = useEventBus();
36914
+ const onChangeRef = React87.useRef(onChange);
36915
+ const changeEventRef = React87.useRef(changeEvent);
36916
+ React87.useEffect(() => {
36917
+ onChangeRef.current = onChange;
36918
+ changeEventRef.current = changeEvent;
36919
+ }, [onChange, changeEvent]);
36920
+ const emitTimerRef = React87.useRef(null);
36921
+ const emitNow = React87.useCallback(() => {
36922
+ const el = ref.current;
36923
+ if (!el) return;
36924
+ const html = sanitizeRichHtml(el.innerHTML);
36925
+ onChangeRef.current?.(html);
36926
+ const evt = changeEventRef.current;
36927
+ if (evt) eventBus.emit(`UI:${evt}`, { value: html });
36928
+ }, [eventBus]);
36929
+ const scheduleEmit = React87.useCallback(() => {
36930
+ if (emitTimerRef.current) clearTimeout(emitTimerRef.current);
36931
+ emitTimerRef.current = setTimeout(() => {
36932
+ emitTimerRef.current = null;
36933
+ emitNow();
36934
+ }, CHANGE_DEBOUNCE_MS);
36935
+ }, [emitNow]);
36936
+ const flushEmit = React87.useCallback(() => {
36937
+ if (!emitTimerRef.current) return;
36938
+ clearTimeout(emitTimerRef.current);
36939
+ emitTimerRef.current = null;
36940
+ emitNow();
36941
+ }, [emitNow]);
36942
+ React87.useEffect(() => () => {
36943
+ if (emitTimerRef.current) {
36944
+ clearTimeout(emitTimerRef.current);
36945
+ emitTimerRef.current = null;
36946
+ emitNow();
36947
+ }
36948
+ }, [emitNow]);
36949
+ React87.useEffect(() => {
36950
+ if (readOnly) return;
36951
+ const el = ref.current;
36952
+ if (!el) return;
36953
+ if (document.activeElement === el) return;
36954
+ const next = sanitizeRichHtml(value ?? "") || "<p><br></p>";
36955
+ if (el.innerHTML !== next) {
36956
+ el.innerHTML = next;
36957
+ setEmpty(htmlIsEmpty(el));
36958
+ }
36959
+ }, [readOnly, value]);
36960
+ React87.useEffect(() => {
36961
+ if (readOnly) return;
36962
+ document.execCommand("defaultParagraphSeparator", false, "p");
36963
+ }, [readOnly]);
36964
+ React87.useEffect(() => {
36965
+ if (readOnly || !showToolbar) return;
36966
+ const onSelectionChange = () => {
36967
+ const el = ref.current;
36968
+ if (!el) return;
36969
+ setToolbar(readToolbarState(el));
36970
+ };
36971
+ document.addEventListener("selectionchange", onSelectionChange);
36972
+ return () => document.removeEventListener("selectionchange", onSelectionChange);
36973
+ }, [readOnly, showToolbar]);
36974
+ const tryInlineMarkdown = React87.useCallback(() => {
36975
+ const root = ref.current;
36976
+ if (!root) return false;
36977
+ const sel = window.getSelection();
36978
+ if (!sel || !sel.isCollapsed || sel.rangeCount === 0) return false;
36979
+ const node = sel.anchorNode;
36980
+ if (!node || node.nodeType !== Node.TEXT_NODE || !root.contains(node)) return false;
36981
+ if (node.parentElement?.closest("code, pre")) return false;
36982
+ const text = (node.textContent ?? "").slice(0, sel.anchorOffset);
36983
+ for (const rule of MD_INLINE_RULES) {
36984
+ const m = rule.pattern.exec(text);
36985
+ if (!m) continue;
36986
+ const inner = rule.wrap === "em" ? m[2] : m[1];
36987
+ if (!inner || !inner.trim()) continue;
36988
+ const lead = rule.wrap === "em" ? m[1]?.length ?? 0 : 0;
36989
+ const range = document.createRange();
36990
+ range.setStart(node, m.index + lead);
36991
+ range.setEnd(node, sel.anchorOffset);
36992
+ const el = document.createElement(rule.wrap);
36993
+ el.textContent = inner;
36994
+ range.deleteContents();
36995
+ range.insertNode(el);
36996
+ const after = document.createTextNode("\u200B");
36997
+ el.parentNode?.insertBefore(after, el.nextSibling);
36998
+ const caret = document.createRange();
36999
+ caret.setStart(after, 1);
37000
+ caret.collapse(true);
37001
+ sel.removeAllRanges();
37002
+ sel.addRange(caret);
37003
+ return true;
37004
+ }
37005
+ return false;
37006
+ }, []);
37007
+ const tryBlockMarkdown = React87.useCallback(() => {
37008
+ const root = ref.current;
37009
+ if (!root) return false;
37010
+ const sel = window.getSelection();
37011
+ if (!sel || !sel.isCollapsed || sel.rangeCount === 0) return false;
37012
+ const node = sel.anchorNode;
37013
+ if (!node || node.nodeType !== Node.TEXT_NODE || !root.contains(node)) return false;
37014
+ const block = closestBlock(node, root);
37015
+ if (block ? block.firstChild !== node : root.firstChild !== node) return false;
37016
+ const prefix = (node.textContent ?? "").slice(0, sel.anchorOffset);
37017
+ const rule = MD_BLOCK_PREFIXES[prefix];
37018
+ if (!rule) return false;
37019
+ if (node.parentElement?.closest("pre, code")) return false;
37020
+ if (rule.kind === "list" && node.parentElement?.closest("li")) return false;
37021
+ node.deleteData(0, prefix.length);
37022
+ const caret = document.createRange();
37023
+ caret.setStart(node, 0);
37024
+ caret.collapse(true);
37025
+ sel.removeAllRanges();
37026
+ sel.addRange(caret);
37027
+ if (rule.kind === "list") {
37028
+ const container = block ?? node.parentElement;
37029
+ const list = document.createElement(rule.command === "insertOrderedList" ? "ol" : "ul");
37030
+ const li = document.createElement("li");
37031
+ if (block) {
37032
+ while (block.firstChild) li.appendChild(block.firstChild);
37033
+ list.appendChild(li);
37034
+ block.replaceWith(list);
37035
+ } else if (container) {
37036
+ li.appendChild(node);
37037
+ list.appendChild(li);
37038
+ container.appendChild(list);
37039
+ }
37040
+ if (!li.firstChild) li.appendChild(document.createElement("br"));
37041
+ const liCaret = document.createRange();
37042
+ liCaret.selectNodeContents(li);
37043
+ liCaret.collapse(true);
37044
+ sel.removeAllRanges();
37045
+ sel.addRange(liCaret);
37046
+ } else {
37047
+ document.execCommand("formatBlock", false, rule.tag);
37048
+ }
37049
+ return true;
37050
+ }, []);
37051
+ const afterEdit = React87.useCallback(() => {
37052
+ const el = ref.current;
37053
+ if (!el) return;
37054
+ tryInlineMarkdown();
37055
+ setEmpty(htmlIsEmpty(el));
37056
+ setToolbar(readToolbarState(el));
37057
+ scheduleEmit();
37058
+ }, [scheduleEmit, tryInlineMarkdown]);
37059
+ const handleKeyDown = React87.useCallback((e) => {
37060
+ if (e.key === " " && tryBlockMarkdown()) {
37061
+ e.preventDefault();
37062
+ afterEdit();
37063
+ }
37064
+ }, [afterEdit, tryBlockMarkdown]);
37065
+ const execInline = React87.useCallback((command) => {
37066
+ document.execCommand(command, false);
37067
+ afterEdit();
37068
+ }, [afterEdit]);
37069
+ const execBlock = React87.useCallback((tag) => {
37070
+ const next = toolbar.block === tag ? "p" : tag;
37071
+ document.execCommand("formatBlock", false, next);
37072
+ afterEdit();
37073
+ }, [afterEdit, toolbar.block]);
37074
+ const execList = React87.useCallback((command) => {
37075
+ document.execCommand(command, false);
37076
+ afterEdit();
37077
+ }, [afterEdit]);
37078
+ const execLink = React87.useCallback(() => {
37079
+ if (toolbar.link) {
37080
+ document.execCommand("unlink", false);
37081
+ afterEdit();
37082
+ return;
37083
+ }
37084
+ const url = window.prompt(t("richTextEditor.linkPrompt"));
37085
+ if (!url) return;
37086
+ const safe = safeUrl(url, ["http://", "https://", "mailto:"]) ?? `https://${url}`;
37087
+ document.execCommand("createLink", false, safe);
37088
+ afterEdit();
37089
+ }, [afterEdit, t, toolbar.link]);
37090
+ const execRule = React87.useCallback(() => {
37091
+ document.execCommand("insertHorizontalRule", false);
37092
+ afterEdit();
37093
+ }, [afterEdit]);
37094
+ if (readOnly) {
37095
+ return /* @__PURE__ */ jsxRuntime.jsxs(Box, { className: cn("almadar-rich-text max-w-none", className), children: [
37096
+ /* @__PURE__ */ jsxRuntime.jsx(RichTextStyles, {}),
37097
+ /* @__PURE__ */ jsxRuntime.jsx(Box, { dangerouslySetInnerHTML: { __html: sanitizeRichHtml(value ?? "") } })
37155
37098
  ] });
37156
37099
  }
37157
- case "bullet-list":
37158
- case "numbered-list": {
37159
- const items = block.children ?? [];
37160
- return /* @__PURE__ */ jsxRuntime.jsxs(
37100
+ return /* @__PURE__ */ jsxRuntime.jsxs(Box, { className: cn("flex flex-col gap-2", className), children: [
37101
+ /* @__PURE__ */ jsxRuntime.jsx(RichTextStyles, {}),
37102
+ showToolbar && /* @__PURE__ */ jsxRuntime.jsxs(
37161
37103
  Box,
37162
37104
  {
37163
- as: block.type === "bullet-list" ? "ul" : "ol",
37105
+ role: "toolbar",
37106
+ "aria-label": t("richTextEditor.editorToolbar"),
37164
37107
  className: cn(
37165
- "space-y-1 pl-6",
37166
- block.type === "bullet-list" ? "list-disc" : "list-decimal"
37108
+ "sticky top-0 z-10 flex flex-wrap items-center gap-0.5 self-start",
37109
+ "rounded-md border border-border bg-background/95 px-1 py-0.5 shadow-sm"
37167
37110
  ),
37168
37111
  children: [
37169
- items.map((child) => /* @__PURE__ */ jsxRuntime.jsxs(Box, { as: "li", className: "group/item flex items-start gap-2", children: [
37170
- /* @__PURE__ */ jsxRuntime.jsx(
37171
- Editable,
37172
- {
37173
- tag: "span",
37174
- value: child.content ?? "",
37175
- readOnly,
37176
- placeholder: t("richBlockEditor.placeholder.listItem"),
37177
- ariaLabel: t("richBlockEditor.aria.listItem"),
37178
- className: "inline-block min-w-[1ch] flex-1",
37179
- onValueChange: (next) => setChildContent(child.id, next)
37180
- }
37181
- ),
37182
- !readOnly && showAffordances && /* @__PURE__ */ jsxRuntime.jsx(
37183
- Button,
37184
- {
37185
- type: "button",
37186
- variant: "ghost",
37187
- "aria-label": t("richBlockEditor.aria.removeListItem"),
37188
- className: cn(
37189
- "h-5 w-5 shrink-0 rounded-sm text-muted-foreground p-0 gap-0",
37190
- "opacity-0 group-hover/item:opacity-100 hover:bg-muted hover:text-foreground"
37191
- ),
37192
- onClick: () => removeListItem(child.id),
37193
- children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "trash", className: "w-3 h-3" })
37194
- }
37195
- )
37196
- ] }, child.id)),
37197
- !readOnly && showAffordances && /* @__PURE__ */ jsxRuntime.jsx(Box, { as: "li", className: "list-none pl-0", children: /* @__PURE__ */ jsxRuntime.jsxs(
37198
- Button,
37199
- {
37200
- type: "button",
37201
- variant: "ghost",
37202
- className: cn(
37203
- "inline-flex items-center gap-1 text-xs text-muted-foreground",
37204
- "hover:text-foreground p-0 h-auto"
37205
- ),
37206
- onClick: addListItem,
37207
- children: [
37208
- /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "plus", className: "w-3 h-3" }),
37209
- " ",
37210
- t("richBlockEditor.addItem")
37211
- ]
37212
- }
37213
- ) })
37112
+ /* @__PURE__ */ jsxRuntime.jsx(ToolbarButton, { icon: LucideIcons2.Bold, label: t("richTextEditor.bold"), active: toolbar.bold, onExec: () => execInline("bold") }),
37113
+ /* @__PURE__ */ jsxRuntime.jsx(ToolbarButton, { icon: LucideIcons2.Italic, label: t("richTextEditor.italic"), active: toolbar.italic, onExec: () => execInline("italic") }),
37114
+ /* @__PURE__ */ jsxRuntime.jsx(ToolbarButton, { icon: LucideIcons2.Underline, label: t("richTextEditor.underline"), active: toolbar.underline, onExec: () => execInline("underline") }),
37115
+ /* @__PURE__ */ jsxRuntime.jsx(ToolbarButton, { icon: LucideIcons2.Strikethrough, label: t("richTextEditor.strikethrough"), active: toolbar.strikeThrough, onExec: () => execInline("strikeThrough") }),
37116
+ /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "mx-1 h-5 w-px bg-border" }),
37117
+ /* @__PURE__ */ jsxRuntime.jsx(ToolbarButton, { icon: LucideIcons2.Heading1, label: t("richTextEditor.heading1"), active: toolbar.block === "h1", onExec: () => execBlock("h1") }),
37118
+ /* @__PURE__ */ jsxRuntime.jsx(ToolbarButton, { icon: LucideIcons2.Heading2, label: t("richTextEditor.heading2"), active: toolbar.block === "h2", onExec: () => execBlock("h2") }),
37119
+ /* @__PURE__ */ jsxRuntime.jsx(ToolbarButton, { icon: LucideIcons2.Heading3, label: t("richTextEditor.heading3"), active: toolbar.block === "h3", onExec: () => execBlock("h3") }),
37120
+ /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "mx-1 h-5 w-px bg-border" }),
37121
+ /* @__PURE__ */ jsxRuntime.jsx(ToolbarButton, { icon: LucideIcons2.List, label: t("richTextEditor.bulletList"), active: toolbar.bullets, onExec: () => execList("insertUnorderedList") }),
37122
+ /* @__PURE__ */ jsxRuntime.jsx(ToolbarButton, { icon: LucideIcons2.ListOrdered, label: t("richTextEditor.numberedList"), active: toolbar.numbers, onExec: () => execList("insertOrderedList") }),
37123
+ /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "mx-1 h-5 w-px bg-border" }),
37124
+ /* @__PURE__ */ jsxRuntime.jsx(ToolbarButton, { icon: LucideIcons2.Quote, label: t("richTextEditor.quote"), active: toolbar.block === "blockquote", onExec: () => execBlock("blockquote") }),
37125
+ /* @__PURE__ */ jsxRuntime.jsx(ToolbarButton, { icon: LucideIcons2.Code, label: t("richTextEditor.code"), active: toolbar.block === "pre", onExec: () => execBlock("pre") }),
37126
+ /* @__PURE__ */ jsxRuntime.jsx(ToolbarButton, { icon: LucideIcons2.Link, label: toolbar.link ? t("richTextEditor.removeLink") : t("richTextEditor.link"), active: toolbar.link, onExec: execLink }),
37127
+ /* @__PURE__ */ jsxRuntime.jsx(ToolbarButton, { icon: LucideIcons2.Minus, label: t("richTextEditor.divider"), onExec: execRule })
37214
37128
  ]
37215
37129
  }
37216
- );
37217
- }
37218
- case "paragraph":
37219
- default:
37220
- return /* @__PURE__ */ jsxRuntime.jsx(
37221
- Editable,
37130
+ ),
37131
+ /* @__PURE__ */ jsxRuntime.jsx(
37132
+ Box,
37222
37133
  {
37223
- tag: "p",
37224
- value: block.content ?? "",
37225
- readOnly,
37226
- placeholder: placeholder ?? t("richBlockEditor.placeholder.paragraph"),
37227
- ariaLabel: t("richBlockEditor.aria.paragraphBlock"),
37228
- className: "leading-7",
37229
- onValueChange: setContent
37134
+ ref,
37135
+ contentEditable: true,
37136
+ suppressContentEditableWarning: true,
37137
+ role: "textbox",
37138
+ "aria-multiline": "true",
37139
+ "aria-label": t("richTextEditor.editorSurface"),
37140
+ "data-placeholder": placeholder ?? t("richTextEditor.placeholder"),
37141
+ "data-empty": empty ? "true" : "false",
37142
+ className: cn(
37143
+ "almadar-rich-text max-w-none min-h-[8rem] outline-none",
37144
+ "data-[empty=true]:before:content-[attr(data-placeholder)]",
37145
+ "data-[empty=true]:before:text-muted-foreground/60",
37146
+ "data-[empty=true]:before:pointer-events-none",
37147
+ "data-[empty=true]:before:absolute",
37148
+ "relative cursor-text"
37149
+ ),
37150
+ onInput: afterEdit,
37151
+ onKeyDown: handleKeyDown,
37152
+ onBlur: flushEmit
37230
37153
  }
37231
- );
37232
- }
37154
+ )
37155
+ ] });
37156
+ };
37157
+ RichTextEditor.displayName = "RichTextEditor";
37158
+ }
37159
+ });
37160
+ function renderIconInput4(icon, props) {
37161
+ return typeof icon === "string" ? /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: icon, ...props }) : /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon, ...props });
37162
+ }
37163
+ function DocumentPanel({
37164
+ entity,
37165
+ title,
37166
+ subtitle,
37167
+ value,
37168
+ actions,
37169
+ maxInlineActions,
37170
+ editing = false,
37171
+ titleCommitEvent,
37172
+ contentChangeEvent,
37173
+ editEvent,
37174
+ doneEvent,
37175
+ editLabel,
37176
+ doneLabel,
37177
+ autosaveHint,
37178
+ placeholder,
37179
+ className
37180
+ }) {
37181
+ const eventBus = useEventBus();
37182
+ const { t } = hooks.useTranslate();
37183
+ const [titleDraft, setTitleDraft] = React87.useState(null);
37184
+ const recordId = entity?.id !== void 0 && entity?.id !== null ? String(entity.id) : "";
37185
+ const actionDefs = actions ?? [];
37186
+ const inlineCap = Math.min(maxInlineActions ?? 1, 1);
37187
+ const inlineActions = actionDefs.filter((a) => a.variant === "primary").slice(0, inlineCap);
37188
+ const menuActions = actionDefs.filter((a) => !inlineActions.includes(a));
37189
+ const fireAction = (action) => {
37190
+ if (!action.event) return;
37191
+ eventBus.emit(`UI:${action.event}`, { id: recordId, row: entity });
37233
37192
  };
37234
- return /* @__PURE__ */ jsxRuntime.jsxs(
37193
+ const commitTitle = () => {
37194
+ if (!titleCommitEvent) return;
37195
+ const next = (titleDraft ?? "").trim();
37196
+ setTitleDraft(null);
37197
+ if (!next || next === title) return;
37198
+ eventBus.emit(`UI:${titleCommitEvent}`, { title: next, id: recordId });
37199
+ };
37200
+ const untitled = t("documentPanel.untitled") || "Untitled";
37201
+ const titleNode = titleCommitEvent && titleDraft !== null ? /* @__PURE__ */ jsxRuntime.jsx(
37202
+ Input,
37203
+ {
37204
+ value: titleDraft,
37205
+ autoFocus: true,
37206
+ "aria-label": t("common.title"),
37207
+ className: "h-auto py-1 text-3xl font-bold tracking-tight",
37208
+ onFocus: (e) => e.currentTarget.select(),
37209
+ onChange: (e) => setTitleDraft(e.target.value),
37210
+ onBlur: commitTitle,
37211
+ onKeyDown: (e) => {
37212
+ if (e.key === "Enter") {
37213
+ e.preventDefault();
37214
+ commitTitle();
37215
+ } else if (e.key === "Escape") {
37216
+ e.preventDefault();
37217
+ setTitleDraft(null);
37218
+ }
37219
+ },
37220
+ "data-testid": "document-title-input"
37221
+ }
37222
+ ) : titleCommitEvent ? /* @__PURE__ */ jsxRuntime.jsx(
37235
37223
  Box,
37236
37224
  {
37237
- className: cn(
37238
- "group relative flex items-start gap-2 rounded-sm",
37239
- "px-2 py-1 hover:bg-muted/30"
37240
- ),
37241
- "data-block-id": block.id,
37242
- "data-block-type": block.type,
37243
- children: [
37244
- !readOnly && showAffordances && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "flex w-8 shrink-0 items-center pt-1", children: /* @__PURE__ */ jsxRuntime.jsx(
37245
- BlockMenu,
37225
+ role: "button",
37226
+ tabIndex: 0,
37227
+ className: "cursor-text rounded px-1 -mx-1 transition-colors hover:bg-muted/40",
37228
+ onClick: () => setTitleDraft(title ?? ""),
37229
+ onKeyDown: (e) => {
37230
+ if (e.key === "Enter" || e.key === " ") {
37231
+ e.preventDefault();
37232
+ setTitleDraft(title ?? "");
37233
+ }
37234
+ },
37235
+ "data-testid": "document-title-editable",
37236
+ children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h2", weight: "bold", className: cn(!title && "text-muted-foreground"), children: title || untitled })
37237
+ }
37238
+ ) : /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h2", weight: "bold", children: title || untitled });
37239
+ const emitWithId = (event) => () => {
37240
+ if (event) eventBus.emit(`UI:${event}`, { id: recordId });
37241
+ };
37242
+ return /* @__PURE__ */ jsxRuntime.jsx(Card, { variant: "elevated", className: cn("w-full", className), children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", className: "p-6 sm:p-8", children: [
37243
+ /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", className: "justify-between items-start", children: [
37244
+ /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "xs", className: "flex-1 min-w-0", children: [
37245
+ titleNode,
37246
+ subtitle ? /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", color: "secondary", children: subtitle }) : null
37247
+ ] }),
37248
+ /* @__PURE__ */ jsxRuntime.jsx(HStack, { gap: "xs", className: "flex-shrink-0 pt-1 items-center", children: editing ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
37249
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", color: "muted", className: "hidden sm:block", children: autosaveHint ?? (t("documentPanel.autosaveHint") || "") }),
37250
+ /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "primary", size: "sm", onClick: emitWithId(doneEvent), "data-testid": "document-done", children: doneLabel ?? (t("documentPanel.done") || "Done") })
37251
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
37252
+ editEvent && /* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "ghost", size: "sm", onClick: emitWithId(editEvent), "data-testid": "document-edit", children: [
37253
+ /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "edit", size: "xs", className: "mr-1" }),
37254
+ editLabel ?? (t("documentPanel.edit") || "Edit")
37255
+ ] }),
37256
+ inlineActions.map((action, idx) => /* @__PURE__ */ jsxRuntime.jsxs(
37257
+ Button,
37246
37258
  {
37247
- block,
37248
- readOnly,
37249
- onDelete,
37250
- onDuplicate,
37251
- onChangeType
37259
+ variant: "primary",
37260
+ size: "sm",
37261
+ onClick: () => fireAction(action),
37262
+ "data-testid": `action-${action.event}`,
37263
+ children: [
37264
+ action.icon && renderIconInput4(action.icon, { size: "xs", className: "mr-1" }),
37265
+ action.label
37266
+ ]
37267
+ },
37268
+ idx
37269
+ )),
37270
+ menuActions.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
37271
+ Menu,
37272
+ {
37273
+ position: "bottom-end",
37274
+ trigger: /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "ghost", size: "sm", "aria-label": t("common.actions"), "data-testid": "action-overflow", children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "more-horizontal", size: "xs" }) }),
37275
+ items: menuActions.map((action) => ({
37276
+ label: action.label,
37277
+ icon: action.icon,
37278
+ variant: action.variant === "danger" ? "danger" : "default",
37279
+ onClick: () => fireAction(action)
37280
+ }))
37252
37281
  }
37253
- ) }),
37254
- /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "min-w-0 flex-1", children: renderBody() })
37255
- ]
37256
- }
37257
- );
37282
+ )
37283
+ ] }) })
37284
+ ] }),
37285
+ editing ? /* @__PURE__ */ jsxRuntime.jsx(RichTextEditor, { value, changeEvent: contentChangeEvent, placeholder }) : /* @__PURE__ */ jsxRuntime.jsx(
37286
+ Box,
37287
+ {
37288
+ onClick: emitWithId(editEvent),
37289
+ className: cn(
37290
+ "rounded-md px-1 -mx-1 min-h-[16rem]",
37291
+ editEvent && "cursor-text transition-colors hover:bg-muted/30"
37292
+ ),
37293
+ "data-testid": "document-body",
37294
+ children: value && value !== "" ? /* @__PURE__ */ jsxRuntime.jsx(RichTextEditor, { value, readOnly: true }) : /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", color: "muted", children: placeholder ?? (t("documentPanel.placeholder") || "") })
37295
+ }
37296
+ )
37297
+ ] }) });
37258
37298
  }
37259
- var TOOLBAR_ENTRIES, BLOCK_TYPE_LABEL_KEY, CHANGEABLE_TYPES, _idSeq, BLOCK_TYPES, RichBlockEditor;
37260
- var init_RichBlockEditor = __esm({
37261
- "components/core/molecules/RichBlockEditor.tsx"() {
37299
+ var init_DocumentPanel = __esm({
37300
+ "components/core/molecules/DocumentPanel.tsx"() {
37262
37301
  "use client";
37263
37302
  init_cn();
37264
- init_Card();
37303
+ init_useEventBus();
37304
+ init_Box();
37305
+ init_Stack();
37265
37306
  init_Typography();
37266
37307
  init_Button();
37267
- init_Box();
37268
- init_Divider();
37269
- init_Input();
37308
+ init_Card();
37270
37309
  init_Icon();
37271
- init_useEventBus();
37272
- TOOLBAR_ENTRIES = [
37273
- { type: "paragraph", labelKey: "richBlockEditor.toolbar.text", icon: LucideIcons2.Type },
37274
- { type: "heading-1", labelKey: "richBlockEditor.toolbar.h1", icon: LucideIcons2.Heading1 },
37275
- { type: "heading-2", labelKey: "richBlockEditor.toolbar.h2", icon: LucideIcons2.Heading2 },
37276
- { type: "heading-3", labelKey: "richBlockEditor.toolbar.h3", icon: LucideIcons2.Heading3 },
37277
- { type: "bullet-list", labelKey: "richBlockEditor.toolbar.bulletList", icon: LucideIcons2.List },
37278
- { type: "numbered-list", labelKey: "richBlockEditor.toolbar.numbered", icon: LucideIcons2.ListOrdered },
37279
- { type: "quote", labelKey: "richBlockEditor.toolbar.quote", icon: LucideIcons2.Quote },
37280
- { type: "code", labelKey: "richBlockEditor.toolbar.code", icon: LucideIcons2.Code },
37281
- { type: "divider", labelKey: "richBlockEditor.toolbar.divider", icon: LucideIcons2.Minus },
37282
- { type: "image", labelKey: "richBlockEditor.toolbar.image", icon: LucideIcons2.Image }
37283
- ];
37284
- BLOCK_TYPE_LABEL_KEY = {
37285
- paragraph: "richBlockEditor.blockType.paragraph",
37286
- "heading-1": "richBlockEditor.blockType.heading1",
37287
- "heading-2": "richBlockEditor.blockType.heading2",
37288
- "heading-3": "richBlockEditor.blockType.heading3",
37289
- "bullet-list": "richBlockEditor.blockType.bulletList",
37290
- "numbered-list": "richBlockEditor.blockType.numberedList",
37291
- quote: "richBlockEditor.blockType.quote",
37292
- code: "richBlockEditor.blockType.code",
37293
- divider: "richBlockEditor.blockType.divider",
37294
- image: "richBlockEditor.blockType.image"
37295
- };
37296
- CHANGEABLE_TYPES = [
37297
- "paragraph",
37298
- "heading-1",
37299
- "heading-2",
37300
- "heading-3",
37301
- "bullet-list",
37302
- "numbered-list",
37303
- "quote",
37304
- "code"
37305
- ];
37306
- _idSeq = 0;
37307
- BLOCK_TYPES = /* @__PURE__ */ new Set([
37308
- "paragraph",
37309
- "heading-1",
37310
- "heading-2",
37311
- "heading-3",
37312
- "bullet-list",
37313
- "numbered-list",
37314
- "quote",
37315
- "code",
37316
- "divider",
37317
- "image"
37318
- ]);
37319
- RichBlockEditor = ({
37320
- initialBlocks,
37321
- onChange,
37322
- changeEvent,
37323
- readOnly = false,
37324
- placeholder,
37325
- enableBlocks = true,
37326
- showToolbar = true,
37327
- className
37328
- }) => {
37329
- const { t } = hooks.useTranslate();
37330
- const [blocks, setBlocks] = React87.useState(
37331
- () => normalizeBlocks(initialBlocks)
37332
- );
37333
- const onChangeRef = React87.useRef(onChange);
37334
- React87.useEffect(() => {
37335
- onChangeRef.current = onChange;
37336
- }, [onChange]);
37337
- const eventBus = useEventBus();
37338
- const changeEventRef = React87.useRef(changeEvent);
37339
- React87.useEffect(() => {
37340
- changeEventRef.current = changeEvent;
37341
- }, [changeEvent]);
37342
- const commit = React87.useCallback((next) => {
37343
- setBlocks(next);
37344
- onChangeRef.current?.(next);
37345
- const evt = changeEventRef.current;
37346
- if (evt) eventBus.emit(`UI:${evt}`, { blocks: next });
37347
- }, [eventBus]);
37348
- const handleAppend = React87.useCallback(
37349
- (type) => {
37350
- if (readOnly) return;
37351
- commit([...blocks, createBlock(type)]);
37352
- },
37353
- [blocks, commit, readOnly]
37354
- );
37355
- const handleUpdate = React87.useCallback(
37356
- (id, updater) => {
37357
- commit(replaceBlock(blocks, id, updater));
37358
- },
37359
- [blocks, commit]
37360
- );
37361
- const handleDelete = React87.useCallback(
37362
- (id) => {
37363
- const next = removeBlock(blocks, id);
37364
- commit(next.length > 0 ? next : [createBlock("paragraph")]);
37365
- },
37366
- [blocks, commit]
37367
- );
37368
- const handleDuplicate = React87.useCallback(
37369
- (id) => {
37370
- const target = blocks.find((b) => b.id === id);
37371
- if (!target) return;
37372
- commit(insertAfter(blocks, id, duplicateBlock(target)));
37373
- },
37374
- [blocks, commit]
37310
+ init_Input();
37311
+ init_Menu();
37312
+ init_RichTextEditor();
37313
+ }
37314
+ });
37315
+ function renderIconInput5(icon, props) {
37316
+ return typeof icon === "string" ? /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: icon, ...props }) : /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon, ...props });
37317
+ }
37318
+ function fieldName(field) {
37319
+ return field.name ?? field.key ?? "";
37320
+ }
37321
+ function relationLabel(value) {
37322
+ if (value === null || typeof value !== "object" || Array.isArray(value) || value instanceof Date) return null;
37323
+ for (const key of ["name", "title", "label"]) {
37324
+ const candidate = value[key];
37325
+ if (typeof candidate === "string" && candidate !== "") return candidate;
37326
+ }
37327
+ const id = value.id;
37328
+ return id !== void 0 && id !== null ? String(id) : null;
37329
+ }
37330
+ function fieldKind(field, value) {
37331
+ if (field.kind) return field.kind;
37332
+ if (field.options && field.options.length > 0) return "select";
37333
+ if (typeof value === "boolean" || field.format === "boolean") return "boolean";
37334
+ if (Array.isArray(value) || relationLabel(value ?? null) !== null) return "readonly";
37335
+ return "text";
37336
+ }
37337
+ function DocumentDetails({
37338
+ entity,
37339
+ fields,
37340
+ metaCommitEvent,
37341
+ title,
37342
+ className
37343
+ }) {
37344
+ const eventBus = useEventBus();
37345
+ const { t } = hooks.useTranslate();
37346
+ const [fieldDraft, setFieldDraft] = React87.useState(null);
37347
+ const recordId = entity?.id !== void 0 && entity?.id !== null ? String(entity.id) : "";
37348
+ const fieldDefs = (fields ?? []).filter((f3) => fieldName(f3) !== "");
37349
+ if (fieldDefs.length === 0) return null;
37350
+ const commitField = (name, next, current) => {
37351
+ setFieldDraft(null);
37352
+ if (!metaCommitEvent || next === current) return;
37353
+ eventBus.emit(`UI:${metaCommitEvent}`, { id: recordId, patch: { id: recordId, [name]: next } });
37354
+ };
37355
+ const renderValue = (field) => {
37356
+ const name = fieldName(field);
37357
+ const raw = getNestedValue(entity ?? {}, name);
37358
+ const kind = fieldKind(field, raw);
37359
+ const label = field.label ?? field.header ?? humanizeFieldName(name);
37360
+ if (kind === "boolean") {
37361
+ return /* @__PURE__ */ jsxRuntime.jsx(
37362
+ Switch,
37363
+ {
37364
+ checked: Boolean(raw),
37365
+ disabled: !metaCommitEvent,
37366
+ "aria-label": label,
37367
+ onChange: (checked) => commitField(name, checked, Boolean(raw))
37368
+ }
37375
37369
  );
37376
- const handleChangeType = React87.useCallback(
37377
- (id, type) => {
37378
- commit(
37379
- replaceBlock(blocks, id, (b) => changeBlockType(b, type))
37380
- );
37381
- },
37382
- [blocks, commit]
37370
+ }
37371
+ if (kind === "select") {
37372
+ return /* @__PURE__ */ jsxRuntime.jsx(
37373
+ Select,
37374
+ {
37375
+ value: raw !== void 0 && raw !== null ? String(raw) : "",
37376
+ disabled: !metaCommitEvent,
37377
+ "aria-label": label,
37378
+ className: "h-8 w-full",
37379
+ options: (field.options ?? []).map((opt) => ({ value: opt, label: humanizeEnumValue(opt) })),
37380
+ onChange: (e) => commitField(name, e.target.value, raw)
37381
+ }
37383
37382
  );
37384
- return /* @__PURE__ */ jsxRuntime.jsxs(
37385
- Card,
37383
+ }
37384
+ if (kind === "readonly" || !metaCommitEvent) {
37385
+ if (Array.isArray(raw)) {
37386
+ if (raw.length === 0) {
37387
+ return /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", color: "secondary", children: "\u2014" });
37388
+ }
37389
+ return /* @__PURE__ */ jsxRuntime.jsx(HStack, { gap: "xs", className: "flex-wrap", children: raw.map((item, i) => /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: "default", children: relationLabel(item) ?? humanizeEnumValue(String(item)) }, i)) });
37390
+ }
37391
+ const shown2 = raw === void 0 || raw === null || raw === "" ? "\u2014" : relationLabel(raw) ?? formatValue(raw, field.format);
37392
+ return /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", className: "break-words", children: shown2 });
37393
+ }
37394
+ if (fieldDraft?.name === name) {
37395
+ return /* @__PURE__ */ jsxRuntime.jsx(
37396
+ Input,
37386
37397
  {
37387
- variant: "bordered",
37388
- padding: "none",
37389
- className: cn("flex flex-col text-card-foreground", className),
37390
- children: [
37391
- enableBlocks && showToolbar && !readOnly && /* @__PURE__ */ jsxRuntime.jsx(
37392
- Box,
37393
- {
37394
- role: "toolbar",
37395
- "aria-label": t("richBlockEditor.editorToolbar"),
37396
- className: cn(
37397
- "flex flex-wrap items-center gap-1",
37398
- "border-b border-border bg-muted/30 px-2 py-2"
37399
- ),
37400
- children: TOOLBAR_ENTRIES.map((entry) => {
37401
- const Icon2 = entry.icon;
37402
- const entryLabel = t(entry.labelKey);
37403
- return /* @__PURE__ */ jsxRuntime.jsxs(
37404
- Button,
37405
- {
37406
- type: "button",
37407
- variant: "ghost",
37408
- size: "sm",
37409
- "aria-label": t("richBlockEditor.insertEntry", { label: entryLabel }),
37410
- title: entryLabel,
37411
- onClick: () => handleAppend(entry.type),
37412
- children: [
37413
- /* @__PURE__ */ jsxRuntime.jsx(Icon2, { size: 14 }),
37414
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { as: "span", variant: "caption", className: "ml-1 hidden text-xs sm:inline", children: entryLabel })
37415
- ]
37416
- },
37417
- entry.type
37418
- );
37419
- })
37420
- }
37421
- ),
37422
- /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "flex flex-col gap-1 px-3 py-3", children: blocks.map((block) => /* @__PURE__ */ jsxRuntime.jsx(
37423
- BlockRow,
37424
- {
37425
- block,
37426
- readOnly,
37427
- showAffordances: enableBlocks,
37428
- placeholder,
37429
- onUpdate: (updater) => handleUpdate(block.id, updater),
37430
- onDelete: () => handleDelete(block.id),
37431
- onDuplicate: () => handleDuplicate(block.id),
37432
- onChangeType: (type) => handleChangeType(block.id, type)
37433
- },
37434
- block.id
37435
- )) })
37436
- ]
37398
+ value: fieldDraft.value,
37399
+ autoFocus: true,
37400
+ "aria-label": label,
37401
+ className: "h-8 w-full",
37402
+ onChange: (e) => setFieldDraft({ name, value: e.target.value }),
37403
+ onBlur: () => commitField(name, fieldDraft.value.trim(), raw),
37404
+ onKeyDown: (e) => {
37405
+ if (e.key === "Enter") {
37406
+ e.preventDefault();
37407
+ commitField(name, fieldDraft.value.trim(), raw);
37408
+ } else if (e.key === "Escape") {
37409
+ e.preventDefault();
37410
+ setFieldDraft(null);
37411
+ }
37412
+ },
37413
+ "data-testid": `document-property-input-${name}`
37437
37414
  }
37438
37415
  );
37439
- };
37440
- RichBlockEditor.displayName = "RichBlockEditor";
37416
+ }
37417
+ const shown = raw === void 0 || raw === null || raw === "" ? "\u2014" : relationLabel(raw) ?? formatValue(raw, field.format);
37418
+ return /* @__PURE__ */ jsxRuntime.jsx(
37419
+ Box,
37420
+ {
37421
+ role: "button",
37422
+ tabIndex: 0,
37423
+ className: cn(
37424
+ "cursor-text rounded px-1 -mx-1 transition-colors hover:bg-muted/40",
37425
+ (raw === void 0 || raw === null || raw === "") && "text-muted-foreground"
37426
+ ),
37427
+ onClick: () => setFieldDraft({ name, value: raw !== void 0 && raw !== null ? String(raw) : "" }),
37428
+ onKeyDown: (e) => {
37429
+ if (e.key === "Enter" || e.key === " ") {
37430
+ e.preventDefault();
37431
+ setFieldDraft({ name, value: raw !== void 0 && raw !== null ? String(raw) : "" });
37432
+ }
37433
+ },
37434
+ "data-testid": `document-property-${name}`,
37435
+ children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", className: "break-words", children: shown })
37436
+ }
37437
+ );
37438
+ };
37439
+ return /* @__PURE__ */ jsxRuntime.jsx(Card, { variant: "bordered", className: cn("w-full", className), children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", className: "p-4", children: [
37440
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", color: "secondary", weight: "medium", className: "uppercase tracking-wide", children: title ?? (t("documentDetails.title") || "Details") }),
37441
+ /* @__PURE__ */ jsxRuntime.jsx(VStack, { gap: "sm", children: fieldDefs.map((field) => {
37442
+ const name = fieldName(field);
37443
+ const label = field.label ?? field.header ?? humanizeFieldName(name);
37444
+ return /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "xs", children: [
37445
+ /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "xs", className: "items-center", children: [
37446
+ field.icon && renderIconInput5(field.icon, { size: "xs", className: "text-muted-foreground" }),
37447
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", color: "muted", children: label })
37448
+ ] }),
37449
+ /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "min-w-0", children: renderValue(field) })
37450
+ ] }, name);
37451
+ }) })
37452
+ ] }) });
37453
+ }
37454
+ var init_DocumentDetails = __esm({
37455
+ "components/core/molecules/DocumentDetails.tsx"() {
37456
+ "use client";
37457
+ init_cn();
37458
+ init_format();
37459
+ init_getNestedValue();
37460
+ init_useEventBus();
37461
+ init_Box();
37462
+ init_Stack();
37463
+ init_Typography();
37464
+ init_Badge();
37465
+ init_Card();
37466
+ init_Icon();
37467
+ init_Input();
37468
+ init_Select();
37469
+ init_Switch();
37441
37470
  }
37442
37471
  });
37443
37472
  function collectInitiallyCollapsed(nodes, acc) {
@@ -42386,8 +42415,8 @@ var init_DataTable = __esm({
42386
42415
  DataTable.displayName = "DataTable";
42387
42416
  }
42388
42417
  });
42389
- function getBadgeVariant(fieldName, value) {
42390
- const name = fieldName.toLowerCase();
42418
+ function getBadgeVariant(fieldName2, value) {
42419
+ const name = fieldName2.toLowerCase();
42391
42420
  const val = String(value).toLowerCase();
42392
42421
  if (name.includes("status")) {
42393
42422
  if (val.includes("complete") || val.includes("done") || val.includes("active"))
@@ -42403,12 +42432,12 @@ function getBadgeVariant(fieldName, value) {
42403
42432
  }
42404
42433
  return "default";
42405
42434
  }
42406
- function formatFieldValue2(value, fieldName) {
42435
+ function formatFieldValue2(value, fieldName2) {
42407
42436
  if (typeof value === "number") {
42408
- if (fieldName.toLowerCase().includes("progress") || fieldName.toLowerCase().includes("percent")) {
42437
+ if (fieldName2.toLowerCase().includes("progress") || fieldName2.toLowerCase().includes("percent")) {
42409
42438
  return `${value}%`;
42410
42439
  }
42411
- if (fieldName.toLowerCase().includes("budget") || fieldName.toLowerCase().includes("cost")) {
42440
+ if (fieldName2.toLowerCase().includes("budget") || fieldName2.toLowerCase().includes("cost")) {
42412
42441
  return `$${value.toLocaleString()}`;
42413
42442
  }
42414
42443
  return value.toLocaleString();
@@ -42418,7 +42447,7 @@ function formatFieldValue2(value, fieldName) {
42418
42447
  }
42419
42448
  return String(value);
42420
42449
  }
42421
- function renderRichFieldValue(value, fieldName, fieldType, meta) {
42450
+ function renderRichFieldValue(value, fieldName2, fieldType, meta) {
42422
42451
  if (value === void 0 || value === null) return "\u2014";
42423
42452
  const str = String(value);
42424
42453
  switch (fieldType) {
@@ -42429,7 +42458,7 @@ function renderRichFieldValue(value, fieldName, fieldType, meta) {
42429
42458
  "img",
42430
42459
  {
42431
42460
  src: str,
42432
- alt: formatFieldLabel(fieldName),
42461
+ alt: formatFieldLabel(fieldName2),
42433
42462
  className: "max-w-full max-h-64 rounded-md object-contain",
42434
42463
  loading: "lazy"
42435
42464
  }
@@ -42564,9 +42593,9 @@ function renderRichFieldValue(value, fieldName, fieldType, meta) {
42564
42593
  return /* @__PURE__ */ jsxRuntime.jsx("a", { href: `tel:${str}`, className: "text-primary hover:underline", children: str });
42565
42594
  default:
42566
42595
  if (meta?.values && meta.values.length > 0 && meta.values.includes(str)) {
42567
- return /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: getBadgeVariant(fieldName, str), children: humanizeEnumValue(str) });
42596
+ return /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: getBadgeVariant(fieldName2, str), children: humanizeEnumValue(str) });
42568
42597
  }
42569
- return formatFieldValue2(value, fieldName);
42598
+ return formatFieldValue2(value, fieldName2);
42570
42599
  }
42571
42600
  }
42572
42601
  function normalizeFieldDefs(fields) {
@@ -43036,16 +43065,11 @@ var init_DetailPanel = __esm({
43036
43065
  footer
43037
43066
  ] })
43038
43067
  ] }) });
43039
- return /* @__PURE__ */ jsxRuntime.jsx(
43040
- Box,
43041
- {
43042
- className: cn(
43043
- slideOver && "fixed inset-y-0 right-0 w-full max-w-2xl bg-card shadow-lg z-50 overflow-y-auto p-6",
43044
- className
43045
- ),
43046
- children: content
43047
- }
43048
- );
43068
+ if (!slideOver) {
43069
+ return /* @__PURE__ */ jsxRuntime.jsx(Box, { className, children: content });
43070
+ }
43071
+ const panel = /* @__PURE__ */ jsxRuntime.jsx(Box, { className: cn("fixed inset-y-0 right-0 w-full max-w-2xl bg-card shadow-lg z-50 overflow-y-auto p-6", className), children: content });
43072
+ return typeof document === "undefined" ? panel : reactDom.createPortal(panel, document.body);
43049
43073
  };
43050
43074
  DetailPanel.displayName = "DetailPanel";
43051
43075
  }
@@ -43414,7 +43438,21 @@ var init_Form = __esm({
43414
43438
  const normalizedInitialData = React87__namespace.default.useMemo(() => {
43415
43439
  const entityRowAsInitial = isPlainEntityRow(entity) ? entity : void 0;
43416
43440
  const callerInitial = initialData !== null && typeof initialData === "object" && !Array.isArray(initialData) ? initialData : {};
43417
- return entityRowAsInitial !== void 0 ? { ...entityRowAsInitial, ...callerInitial } : callerInitial;
43441
+ const merged = entityRowAsInitial !== void 0 ? { ...entityRowAsInitial, ...callerInitial } : callerInitial;
43442
+ const toId = (value) => {
43443
+ if (value === null || value === void 0 || typeof value !== "object" || value instanceof Date) return value;
43444
+ if (Array.isArray(value)) {
43445
+ return value.map(
43446
+ (item) => item !== null && typeof item === "object" && !Array.isArray(item) && !(item instanceof Date) && item.id !== void 0 && item.id !== null ? String(item.id) : item
43447
+ );
43448
+ }
43449
+ return value.id !== void 0 && value.id !== null ? String(value.id) : value;
43450
+ };
43451
+ const normalized = {};
43452
+ for (const [key, value] of Object.entries(merged)) {
43453
+ normalized[key] = key === "id" ? value : toId(value);
43454
+ }
43455
+ return normalized;
43418
43456
  }, [entity, initialData]);
43419
43457
  const entityDerivedFields = React87__namespace.default.useMemo(() => {
43420
43458
  if (fields && fields.length > 0) return void 0;
@@ -43543,8 +43581,8 @@ var init_Form = __esm({
43543
43581
  checkViolations(name, newFormData);
43544
43582
  };
43545
43583
  const isFieldVisible = React87__namespace.default.useCallback(
43546
- (fieldName) => {
43547
- const condition = conditionalFields[fieldName];
43584
+ (fieldName2) => {
43585
+ const condition = conditionalFields[fieldName2];
43548
43586
  if (!condition) return true;
43549
43587
  return Boolean(evaluateFormExpression(condition, evalContext));
43550
43588
  },
@@ -43590,9 +43628,9 @@ var init_Form = __esm({
43590
43628
  };
43591
43629
  const handleInvalid = (e) => {
43592
43630
  const target = e.target;
43593
- const fieldName = target.getAttribute("data-field-name") ?? target.name ?? "";
43631
+ const fieldName2 = target.getAttribute("data-field-name") ?? target.name ?? "";
43594
43632
  const fieldMessage = target.validationMessage || "Invalid value";
43595
- debug("forms", "invalid", { mode: formMode, fieldName, fieldMessage });
43633
+ debug("forms", "invalid", { mode: formMode, fieldName: fieldName2, fieldMessage });
43596
43634
  queueMicrotask(() => {
43597
43635
  const form = formRef.current;
43598
43636
  if (!form) return;
@@ -43628,22 +43666,22 @@ var init_Form = __esm({
43628
43666
  };
43629
43667
  const renderField = React87__namespace.default.useCallback(
43630
43668
  (field) => {
43631
- const fieldName = field.name || field.field;
43632
- if (!fieldName) return null;
43633
- if (!isFieldVisible(fieldName)) {
43669
+ const fieldName2 = field.name || field.field;
43670
+ if (!fieldName2) return null;
43671
+ if (!isFieldVisible(fieldName2)) {
43634
43672
  return null;
43635
43673
  }
43636
43674
  const inputType = determineInputType(field);
43637
- const label = field.label || fieldName.charAt(0).toUpperCase() + fieldName.slice(1).replace(/([A-Z])/g, " $1");
43638
- const currentValue = formData[fieldName] ?? field.defaultValue ?? "";
43639
- return /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "xs", "data-field": fieldName, children: [
43675
+ const label = field.label || fieldName2.charAt(0).toUpperCase() + fieldName2.slice(1).replace(/([A-Z])/g, " $1");
43676
+ const currentValue = formData[fieldName2] ?? field.defaultValue ?? "";
43677
+ return /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "xs", "data-field": fieldName2, children: [
43640
43678
  inputType !== "checkbox" && /* @__PURE__ */ jsxRuntime.jsxs(Typography, { as: "label", variant: "label", weight: "bold", children: [
43641
43679
  label,
43642
43680
  field.required && /* @__PURE__ */ jsxRuntime.jsx(Typography, { as: "span", color: "error", className: "ml-1", children: "*" })
43643
43681
  ] }),
43644
- renderFieldInput(field, fieldName, inputType, currentValue, label),
43682
+ renderFieldInput(field, fieldName2, inputType, currentValue, label),
43645
43683
  field.hint && /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", color: "muted", children: field.hint })
43646
- ] }, fieldName);
43684
+ ] }, fieldName2);
43647
43685
  },
43648
43686
  [formData, isFieldVisible, relationsData, relationsLoading, isLoading]
43649
43687
  );
@@ -43672,8 +43710,8 @@ var init_Form = __esm({
43672
43710
  }
43673
43711
  return field;
43674
43712
  }).map((field) => {
43675
- const fieldName = field.name || field.field;
43676
- const override = fieldOverrides?.find((o) => o.name === fieldName);
43713
+ const fieldName2 = field.name || field.field;
43714
+ const override = fieldOverrides?.find((o) => o.name === fieldName2);
43677
43715
  if (!override) return field;
43678
43716
  return {
43679
43717
  ...field,
@@ -43729,11 +43767,11 @@ var init_Form = __esm({
43729
43767
  ] }, section.id);
43730
43768
  }).filter(Boolean);
43731
43769
  }, [sections, isSectionVisible, collapsedSections, renderField, gap]);
43732
- function renderFieldInput(field, fieldName, inputType, currentValue, label) {
43770
+ function renderFieldInput(field, fieldName2, inputType, currentValue, label) {
43733
43771
  const commonProps = {
43734
- id: fieldName,
43735
- name: fieldName,
43736
- "data-field-name": fieldName,
43772
+ id: fieldName2,
43773
+ name: fieldName2,
43774
+ "data-field-name": fieldName2,
43737
43775
  required: field.required,
43738
43776
  disabled: isLoading,
43739
43777
  placeholder: field.placeholder,
@@ -43747,7 +43785,7 @@ var init_Form = __esm({
43747
43785
  ...commonProps,
43748
43786
  label: label + (field.required ? " *" : ""),
43749
43787
  checked: Boolean(currentValue),
43750
- onChange: (e) => handleChange(fieldName, e.target.checked)
43788
+ onChange: (e) => handleChange(fieldName2, e.target.checked)
43751
43789
  }
43752
43790
  );
43753
43791
  case "textarea":
@@ -43756,7 +43794,7 @@ var init_Form = __esm({
43756
43794
  {
43757
43795
  ...commonProps,
43758
43796
  value: String(currentValue),
43759
- onChange: (e) => handleChange(fieldName, e.target.value),
43797
+ onChange: (e) => handleChange(fieldName2, e.target.value),
43760
43798
  minLength: field.min,
43761
43799
  maxLength: field.max
43762
43800
  }
@@ -43769,14 +43807,14 @@ var init_Form = __esm({
43769
43807
  ...commonProps,
43770
43808
  options,
43771
43809
  value: String(currentValue),
43772
- onValueChange: (v) => handleChange(fieldName, v),
43810
+ onValueChange: (v) => handleChange(fieldName2, v),
43773
43811
  placeholder: field.placeholder || `Select ${label}...`
43774
43812
  }
43775
43813
  );
43776
43814
  }
43777
43815
  case "relation": {
43778
- const relationOptions = relationsData[fieldName] || [];
43779
- const relationLoading = relationsLoading[fieldName] || false;
43816
+ const relationOptions = relationsData[fieldName2] || [];
43817
+ const relationLoading = relationsLoading[fieldName2] || false;
43780
43818
  if (field.relation?.cardinality !== void 0 && MANY_CARDINALITIES.includes(field.relation.cardinality)) {
43781
43819
  const selectedValues = Array.isArray(currentValue) ? currentValue.map((v) => String(v)) : [];
43782
43820
  return /* @__PURE__ */ jsxRuntime.jsx(
@@ -43788,7 +43826,7 @@ var init_Form = __esm({
43788
43826
  clearable: true,
43789
43827
  options: [...relationOptions],
43790
43828
  value: selectedValues,
43791
- onValueChange: (value) => handleChange(fieldName, Array.isArray(value) ? value : [value]),
43829
+ onValueChange: (value) => handleChange(fieldName2, Array.isArray(value) ? value : [value]),
43792
43830
  placeholder: field.placeholder || `Select ${label}...`
43793
43831
  }
43794
43832
  );
@@ -43798,7 +43836,7 @@ var init_Form = __esm({
43798
43836
  {
43799
43837
  ...commonProps,
43800
43838
  value: currentValue ? String(currentValue) : void 0,
43801
- onChange: (value) => handleChange(fieldName, value),
43839
+ onChange: (value) => handleChange(fieldName2, value),
43802
43840
  options: relationOptions,
43803
43841
  isLoading: relationLoading,
43804
43842
  placeholder: field.placeholder || `Select ${label}...`,
@@ -43815,7 +43853,7 @@ var init_Form = __esm({
43815
43853
  placeholder: field.placeholder,
43816
43854
  disabled: isLoading,
43817
43855
  value: arrayValue,
43818
- onChange: (next) => handleChange(fieldName, [...next])
43856
+ onChange: (next) => handleChange(fieldName2, [...next])
43819
43857
  }
43820
43858
  );
43821
43859
  }
@@ -43827,7 +43865,7 @@ var init_Form = __esm({
43827
43865
  type: "number",
43828
43866
  value: currentValue !== void 0 && currentValue !== "" ? String(currentValue) : "",
43829
43867
  onChange: (e) => handleChange(
43830
- fieldName,
43868
+ fieldName2,
43831
43869
  e.target.value ? Number(e.target.value) : void 0
43832
43870
  ),
43833
43871
  min: field.min,
@@ -43844,7 +43882,7 @@ var init_Form = __esm({
43844
43882
  icon: LucideIcons2.DollarSign,
43845
43883
  value: currentValue !== void 0 && currentValue !== "" ? String(currentValue) : "",
43846
43884
  onChange: (e) => handleChange(
43847
- fieldName,
43885
+ fieldName2,
43848
43886
  e.target.value ? Number(e.target.value) : void 0
43849
43887
  ),
43850
43888
  min: field.min,
@@ -43862,7 +43900,7 @@ var init_Form = __esm({
43862
43900
  const f3 = files[0];
43863
43901
  if (!f3) return;
43864
43902
  const reader = new FileReader();
43865
- reader.onload = () => handleChange(fieldName, {
43903
+ reader.onload = () => handleChange(fieldName2, {
43866
43904
  name: f3.name,
43867
43905
  mimeType: f3.type,
43868
43906
  sizeBytes: f3.size,
@@ -43879,7 +43917,7 @@ var init_Form = __esm({
43879
43917
  ...commonProps,
43880
43918
  type: "date",
43881
43919
  value: formatDateValue(currentValue),
43882
- onChange: (e) => handleChange(fieldName, e.target.value)
43920
+ onChange: (e) => handleChange(fieldName2, e.target.value)
43883
43921
  }
43884
43922
  );
43885
43923
  case "datetime-local":
@@ -43889,7 +43927,7 @@ var init_Form = __esm({
43889
43927
  ...commonProps,
43890
43928
  type: "datetime-local",
43891
43929
  value: formatDateTimeValue(currentValue),
43892
- onChange: (e) => handleChange(fieldName, e.target.value)
43930
+ onChange: (e) => handleChange(fieldName2, e.target.value)
43893
43931
  }
43894
43932
  );
43895
43933
  case "email":
@@ -43899,7 +43937,7 @@ var init_Form = __esm({
43899
43937
  ...commonProps,
43900
43938
  type: "email",
43901
43939
  value: String(currentValue),
43902
- onChange: (e) => handleChange(fieldName, e.target.value),
43940
+ onChange: (e) => handleChange(fieldName2, e.target.value),
43903
43941
  minLength: field.min,
43904
43942
  maxLength: field.max
43905
43943
  }
@@ -43911,7 +43949,7 @@ var init_Form = __esm({
43911
43949
  ...commonProps,
43912
43950
  type: "url",
43913
43951
  value: String(currentValue),
43914
- onChange: (e) => handleChange(fieldName, e.target.value),
43952
+ onChange: (e) => handleChange(fieldName2, e.target.value),
43915
43953
  minLength: field.min,
43916
43954
  maxLength: field.max
43917
43955
  }
@@ -43923,7 +43961,7 @@ var init_Form = __esm({
43923
43961
  ...commonProps,
43924
43962
  type: "password",
43925
43963
  value: String(currentValue),
43926
- onChange: (e) => handleChange(fieldName, e.target.value),
43964
+ onChange: (e) => handleChange(fieldName2, e.target.value),
43927
43965
  minLength: field.min,
43928
43966
  maxLength: field.max
43929
43967
  }
@@ -43936,7 +43974,7 @@ var init_Form = __esm({
43936
43974
  ...commonProps,
43937
43975
  type: "text",
43938
43976
  value: String(currentValue),
43939
- onChange: (e) => handleChange(fieldName, e.target.value),
43977
+ onChange: (e) => handleChange(fieldName2, e.target.value),
43940
43978
  minLength: field.min,
43941
43979
  maxLength: field.max
43942
43980
  }
@@ -44293,7 +44331,7 @@ function entityFieldsFromListItem(item) {
44293
44331
  }
44294
44332
  return result;
44295
44333
  }
44296
- function getStatusStyle(fieldName, value) {
44334
+ function getStatusStyle(fieldName2, value) {
44297
44335
  const val = String(value).toLowerCase();
44298
44336
  if (val.includes("complete") || val.includes("done"))
44299
44337
  return STATUS_STYLES.complete;
@@ -44309,12 +44347,12 @@ function getStatusStyle(fieldName, value) {
44309
44347
  if (val.includes("low")) return STATUS_STYLES.low;
44310
44348
  return STATUS_STYLES.default;
44311
44349
  }
44312
- function formatValue3(value, fieldName) {
44350
+ function formatValue3(value, fieldName2) {
44313
44351
  if (typeof value === "number") {
44314
- if (fieldName.toLowerCase().includes("progress") || fieldName.toLowerCase().includes("percent")) {
44352
+ if (fieldName2.toLowerCase().includes("progress") || fieldName2.toLowerCase().includes("percent")) {
44315
44353
  return `${value}%`;
44316
44354
  }
44317
- if (fieldName.toLowerCase().includes("budget") || fieldName.toLowerCase().includes("cost")) {
44355
+ if (fieldName2.toLowerCase().includes("budget") || fieldName2.toLowerCase().includes("cost")) {
44318
44356
  return new Intl.NumberFormat("en-US", {
44319
44357
  style: "currency",
44320
44358
  currency: "USD",
@@ -44337,8 +44375,8 @@ function formatValue3(value, fieldName) {
44337
44375
  }
44338
44376
  return String(value);
44339
44377
  }
44340
- function formatFieldLabel2(fieldName) {
44341
- return humanizeFieldName(fieldName).replace(/\sId$/, "").trim();
44378
+ function formatFieldLabel2(fieldName2) {
44379
+ return humanizeFieldName(fieldName2).replace(/\sId$/, "").trim();
44342
44380
  }
44343
44381
  var STATUS_STYLES, StatusBadge, ProgressIndicator, List3;
44344
44382
  var init_List = __esm({
@@ -44406,9 +44444,9 @@ var init_List = __esm({
44406
44444
  };
44407
44445
  StatusBadge = ({
44408
44446
  value,
44409
- fieldName
44447
+ fieldName: fieldName2
44410
44448
  }) => {
44411
- const style = getStatusStyle(fieldName, value);
44449
+ const style = getStatusStyle(fieldName2, value);
44412
44450
  return /* @__PURE__ */ jsxRuntime.jsxs(
44413
44451
  Typography,
44414
44452
  {
@@ -47321,8 +47359,8 @@ var init_StatCard = __esm({
47321
47359
  return items.length;
47322
47360
  }
47323
47361
  if (field.includes(":")) {
47324
- const [fieldName, fieldValue] = field.split(":");
47325
- return items.filter((item) => item[fieldName] === fieldValue).length;
47362
+ const [fieldName2, fieldValue] = field.split(":");
47363
+ return items.filter((item) => item[fieldName2] === fieldValue).length;
47326
47364
  }
47327
47365
  const fieldExistsOnItems = items.some((item) => field in item);
47328
47366
  if (fieldExistsOnItems) {
@@ -48842,6 +48880,8 @@ var init_component_registry_generated = __esm({
48842
48880
  init_DocSearch();
48843
48881
  init_DocSidebar();
48844
48882
  init_DocTOC();
48883
+ init_DocumentDetails();
48884
+ init_DocumentPanel();
48845
48885
  init_DocumentViewer();
48846
48886
  init_DrawFxLayer();
48847
48887
  init_DrawGroup();
@@ -48951,7 +48991,7 @@ var init_component_registry_generated = __esm({
48951
48991
  init_RelationSelect();
48952
48992
  init_RepeatableFormSection();
48953
48993
  init_ReplyTree();
48954
- init_RichBlockEditor();
48994
+ init_RichTextEditor();
48955
48995
  init_RuntimeDebugger2();
48956
48996
  init_ScaledDiagram();
48957
48997
  init_ScoreDisplay();
@@ -49114,6 +49154,8 @@ var init_component_registry_generated = __esm({
49114
49154
  "DocSearch": DocSearch,
49115
49155
  "DocSidebar": DocSidebar,
49116
49156
  "DocTOC": DocTOC,
49157
+ "DocumentDetails": DocumentDetails,
49158
+ "DocumentPanel": DocumentPanel,
49117
49159
  "DocumentViewer": DocumentViewer,
49118
49160
  "DrawFxLayer": DrawFxLayer,
49119
49161
  "DrawGroup": DrawGroup,
@@ -49225,7 +49267,7 @@ var init_component_registry_generated = __esm({
49225
49267
  "RelationSelect": RelationSelect,
49226
49268
  "RepeatableFormSection": RepeatableFormSection,
49227
49269
  "ReplyTree": ReplyTree,
49228
- "RichBlockEditor": RichBlockEditor,
49270
+ "RichTextEditor": RichTextEditor,
49229
49271
  "RuntimeDebugger": RuntimeDebugger,
49230
49272
  "ScaledDiagram": ScaledDiagram,
49231
49273
  "ScoreDisplay": ScoreDisplay,
@@ -49379,10 +49421,10 @@ function enrichFormFields(fields, entityDef) {
49379
49421
  }
49380
49422
  if (field && typeof field === "object" && !Array.isArray(field) && !React87__namespace.default.isValidElement(field) && !(field instanceof Date)) {
49381
49423
  const obj = field;
49382
- const fieldName = typeof obj.name === "string" ? obj.name : typeof obj.field === "string" ? obj.field : void 0;
49383
- if (!fieldName) return field;
49424
+ const fieldName2 = typeof obj.name === "string" ? obj.name : typeof obj.field === "string" ? obj.field : void 0;
49425
+ if (!fieldName2) return field;
49384
49426
  if (obj.type || obj.inputType) return field;
49385
- const entityField = fieldMap.get(fieldName);
49427
+ const entityField = fieldMap.get(fieldName2);
49386
49428
  if (!entityField) return field;
49387
49429
  const enriched = { ...obj, type: entityField.type };
49388
49430
  if (entityField.required && !("required" in obj)) {
@@ -49432,9 +49474,9 @@ function enrichDetailFields(fields, entityDef) {
49432
49474
  }
49433
49475
  if (field && typeof field === "object" && !Array.isArray(field) && !React87__namespace.default.isValidElement(field) && !(field instanceof Date)) {
49434
49476
  const obj = field;
49435
- const fieldName = typeof obj.key === "string" ? obj.key : typeof obj.name === "string" ? obj.name : void 0;
49436
- if (!fieldName || obj.type) return field;
49437
- const meta = metaFor(fieldName);
49477
+ const fieldName2 = typeof obj.key === "string" ? obj.key : typeof obj.name === "string" ? obj.name : void 0;
49478
+ if (!fieldName2 || obj.type) return field;
49479
+ const meta = metaFor(fieldName2);
49438
49480
  return meta ? { ...obj, ...meta } : field;
49439
49481
  }
49440
49482
  return field;
@@ -50273,9 +50315,9 @@ function UISlotRenderer({
50273
50315
  "ui-slot-renderer relative min-h-full",
50274
50316
  className
50275
50317
  ), children: [
50276
- /* @__PURE__ */ jsxRuntime.jsxs(Box, { className: "flex min-h-full", children: [
50277
- /* @__PURE__ */ jsxRuntime.jsx(UISlotComponent, { slot: "sidebar", className: "ui-slot-sidebar min-w-0 shrink-0" }),
50278
- /* @__PURE__ */ jsxRuntime.jsx(UISlotComponent, { slot: "main", className: "ui-slot-main flex-1 min-h-[200px]" })
50318
+ /* @__PURE__ */ jsxRuntime.jsxs(Box, { className: "flex min-h-full flex-col lg:flex-row", children: [
50319
+ /* @__PURE__ */ jsxRuntime.jsx(UISlotComponent, { slot: "sidebar", className: "ui-slot-sidebar min-w-0 lg:shrink-0" }),
50320
+ /* @__PURE__ */ jsxRuntime.jsx(UISlotComponent, { slot: "main", className: "ui-slot-main flex-1 min-w-0 min-h-[200px]" })
50279
50321
  ] }),
50280
50322
  /* @__PURE__ */ jsxRuntime.jsx(UISlotComponent, { slot: "modal", portal: true }),
50281
50323
  /* @__PURE__ */ jsxRuntime.jsx(UISlotComponent, { slot: "drawer", portal: true }),