@almadar/ui 5.13.2 → 5.14.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.
@@ -10,9 +10,9 @@ import { Loader2, X, List, Printer, ChevronRight, ChevronLeft, GitBranch, Pencil
10
10
  import * as PhosphorIcons from '@phosphor-icons/react';
11
11
  import * as TablerIcons from '@tabler/icons-react';
12
12
  import * as FaIcons from 'react-icons/fa';
13
+ import { createPortal } from 'react-dom';
13
14
  import { evaluate, createMinimalContext } from '@almadar/evaluator';
14
15
  import { useUISlots } from '@almadar/ui/context';
15
- import { createPortal } from 'react-dom';
16
16
  import { Link, Outlet, useLocation } from 'react-router-dom';
17
17
  import ELK from 'elkjs/lib/elk.bundled.js';
18
18
  import ReactMarkdown from 'react-markdown';
@@ -1992,7 +1992,7 @@ var init_Modal = __esm({
1992
1992
  document.body.style.overflow = "";
1993
1993
  };
1994
1994
  }, [isOpen]);
1995
- if (!isOpen) return null;
1995
+ if (!isOpen || typeof document === "undefined") return null;
1996
1996
  const handleClose = () => {
1997
1997
  if (closeEvent) eventBus.emit(`UI:${closeEvent}`, {});
1998
1998
  onClose();
@@ -2002,124 +2002,127 @@ var init_Modal = __esm({
2002
2002
  handleClose();
2003
2003
  }
2004
2004
  };
2005
- return /* @__PURE__ */ jsxs(Fragment, { children: [
2006
- /* @__PURE__ */ jsx(
2007
- Overlay,
2008
- {
2009
- isVisible: isOpen,
2010
- onClick: handleOverlayClick,
2011
- className: "z-40"
2012
- }
2013
- ),
2014
- /* @__PURE__ */ jsx(
2015
- Box,
2016
- {
2017
- className: cn(
2018
- "fixed inset-0 z-50 pointer-events-none",
2019
- "flex items-start justify-center px-4 pb-4 pt-[10vh]",
2020
- "max-sm:items-stretch max-sm:p-0 max-sm:pt-0"
2021
- ),
2022
- children: /* @__PURE__ */ jsxs(
2023
- Dialog,
2024
- {
2025
- ref: modalRef,
2026
- open: true,
2027
- className: cn(
2028
- // Reset browser-default dialog chrome — we own styling. `static`
2029
- // overrides the user-agent `position: absolute` so the parent
2030
- // flex container's `justify-center` actually centers the dialog
2031
- // (without this, the dialog drops out of flex flow and `m-0`
2032
- // kills the user-agent's `margin: auto` centering, pinning the
2033
- // dialog to top-left).
2034
- "static m-0 p-0 border-0 bg-transparent",
2035
- // Pre-existing dialog frame
2036
- "pointer-events-auto w-full flex flex-col bg-surface border shadow-elevation-dialog rounded-container",
2037
- // Desktop sizing + viewport-aware floor.
2038
- sizeClasses2[size],
2039
- minWidthClasses[size],
2040
- "max-h-[80vh]",
2041
- // Mobile: take the entire screen. Override desktop max-w cap,
2042
- // full height, no rounded corners, no min-width.
2043
- "max-sm:max-w-none max-sm:max-h-none max-sm:w-full max-sm:h-full max-sm:rounded-none",
2044
- lookStyles[look],
2045
- className
2046
- ),
2047
- style: dragY > 0 ? {
2048
- transform: `translateY(${dragY}px)`,
2049
- transition: isDragging.current ? "none" : "transform 200ms ease-out"
2050
- } : void 0,
2051
- ...title && { "aria-labelledby": "modal-title" },
2052
- children: [
2053
- /* @__PURE__ */ jsx(
2054
- Box,
2055
- {
2056
- className: "hidden max-sm:flex justify-center py-2 cursor-grab active:cursor-grabbing touch-none",
2057
- onPointerDown: (e) => {
2058
- if (!swipeDownToClose) return;
2059
- dragStartY.current = e.clientY;
2060
- isDragging.current = true;
2061
- e.target.setPointerCapture(e.pointerId);
2062
- },
2063
- onPointerMove: (e) => {
2064
- if (!isDragging.current) return;
2065
- const dy = Math.max(0, e.clientY - dragStartY.current);
2066
- setDragY(dy);
2067
- },
2068
- onPointerUp: () => {
2069
- if (!isDragging.current) return;
2070
- isDragging.current = false;
2071
- if (dragY > 100) {
2072
- handleClose();
2073
- }
2074
- setDragY(0);
2075
- },
2076
- onPointerCancel: () => {
2077
- isDragging.current = false;
2078
- setDragY(0);
2079
- },
2080
- children: /* @__PURE__ */ jsx(Box, { className: "w-10 h-1 rounded-full bg-border" })
2081
- }
2005
+ return createPortal(
2006
+ /* @__PURE__ */ jsxs(Fragment, { children: [
2007
+ /* @__PURE__ */ jsx(
2008
+ Overlay,
2009
+ {
2010
+ isVisible: isOpen,
2011
+ onClick: handleOverlayClick,
2012
+ className: "z-[1000]"
2013
+ }
2014
+ ),
2015
+ /* @__PURE__ */ jsx(
2016
+ Box,
2017
+ {
2018
+ className: cn(
2019
+ "fixed inset-0 z-[1001] pointer-events-none",
2020
+ "flex items-start justify-center px-4 pb-4 pt-[10vh]",
2021
+ "max-sm:items-stretch max-sm:p-0 max-sm:pt-0"
2022
+ ),
2023
+ children: /* @__PURE__ */ jsxs(
2024
+ Dialog,
2025
+ {
2026
+ ref: modalRef,
2027
+ open: true,
2028
+ className: cn(
2029
+ // Reset browser-default dialog chrome we own styling. `static`
2030
+ // overrides the user-agent `position: absolute` so the parent
2031
+ // flex container's `justify-center` actually centers the dialog
2032
+ // (without this, the dialog drops out of flex flow and `m-0`
2033
+ // kills the user-agent's `margin: auto` centering, pinning the
2034
+ // dialog to top-left).
2035
+ "static m-0 p-0 border-0 bg-transparent",
2036
+ // Pre-existing dialog frame
2037
+ "pointer-events-auto w-full flex flex-col bg-surface border shadow-elevation-dialog rounded-container",
2038
+ // Desktop sizing + viewport-aware floor.
2039
+ sizeClasses2[size],
2040
+ minWidthClasses[size],
2041
+ "max-h-[80vh]",
2042
+ // Mobile: take the entire screen. Override desktop max-w cap,
2043
+ // full height, no rounded corners, no min-width.
2044
+ "max-sm:max-w-none max-sm:max-h-none max-sm:w-full max-sm:h-full max-sm:rounded-none",
2045
+ lookStyles[look],
2046
+ className
2082
2047
  ),
2083
- (title || showCloseButton) && /* @__PURE__ */ jsxs(
2084
- Box,
2085
- {
2086
- className: cn(
2087
- "px-6 py-4 flex items-center justify-between",
2088
- "border-b-[length:var(--border-width)] border-border"
2089
- ),
2090
- children: [
2091
- title && /* @__PURE__ */ jsx(Typography, { variant: "h4", as: "h2", id: "modal-title", children: title }),
2092
- showCloseButton && /* @__PURE__ */ jsx(
2093
- Button,
2094
- {
2095
- variant: "ghost",
2096
- size: "sm",
2097
- icon: "x",
2098
- onClick: handleClose,
2099
- "data-event": "CLOSE",
2100
- "aria-label": "Close modal"
2048
+ style: dragY > 0 ? {
2049
+ transform: `translateY(${dragY}px)`,
2050
+ transition: isDragging.current ? "none" : "transform 200ms ease-out"
2051
+ } : void 0,
2052
+ ...title && { "aria-labelledby": "modal-title" },
2053
+ children: [
2054
+ /* @__PURE__ */ jsx(
2055
+ Box,
2056
+ {
2057
+ className: "hidden max-sm:flex justify-center py-2 cursor-grab active:cursor-grabbing touch-none",
2058
+ onPointerDown: (e) => {
2059
+ if (!swipeDownToClose) return;
2060
+ dragStartY.current = e.clientY;
2061
+ isDragging.current = true;
2062
+ e.target.setPointerCapture(e.pointerId);
2063
+ },
2064
+ onPointerMove: (e) => {
2065
+ if (!isDragging.current) return;
2066
+ const dy = Math.max(0, e.clientY - dragStartY.current);
2067
+ setDragY(dy);
2068
+ },
2069
+ onPointerUp: () => {
2070
+ if (!isDragging.current) return;
2071
+ isDragging.current = false;
2072
+ if (dragY > 100) {
2073
+ handleClose();
2101
2074
  }
2102
- )
2103
- ]
2104
- }
2105
- ),
2106
- /* @__PURE__ */ jsx(Box, { className: "flex-1 overflow-y-auto p-6", children }),
2107
- footer && /* @__PURE__ */ jsx(
2108
- Box,
2109
- {
2110
- className: cn(
2111
- "px-6 py-4 bg-muted",
2112
- "border-t-[length:var(--border-width)] border-border"
2113
- ),
2114
- children: footer
2115
- }
2116
- )
2117
- ]
2118
- }
2119
- )
2120
- }
2121
- )
2122
- ] });
2075
+ setDragY(0);
2076
+ },
2077
+ onPointerCancel: () => {
2078
+ isDragging.current = false;
2079
+ setDragY(0);
2080
+ },
2081
+ children: /* @__PURE__ */ jsx(Box, { className: "w-10 h-1 rounded-full bg-border" })
2082
+ }
2083
+ ),
2084
+ (title || showCloseButton) && /* @__PURE__ */ jsxs(
2085
+ Box,
2086
+ {
2087
+ className: cn(
2088
+ "px-6 py-4 flex items-center justify-between",
2089
+ "border-b-[length:var(--border-width)] border-border"
2090
+ ),
2091
+ children: [
2092
+ title && /* @__PURE__ */ jsx(Typography, { variant: "h4", as: "h2", id: "modal-title", children: title }),
2093
+ showCloseButton && /* @__PURE__ */ jsx(
2094
+ Button,
2095
+ {
2096
+ variant: "ghost",
2097
+ size: "sm",
2098
+ icon: "x",
2099
+ onClick: handleClose,
2100
+ "data-event": "CLOSE",
2101
+ "aria-label": "Close modal"
2102
+ }
2103
+ )
2104
+ ]
2105
+ }
2106
+ ),
2107
+ /* @__PURE__ */ jsx(Box, { className: "flex-1 overflow-y-auto p-6", children }),
2108
+ footer && /* @__PURE__ */ jsx(
2109
+ Box,
2110
+ {
2111
+ className: cn(
2112
+ "px-6 py-4 bg-muted",
2113
+ "border-t-[length:var(--border-width)] border-border"
2114
+ ),
2115
+ children: footer
2116
+ }
2117
+ )
2118
+ ]
2119
+ }
2120
+ )
2121
+ }
2122
+ )
2123
+ ] }),
2124
+ document.body
2125
+ );
2123
2126
  };
2124
2127
  Modal.displayName = "Modal";
2125
2128
  }
@@ -13915,7 +13918,7 @@ var init_CodeBlock = __esm({
13915
13918
  };
13916
13919
  };
13917
13920
  }, [errorLines]);
13918
- const isFoldable = foldableProp ?? (language === "orb" || language === "json");
13921
+ const isFoldable = foldableProp ?? true;
13919
13922
  const [collapsed, setCollapsed] = useState(() => /* @__PURE__ */ new Set());
13920
13923
  const foldRegions = useMemo(
13921
13924
  () => isFoldable ? computeFoldRegions(code) : [],
@@ -13938,6 +13941,8 @@ var init_CodeBlock = __esm({
13938
13941
  collapsedRef.current = collapsed;
13939
13942
  const foldStartMapRef = useRef(foldStartMap);
13940
13943
  foldStartMapRef.current = foldStartMap;
13944
+ const hiddenLinesRef = useRef(hiddenLines);
13945
+ hiddenLinesRef.current = hiddenLines;
13941
13946
  const toggleFold = useCallback((lineNum) => {
13942
13947
  setCollapsed((prev) => {
13943
13948
  const next = new Set(prev);
@@ -14050,6 +14055,60 @@ var init_CodeBlock = __esm({
14050
14055
  eventBus.emit("UI:COPY_CODE", { language, success: false });
14051
14056
  }
14052
14057
  };
14058
+ const handleSelectionCopy = useCallback((e) => {
14059
+ if (hiddenLinesRef.current.size === 0) return;
14060
+ const sel = typeof window !== "undefined" ? window.getSelection() : null;
14061
+ if (!sel || sel.rangeCount === 0 || sel.isCollapsed) return;
14062
+ const lineOf = (node) => {
14063
+ const start = node instanceof HTMLElement ? node : node?.parentElement ?? null;
14064
+ const lineEl = start?.closest("[data-line]");
14065
+ if (!lineEl) return null;
14066
+ const n = parseInt(lineEl.getAttribute("data-line") ?? "", 10);
14067
+ return Number.isNaN(n) ? null : n;
14068
+ };
14069
+ const range = sel.getRangeAt(0);
14070
+ let a = lineOf(range.startContainer);
14071
+ let b = lineOf(range.endContainer);
14072
+ if (a === null || b === null) {
14073
+ const container = codeRef.current;
14074
+ if (!container) return;
14075
+ let min = Infinity, max = -Infinity;
14076
+ container.querySelectorAll("[data-line]").forEach((el) => {
14077
+ if (!sel.containsNode(el, true)) return;
14078
+ const n = parseInt(el.getAttribute("data-line") ?? "", 10);
14079
+ if (!Number.isNaN(n)) {
14080
+ min = Math.min(min, n);
14081
+ max = Math.max(max, n);
14082
+ }
14083
+ });
14084
+ if (min === Infinity) return;
14085
+ a = a ?? min;
14086
+ b = b ?? max;
14087
+ }
14088
+ if (a > b) [a, b] = [b, a];
14089
+ let touchesFold = false;
14090
+ for (let i = a; i <= b; i++) {
14091
+ if (hiddenLinesRef.current.has(i) || foldStartMapRef.current.has(i) && collapsedRef.current.has(i)) {
14092
+ touchesFold = true;
14093
+ break;
14094
+ }
14095
+ }
14096
+ if (!touchesFold) return;
14097
+ let endLine = b;
14098
+ let changed = true;
14099
+ while (changed) {
14100
+ changed = false;
14101
+ foldStartMapRef.current.forEach((region, start) => {
14102
+ if (start >= a && start <= endLine && collapsedRef.current.has(start) && region.end > endLine) {
14103
+ endLine = region.end;
14104
+ changed = true;
14105
+ }
14106
+ });
14107
+ }
14108
+ const full = code.split("\n").slice(a, endLine + 1).join("\n");
14109
+ e.clipboardData.setData("text/plain", full);
14110
+ e.preventDefault();
14111
+ }, [code]);
14053
14112
  const hasHeader = showLanguageBadge || showCopyButton;
14054
14113
  return /* @__PURE__ */ jsxs(Box, { className: `relative group ${className || ""}`, style: { display: "flex", flexDirection: "column", height: "100%" }, children: [
14055
14114
  hasHeader && /* @__PURE__ */ jsxs(
@@ -14200,6 +14259,7 @@ var init_CodeBlock = __esm({
14200
14259
  "div",
14201
14260
  {
14202
14261
  ref: scrollRef,
14262
+ onCopy: handleSelectionCopy,
14203
14263
  style: {
14204
14264
  flex: 1,
14205
14265
  minHeight: 0,