@deepseek-ai/dsh-client-ui-primitives 0.1.5-rc.2 → 0.1.6-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/index.js CHANGED
@@ -6,30 +6,34 @@ import css$2 from "./Button.module.css";
6
6
  import css$3 from "./Pill.module.css";
7
7
  import css$4 from "./Tag.module.css";
8
8
  import css$5 from "./Switch.module.css";
9
- import css$6 from "./Input.module.css";
10
- import { Fragment as Fragment$1, cloneElement, createElement, memo, useCallback, useEffect, useId, useLayoutEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
9
+ import css$6 from "./Checkbox.module.css";
10
+ import css$7 from "./Input.module.css";
11
+ import { Fragment as Fragment$1, cloneElement, createContext, createElement, memo, useCallback, useContext, useEffect, useId, useLayoutEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
11
12
  import { createPortal } from "react-dom";
12
- import css$7 from "./Menu.module.css";
13
- import css$8 from "./HoverCard.module.css";
14
- import css$9 from "./Modal.module.css";
15
- import css$10 from "./OnboardingSurface.module.css";
16
- import css$11 from "./RiskConfirmation.module.css";
17
- import css$12 from "./ConnectionIndicator.module.css";
18
- import css$13 from "./FileTypeIcon.module.css";
19
- import css$14 from "./user-text.module.css";
20
- import css$15 from "./Tooltip.module.css";
21
- import css$16 from "./Toast.module.css";
22
- import css$17 from "./JsonTree.module.css";
13
+ import css$8 from "./Menu.module.css";
14
+ import css$9 from "./HoverCard.module.css";
15
+ import css$10 from "./Modal.module.css";
16
+ import css$11 from "./OnboardingSurface.module.css";
17
+ import css$12 from "./RiskConfirmation.module.css";
18
+ import css$13 from "./ConnectionIndicator.module.css";
19
+ import css$14 from "./FileTypeIcon.module.css";
20
+ import { siAliexpress, siApple, siBaidu, siBilibili, siCsdn, siDuckduckgo, siEbay, siFacebook, siGithub, siGitlab, siGoogle, siInstagram, siJuejin, siMdnwebdocs, siNetflix, siNpm, siPypi, siQq, siQuora, siReddit, siSinaweibo, siSpotify, siStackoverflow, siTaobao, siTelegram, siTiktok, siV2ex, siWechat, siWhatsapp, siWikipedia, siX, siYcombinator, siYoutube, siZhihu } from "simple-icons";
21
+ import css$15 from "./user-text.module.css";
22
+ import markdownCss from "./markdown/MarkdownText.module.css";
23
+ import css$16 from "./Tooltip.module.css";
24
+ import css$17 from "./Toast.module.css";
25
+ import css$18 from "./JsonTree.module.css";
23
26
  import Anser from "anser";
24
- import css$18 from "./TerminalBlock.module.css";
27
+ import css$19 from "./TerminalBlock.module.css";
25
28
  import { createCssVariablesTheme, createHighlighterCoreSync } from "shiki/core";
26
29
  import { createJavaScriptRegexEngine, defaultJavaScriptRegexConstructor } from "shiki/engine/javascript";
27
30
  import langTs from "@shikijs/langs/typescript";
28
31
  import langBash from "@shikijs/langs/shellscript";
29
32
  import langJson from "@shikijs/langs/json";
30
- import css$19 from "./ReadBlock.module.css";
31
- import css$20 from "./DiffBlock.module.css";
32
- import css$21 from "./SearchBlock.module.css";
33
+ import css$20 from "./ReadBlock.module.css";
34
+ import { structuredPatch } from "diff";
35
+ import css$21 from "./DiffBlock.module.css";
36
+ import css$22 from "./SearchBlock.module.css";
33
37
  import { fromMarkdown } from "mdast-util-from-markdown";
34
38
  import { gfmFromMarkdown } from "mdast-util-gfm";
35
39
  import { mathFromMarkdown } from "mdast-util-math";
@@ -41,9 +45,8 @@ import { classifyCharacter } from "micromark-util-classify-character";
41
45
  import { codes, constants, types } from "micromark-util-symbol";
42
46
  import { factorySpace } from "micromark-factory-space";
43
47
  import { normalizeUri } from "micromark-util-sanitize-uri";
44
- import css$22 from "./markdown/CodeBlock.module.css";
48
+ import css$23 from "./markdown/CodeBlock.module.css";
45
49
  import katex from "katex";
46
- import css$23 from "./markdown/MarkdownText.module.css";
47
50
  import "katex/dist/katex.min.css";
48
51
  import css$24 from "./WebBlock.module.css";
49
52
  import css$25 from "./markdown/JsonBlock.module.css";
@@ -787,16 +790,16 @@ const IconUserOutline16 = ({ size = 16, className }) => jsxs("svg", {
787
790
  fill: "currentColor"
788
791
  })]
789
792
  });
790
- /** ic_ds_send_outline_16 */
791
- const IconSendOutline16 = ({ size = 16, className }) => jsx("svg", {
793
+ /** ic_ds_paper_plane_outline_14 */
794
+ const IconPaperPlaneOutline14 = ({ size = 14, className }) => jsx("svg", {
792
795
  width: size,
793
796
  height: size,
794
797
  className,
795
- viewBox: "0 0 16 16",
798
+ viewBox: "0 0 14 14",
796
799
  fill: "none",
797
800
  xmlns: "http://www.w3.org/2000/svg",
798
801
  children: jsx("path", {
799
- d: "M8.3125 0.981587C8.66767 1.0545 8.97902 1.20558 9.2627 1.43374C9.48724 1.61438 9.73029 1.85933 9.97949 2.10854L14.707 6.83608L13.293 8.25014L9 3.95717V15.0431H7V3.95717L2.70703 8.25014L1.29297 6.83608L6.02051 2.10854C6.26971 1.85933 6.51277 1.61438 6.7373 1.43374C6.97662 1.24126 7.28445 1.04542 7.6875 0.981587C7.8973 0.94841 8.1031 0.956564 8.3125 0.981587Z",
802
+ d: "M11.8249 1.11733C12.4401 0.929305 13.0795 1.42149 13.0238 2.08968L12.2321 11.5935C12.1751 12.2735 11.4312 12.6646 10.8386 12.3263L7.99539 10.7011L6.0967 12.7005L6.07413 12.7248L6.04808 12.7465C5.55601 13.1565 4.80867 12.8069 4.80833 12.1665V8.69211C4.80843 8.44469 4.90686 8.20727 5.08181 8.0323L9.79684 3.31641L2.46775 6.6988L4.10251 7.35253L3.67364 8.42559L1.57874 7.5878V7.5852C0.807611 7.2988 0.757207 6.21577 1.5145 5.86622L11.7025 1.16421L11.8249 1.11733ZM5.96474 11.1603L6.9614 10.1107L5.96474 9.54118V11.1603ZM6.32937 8.41864L11.1086 11.149L11.791 2.95698L6.32937 8.41864Z",
800
803
  fill: "currentColor"
801
804
  })
802
805
  });
@@ -1428,6 +1431,29 @@ const IconQuestionOutline14 = ({ size = 14, className }) => jsxs("svg", {
1428
1431
  })
1429
1432
  ]
1430
1433
  });
1434
+ /** Info companion to the question outline: the same ring with an "i" glyph. */
1435
+ const IconInfoOutline14 = ({ size = 14, className }) => jsxs("svg", {
1436
+ width: size,
1437
+ height: size,
1438
+ className,
1439
+ viewBox: "0 0 14 14",
1440
+ fill: "none",
1441
+ xmlns: "http://www.w3.org/2000/svg",
1442
+ children: [
1443
+ jsx("path", {
1444
+ d: "M12.5757 7.00012C12.5757 3.92085 10.0794 1.42463 7.00012 1.42456C3.9208 1.42456 1.42456 3.9208 1.42456 7.00012C1.42463 10.0794 3.92085 12.5757 7.00012 12.5757C10.0793 12.5756 12.5756 10.0793 12.5757 7.00012ZM13.8002 7.00012C13.8001 10.7559 10.7559 13.8001 7.00012 13.8002C3.2443 13.8002 0.199291 10.7559 0.199219 7.00012C0.199219 3.24426 3.24426 0.199219 7.00012 0.199219C10.7559 0.199291 13.8002 3.2443 13.8002 7.00012Z",
1445
+ fill: "currentColor"
1446
+ }),
1447
+ jsx("path", {
1448
+ d: "M7.6127 3.18921V4.55986H6.38735V3.18921H7.6127Z",
1449
+ fill: "currentColor"
1450
+ }),
1451
+ jsx("path", {
1452
+ d: "M7.6127 5.68921V10.8109H6.38735V5.68921H7.6127Z",
1453
+ fill: "currentColor"
1454
+ })
1455
+ ]
1456
+ });
1431
1457
  /** Alarm clock outline for active scheduled-task indicators. */
1432
1458
  const IconAlarmClockOutline16 = ({ size = 16, className }) => jsxs("svg", {
1433
1459
  "aria-hidden": "true",
@@ -1498,6 +1524,140 @@ const IconArchiveOutline20 = ({ size = 20, className }) => jsxs("svg", {
1498
1524
  fill: "currentColor"
1499
1525
  })]
1500
1526
  });
1527
+ /** Line-wrapping toggle glyph. */
1528
+ const IconWrapLinesOutline16 = ({ size = 16, className }) => jsx("svg", {
1529
+ width: size,
1530
+ height: size,
1531
+ className,
1532
+ viewBox: "0 0 16 16",
1533
+ fill: "none",
1534
+ stroke: "currentColor",
1535
+ strokeWidth: "1.5",
1536
+ strokeLinejoin: "round",
1537
+ "aria-hidden": "true",
1538
+ children: jsx("path", { d: "M1.5 3.5h13M1.5 7.5h10.25a2.5 2.5 0 0 1 0 5H8m2-2-2 2 2 2M1.5 11.5h3" })
1539
+ });
1540
+ /**
1541
+ * Plan row glyph of the composer menu (#3567): a document with three ruled
1542
+ * lines and a pencil over its corner. Drawn on a 14 grid whose content spans
1543
+ * 0.5..13.5, so at the menu's 16px render it fills the same extent as the
1544
+ * 16-grid glyphs beside it.
1545
+ */
1546
+ const IconPlanOutline14 = ({ size = 14, className }) => jsxs("svg", {
1547
+ width: size,
1548
+ height: size,
1549
+ className,
1550
+ viewBox: "0 0 14 14",
1551
+ fill: "none",
1552
+ xmlns: "http://www.w3.org/2000/svg",
1553
+ children: [
1554
+ jsx("path", {
1555
+ d: "M9.56143 3.14672V4.24774H3.94716V3.14672H9.56143Z",
1556
+ fill: "currentColor"
1557
+ }),
1558
+ jsx("path", {
1559
+ d: "M9.56143 5.44201V6.54304H3.94716V5.44201H9.56143Z",
1560
+ fill: "currentColor"
1561
+ }),
1562
+ jsx("path", {
1563
+ d: "M7.97328 7.73731V8.83833H3.94716V7.73731H7.97328Z",
1564
+ fill: "currentColor"
1565
+ }),
1566
+ jsx("path", {
1567
+ d: "M8.02789 0.500001C8.82106 0.500001 9.46528 0.499108 9.97973 0.553797C10.5054 0.609708 10.9678 0.729732 11.3714 1.01734C11.6057 1.18436 11.8125 1.38729 11.9827 1.61716C12.2758 2.01317 12.3982 2.46699 12.4552 2.98269C12.5109 3.48743 12.51 4.11964 12.51 4.89782V5.48599L11.2928 6.72061V4.89782C11.2928 4.09333 11.2923 3.53731 11.2453 3.1118C11.1995 2.69759 11.1162 2.47711 10.9986 2.31831C10.9034 2.18976 10.7879 2.07638 10.6568 1.98298C10.495 1.86762 10.2704 1.7858 9.84814 1.7409C9.41445 1.69482 8.84789 1.69427 8.02789 1.69427H5.4821C4.66215 1.69427 4.09555 1.69485 3.66184 1.7409C3.23978 1.78576 3.01501 1.86773 2.85315 1.98298C2.72221 2.07632 2.60657 2.18986 2.51139 2.31831C2.39384 2.4771 2.31045 2.69763 2.26467 3.1118C2.21772 3.53731 2.21716 4.09333 2.21716 4.89782V8.91011C2.21716 9.71498 2.21771 10.2714 2.26467 10.697C2.3104 11.111 2.39397 11.3308 2.51139 11.4896C2.60659 11.6182 2.72214 11.7315 2.85315 11.825C3.01505 11.9404 3.23938 12.023 3.66184 12.0679C4.09555 12.114 4.66212 12.1146 5.4821 12.1146H5.97554L4.80224 13.3034C4.3108 13.3002 3.88905 13.2923 3.53026 13.2541C3.00445 13.1982 2.5423 13.0784 2.13857 12.7906C1.90424 12.6235 1.6975 12.4216 1.52725 12.1917C1.23396 11.7955 1.1118 11.3412 1.05483 10.8252C0.999149 10.3205 1 9.68831 1 8.91011V4.89782C1 4.11964 0.999094 3.48743 1.05483 2.98269C1.11182 2.46701 1.23416 2.01316 1.52725 1.61716C1.69745 1.3874 1.90437 1.1843 2.13857 1.01734C2.54218 0.729872 3.00468 0.609682 3.53026 0.553797C4.04472 0.499135 4.68896 0.500001 5.4821 0.500001H8.02789Z",
1568
+ fill: "currentColor"
1569
+ }),
1570
+ jsx("path", {
1571
+ d: "M12.6413 13.2999H8.82536L10.0608 12.1056H12.6413V13.2999Z",
1572
+ fill: "currentColor"
1573
+ }),
1574
+ jsx("path", {
1575
+ d: "M7.22216 11.8899L6.53753 13.2335C6.4571 13.3913 6.62775 13.5587 6.78861 13.4798L8.15787 12.8081L13 8.08806L12.0643 7.16994L7.22216 11.8899Z",
1576
+ fill: "currentColor"
1577
+ })
1578
+ ]
1579
+ });
1580
+ /**
1581
+ * Compact row glyph of the composer menu: the composer's context-usage ring
1582
+ * (ContextMeter) frozen at its resting look — a quiet track with one filled
1583
+ * quarter arc. A restyle of the live ring revisits this copy.
1584
+ */
1585
+ const IconCompactOutline16 = ({ size = 16, className }) => jsxs("svg", {
1586
+ width: size,
1587
+ height: size,
1588
+ className,
1589
+ viewBox: "0 0 16 16",
1590
+ fill: "none",
1591
+ xmlns: "http://www.w3.org/2000/svg",
1592
+ children: [jsx("circle", {
1593
+ cx: "8",
1594
+ cy: "8",
1595
+ r: "6.4",
1596
+ stroke: "currentColor",
1597
+ strokeWidth: "1.6",
1598
+ opacity: "0.35"
1599
+ }), jsx("path", {
1600
+ d: "M8 1.6A6.4 6.4 0 0 1 14.4 8",
1601
+ stroke: "currentColor",
1602
+ strokeWidth: "1.6",
1603
+ strokeLinecap: "round"
1604
+ })]
1605
+ });
1606
+ /**
1607
+ * The permission shield contour on the 16 grid (design set 1556), stroked at
1608
+ * {@link SHIELD_OUTLINE_STROKE}. The composer's permission selector composes
1609
+ * its mode marks (check, pencil, exclamation) over this same path inside one
1610
+ * svg, so the geometry lives here once.
1611
+ */
1612
+ const SHIELD_OUTLINE_PATH = "M8.20554 0.899994L14.7901 3.36857V7.01026C14.7901 12 11.0466 14.2103 8.20554 15.3C5.36446 14.2103 1.62012 12 1.62012 7.01026V3.36857L8.20554 0.899994Z";
1613
+ /** Stroke width of {@link SHIELD_OUTLINE_PATH}. */
1614
+ const SHIELD_OUTLINE_STROKE = "1.31831";
1615
+ /** Permission row glyph of the composer menu: the shield contour alone, without a mode mark. */
1616
+ const IconShieldOutline16 = ({ size = 16, className }) => jsx("svg", {
1617
+ width: size,
1618
+ height: size,
1619
+ className,
1620
+ viewBox: "0 0 16 16",
1621
+ fill: "none",
1622
+ xmlns: "http://www.w3.org/2000/svg",
1623
+ children: jsx("path", {
1624
+ d: SHIELD_OUTLINE_PATH,
1625
+ stroke: "currentColor",
1626
+ strokeWidth: SHIELD_OUTLINE_STROKE,
1627
+ strokeLinejoin: "round"
1628
+ })
1629
+ });
1630
+ /** Plugin pinwheel (design handoff for the plugin manager): four interleaved arcs on the 16 grid. */
1631
+ const IconPluginPinwheelOutline16 = ({ size = 16, className }) => jsxs("svg", {
1632
+ width: size,
1633
+ height: size,
1634
+ className,
1635
+ viewBox: "0 0 16 16",
1636
+ fill: "none",
1637
+ xmlns: "http://www.w3.org/2000/svg",
1638
+ children: [
1639
+ jsx("path", {
1640
+ d: "M7.84457 5.06199C11.6605 4.93876 14.7962 6.14848 14.8484 7.76397C14.8875 8.97461 13.1838 10.0696 10.7215 10.5942",
1641
+ stroke: "currentColor",
1642
+ strokeWidth: "1.2"
1643
+ }),
1644
+ jsx("path", {
1645
+ d: "M5.12742 8.07731C5.00419 4.26138 6.21391 1.12568 7.8294 1.07351C9.04004 1.03441 10.135 2.73808 10.6596 5.20037",
1646
+ stroke: "currentColor",
1647
+ strokeWidth: "1.2"
1648
+ }),
1649
+ jsx("path", {
1650
+ d: "M8.02457 10.6802C4.20865 10.8034 1.07294 9.5937 1.02077 7.97821C0.981678 6.76758 2.68535 5.67262 5.14763 5.14798",
1651
+ stroke: "currentColor",
1652
+ strokeWidth: "1.2"
1653
+ }),
1654
+ jsx("path", {
1655
+ d: "M10.7476 7.89535C10.8708 11.7113 9.66109 14.847 8.0456 14.8991C6.83496 14.9382 5.74 13.2346 5.21536 10.7723",
1656
+ stroke: "currentColor",
1657
+ strokeWidth: "1.2"
1658
+ })
1659
+ ]
1660
+ });
1501
1661
  //#endregion
1502
1662
  //#region lib/types/DisclosureRow.js
1503
1663
  /**
@@ -1639,6 +1799,33 @@ function Switch({ checked, onChange, label, disabled = false, title, className }
1639
1799
  });
1640
1800
  }
1641
1801
  //#endregion
1802
+ //#region lib/types/Checkbox.js
1803
+ /** Controlled native checkbox with a caller-owned visible and accessible label. */
1804
+ /**
1805
+ * Render a labeled checkbox with native keyboard and form semantics.
1806
+ * @param props.checked - current checked state.
1807
+ * @param props.onChange - receives the requested checked state.
1808
+ * @param props.label - localized visible and accessible label.
1809
+ * @param props.disabled - whether the control refuses changes.
1810
+ * @param props.title - optional localized hover text.
1811
+ * @param props.className - extra class for the label's placement.
1812
+ * @returns the label containing its checkbox.
1813
+ */
1814
+ function Checkbox({ checked, onChange, label, disabled = false, title, className }) {
1815
+ return jsxs("label", {
1816
+ className: clsx(css$6.checkbox, className),
1817
+ title,
1818
+ children: [jsx("input", {
1819
+ type: "checkbox",
1820
+ checked,
1821
+ disabled,
1822
+ onChange: (event) => {
1823
+ onChange(event.target.checked);
1824
+ }
1825
+ }), jsx("span", { children: label })]
1826
+ });
1827
+ }
1828
+ //#endregion
1642
1829
  //#region lib/types/Input.js
1643
1830
  /**
1644
1831
  * Render a text input with an optional leading icon.
@@ -1647,12 +1834,12 @@ function Switch({ checked, onChange, label, disabled = false, title, className }
1647
1834
  */
1648
1835
  function Input({ icon, className, ...rest }) {
1649
1836
  return jsxs("span", {
1650
- className: clsx(css$6.wrap, className),
1837
+ className: clsx(css$7.wrap, className),
1651
1838
  children: [icon != null && jsx("span", {
1652
- className: css$6.icon,
1839
+ className: css$7.icon,
1653
1840
  children: icon
1654
1841
  }), jsx("input", {
1655
- className: css$6.input,
1842
+ className: css$7.input,
1656
1843
  ...rest
1657
1844
  })]
1658
1845
  });
@@ -1701,8 +1888,13 @@ const MEASURE_STYLE = {
1701
1888
  top: 0
1702
1889
  };
1703
1890
  /**
1704
- * Render an anchored dropdown menu.
1705
- * @param props.autoFocus - focus the first item on open and enable arrow-key navigation; Escape focuses the anchor's first button.
1891
+ * Render an anchored dropdown menu. While the list is open its keys mirror the
1892
+ * composer's: Tab settles the focused row — from the trigger, Tab enters the
1893
+ * list instead — and Escape or Shift+Tab close it and return focus to the
1894
+ * anchor's first button, and selecting a row does the same — the rows unmount
1895
+ * with the list. Only a keyboard on the trigger or inside the list is
1896
+ * intercepted; Tab presses elsewhere on the page stay the browser's.
1897
+ * @param props.autoFocus - focus the first item on open; the arrow keys walk the list either way.
1706
1898
  * @param props.open - whether the list is showing (owner-controlled).
1707
1899
  * @param props.anchor - the trigger element (rendered in place).
1708
1900
  * @param props.items - selectable rows and optional separators.
@@ -1741,6 +1933,44 @@ const MEASURE_STYLE = {
1741
1933
  function Menu({ open, anchor, items, selectedId, selectedIds, onSelect, onClose, align = "start", side = "bottom", portal = false, closeOnPointerLeave = false, dense = false, compact = false, autoFocus = false, selection = "check", getAnchorRect, footer, className }) {
1742
1934
  const rootRef = useRef(null);
1743
1935
  const listRef = useRef(null);
1936
+ /** Index the arrow walk last focused, the resume point when focus left the rows. */
1937
+ const walkIndex = useRef(null);
1938
+ /**
1939
+ * The control that had the keyboard when this menu opened — its own trigger,
1940
+ * which an anchor that wraps several controls (a split button) would not be
1941
+ * able to name by position.
1942
+ */
1943
+ const triggerRef = useRef(null);
1944
+ /**
1945
+ * Hand the keyboard back to the trigger that opened the menu — or, when the
1946
+ * anchor never held it, to the anchor's first button. Focus left on a removed
1947
+ * row otherwise falls to the page body, where the next Tab restarts from the
1948
+ * top of the page.
1949
+ */
1950
+ const refocusAnchor = () => {
1951
+ const trigger = triggerRef.current;
1952
+ if (trigger !== null && document.contains(trigger) && !trigger.disabled) {
1953
+ trigger.focus();
1954
+ return;
1955
+ }
1956
+ rootRef.current?.querySelector("button:not(:disabled)")?.focus();
1957
+ };
1958
+ /**
1959
+ * Post-selection focus, for the paths where the rows unmount with the list.
1960
+ * A selection whose owner keeps the menu open is left alone, and so is an
1961
+ * owner that moved focus itself (a presented file card hands it to its
1962
+ * preview button): only a keyboard left on the closing list (or on the body
1963
+ * its removal produced) comes back to the trigger.
1964
+ */
1965
+ const refocusAfterSelection = () => {
1966
+ queueMicrotask(() => {
1967
+ if (openRef.current) return;
1968
+ const active = document.activeElement;
1969
+ if (active === null || active === document.body || listRef.current?.contains(active) === true) refocusAnchor();
1970
+ });
1971
+ };
1972
+ const openRef = useRef(open);
1973
+ openRef.current = open;
1744
1974
  const [openSubmenuId, setOpenSubmenuId] = useState(null);
1745
1975
  const [fixedPos, setFixedPos] = useState(null);
1746
1976
  const { arm: armClose, cancel: cancelClose } = usePointerGrace(onClose);
@@ -1796,11 +2026,23 @@ function Menu({ open, anchor, items, selectedId, selectedIds, onSelect, onClose,
1796
2026
  getAnchorRect
1797
2027
  ]);
1798
2028
  useEffect(() => {
1799
- if (open && autoFocus) listRef.current?.querySelector("button:not(:disabled)")?.focus();
2029
+ if (!open) {
2030
+ triggerRef.current = null;
2031
+ return;
2032
+ }
2033
+ const active = document.activeElement;
2034
+ triggerRef.current = active instanceof HTMLElement && rootRef.current?.contains(active) === true ? active : null;
2035
+ }, [open]);
2036
+ useEffect(() => {
2037
+ if (!open || !autoFocus) return;
2038
+ const first = listRef.current?.querySelector("button:not(:disabled)");
2039
+ walkIndex.current = first === void 0 || first === null ? null : 0;
2040
+ first?.focus();
1800
2041
  }, [open, autoFocus]);
1801
2042
  useEffect(() => {
1802
2043
  if (!open) {
1803
2044
  setOpenSubmenuId(null);
2045
+ walkIndex.current = null;
1804
2046
  return;
1805
2047
  }
1806
2048
  const onPointerDown = (e) => {
@@ -1810,21 +2052,52 @@ function Menu({ open, anchor, items, selectedId, selectedIds, onSelect, onClose,
1810
2052
  onClose();
1811
2053
  };
1812
2054
  const onKeyDown = (e) => {
2055
+ const focused = document.activeElement;
2056
+ const insideList = listRef.current?.contains(focused) === true;
2057
+ const anchored = rootRef.current?.contains(focused) === true || insideList;
1813
2058
  if (e.key === "Escape") {
1814
2059
  onClose();
1815
- if (autoFocus) rootRef.current?.querySelector("button")?.focus();
2060
+ if (anchored || autoFocus) refocusAnchor();
2061
+ }
2062
+ if (e.key === "Tab") {
2063
+ const list = listRef.current;
2064
+ if (list === null || !anchored) return;
2065
+ if (e.shiftKey) {
2066
+ e.preventDefault();
2067
+ onClose();
2068
+ refocusAnchor();
2069
+ return;
2070
+ }
2071
+ if (insideList) {
2072
+ if (focused instanceof Element && focused.getAttribute("role") === "menuitem") {
2073
+ e.preventDefault();
2074
+ focused.click();
2075
+ }
2076
+ return;
2077
+ }
2078
+ const row = list.querySelector("button:not(:disabled)");
2079
+ if (row === null) return;
2080
+ e.preventDefault();
2081
+ row.focus();
2082
+ walkIndex.current = 0;
2083
+ return;
1816
2084
  }
1817
- if (!autoFocus || ![
2085
+ if (![
1818
2086
  "ArrowDown",
1819
2087
  "ArrowUp",
1820
2088
  "Home",
1821
2089
  "End"
1822
2090
  ].includes(e.key)) return;
1823
- const buttons = Array.from(listRef.current?.querySelectorAll("button:not(:disabled)") ?? []);
1824
- const index = buttons.indexOf(document.activeElement);
1825
- if (index < 0) return;
2091
+ const list = listRef.current;
2092
+ if (list === null || !anchored) return;
2093
+ const buttons = Array.from(list.querySelectorAll("button:not(:disabled)"));
2094
+ if (buttons.length === 0) return;
2095
+ const index = buttons.indexOf(focused);
2096
+ const from = index >= 0 ? index : walkIndex.current;
2097
+ const next = e.key === "Home" ? 0 : e.key === "End" ? buttons.length - 1 : from === null ? e.key === "ArrowDown" ? 0 : buttons.length - 1 : (from + (e.key === "ArrowDown" ? 1 : -1) + buttons.length) % buttons.length;
1826
2098
  e.preventDefault();
1827
- buttons[e.key === "Home" ? 0 : e.key === "End" ? buttons.length - 1 : (index + (e.key === "ArrowDown" ? 1 : -1) + buttons.length) % buttons.length]?.focus();
2099
+ walkIndex.current = next;
2100
+ buttons[next]?.focus();
1828
2101
  };
1829
2102
  const onWindowBlur = () => {
1830
2103
  if (document.activeElement instanceof HTMLIFrameElement) onClose();
@@ -1848,11 +2121,11 @@ function Menu({ open, anchor, items, selectedId, selectedIds, onSelect, onClose,
1848
2121
  const scrollable = !items.some((entry) => !isSeparator(entry) && !isLabel(entry) && entry.submenu !== void 0 && entry.submenu.length > 0);
1849
2122
  const renderEntry = (entry) => {
1850
2123
  if (isSeparator(entry)) return jsx("div", {
1851
- className: css$7.separator,
2124
+ className: css$8.separator,
1852
2125
  role: "separator"
1853
2126
  }, entry.id);
1854
2127
  if (isLabel(entry)) return jsx("div", {
1855
- className: css$7.label,
2128
+ className: css$8.label,
1856
2129
  role: "presentation",
1857
2130
  children: entry.text
1858
2131
  }, entry.id);
@@ -1860,7 +2133,7 @@ function Menu({ open, anchor, items, selectedId, selectedIds, onSelect, onClose,
1860
2133
  const subOpen = hasSub && openSubmenuId === entry.id;
1861
2134
  const selected = entry.id === selectedId || selectedIds?.includes(entry.id) === true;
1862
2135
  return jsxs("div", {
1863
- className: css$7.itemWrap,
2136
+ className: css$8.itemWrap,
1864
2137
  onMouseEnter: () => {
1865
2138
  setOpenSubmenuId(hasSub ? entry.id : null);
1866
2139
  },
@@ -1870,7 +2143,7 @@ function Menu({ open, anchor, items, selectedId, selectedIds, onSelect, onClose,
1870
2143
  children: [jsxs("button", {
1871
2144
  type: "button",
1872
2145
  role: "menuitem",
1873
- className: clsx(css$7.item, selected && (selection === "fill" ? css$7.selectedFill : css$7.selected), entry.danger === true && css$7.danger),
2146
+ className: clsx(css$8.item, selected && (selection === "fill" ? css$8.selectedFill : css$8.selected), entry.danger === true && css$8.danger),
1874
2147
  disabled: entry.disabled,
1875
2148
  "aria-haspopup": hasSub ? "menu" : void 0,
1876
2149
  "aria-expanded": hasSub ? subOpen : void 0,
@@ -1883,34 +2156,36 @@ function Menu({ open, anchor, items, selectedId, selectedIds, onSelect, onClose,
1883
2156
  return;
1884
2157
  }
1885
2158
  onSelect(entry.id);
2159
+ refocusAfterSelection();
1886
2160
  },
1887
2161
  children: [
1888
2162
  entry.icon !== void 0 && jsx("span", {
1889
- className: css$7.itemIcon,
2163
+ className: css$8.itemIcon,
1890
2164
  children: entry.icon
1891
2165
  }),
1892
2166
  jsx("span", {
1893
- className: css$7.itemLabel,
2167
+ className: css$8.itemLabel,
1894
2168
  children: entry.label
1895
2169
  }),
1896
- selected && selection === "check" && jsx(IconCheckOutline16, { className: css$7.check })
2170
+ selected && selection === "check" && jsx(IconCheckOutline16, { className: css$8.check })
1897
2171
  ]
1898
2172
  }), subOpen && entry.submenu !== void 0 && jsx("div", {
1899
- className: clsx(css$7.submenu, compact && css$7.compactList),
2173
+ className: clsx(css$8.submenu, compact && css$8.compactList),
1900
2174
  role: "menu",
1901
2175
  children: entry.submenu.map((sub) => jsxs("button", {
1902
2176
  type: "button",
1903
2177
  role: "menuitem",
1904
- className: css$7.item,
2178
+ className: css$8.item,
1905
2179
  disabled: sub.disabled,
1906
2180
  onClick: () => {
1907
2181
  onSelect(sub.id);
2182
+ refocusAfterSelection();
1908
2183
  },
1909
2184
  children: [sub.icon !== void 0 && jsx("span", {
1910
- className: css$7.itemIcon,
2185
+ className: css$8.itemIcon,
1911
2186
  children: sub.icon
1912
2187
  }), jsx("span", {
1913
- className: css$7.itemLabel,
2188
+ className: css$8.itemLabel,
1914
2189
  children: sub.label
1915
2190
  })]
1916
2191
  }, sub.id))
@@ -1919,25 +2194,25 @@ function Menu({ open, anchor, items, selectedId, selectedIds, onSelect, onClose,
1919
2194
  };
1920
2195
  const list = open && jsxs("div", {
1921
2196
  ref: listRef,
1922
- className: clsx(css$7.list, dense && css$7.denseList, compact && css$7.compactList, scrollable && css$7.scrollable, portal && css$7.portal, side === "top" && !portal && css$7.sideTop, align === "end" && !portal && css$7.alignEnd),
2197
+ className: clsx(css$8.list, dense && css$8.denseList, compact && css$8.compactList, scrollable && css$8.scrollable, portal && css$8.portal, side === "top" && !portal && css$8.sideTop, align === "end" && !portal && css$8.alignEnd),
1923
2198
  style: portal ? fixedPos ?? MEASURE_STYLE : void 0,
1924
2199
  role: "menu",
1925
2200
  onClick: (e) => {
1926
2201
  e.stopPropagation();
1927
2202
  },
1928
2203
  children: [jsx("div", {
1929
- className: css$7.viewport,
2204
+ className: css$8.viewport,
1930
2205
  role: "presentation",
1931
2206
  children: items.map(renderEntry)
1932
2207
  }), footer !== void 0 && footer.length > 0 && jsx("div", {
1933
- className: css$7.footer,
2208
+ className: css$8.footer,
1934
2209
  role: "presentation",
1935
2210
  children: footer.map(renderEntry)
1936
2211
  })]
1937
2212
  });
1938
2213
  return jsxs("span", {
1939
2214
  ref: rootRef,
1940
- className: clsx(css$7.root, className),
2215
+ className: clsx(css$8.root, className),
1941
2216
  onPointerEnter: closeOnPointerLeave ? cancelClose : void 0,
1942
2217
  onPointerLeave: closeOnPointerLeave ? () => {
1943
2218
  if (open) armClose();
@@ -2237,7 +2512,7 @@ function HoverCard({ anchor, content, openDelayMs = 500, disabled = false, copyT
2237
2512
  const copyable = copyText !== void 0;
2238
2513
  const card = open && pos !== null && jsx("div", {
2239
2514
  ref: cardRef,
2240
- className: `${css$8.card}${copyable ? ` ${css$8.copyable}` : ""}${copied ? ` ${css$8.feedback}` : ""}`,
2515
+ className: `${css$9.card}${copyable ? ` ${css$9.copyable}` : ""}${copied ? ` ${css$9.feedback}` : ""}`,
2241
2516
  style: {
2242
2517
  ...pos,
2243
2518
  minHeight: copied && copyHeightRef.current !== null ? copyHeightRef.current : void 0
@@ -2258,14 +2533,14 @@ function HoverCard({ anchor, content, openDelayMs = 500, disabled = false, copyT
2258
2533
  copy(copyText);
2259
2534
  } : void 0,
2260
2535
  children: copied ? jsx("span", {
2261
- className: css$8.copied,
2536
+ className: css$9.copied,
2262
2537
  "aria-hidden": "true",
2263
2538
  children: copiedLabel
2264
2539
  }) : content
2265
2540
  });
2266
2541
  return jsxs("span", {
2267
2542
  ref: rootRef,
2268
- className: css$8.root,
2543
+ className: css$9.root,
2269
2544
  onPointerEnter: () => {
2270
2545
  if (disabled) return;
2271
2546
  cancelClose();
@@ -2288,7 +2563,7 @@ function HoverCard({ anchor, content, openDelayMs = 500, disabled = false, copyT
2288
2563
  children: [
2289
2564
  anchor,
2290
2565
  open && copyable && jsx("span", {
2291
- className: css$8.status,
2566
+ className: css$9.status,
2292
2567
  role: "status",
2293
2568
  children: copied ? copiedLabel : ""
2294
2569
  }),
@@ -2301,7 +2576,8 @@ function HoverCard({ anchor, content, openDelayMs = 500, disabled = false, copyT
2301
2576
  /**
2302
2577
  * Render a centered, body-portaled modal over a blurred page mask.
2303
2578
  * @param props.open - whether the dialog is showing.
2304
- * @param props.onClose - Escape or mask click.
2579
+ * @param props.onClose - Escape or mask click; while a menu is open inside the
2580
+ * dialog, Escape belongs to that menu first.
2305
2581
  * @param props.title - dialog heading (aria-label in every mode).
2306
2582
  * @param props.closeLabel - localized accessible close-button label.
2307
2583
  * @param props.description - optional supporting sentence under the title.
@@ -2325,44 +2601,44 @@ function Modal({ open, onClose, title, closeLabel, description, children, footer
2325
2601
  }, [open, onClose]);
2326
2602
  if (!open) return null;
2327
2603
  return createPortal(jsxs("div", {
2328
- className: css$9.root,
2604
+ className: css$10.root,
2329
2605
  role: "presentation",
2330
2606
  children: [jsx("div", {
2331
- className: css$9.mask,
2607
+ className: css$10.mask,
2332
2608
  "aria-hidden": "true",
2333
2609
  onClick: onClose
2334
2610
  }), jsx("div", {
2335
- className: clsx(css$9.dialog, className),
2611
+ className: clsx(css$10.dialog, className),
2336
2612
  role: "dialog",
2337
2613
  "aria-modal": "true",
2338
2614
  "aria-label": title,
2339
2615
  children: headless ? children : jsxs(Fragment, { children: [jsxs("div", {
2340
- className: clsx(css$9.content, contentClassName),
2616
+ className: clsx(css$10.content, contentClassName),
2341
2617
  children: [
2342
2618
  jsxs("div", {
2343
- className: css$9.header,
2619
+ className: css$10.header,
2344
2620
  children: [jsx("h2", {
2345
- className: css$9.title,
2621
+ className: css$10.title,
2346
2622
  children: title
2347
2623
  }), jsx("button", {
2348
2624
  type: "button",
2349
- className: css$9.close,
2625
+ className: css$10.close,
2350
2626
  "aria-label": closeLabel,
2351
2627
  onClick: onClose,
2352
2628
  children: jsx(IconCloseOutline16, { size: 14 })
2353
2629
  })]
2354
2630
  }),
2355
2631
  description !== void 0 && description !== "" && jsx("p", {
2356
- className: css$9.description,
2632
+ className: css$10.description,
2357
2633
  children: description
2358
2634
  }),
2359
2635
  children !== void 0 && jsx("div", {
2360
- className: css$9.body,
2636
+ className: css$10.body,
2361
2637
  children
2362
2638
  })
2363
2639
  ]
2364
2640
  }), footer !== void 0 && jsx("div", {
2365
- className: css$9.footer,
2641
+ className: css$10.footer,
2366
2642
  children: footer
2367
2643
  })] })
2368
2644
  })]
@@ -2386,13 +2662,13 @@ function OnboardingSurface({ children }) {
2386
2662
  };
2387
2663
  }, []);
2388
2664
  return createPortal(jsxs("div", {
2389
- className: css$10.onboardingOverlay,
2665
+ className: css$11.onboardingOverlay,
2390
2666
  role: "presentation",
2391
2667
  children: [jsx("div", {
2392
- className: css$10.onboardingMask,
2668
+ className: css$11.onboardingMask,
2393
2669
  "aria-hidden": "true"
2394
2670
  }), jsx("div", {
2395
- className: css$10.onboardingStage,
2671
+ className: css$11.onboardingStage,
2396
2672
  children
2397
2673
  })]
2398
2674
  }), document.body);
@@ -2413,28 +2689,28 @@ function RiskConfirmation({ open, title, description, acknowledgeLabel, cancelLa
2413
2689
  onClose: onCancel,
2414
2690
  title,
2415
2691
  closeLabel,
2416
- className: css$11.confirmation ?? "",
2417
- contentClassName: css$11.confirmationContent ?? "",
2692
+ className: css$12.confirmation ?? "",
2693
+ contentClassName: css$12.confirmationContent ?? "",
2418
2694
  footer: jsxs(Fragment, { children: [jsx(Button, {
2419
2695
  variant: "outline",
2420
- className: css$11.modalAction,
2696
+ className: css$12.modalAction,
2421
2697
  onClick: onCancel,
2422
2698
  children: cancelLabel
2423
2699
  }), jsx(Button, {
2424
2700
  variant: "primary",
2425
- className: css$11.confirmAction,
2701
+ className: css$12.confirmAction,
2426
2702
  disabled: disabled || !acknowledged,
2427
2703
  onClick: onConfirm,
2428
2704
  children: confirmLabel
2429
2705
  })] }),
2430
2706
  children: [jsxs("div", {
2431
- className: css$11.warning,
2707
+ className: css$12.warning,
2432
2708
  children: [jsx(IconWarningOutline16, {
2433
2709
  size: 18,
2434
- className: css$11.warningIcon
2710
+ className: css$12.warningIcon
2435
2711
  }), jsx("p", { children: description })]
2436
2712
  }), jsxs("label", {
2437
- className: css$11.acknowledgement,
2713
+ className: css$12.acknowledgement,
2438
2714
  children: [jsx("input", {
2439
2715
  type: "checkbox",
2440
2716
  checked: acknowledged,
@@ -2449,11 +2725,15 @@ function RiskConfirmation({ open, title, description, acknowledgeLabel, cancelLa
2449
2725
  }
2450
2726
  //#endregion
2451
2727
  //#region lib/types/ConnectionIndicator.js
2728
+ /** Exit-transition length; keep equal to the `.leaving` transition duration in the stylesheet. */
2729
+ const EXIT_MS = 150;
2452
2730
  /**
2453
- * Render an inline connection-recovery control.
2731
+ * Render an inline connection-recovery control. The outage and retry-attempt
2732
+ * states are one button whose static label already names the retry action;
2733
+ * clicking it requests an immediate reconnect. The indicator animates in on
2734
+ * appearance and fades out for {@link EXIT_MS} before unmounting.
2454
2735
  * @param props.state - visible outage, retry-attempt, or recovered state.
2455
- * @param props.disconnectedLabel - localized outage text.
2456
- * @param props.reconnectLabel - localized action text shown on hover or focus.
2736
+ * @param props.disconnectedLabel - localized outage text naming the retry action.
2457
2737
  * @param props.connectingLabel - localized retry text followed by the attempt dots.
2458
2738
  * @param props.recoveredLabel - localized recovery confirmation.
2459
2739
  * @param props.reconnectActionLabel - accessible label for the outage action.
@@ -2461,87 +2741,68 @@ function RiskConfirmation({ open, title, description, acknowledgeLabel, cancelLa
2461
2741
  * @param props.onReconnect - request an immediate reconnect attempt.
2462
2742
  * @returns the indicator, or null when no connection feedback is active.
2463
2743
  */
2464
- function ConnectionIndicator({ state, disconnectedLabel, reconnectLabel, connectingLabel, recoveredLabel, reconnectActionLabel, restartActionLabel, onReconnect }) {
2465
- if (state === void 0) return null;
2466
- const sizeLabels = jsxs(Fragment, { children: [
2467
- jsx("span", {
2468
- className: css$12.sizeLabel,
2469
- "aria-hidden": "true",
2470
- children: disconnectedLabel
2471
- }),
2472
- jsx("span", {
2473
- className: css$12.sizeLabel,
2474
- "aria-hidden": "true",
2475
- children: reconnectLabel
2476
- }),
2477
- jsxs("span", {
2478
- className: css$12.sizeLabel,
2479
- "aria-hidden": "true",
2480
- children: [connectingLabel, jsx("span", {
2481
- className: css$12.dots,
2482
- children: "..."
2483
- })]
2484
- }),
2485
- jsx("span", {
2486
- className: css$12.sizeLabel,
2487
- "aria-hidden": "true",
2488
- children: recoveredLabel
2489
- })
2490
- ] });
2491
- if (state === "recovered") return jsxs("div", {
2492
- className: `${css$12.indicator} ${css$12.success}`,
2744
+ function ConnectionIndicator({ state, disconnectedLabel, connectingLabel, recoveredLabel, reconnectActionLabel, restartActionLabel, onReconnect }) {
2745
+ const [rendered, setRendered] = useState(state);
2746
+ const leaving = state === void 0 && rendered !== void 0;
2747
+ useEffect(() => {
2748
+ if (state !== void 0) {
2749
+ setRendered(state);
2750
+ return;
2751
+ }
2752
+ if (rendered === void 0) return;
2753
+ const timeout = window.setTimeout(() => {
2754
+ setRendered(void 0);
2755
+ }, EXIT_MS);
2756
+ return () => {
2757
+ window.clearTimeout(timeout);
2758
+ };
2759
+ }, [state, rendered]);
2760
+ if (rendered === void 0) return null;
2761
+ const leavingClass = leaving ? ` ${css$13.leaving}` : "";
2762
+ if (rendered === "recovered") return jsxs("div", {
2763
+ className: `${css$13.indicator} ${css$13.success}${leavingClass}`,
2493
2764
  role: "status",
2494
2765
  "aria-label": recoveredLabel,
2495
2766
  children: [jsx("span", {
2496
- className: css$12.icon,
2767
+ className: css$13.icon,
2497
2768
  "aria-hidden": "true",
2498
2769
  children: jsx(IconCheckOutline16, { size: 14 })
2499
- }), jsxs("span", {
2500
- className: css$12.label,
2501
- children: [sizeLabels, jsx("span", {
2502
- className: css$12.stateLabel,
2503
- children: recoveredLabel
2504
- })]
2770
+ }), jsx("span", {
2771
+ className: css$13.label,
2772
+ children: recoveredLabel
2505
2773
  })]
2506
2774
  });
2507
- const connecting = state === "connecting";
2775
+ const connecting = rendered === "connecting";
2508
2776
  return jsxs("button", {
2509
2777
  type: "button",
2510
- className: `${css$12.indicator} ${css$12.warning}`,
2511
- "data-phase": state,
2778
+ className: `${css$13.indicator} ${css$13.warning}${leavingClass}`,
2779
+ "data-phase": rendered,
2512
2780
  "aria-label": connecting ? restartActionLabel : reconnectActionLabel,
2513
2781
  onClick: onReconnect,
2514
2782
  children: [jsx("span", {
2515
- className: css$12.icon,
2783
+ className: css$13.icon,
2516
2784
  "aria-hidden": "true",
2517
- children: jsx(IconWarningOutline16, { size: 14 })
2518
- }), jsxs("span", {
2519
- className: css$12.label,
2520
- children: [
2521
- sizeLabels,
2522
- jsx("span", {
2523
- className: css$12.stateLabel,
2524
- children: connecting ? jsxs(Fragment, { children: [connectingLabel, jsxs("span", {
2525
- className: css$12.dots,
2526
- "aria-hidden": "true",
2527
- children: [
2528
- jsx("span", { children: "." }),
2529
- jsx("span", {
2530
- className: css$12.secondDot,
2531
- children: "."
2532
- }),
2533
- jsx("span", {
2534
- className: css$12.thirdDot,
2535
- children: "."
2536
- })
2537
- ]
2538
- })] }) : disconnectedLabel
2539
- }),
2540
- jsx("span", {
2541
- className: css$12.hoverLabel,
2542
- children: reconnectLabel
2543
- })
2544
- ]
2785
+ children: connecting ? jsx(IconLoadingOutline16, {
2786
+ size: 14,
2787
+ className: css$13.spinner
2788
+ }) : jsx(IconRefreshOutline14, { size: 14 })
2789
+ }), jsx("span", {
2790
+ className: css$13.label,
2791
+ children: connecting ? jsxs(Fragment, { children: [connectingLabel, jsxs("span", {
2792
+ className: css$13.dots,
2793
+ "aria-hidden": "true",
2794
+ children: [
2795
+ jsx("span", { children: "." }),
2796
+ jsx("span", {
2797
+ className: css$13.secondDot,
2798
+ children: "."
2799
+ }),
2800
+ jsx("span", {
2801
+ className: css$13.thirdDot,
2802
+ children: "."
2803
+ })
2804
+ ]
2805
+ })] }) : disconnectedLabel
2545
2806
  })]
2546
2807
  });
2547
2808
  }
@@ -3378,7 +3639,103 @@ function FileTypeIcon(props) {
3378
3639
  type: resolvedType,
3379
3640
  size,
3380
3641
  className
3381
- }) : glyph(resolvedType, size, clsx(css$13.icon, css$13[resolvedType], className));
3642
+ }) : glyph(resolvedType, size, clsx(css$14.icon, css$14[resolvedType], className));
3643
+ }
3644
+ //#endregion
3645
+ //#region lib/types/SiteGlyph.js
3646
+ /**
3647
+ * Host suffix to site mark, covering the developer sites the transcript
3648
+ * usually cites plus the mainstream search, video, social, shopping, and
3649
+ * reference sites a general audience links. A host matches a suffix when it
3650
+ * equals it or is a subdomain of it, and the longest matching suffix wins, so
3651
+ * `weixin.qq.com` keeps WeChat while `qq.com` keeps QQ and `gist.github.com`
3652
+ * needs no entry of its own.
3653
+ */
3654
+ const SITE_HOSTS = {
3655
+ "github.com": siGithub,
3656
+ "github.io": siGithub,
3657
+ "raw.githubusercontent.com": siGithub,
3658
+ "gitlab.com": siGitlab,
3659
+ "npmjs.com": siNpm,
3660
+ "pypi.org": siPypi,
3661
+ "stackoverflow.com": siStackoverflow,
3662
+ "developer.mozilla.org": siMdnwebdocs,
3663
+ "wikipedia.org": siWikipedia,
3664
+ "news.ycombinator.com": siYcombinator,
3665
+ "youtube.com": siYoutube,
3666
+ "youtu.be": siYoutube,
3667
+ "x.com": siX,
3668
+ "twitter.com": siX,
3669
+ "bilibili.com": siBilibili,
3670
+ "zhihu.com": siZhihu,
3671
+ "juejin.cn": siJuejin,
3672
+ "csdn.net": siCsdn,
3673
+ "google.com": siGoogle,
3674
+ "baidu.com": siBaidu,
3675
+ "duckduckgo.com": siDuckduckgo,
3676
+ "tiktok.com": siTiktok,
3677
+ "netflix.com": siNetflix,
3678
+ "spotify.com": siSpotify,
3679
+ "facebook.com": siFacebook,
3680
+ "instagram.com": siInstagram,
3681
+ "reddit.com": siReddit,
3682
+ "telegram.org": siTelegram,
3683
+ "t.me": siTelegram,
3684
+ "weixin.qq.com": siWechat,
3685
+ "qq.com": siQq,
3686
+ "whatsapp.com": siWhatsapp,
3687
+ "wa.me": siWhatsapp,
3688
+ "weibo.com": siSinaweibo,
3689
+ "taobao.com": siTaobao,
3690
+ "aliexpress.com": siAliexpress,
3691
+ "ebay.com": siEbay,
3692
+ "quora.com": siQuora,
3693
+ "v2ex.com": siV2ex,
3694
+ "apple.com": siApple
3695
+ };
3696
+ /**
3697
+ * Resolve the mark named by an external destination.
3698
+ * @param href - The link destination; only an absolute http(s) URL can name a host.
3699
+ * @returns The matched site mark, or undefined when the host is unknown or not http(s).
3700
+ */
3701
+ function siteIcon(href) {
3702
+ let host;
3703
+ try {
3704
+ const url = new URL(href);
3705
+ if (url.protocol !== "http:" && url.protocol !== "https:") return void 0;
3706
+ host = url.hostname.toLowerCase();
3707
+ } catch {
3708
+ return;
3709
+ }
3710
+ let match;
3711
+ let matched = 0;
3712
+ for (const [suffix, icon] of Object.entries(SITE_HOSTS)) if ((host === suffix || host.endsWith(`.${suffix}`)) && suffix.length > matched) {
3713
+ match = icon;
3714
+ matched = suffix.length;
3715
+ }
3716
+ return match;
3717
+ }
3718
+ /**
3719
+ * Render the site mark for a known external destination.
3720
+ * @param props - The destination and the icon sizing seat.
3721
+ * @returns The site's mark riding currentColor, or undefined for an unknown site.
3722
+ */
3723
+ function siteGlyph({ href, size, className }) {
3724
+ const icon = href === void 0 ? void 0 : siteIcon(href);
3725
+ if (icon === void 0) return void 0;
3726
+ return jsx("svg", {
3727
+ width: size,
3728
+ height: size,
3729
+ className,
3730
+ viewBox: "-2 -2 28 28",
3731
+ fill: "none",
3732
+ xmlns: "http://www.w3.org/2000/svg",
3733
+ "aria-hidden": true,
3734
+ children: jsx("path", {
3735
+ d: icon.path,
3736
+ fill: "currentColor"
3737
+ })
3738
+ });
3382
3739
  }
3383
3740
  //#endregion
3384
3741
  //#region lib/types/LinkIcon.js
@@ -3525,13 +3882,18 @@ function assertNever$1(value) {
3525
3882
  }
3526
3883
  /**
3527
3884
  * Render the leading glyph for one clickable artifact link.
3528
- * @param props - The link category, optional size (default 14px — the inline
3529
- * link text size these glyphs sit beside), and optional CSS class.
3885
+ * @param props - The link category, the optional destination that can select a
3886
+ * site mark, optional size (default 14px — the inline link text size these
3887
+ * glyphs sit beside), and optional CSS class.
3530
3888
  * @returns The category's SVG glyph, riding currentColor.
3531
3889
  */
3532
- function LinkIcon({ kind, size = 14, className }) {
3890
+ function LinkIcon({ kind, href, size = 14, className }) {
3533
3891
  switch (kind) {
3534
- case "url": return jsx(GlobeGlyph, {
3892
+ case "url": return siteGlyph({
3893
+ href,
3894
+ size,
3895
+ className
3896
+ }) ?? jsx(GlobeGlyph, {
3535
3897
  size,
3536
3898
  className
3537
3899
  });
@@ -3573,9 +3935,10 @@ const TRAILING_PUNCTUATION_RE = /[.,;:!?,。;:!?]+$/u;
3573
3935
  * host loaded for this message, or the command a command bubble echoes
3574
3936
  * (unsent queue rows pass none).
3575
3937
  * @param slashKind - the chip kind those tokens render as.
3938
+ * @param references - optional file and skill preview actions; session and command tokens stay labels.
3576
3939
  * @returns inline nodes covering the whole text.
3577
3940
  */
3578
- function projectUserText(text, sessionLabels, slashNames = [], slashKind = "skill") {
3941
+ function projectUserText(text, sessionLabels, slashNames = [], slashKind = "skill", references) {
3579
3942
  const ranges = [];
3580
3943
  SESSION_WIRE_RE.lastIndex = 0;
3581
3944
  let wire;
@@ -3620,7 +3983,7 @@ function projectUserText(text, sessionLabels, slashNames = [], slashKind = "skil
3620
3983
  let cursor = 0;
3621
3984
  const pushPlain = (from, to) => {
3622
3985
  parts.push(jsx("span", {
3623
- className: css$14.plainRun,
3986
+ className: css$15.plainRun,
3624
3987
  children: text.slice(from, to)
3625
3988
  }, `t${from}`));
3626
3989
  };
@@ -3628,22 +3991,39 @@ function projectUserText(text, sessionLabels, slashNames = [], slashKind = "skil
3628
3991
  if (range.start < cursor) continue;
3629
3992
  const { start: tokenStart, end, label, kind } = range;
3630
3993
  if (tokenStart > cursor) pushPlain(cursor, tokenStart);
3631
- const referenceKind = kind === "session" ? "session" : label.startsWith("@") ? label.endsWith("/") ? "folder" : "file" : void 0;
3994
+ const referenceKind = kind === "session" ? "session" : label.startsWith("@") ? label.replace(/^@"|"$/gu, "").endsWith("/") ? "folder" : "file" : void 0;
3632
3995
  const displayLabel = range.display ?? (referenceKind === void 0 ? label : referenceKind === "session" ? label.slice(1) : label.slice(1).replace(/^"|"$/gu, "").split(/[\\/]/u).filter(Boolean).at(-1) ?? label.slice(1));
3633
- parts.push(jsxs("span", {
3634
- className: clsx(css$14.refChip, referenceKind === void 0 && css$14.slashChip),
3996
+ const contents = jsxs(Fragment, { children: [referenceKind !== void 0 && jsx(ReferenceIcon, {
3997
+ kind: referenceKind,
3998
+ size: 16,
3999
+ className: css$15.refIcon
4000
+ }), displayLabel] });
4001
+ const open = references === void 0 ? void 0 : referenceKind === "file" ? () => {
4002
+ references.openFile(label.slice(1).replace(/^"|"$/gu, ""));
4003
+ } : referenceKind === void 0 && slashKind === "skill" ? () => {
4004
+ references.openSkill(label.slice(1));
4005
+ } : void 0;
4006
+ const className = clsx(css$15.refChip, referenceKind === void 0 && css$15.slashChip);
4007
+ parts.push(open === void 0 ? jsx("span", {
4008
+ className,
4009
+ "data-ref-chip": referenceKind ?? slashKind,
4010
+ title: label,
4011
+ children: contents
4012
+ }, tokenStart) : jsx("button", {
4013
+ type: "button",
4014
+ className: clsx(className, markdownCss.fileMention),
3635
4015
  "data-ref-chip": referenceKind ?? slashKind,
3636
4016
  title: label,
3637
- children: [referenceKind !== void 0 && jsx(ReferenceIcon, {
3638
- kind: referenceKind,
3639
- size: 16,
3640
- className: css$14.refIcon
3641
- }), displayLabel]
4017
+ onClick: (event) => {
4018
+ if (event.detail > 1 || event.detail !== 0 && event.currentTarget.ownerDocument.getSelection()?.isCollapsed === false) return;
4019
+ open();
4020
+ },
4021
+ children: contents
3642
4022
  }, tokenStart));
3643
4023
  cursor = end;
3644
4024
  }
3645
4025
  if (parts.length === 0) return jsx("span", {
3646
- className: css$14.plainRun,
4026
+ className: css$15.plainRun,
3647
4027
  children: text
3648
4028
  });
3649
4029
  if (cursor < text.length) pushPlain(cursor, text.length);
@@ -3652,6 +4032,12 @@ function projectUserText(text, sessionLabels, slashNames = [], slashKind = "skil
3652
4032
  //#endregion
3653
4033
  //#region lib/types/Tooltip.js
3654
4034
  /**
4035
+ * Suppression channel from a tooltip to the tooltips above it: a tooltip hands
4036
+ * this setter to its own descendants, and a visible descendant bubble calls it
4037
+ * so the ancestor withdraws its bubble for as long as the descendant shows one.
4038
+ */
4039
+ const TooltipSuppression = createContext(null);
4040
+ /**
3655
4041
  * Attach a hover/focus tooltip to an anchor element.
3656
4042
  * @param props.label - bubble text, or a resolver evaluated only while the bubble is visible.
3657
4043
  * @param props.side - placement relative to the anchor (default 'right').
@@ -3711,6 +4097,18 @@ function Tooltip({ label, side = "right", delayMs = 0, disabled = false, maxWidt
3711
4097
  hover: false,
3712
4098
  focus: false
3713
4099
  });
4100
+ const suppressAncestors = useContext(TooltipSuppression);
4101
+ const [suppressed, setSuppressed] = useState(false);
4102
+ const announce = useCallback((active) => {
4103
+ suppressAncestors?.(active);
4104
+ }, [suppressAncestors]);
4105
+ const visible = pos !== null && !disabled;
4106
+ useEffect(() => {
4107
+ announce(visible);
4108
+ return () => {
4109
+ announce(false);
4110
+ };
4111
+ }, [announce, visible]);
3714
4112
  const cancelShow = useCallback(() => {
3715
4113
  if (showTimer.current === null) return;
3716
4114
  clearTimeout(showTimer.current);
@@ -3739,6 +4137,7 @@ function Tooltip({ label, side = "right", delayMs = 0, disabled = false, maxWidt
3739
4137
  top: r.top,
3740
4138
  bottom: r.bottom
3741
4139
  });
4140
+ announce(true);
3742
4141
  };
3743
4142
  const showAfterHoverDelay = () => {
3744
4143
  cancelShow();
@@ -3751,46 +4150,53 @@ function Tooltip({ label, side = "right", delayMs = 0, disabled = false, maxWidt
3751
4150
  show();
3752
4151
  }, delayMs);
3753
4152
  };
4153
+ const withdraw = () => {
4154
+ setPos(null);
4155
+ announce(false);
4156
+ };
3754
4157
  const hide = () => {
3755
4158
  cancelShow();
3756
- if (!triggers.current.hover && !triggers.current.focus) setPos(null);
4159
+ if (!triggers.current.hover && !triggers.current.focus) withdraw();
3757
4160
  };
3758
- return jsxs(Fragment, { children: [cloneElement(children, {
3759
- ref: mergedRef,
3760
- onMouseEnter: (e) => {
3761
- children.props.onMouseEnter?.(e);
3762
- triggers.current.hover = true;
3763
- showAfterHoverDelay();
3764
- },
3765
- onMouseLeave: (e) => {
3766
- children.props.onMouseLeave?.(e);
3767
- triggers.current.hover = false;
3768
- cancelShow();
3769
- setPos(null);
3770
- },
3771
- onFocus: (e) => {
3772
- children.props.onFocus?.(e);
3773
- triggers.current.focus = true;
3774
- cancelShow();
3775
- show();
3776
- },
3777
- onBlur: (e) => {
3778
- children.props.onBlur?.(e);
3779
- triggers.current.focus = false;
3780
- hide();
3781
- }
3782
- }), pos !== null && jsx("span", {
3783
- ref: bubble,
3784
- className: css$15.bubble,
3785
- "data-side": placement,
3786
- style: {
3787
- left: pos.x,
3788
- top: y,
3789
- ...maxWidth === void 0 ? {} : { maxWidth }
3790
- },
3791
- role: "tooltip",
3792
- children: resolvedLabel
3793
- })] });
4161
+ return jsxs(TooltipSuppression.Provider, {
4162
+ value: setSuppressed,
4163
+ children: [cloneElement(children, {
4164
+ ref: mergedRef,
4165
+ onMouseEnter: (e) => {
4166
+ children.props.onMouseEnter?.(e);
4167
+ triggers.current.hover = true;
4168
+ showAfterHoverDelay();
4169
+ },
4170
+ onMouseLeave: (e) => {
4171
+ children.props.onMouseLeave?.(e);
4172
+ triggers.current.hover = false;
4173
+ cancelShow();
4174
+ withdraw();
4175
+ },
4176
+ onFocus: (e) => {
4177
+ children.props.onFocus?.(e);
4178
+ triggers.current.focus = true;
4179
+ cancelShow();
4180
+ show();
4181
+ },
4182
+ onBlur: (e) => {
4183
+ children.props.onBlur?.(e);
4184
+ triggers.current.focus = false;
4185
+ hide();
4186
+ }
4187
+ }), visible && !suppressed && jsx("span", {
4188
+ ref: bubble,
4189
+ className: css$16.bubble,
4190
+ "data-side": placement,
4191
+ style: {
4192
+ left: pos.x,
4193
+ top: y,
4194
+ ...maxWidth === void 0 ? {} : { maxWidth }
4195
+ },
4196
+ role: "tooltip",
4197
+ children: resolvedLabel
4198
+ })]
4199
+ });
3794
4200
  }
3795
4201
  //#endregion
3796
4202
  //#region lib/types/Toast.js
@@ -3842,18 +4248,18 @@ function Toast({ text, icon, anchor, holdMs = HOLD_MS, onDone }) {
3842
4248
  };
3843
4249
  }, [anchor]);
3844
4250
  return createPortal(jsxs("div", {
3845
- className: css$16.toast,
4251
+ className: css$17.toast,
3846
4252
  role: "alert",
3847
4253
  style: {
3848
4254
  ...left === null ? {} : { left },
3849
4255
  "--dsh-toast-hold": `${String(holdMs)}ms`
3850
4256
  },
3851
4257
  children: [icon !== void 0 && jsx("span", {
3852
- className: css$16.icon,
4258
+ className: css$17.icon,
3853
4259
  "aria-hidden": true,
3854
4260
  children: icon
3855
4261
  }), jsx("span", {
3856
- className: css$16.text,
4262
+ className: css$17.text,
3857
4263
  children: text
3858
4264
  })]
3859
4265
  }), document.body);
@@ -3925,9 +4331,10 @@ function relativeTime(at, now) {
3925
4331
  //#region lib/types/rank-by-name.js
3926
4332
  /**
3927
4333
  * Shared ranking for `/` menu candidates: the query must be a
3928
- * case-insensitive ordered subsequence of the candidate name. Prefix hits
3929
- * rank first, then the strongest alignment score, then the source order of
3930
- * the input. Decision record:
4334
+ * case-insensitive ordered subsequence of the candidate name or, when the
4335
+ * candidate carries one, of its display label (a localized title). Prefix
4336
+ * hits rank first, then the strongest alignment score over either key, then
4337
+ * the source order of the input. Decision record:
3931
4338
  * .agents/notes/archived/feature/2026-08-04-web-slash-command-fuzzy-discovery.md
3932
4339
  */
3933
4340
  /** Extra weight for name starts and separator boundaries. */
@@ -3969,7 +4376,9 @@ function alignmentScore(name, query) {
3969
4376
  }
3970
4377
  /**
3971
4378
  * Rank named items by a menu query.
3972
- * @param items - candidates in source order (a host catalog, then client contributions).
4379
+ * @param items - candidates in source order (a host catalog, then client
4380
+ * contributions); an item's optional `label` is a second search key beside
4381
+ * its name.
3973
4382
  * @param rawQuery - the text typed after the trigger, matched case-insensitively.
3974
4383
  * @returns the matching items: prefix hits first, then by alignment score,
3975
4384
  * then in source order. The input list itself for an empty query.
@@ -3979,12 +4388,20 @@ function rankByName(items, rawQuery) {
3979
4388
  if (query === "") return items;
3980
4389
  const ranked = [];
3981
4390
  items.forEach((item, index) => {
3982
- const name = item.name.toLowerCase();
3983
- const score = alignmentScore(name, query);
4391
+ const keys = item.label === void 0 ? [item.name] : [item.name, item.label];
4392
+ let prefix = false;
4393
+ let score;
4394
+ for (const key of keys) {
4395
+ const lower = key.toLowerCase();
4396
+ const keyScore = alignmentScore(lower, query);
4397
+ if (keyScore === void 0) continue;
4398
+ prefix ||= lower.startsWith(query);
4399
+ score = score === void 0 ? keyScore : Math.max(score, keyScore);
4400
+ }
3984
4401
  if (score !== void 0) ranked.push({
3985
4402
  item,
3986
4403
  index,
3987
- prefix: name.startsWith(query),
4404
+ prefix,
3988
4405
  score
3989
4406
  });
3990
4407
  });
@@ -3992,6 +4409,18 @@ function rankByName(items, rawQuery) {
3992
4409
  return ranked.map((match) => match.item);
3993
4410
  }
3994
4411
  //#endregion
4412
+ //#region lib/types/darwin-desktop.js
4413
+ /** macOS desktop detection for hiddenInset-titlebar layout variants. */
4414
+ /**
4415
+ * Whether the client runs in the macOS desktop shell: the Electron preload
4416
+ * marks `<html>` with `data-platform="darwin"`; plain web never sets it.
4417
+ * Read at render time — the mark may arrive as late as DOMContentLoaded.
4418
+ * @returns true only inside the macOS Electron shell.
4419
+ */
4420
+ function isDarwinDesktop() {
4421
+ return document.documentElement.dataset.platform === "darwin";
4422
+ }
4423
+ //#endregion
3995
4424
  //#region lib/types/JsonTree.js
3996
4425
  const OBJECT_PREVIEW_LIMIT = 4;
3997
4426
  const ARRAY_PREVIEW_LIMIT = 5;
@@ -4028,6 +4457,80 @@ function objectCopyMenuItems(labels) {
4028
4457
  }
4029
4458
  ];
4030
4459
  }
4460
+ /** Notify only the old and new row actions; JSON values do not subscribe to hover state. */
4461
+ function createCopyStore() {
4462
+ let current;
4463
+ const listeners = /* @__PURE__ */ new Map();
4464
+ return {
4465
+ get: () => current,
4466
+ set(next) {
4467
+ const previous = current?.id;
4468
+ current = next;
4469
+ for (const id of new Set([previous, next?.id])) {
4470
+ if (id === void 0) continue;
4471
+ for (const listener of listeners.get(id) ?? []) listener();
4472
+ }
4473
+ },
4474
+ subscribe(id, listener) {
4475
+ let row = listeners.get(id);
4476
+ if (row === void 0) listeners.set(id, row = /* @__PURE__ */ new Set());
4477
+ row.add(listener);
4478
+ return () => {
4479
+ row.delete(listener);
4480
+ if (row.size === 0) listeners.delete(id);
4481
+ };
4482
+ }
4483
+ };
4484
+ }
4485
+ function JsonCopyAction({ store, target, persistent, labels, onCopy, onClose }) {
4486
+ const id = pathId(target.path);
4487
+ const subscribe = useCallback((listener) => store.subscribe(id, listener), [id, store]);
4488
+ const getSnapshot = () => {
4489
+ const current = store.get();
4490
+ return current?.id === id ? current : void 0;
4491
+ };
4492
+ const snapshot = useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
4493
+ const buttonRef = useRef(null);
4494
+ const state = snapshot?.state ?? "idle";
4495
+ const object = typeof target.value === "object" && target.value !== null;
4496
+ const copyTitle = state === "copied" ? labels.copied : state === "failed" ? labels.copyFailed : object ? labels.copyPrettyJson : labels.copyValue;
4497
+ return jsx("span", {
4498
+ className: css$18.copySlot,
4499
+ children: (persistent || snapshot !== void 0) && jsx(Menu, {
4500
+ open: snapshot?.menuOpen === true,
4501
+ compact: true,
4502
+ portal: true,
4503
+ align: "end",
4504
+ anchor: jsx("button", {
4505
+ ref: buttonRef,
4506
+ type: "button",
4507
+ className: css$18.actionButton,
4508
+ "data-json-copy-button": true,
4509
+ "data-state": state,
4510
+ "aria-label": copyTitle,
4511
+ title: labels.copyButtonTitle(copyTitle),
4512
+ onClick: () => void onCopy(target, object ? "prettyJson" : "value"),
4513
+ onContextMenu: (event) => {
4514
+ event.preventDefault();
4515
+ event.stopPropagation();
4516
+ store.set({
4517
+ id,
4518
+ target,
4519
+ state,
4520
+ menuOpen: true
4521
+ });
4522
+ },
4523
+ children: state === "copied" ? jsx(IconCheckOutline16, { size: 12 }) : jsx(IconCopyOutline16, { size: 12 })
4524
+ }),
4525
+ items: object ? objectCopyMenuItems(labels) : valueCopyMenuItems(labels),
4526
+ onSelect: (mode) => {
4527
+ onCopy(target, mode);
4528
+ },
4529
+ onClose,
4530
+ getAnchorRect: () => buttonRef.current.getBoundingClientRect()
4531
+ })
4532
+ });
4533
+ }
4031
4534
  function isExpandableValue(value) {
4032
4535
  return typeof value === "object" && value !== null && !(value instanceof Date);
4033
4536
  }
@@ -4040,35 +4543,35 @@ function bracketOf(value) {
4040
4543
  }
4041
4544
  function previewPrimitive(value) {
4042
4545
  if (value === null) return jsx("span", {
4043
- className: css$17.keywordValue,
4546
+ className: css$18.keywordValue,
4044
4547
  children: "null"
4045
4548
  });
4046
4549
  if (typeof value === "string") return jsx("span", {
4047
- className: css$17.stringValue,
4550
+ className: css$18.stringValue,
4048
4551
  children: JSON.stringify(value)
4049
4552
  });
4050
4553
  if (typeof value === "number") return jsx("span", {
4051
- className: css$17.numberValue,
4554
+ className: css$18.numberValue,
4052
4555
  children: String(value)
4053
4556
  });
4054
4557
  if (typeof value === "boolean") return jsx("span", {
4055
- className: css$17.keywordValue,
4558
+ className: css$18.keywordValue,
4056
4559
  children: String(value)
4057
4560
  });
4058
4561
  if (typeof value === "bigint") return jsx("span", {
4059
- className: css$17.otherValue,
4562
+ className: css$18.otherValue,
4060
4563
  children: value.toString()
4061
4564
  });
4062
4565
  if (typeof value === "undefined") return jsx("span", {
4063
- className: css$17.otherValue,
4566
+ className: css$18.otherValue,
4064
4567
  children: "undefined"
4065
4568
  });
4066
4569
  if (typeof value === "symbol") return jsx("span", {
4067
- className: css$17.otherValue,
4570
+ className: css$18.otherValue,
4068
4571
  children: value.description ?? "Symbol"
4069
4572
  });
4070
4573
  if (typeof value === "function") return jsx("span", {
4071
- className: css$17.otherValue,
4574
+ className: css$18.otherValue,
4072
4575
  children: value.name || "Function"
4073
4576
  });
4074
4577
  return null;
@@ -4082,71 +4585,71 @@ function previewValue(value, depth) {
4082
4585
  const [open, close] = bracketOf(value);
4083
4586
  return jsxs(Fragment, { children: [
4084
4587
  jsx("span", {
4085
- className: css$17.punctuation,
4588
+ className: css$18.punctuation,
4086
4589
  children: open
4087
4590
  }),
4088
4591
  depth >= PREVIEW_DEPTH_LIMIT ? jsx("span", {
4089
- className: css$17.previewEllipsis,
4592
+ className: css$18.previewEllipsis,
4090
4593
  children: "…"
4091
4594
  }) : visible.map(([key, item], index) => jsxs("span", { children: [
4092
4595
  index > 0 && jsx("span", {
4093
- className: css$17.punctuation,
4596
+ className: css$18.punctuation,
4094
4597
  children: ", "
4095
4598
  }),
4096
4599
  !array && jsxs(Fragment, { children: [jsx("span", {
4097
- className: css$17.previewProperty,
4600
+ className: css$18.previewProperty,
4098
4601
  children: key
4099
4602
  }), jsx("span", {
4100
- className: css$17.punctuation,
4603
+ className: css$18.punctuation,
4101
4604
  children: ": "
4102
4605
  })] }),
4103
4606
  previewValue(item, depth + 1)
4104
4607
  ] }, key)),
4105
4608
  depth < PREVIEW_DEPTH_LIMIT && entries.length > limit && jsx("span", {
4106
- className: css$17.previewEllipsis,
4609
+ className: css$18.previewEllipsis,
4107
4610
  children: ", …"
4108
4611
  }),
4109
4612
  jsx("span", {
4110
- className: css$17.punctuation,
4613
+ className: css$18.punctuation,
4111
4614
  children: close
4112
4615
  })
4113
4616
  ] });
4114
4617
  }
4115
4618
  function primitiveValue(value) {
4116
4619
  if (value === null) return jsx("span", {
4117
- className: css$17.keywordValue,
4620
+ className: css$18.keywordValue,
4118
4621
  children: "null"
4119
4622
  });
4120
4623
  if (typeof value === "string") return jsx("span", {
4121
- className: css$17.stringValue,
4624
+ className: css$18.stringValue,
4122
4625
  children: JSON.stringify(value)
4123
4626
  });
4124
4627
  if (typeof value === "boolean") return jsx("span", {
4125
- className: css$17.keywordValue,
4628
+ className: css$18.keywordValue,
4126
4629
  children: String(value)
4127
4630
  });
4128
4631
  if (typeof value === "number") return jsx("span", {
4129
- className: css$17.numberValue,
4632
+ className: css$18.numberValue,
4130
4633
  children: String(value)
4131
4634
  });
4132
4635
  if (typeof value === "bigint") return jsx("span", {
4133
- className: css$17.numberValue,
4636
+ className: css$18.numberValue,
4134
4637
  children: `${value.toString()}n`
4135
4638
  });
4136
4639
  if (value instanceof Date) return jsx("span", {
4137
- className: css$17.otherValue,
4640
+ className: css$18.otherValue,
4138
4641
  children: value.toISOString()
4139
4642
  });
4140
4643
  if (typeof value === "function") return jsxs("span", {
4141
- className: css$17.otherValue,
4644
+ className: css$18.otherValue,
4142
4645
  children: ["function() ", "{ }"]
4143
4646
  });
4144
4647
  if (typeof value === "undefined") return jsx("span", {
4145
- className: css$17.otherValue,
4648
+ className: css$18.otherValue,
4146
4649
  children: "undefined"
4147
4650
  });
4148
4651
  return jsx("span", {
4149
- className: css$17.otherValue,
4652
+ className: css$18.otherValue,
4150
4653
  children: value.toString()
4151
4654
  });
4152
4655
  }
@@ -4174,12 +4677,177 @@ function moveFocus(button, direction) {
4174
4677
  function NodeField({ field, expandable, onToggle }) {
4175
4678
  if (field === void 0) return null;
4176
4679
  return jsxs("span", {
4177
- className: clsx(css$17.label, expandable && css$17.clickableLabel),
4680
+ className: clsx(css$18.label, expandable && css$18.clickableLabel),
4178
4681
  onClick: expandable ? onToggle : void 0,
4179
4682
  children: [fieldText(field), ":"]
4180
4683
  });
4181
4684
  }
4182
- function JsonTreeNode({ field, initialExpanded, labels, lastElement, onClaimTabStop, onRowHover, path, tabStopId, value }) {
4685
+ function JsonString({ collapsedStringLines, stringWrapping, field, labels, lastElement, renderCopy, value }) {
4686
+ const contentsId = useId();
4687
+ const contentRef = useRef(null);
4688
+ const rawRef = useRef(null);
4689
+ const [expanded, setExpanded] = useState(false);
4690
+ const [wrapped, setWrapped] = useState(false);
4691
+ const [truncated, setTruncated] = useState(false);
4692
+ useLayoutEffect(() => {
4693
+ if (expanded) return;
4694
+ const content = contentRef.current;
4695
+ const measure = () => {
4696
+ const lineHeight = Number.parseFloat(getComputedStyle(content).lineHeight);
4697
+ setTruncated(content.scrollHeight > lineHeight * collapsedStringLines);
4698
+ };
4699
+ measure();
4700
+ if (typeof ResizeObserver === "undefined") return;
4701
+ const observer = new ResizeObserver(measure);
4702
+ observer.observe(content);
4703
+ return () => {
4704
+ observer.disconnect();
4705
+ };
4706
+ }, [
4707
+ collapsedStringLines,
4708
+ expanded,
4709
+ field,
4710
+ lastElement,
4711
+ value
4712
+ ]);
4713
+ useLayoutEffect(() => {
4714
+ if (!expanded) return;
4715
+ const raw = rawRef.current;
4716
+ const clips = [];
4717
+ const tree = raw.closest(`.${css$18.root}`);
4718
+ for (let parent = tree.parentElement; parent !== null; parent = parent.parentElement) if (/auto|scroll|hidden|clip/.test(getComputedStyle(parent).overflowY)) clips.push(parent);
4719
+ const measure = () => {
4720
+ let top = 0;
4721
+ let bottom = window.innerHeight;
4722
+ for (const clip of clips) {
4723
+ const rect = clip.getBoundingClientRect();
4724
+ const style = getComputedStyle(clip);
4725
+ top = Math.max(top, rect.top + clip.clientTop);
4726
+ bottom = Math.min(bottom, rect.top + clip.clientTop + clip.clientHeight - Number.parseFloat(style.paddingBottom));
4727
+ }
4728
+ const available = bottom - Math.max(top, raw.getBoundingClientRect().top);
4729
+ raw.style.maxHeight = `${Math.max(16, available - 4)}px`;
4730
+ };
4731
+ measure();
4732
+ const observer = typeof ResizeObserver === "undefined" ? void 0 : new ResizeObserver(measure);
4733
+ observer?.observe(raw);
4734
+ for (const clip of clips) observer?.observe(clip);
4735
+ window.addEventListener("resize", measure);
4736
+ window.addEventListener("scroll", measure, true);
4737
+ return () => {
4738
+ observer?.disconnect();
4739
+ window.removeEventListener("resize", measure);
4740
+ window.removeEventListener("scroll", measure, true);
4741
+ };
4742
+ }, [expanded, value]);
4743
+ if (expanded) {
4744
+ const fieldId = `${contentsId}-field`;
4745
+ return jsxs("div", {
4746
+ className: css$18.stringField,
4747
+ "data-expanded": true,
4748
+ children: [
4749
+ field !== void 0 && jsxs("span", {
4750
+ id: fieldId,
4751
+ className: css$18.label,
4752
+ children: [fieldText(field), ":"]
4753
+ }),
4754
+ jsx("pre", {
4755
+ ref: rawRef,
4756
+ id: contentsId,
4757
+ className: css$18.stringRaw,
4758
+ "data-wrap": wrapped,
4759
+ tabIndex: 0,
4760
+ "aria-labelledby": field === void 0 ? void 0 : fieldId,
4761
+ children: value
4762
+ }),
4763
+ !lastElement && jsx("span", {
4764
+ className: css$18.punctuation,
4765
+ children: ","
4766
+ }),
4767
+ jsxs("div", {
4768
+ className: css$18.stringActions,
4769
+ children: [
4770
+ stringWrapping !== void 0 && jsx("button", {
4771
+ type: "button",
4772
+ className: css$18.actionButton,
4773
+ "aria-label": stringWrapping.label,
4774
+ title: stringWrapping.label,
4775
+ "aria-pressed": wrapped,
4776
+ "aria-controls": contentsId,
4777
+ onClick: () => {
4778
+ const next = !wrapped;
4779
+ setWrapped(next);
4780
+ stringWrapping.setDefault(next);
4781
+ },
4782
+ children: jsx(IconWrapLinesOutline16, { size: 12 })
4783
+ }),
4784
+ jsx("button", {
4785
+ type: "button",
4786
+ className: css$18.actionButton,
4787
+ "aria-label": labels.collapseNode,
4788
+ title: labels.collapseNode,
4789
+ "aria-expanded": true,
4790
+ "aria-controls": contentsId,
4791
+ onClick: () => {
4792
+ setExpanded(false);
4793
+ },
4794
+ children: jsx("svg", {
4795
+ width: "12",
4796
+ height: "12",
4797
+ viewBox: "0 0 16 16",
4798
+ fill: "none",
4799
+ stroke: "currentColor",
4800
+ strokeWidth: "1.5",
4801
+ "aria-hidden": "true",
4802
+ children: jsx("path", { d: "M9.5 1.5v5h5M1.5 9.5h5v5" })
4803
+ })
4804
+ }),
4805
+ renderCopy?.(true)
4806
+ ]
4807
+ })
4808
+ ]
4809
+ });
4810
+ }
4811
+ return jsxs(Fragment, { children: [renderCopy?.(), jsx("span", {
4812
+ className: css$18.stringField,
4813
+ "data-expanded": expanded,
4814
+ children: jsxs("span", {
4815
+ ref: contentRef,
4816
+ id: contentsId,
4817
+ className: css$18.stringText,
4818
+ children: [
4819
+ truncated && jsx("span", {
4820
+ className: css$18.stringToggleSlot,
4821
+ children: jsxs("button", {
4822
+ type: "button",
4823
+ className: css$18.stringToggle,
4824
+ "aria-label": labels.expandNode,
4825
+ "aria-expanded": false,
4826
+ "aria-controls": contentsId,
4827
+ onClick: () => {
4828
+ setWrapped(stringWrapping?.getDefault() ?? false);
4829
+ setExpanded(true);
4830
+ },
4831
+ children: [jsx("span", {
4832
+ "aria-hidden": "true",
4833
+ children: "…"
4834
+ }), labels.expandNode]
4835
+ })
4836
+ }),
4837
+ field !== void 0 && jsxs("span", {
4838
+ className: css$18.label,
4839
+ children: [fieldText(field), ":"]
4840
+ }),
4841
+ primitiveValue(value),
4842
+ !lastElement && jsx("span", {
4843
+ className: css$18.punctuation,
4844
+ children: ","
4845
+ })
4846
+ ]
4847
+ })
4848
+ })] });
4849
+ }
4850
+ function JsonTreeNode({ collapsedStringLines, stringWrapping, field, initialExpanded, labels, lastElement, onClaimTabStop, onRowHover, path, renderCopy, tabStopId, value }) {
4183
4851
  const contentsId = useId();
4184
4852
  const expanderRef = useRef(null);
4185
4853
  const [expanded, setExpanded] = useState(initialExpanded);
@@ -4202,8 +4870,8 @@ function JsonTreeNode({ field, initialExpanded, labels, lastElement, onClaimTabS
4202
4870
  moveFocus(event.currentTarget, event.key === "ArrowUp" ? -1 : 1);
4203
4871
  }
4204
4872
  };
4205
- const row = (children, ariaExpanded) => jsx("div", {
4206
- className: css$17.row,
4873
+ const row = (children, ariaExpanded) => jsxs("div", {
4874
+ className: css$18.row,
4207
4875
  role: "treeitem",
4208
4876
  "aria-expanded": ariaExpanded,
4209
4877
  onMouseOver: (event) => {
@@ -4213,8 +4881,23 @@ function JsonTreeNode({ field, initialExpanded, labels, lastElement, onClaimTabS
4213
4881
  value
4214
4882
  });
4215
4883
  },
4216
- children
4884
+ children: [typeof value !== "string" && renderCopy?.({
4885
+ path,
4886
+ value
4887
+ }), children]
4217
4888
  });
4889
+ if (typeof value === "string") return row(jsx(JsonString, {
4890
+ collapsedStringLines,
4891
+ stringWrapping,
4892
+ field,
4893
+ value,
4894
+ labels,
4895
+ lastElement,
4896
+ renderCopy: renderCopy === void 0 ? void 0 : (persistent) => renderCopy({
4897
+ path,
4898
+ value
4899
+ }, persistent)
4900
+ }));
4218
4901
  if (!container) return row(jsxs(Fragment, { children: [
4219
4902
  jsx(NodeField, {
4220
4903
  field,
@@ -4223,7 +4906,7 @@ function JsonTreeNode({ field, initialExpanded, labels, lastElement, onClaimTabS
4223
4906
  }),
4224
4907
  primitiveValue(value),
4225
4908
  !lastElement && jsx("span", {
4226
- className: css$17.punctuation,
4909
+ className: css$18.punctuation,
4227
4910
  children: ","
4228
4911
  })
4229
4912
  ] }));
@@ -4235,22 +4918,22 @@ function JsonTreeNode({ field, initialExpanded, labels, lastElement, onClaimTabS
4235
4918
  onToggle: toggle
4236
4919
  }),
4237
4920
  jsx("span", {
4238
- className: css$17.punctuation,
4921
+ className: css$18.punctuation,
4239
4922
  children: open
4240
4923
  }),
4241
4924
  jsx("span", {
4242
- className: css$17.punctuation,
4925
+ className: css$18.punctuation,
4243
4926
  children: close
4244
4927
  }),
4245
4928
  !lastElement && jsx("span", {
4246
- className: css$17.punctuation,
4929
+ className: css$18.punctuation,
4247
4930
  children: ","
4248
4931
  })
4249
4932
  ] }));
4250
4933
  return row(jsxs(Fragment, { children: [
4251
4934
  jsx("span", {
4252
4935
  ref: expanderRef,
4253
- className: clsx(css$17.expander, expanded ? css$17.collapseIcon : css$17.expandIcon),
4936
+ className: clsx(css$18.expander, expanded ? css$18.collapseIcon : css$18.expandIcon),
4254
4937
  "data-json-expander": true,
4255
4938
  role: "button",
4256
4939
  "aria-label": expanded ? labels.collapseNode : labels.expandNode,
@@ -4263,24 +4946,31 @@ function JsonTreeNode({ field, initialExpanded, labels, lastElement, onClaimTabS
4263
4946
  onClick: toggle,
4264
4947
  onKeyDown: onExpanderKeyDown
4265
4948
  }),
4266
- jsx(NodeField, {
4267
- field,
4268
- expandable: true,
4269
- onToggle: toggle
4270
- }),
4271
- jsx("span", {
4272
- className: css$17.preview,
4273
- children: previewValue(value, 0)
4274
- }),
4275
- !lastElement && jsx("span", {
4276
- className: css$17.punctuation,
4277
- children: ","
4949
+ jsxs("span", {
4950
+ className: css$18.summary,
4951
+ children: [
4952
+ jsx(NodeField, {
4953
+ field,
4954
+ expandable: true,
4955
+ onToggle: toggle
4956
+ }),
4957
+ jsx("span", {
4958
+ className: css$18.preview,
4959
+ children: previewValue(value, 0)
4960
+ }),
4961
+ !lastElement && jsx("span", {
4962
+ className: css$18.punctuation,
4963
+ children: ","
4964
+ })
4965
+ ]
4278
4966
  }),
4279
4967
  expanded && jsx("ul", {
4280
4968
  id: contentsId,
4281
4969
  role: "group",
4282
- className: css$17.children,
4970
+ className: css$18.children,
4283
4971
  children: entries.map(([key, item], index) => jsx(JsonTreeNode, {
4972
+ collapsedStringLines,
4973
+ stringWrapping,
4284
4974
  field: key,
4285
4975
  value: item,
4286
4976
  path: [...path, Array.isArray(value) ? index : key],
@@ -4289,7 +4979,8 @@ function JsonTreeNode({ field, initialExpanded, labels, lastElement, onClaimTabS
4289
4979
  initialExpanded: false,
4290
4980
  tabStopId,
4291
4981
  onClaimTabStop,
4292
- onRowHover
4982
+ onRowHover,
4983
+ renderCopy
4293
4984
  }, key))
4294
4985
  })
4295
4986
  ] }), expanded);
@@ -4316,19 +5007,15 @@ function copyText$2(target, mode) {
4316
5007
  * @param props - Parsed data, accessible label, and display options.
4317
5008
  * @returns A read-only JSON tree with an optionally fixed-open top level.
4318
5009
  */
4319
- function JsonTree({ data, label, className, copyable = true, expandTopLevel = true, labels }) {
5010
+ function JsonTree({ data, label, className, collapsedStringLines = 3, stringWrapping, copyable = true, expandTopLevel = true, labels }) {
4320
5011
  const rootEntries = entriesOf(data);
4321
5012
  const firstExpandableIndex = rootEntries.findIndex(([, value]) => isExpandableValue(value) && entriesOf(value).length > 0);
4322
5013
  const firstExpandableEntry = rootEntries[firstExpandableIndex];
4323
5014
  const initialTabStopId = expandTopLevel ? firstExpandableEntry === void 0 ? null : pathId([Array.isArray(data) ? firstExpandableIndex : firstExpandableEntry[0]]) : isExpandableValue(data) && rootEntries.length > 0 ? pathId([]) : null;
4324
- const rootRef = useRef(null);
4325
5015
  const activeRowRef = useRef();
4326
- const copyButtonRef = useRef(null);
4327
- const copyMenuOpenRef = useRef(false);
4328
5016
  const resetTimer = useRef();
4329
- const [copyTarget, setCopyTarget] = useState();
4330
- const [copyState, setCopyState] = useState("idle");
4331
- const [copyMenuOpen, setCopyMenuOpen] = useState(false);
5017
+ const copySequence = useRef(0);
5018
+ const [copyStore] = useState(createCopyStore);
4332
5019
  const [tabStopId, setTabStopId] = useState(initialTabStopId);
4333
5020
  const setActiveRow = (row) => {
4334
5021
  activeRowRef.current?.removeAttribute("data-json-copy-active");
@@ -4336,121 +5023,93 @@ function JsonTree({ data, label, className, copyable = true, expandTopLevel = tr
4336
5023
  row?.setAttribute("data-json-copy-active", "");
4337
5024
  };
4338
5025
  const clearCopyTarget = () => {
5026
+ copySequence.current += 1;
5027
+ if (resetTimer.current !== void 0) clearTimeout(resetTimer.current);
4339
5028
  setActiveRow(void 0);
4340
- setCopyTarget(void 0);
4341
- setCopyState("idle");
4342
- copyMenuOpenRef.current = false;
4343
- setCopyMenuOpen(false);
4344
- };
4345
- const copyPosition = (row) => {
4346
- const root = rootRef.current;
4347
- /* v8 ignore next -- row events and viewport listeners run only after the root ref mounts. */
4348
- if (root === null) throw new Error("JsonTree root is not mounted");
4349
- const rootRect = root.getBoundingClientRect();
4350
- const rowRect = row.getBoundingClientRect();
4351
- return {
4352
- left: rootRect.left + root.clientWidth - 26,
4353
- side: rowRect.top - rootRect.top > root.clientHeight / 2 ? "top" : "bottom",
4354
- top: rowRect.top
4355
- };
4356
- };
4357
- const positionCopyButton = (row, target) => {
4358
- const position = copyPosition(row);
4359
- setCopyTarget({
4360
- ...target,
4361
- ...position
4362
- });
4363
- };
4364
- const repositionCopyButton = (row) => {
4365
- const position = copyPosition(row);
4366
- setCopyTarget((current) => {
4367
- /* v8 ignore next -- an active row and its copy target are installed together. */
4368
- if (current === void 0) return current;
4369
- return {
4370
- ...current,
4371
- ...position
4372
- };
4373
- });
5029
+ copyStore.set(void 0);
4374
5030
  };
4375
5031
  useEffect(() => () => {
5032
+ copySequence.current += 1;
4376
5033
  if (resetTimer.current !== void 0) clearTimeout(resetTimer.current);
4377
5034
  activeRowRef.current?.removeAttribute("data-json-copy-active");
4378
5035
  }, []);
4379
5036
  useEffect(() => {
4380
- activeRowRef.current?.removeAttribute("data-json-copy-active");
4381
- activeRowRef.current = void 0;
4382
- copyMenuOpenRef.current = false;
4383
- setCopyTarget(void 0);
4384
- setCopyState("idle");
4385
- setCopyMenuOpen(false);
5037
+ clearCopyTarget();
4386
5038
  setTabStopId(initialTabStopId);
4387
5039
  }, [
4388
5040
  data,
4389
5041
  expandTopLevel,
4390
5042
  initialTabStopId
4391
5043
  ]);
4392
- useEffect(() => {
4393
- const reposition = () => {
4394
- const row = activeRowRef.current;
4395
- if (row !== void 0) repositionCopyButton(row);
4396
- };
4397
- window.addEventListener("scroll", reposition, true);
4398
- window.addEventListener("resize", reposition);
4399
- return () => {
4400
- window.removeEventListener("scroll", reposition, true);
4401
- window.removeEventListener("resize", reposition);
4402
- };
4403
- }, []);
4404
5044
  const handleRowHover = (row, target) => {
4405
- if (!copyable || copyMenuOpenRef.current) return;
5045
+ if (!copyable || copyStore.get()?.menuOpen) return;
4406
5046
  if (activeRowRef.current === row) return;
4407
5047
  setActiveRow(row);
4408
- setCopyState("idle");
4409
- copyMenuOpenRef.current = false;
4410
- setCopyMenuOpen(false);
4411
- positionCopyButton(row, target);
5048
+ copyStore.set({
5049
+ id: pathId(target.path),
5050
+ target,
5051
+ state: "idle",
5052
+ menuOpen: false
5053
+ });
4412
5054
  };
4413
5055
  const handleRootMouseOver = (event) => {
4414
- if (!copyable || copyMenuOpenRef.current) return;
5056
+ if (!copyable || copyStore.get()?.menuOpen) return;
4415
5057
  /* v8 ignore next -- browser mouse events delivered through React target an Element. */
4416
5058
  if (!(event.target instanceof Element)) return;
4417
5059
  if (event.target.closest("[data-json-copy-button]") === null) clearCopyTarget();
4418
5060
  };
4419
- const handleScroll = (_event) => {
4420
- const row = activeRowRef.current;
4421
- if (row !== void 0) repositionCopyButton(row);
4422
- };
4423
- const copy = async (mode) => {
4424
- /* v8 ignore next -- copy controls only render while their target exists. */
4425
- if (copyTarget === void 0) return;
5061
+ const copy = async (target, mode) => {
5062
+ const sequence = ++copySequence.current;
5063
+ const snapshot = {
5064
+ id: pathId(target.path),
5065
+ target,
5066
+ state: "idle",
5067
+ menuOpen: false
5068
+ };
5069
+ copyStore.set(snapshot);
5070
+ let state;
4426
5071
  try {
4427
- await navigator.clipboard.writeText(copyText$2(copyTarget, mode));
4428
- setCopyState("copied");
5072
+ await navigator.clipboard.writeText(copyText$2(target, mode));
5073
+ state = "copied";
4429
5074
  } catch {
4430
- setCopyState("failed");
5075
+ state = "failed";
4431
5076
  }
5077
+ const current = copyStore.get();
5078
+ if (sequence !== copySequence.current || current?.target !== target) return;
5079
+ copyStore.set({
5080
+ ...current,
5081
+ state
5082
+ });
4432
5083
  if (resetTimer.current !== void 0) clearTimeout(resetTimer.current);
4433
5084
  resetTimer.current = setTimeout(() => {
4434
- setCopyState("idle");
5085
+ const current = copyStore.get();
5086
+ if (current?.target === target) copyStore.set({
5087
+ ...current,
5088
+ state: "idle"
5089
+ });
4435
5090
  }, 1500);
4436
5091
  };
4437
5092
  const [rootOpen, rootClose] = bracketOf(data);
4438
- const copyTargetIsObject = typeof copyTarget?.value === "object" && copyTarget.value !== null;
4439
- const defaultCopyMode = copyTargetIsObject ? "prettyJson" : "value";
4440
- const copyTitle = copyState === "copied" ? labels.copied : copyState === "failed" ? labels.copyFailed : copyTargetIsObject ? labels.copyPrettyJson : labels.copyValue;
4441
- return jsxs("div", {
4442
- ref: rootRef,
4443
- className: clsx(css$17.root, className),
5093
+ const renderCopy = copyable ? (target, persistent = false) => jsx(JsonCopyAction, {
5094
+ store: copyStore,
5095
+ target,
5096
+ persistent,
5097
+ labels,
5098
+ onCopy: copy,
5099
+ onClose: clearCopyTarget
5100
+ }) : void 0;
5101
+ return jsx("div", {
5102
+ className: clsx(css$18.root, className),
5103
+ style: { "--json-tree-collapsed-lines": collapsedStringLines },
4444
5104
  onMouseOver: handleRootMouseOver,
4445
5105
  onMouseLeave: () => {
4446
- if (!copyMenuOpenRef.current) clearCopyTarget();
5106
+ if (!copyStore.get()?.menuOpen) clearCopyTarget();
4447
5107
  },
4448
- onScroll: handleScroll,
4449
- children: [expandTopLevel ? jsxs("div", {
4450
- className: css$17.expandedTopLevel,
5108
+ children: expandTopLevel ? jsxs("div", {
5109
+ className: css$18.expandedTopLevel,
4451
5110
  children: [
4452
- jsx("div", {
4453
- className: clsx(css$17.row, css$17.topLevelBracket),
5111
+ jsxs("div", {
5112
+ className: clsx(css$18.row, css$18.topLevelBracket),
4454
5113
  "data-json-root-row": true,
4455
5114
  onMouseOver: (event) => {
4456
5115
  event.stopPropagation();
@@ -4459,16 +5118,21 @@ function JsonTree({ data, label, className, copyable = true, expandTopLevel = tr
4459
5118
  value: data
4460
5119
  });
4461
5120
  },
4462
- children: jsx("span", {
4463
- className: css$17.punctuation,
5121
+ children: [renderCopy?.({
5122
+ path: [],
5123
+ value: data
5124
+ }), jsx("span", {
5125
+ className: css$18.punctuation,
4464
5126
  children: rootOpen
4465
- })
5127
+ })]
4466
5128
  }),
4467
5129
  jsx("div", {
4468
5130
  "aria-label": label,
4469
- className: clsx(css$17.container, css$17.expandedTopLevelContainer),
5131
+ className: clsx(css$18.container, css$18.expandedTopLevelContainer),
4470
5132
  role: "tree",
4471
5133
  children: rootEntries.map(([key, value], index) => jsx(JsonTreeNode, {
5134
+ collapsedStringLines,
5135
+ stringWrapping,
4472
5136
  field: key,
4473
5137
  value,
4474
5138
  path: [Array.isArray(data) ? index : key],
@@ -4477,22 +5141,25 @@ function JsonTree({ data, label, className, copyable = true, expandTopLevel = tr
4477
5141
  initialExpanded: false,
4478
5142
  tabStopId,
4479
5143
  onClaimTabStop: setTabStopId,
4480
- onRowHover: handleRowHover
5144
+ onRowHover: handleRowHover,
5145
+ renderCopy
4481
5146
  }, key))
4482
5147
  }),
4483
5148
  jsx("div", {
4484
- className: clsx(css$17.row, css$17.topLevelBracket),
5149
+ className: clsx(css$18.row, css$18.topLevelBracket),
4485
5150
  children: jsx("span", {
4486
- className: css$17.punctuation,
5151
+ className: css$18.punctuation,
4487
5152
  children: rootClose
4488
5153
  })
4489
5154
  })
4490
5155
  ]
4491
5156
  }) : jsx("div", {
4492
5157
  "aria-label": label,
4493
- className: css$17.container,
5158
+ className: css$18.container,
4494
5159
  role: "tree",
4495
5160
  children: jsx(JsonTreeNode, {
5161
+ collapsedStringLines,
5162
+ stringWrapping,
4496
5163
  value: data,
4497
5164
  path: [],
4498
5165
  labels,
@@ -4500,47 +5167,10 @@ function JsonTree({ data, label, className, copyable = true, expandTopLevel = tr
4500
5167
  initialExpanded: true,
4501
5168
  tabStopId,
4502
5169
  onClaimTabStop: setTabStopId,
4503
- onRowHover: handleRowHover
5170
+ onRowHover: handleRowHover,
5171
+ renderCopy
4504
5172
  })
4505
- }), copyTarget !== void 0 && jsx("span", {
4506
- className: css$17.copyAnchor,
4507
- style: {
4508
- left: copyTarget.left,
4509
- top: copyTarget.top
4510
- },
4511
- children: jsx(Menu, {
4512
- open: copyMenuOpen,
4513
- compact: true,
4514
- portal: true,
4515
- align: "end",
4516
- side: copyTarget.side,
4517
- anchor: jsx("button", {
4518
- ref: copyButtonRef,
4519
- type: "button",
4520
- className: css$17.copyButton,
4521
- "data-json-copy-button": true,
4522
- "data-state": copyState,
4523
- "aria-label": copyTitle,
4524
- title: labels.copyButtonTitle(copyTitle),
4525
- onClick: () => void copy(defaultCopyMode),
4526
- onContextMenu: (event) => {
4527
- event.preventDefault();
4528
- event.stopPropagation();
4529
- copyMenuOpenRef.current = true;
4530
- setCopyMenuOpen(true);
4531
- },
4532
- children: copyState === "copied" ? jsx(IconCheckOutline16, { size: 12 }) : jsx(IconCopyOutline16, { size: 12 })
4533
- }),
4534
- items: copyTargetIsObject ? objectCopyMenuItems(labels) : valueCopyMenuItems(labels),
4535
- onSelect: (id) => {
4536
- copy(id);
4537
- copyMenuOpenRef.current = false;
4538
- setCopyMenuOpen(false);
4539
- },
4540
- onClose: clearCopyTarget,
4541
- getAnchorRect: () => copyButtonRef.current.getBoundingClientRect()
4542
- })
4543
- })]
5173
+ })
4544
5174
  });
4545
5175
  }
4546
5176
  //#endregion
@@ -4551,8 +5181,10 @@ function JsonTree({ data, label, className, copyable = true, expandTopLevel = tr
4551
5181
  * semantic. Black and white both resolve to the primary label color so text
4552
5182
  * stays legible under either theme instead of matching the surface it sits
4553
5183
  * on; bright black takes the tertiary label color (the muted-gray role).
4554
- * Magenta and cyan have no token equivalent in this design system and fall
4555
- * through to anser's literal rgb, as do all 256-palette and truecolor values.
5184
+ * Cyan and bright cyan take two static blues: the design system has no cyan,
5185
+ * and anser's literal bright cyan is unreadable on the light theme's code
5186
+ * surface. Magenta has no token equivalent and falls through to anser's
5187
+ * literal rgb, as do all 256-palette and truecolor values.
4556
5188
  */
4557
5189
  const TOKEN_BY_BASIC_RGB = {
4558
5190
  "0,0,0": "var(--dsw-alias-label-primary)",
@@ -4565,7 +5197,9 @@ const TOKEN_BY_BASIC_RGB = {
4565
5197
  "187,187,0": "var(--dsw-alias-state-warn-primary)",
4566
5198
  "255,255,85": "var(--dsw-alias-state-warn-secondary)",
4567
5199
  "0,0,187": "var(--dsw-alias-state-business-primary)",
4568
- "85,85,255": "var(--dsw-static-blue-400)"
5200
+ "85,85,255": "var(--dsw-static-blue-400)",
5201
+ "0,187,187": "var(--dsw-static-blue-600)",
5202
+ "85,255,255": "var(--dsw-static-blue-500)"
4569
5203
  };
4570
5204
  /**
4571
5205
  * CSS for each SGR attribute anser reports. `blink` is deliberately absent —
@@ -5005,13 +5639,14 @@ function promptLabel(cwd, home) {
5005
5639
  * Status pill text for a settled command, or undefined when the command
5006
5640
  * settled cleanly (exit 0, no signal) and needs no pill — the same
5007
5641
  * distinction the bash tool's own exit-status markers draw.
5008
- * @param exitCode - settled exit code, when known.
5642
+ * @param exitCode - settled exit code, when known; null when the command settled without one.
5009
5643
  * @param signal - settled terminating signal name, when known.
5010
5644
  * @param labels - display copy for the pill text.
5011
5645
  * @returns the pill text, or undefined for a clean exit.
5012
5646
  */
5013
5647
  function statusText(exitCode, signal, labels) {
5014
5648
  if (signal !== void 0) return labels.signal(signal);
5649
+ if (exitCode === null) return labels.noExitCode;
5015
5650
  if (exitCode !== void 0 && exitCode !== 0) return labels.exitCode(exitCode);
5016
5651
  }
5017
5652
  /**
@@ -5025,7 +5660,7 @@ function statusText(exitCode, signal, labels) {
5025
5660
  * settled command whose exit status never reached the view counts as a clean
5026
5661
  * settle: the view says it finished and says nothing went wrong.
5027
5662
  * @param running - the command has not settled.
5028
- * @param exitCode - settled exit code, when known.
5663
+ * @param exitCode - settled exit code, when known; null when the command settled without one.
5029
5664
  * @param signal - settled terminating signal name, when known.
5030
5665
  * @param labels - display copy for the text label.
5031
5666
  * @returns the dot's state and its text label, since the dot is aria-hidden.
@@ -5081,67 +5716,69 @@ function TerminalBlock({ command, cwd, home, output, exitCode, signal, running =
5081
5716
  }, [command]);
5082
5717
  const empty = lines.every((line) => line.every((span) => span.text.trim() === ""));
5083
5718
  const { hidden, capped, headLines, tailLines } = headTailCap(lines.length, maxLines, expanded);
5719
+ const body = !running || !empty;
5084
5720
  return jsxs("div", {
5085
- className: clsx(css$18.block, className),
5721
+ className: clsx(css$19.block, className),
5086
5722
  "data-terminal": "",
5087
5723
  "data-running": running ? "" : void 0,
5724
+ "data-body": body ? "" : void 0,
5088
5725
  children: [jsxs("div", {
5089
- className: css$18.header,
5726
+ className: css$19.header,
5090
5727
  children: [
5091
5728
  jsxs("div", {
5092
- className: css$18.prompt,
5729
+ className: css$19.prompt,
5093
5730
  children: [jsx("span", {
5094
- className: css$18.runStateLabel,
5731
+ className: css$19.runStateLabel,
5095
5732
  children: state.label
5096
5733
  }), commandLines.map((line, index) => jsxs("div", {
5097
- className: css$18.promptLine,
5734
+ className: css$19.promptLine,
5098
5735
  children: [
5099
5736
  index === 0 && jsx(StateDot, {
5100
5737
  state: state.state,
5101
- className: css$18.runState
5738
+ className: css$19.runState
5102
5739
  }),
5103
5740
  jsx("span", {
5104
- className: css$18.cwd,
5741
+ className: css$19.cwd,
5105
5742
  children: index > 0 || cwd === void 0 ? "$" : promptLabel(cwd, home)
5106
5743
  }),
5107
5744
  jsx("span", {
5108
- className: css$18.command,
5745
+ className: css$19.command,
5109
5746
  children: line
5110
5747
  })
5111
5748
  ]
5112
5749
  }, index))]
5113
5750
  }),
5114
5751
  status !== void 0 && jsx(Pill, {
5115
- className: css$18.status,
5752
+ className: css$19.status,
5116
5753
  children: status
5117
5754
  }),
5118
5755
  !running && !empty && jsx("button", {
5119
5756
  type: "button",
5120
- className: css$18.copyButton,
5757
+ className: css$19.copyButton,
5121
5758
  onClick: onCopy,
5122
5759
  children: copied ? copy.copied : copy.copy
5123
5760
  })
5124
5761
  ]
5125
- }), !running && (empty ? jsx("div", {
5126
- className: css$18.empty,
5762
+ }), body && (empty ? jsx("div", {
5763
+ className: css$19.empty,
5127
5764
  children: copy.noOutput
5128
5765
  }) : jsxs("div", {
5129
- className: css$18.output,
5766
+ className: css$19.output,
5130
5767
  children: [
5131
5768
  (capped ? lines.slice(0, headLines) : lines).map((line, index) => jsx("div", {
5132
- className: css$18.line,
5769
+ className: css$19.line,
5133
5770
  children: renderLine$1(line)
5134
5771
  }, index)),
5135
5772
  hidden > 0 && jsx("button", {
5136
5773
  type: "button",
5137
- className: css$18.expand,
5774
+ className: css$19.expand,
5138
5775
  "aria-expanded": expanded,
5139
5776
  "aria-label": expanded ? copy.collapseAria : copy.expandAria(hidden),
5140
5777
  onClick: onToggle,
5141
5778
  children: expanded ? copy.collapse : copy.expand(hidden)
5142
5779
  }),
5143
5780
  capped && lines.slice(lines.length - tailLines).map((line, index) => jsx("div", {
5144
- className: css$18.line,
5781
+ className: css$19.line,
5145
5782
  children: renderLine$1(line)
5146
5783
  }, index))
5147
5784
  ]
@@ -5719,51 +6356,51 @@ function ReadBlock({ label, labels, lines, totalLines, lang, maxLines = 16, clas
5719
6356
  const tailLines = maxLines - headLines;
5720
6357
  const windowed = lines.length < totalLines;
5721
6358
  const rows = (slice) => slice.map(([line, spans]) => jsxs("div", {
5722
- className: css$19.line,
6359
+ className: css$20.line,
5723
6360
  children: [jsx("span", {
5724
- className: css$19.gutter,
6361
+ className: css$20.gutter,
5725
6362
  "aria-hidden": true,
5726
6363
  children: line.number
5727
6364
  }), jsx("span", {
5728
- className: css$19.content,
6365
+ className: css$20.content,
5729
6366
  children: spans === void 0 ? line.text : renderSpans(spans)
5730
6367
  })]
5731
6368
  }, line.number));
5732
6369
  const paired = lines.map((line, index) => [line, highlighted?.[index]]);
5733
6370
  return jsxs("div", {
5734
6371
  ref: rootRef,
5735
- className: clsx(css$19.block, className),
6372
+ className: clsx(css$20.block, className),
5736
6373
  "data-read": "",
5737
6374
  children: [jsxs("div", {
5738
- className: css$19.banner,
6375
+ className: css$20.banner,
5739
6376
  children: [jsx("div", {
5740
- className: css$19.label,
6377
+ className: css$20.label,
5741
6378
  children: label ?? ""
5742
6379
  }), jsxs("div", {
5743
- className: css$19.action,
6380
+ className: css$20.action,
5744
6381
  children: [
5745
6382
  windowed && jsx("span", {
5746
- className: css$19.count,
6383
+ className: css$20.count,
5747
6384
  children: labels.window(lines.length, totalLines)
5748
6385
  }),
5749
6386
  jsx("span", {
5750
- className: css$19.lang,
6387
+ className: css$20.lang,
5751
6388
  children: lang ?? ""
5752
6389
  }),
5753
6390
  lines.length > 0 && jsx("button", {
5754
6391
  type: "button",
5755
- className: css$19.copyButton,
6392
+ className: css$20.copyButton,
5756
6393
  onClick: onCopy,
5757
6394
  children: copied ? labels.copied : labels.copy
5758
6395
  })
5759
6396
  ]
5760
6397
  })]
5761
6398
  }), jsxs("div", {
5762
- className: css$19.body,
6399
+ className: css$20.body,
5763
6400
  children: [
5764
6401
  rows(capped ? paired.slice(0, headLines) : paired),
5765
6402
  hidden > 0 && jsx(FoldToggle, {
5766
- className: css$19.expand,
6403
+ className: css$20.expand,
5767
6404
  expanded,
5768
6405
  hidden,
5769
6406
  labels,
@@ -5785,25 +6422,37 @@ function assertNever(value) {
5785
6422
  }
5786
6423
  /** The dim class per row kind (path/gap chrome vs the diff's own +/- colors). */
5787
6424
  const ROW_CLASS = {
5788
- path: css$20.path,
5789
- del: css$20.del,
5790
- add: css$20.add,
5791
- gap: css$20.gap
6425
+ path: css$21.path,
6426
+ del: css$21.del,
6427
+ add: css$21.add,
6428
+ context: css$21.context,
6429
+ gap: css$21.gap
5792
6430
  };
5793
- /**
5794
- * Total added/removed line counts across hunks — the same numbers the footer
5795
- * prints, exported so a summary row can show them without rebuilding the body.
5796
- * Every old-side line counts toward `removed` and every new-side line toward
5797
- * `added`, under {@link contentLines}'s terminator rule.
6431
+ /** Bound synchronous edit-graph search; one replacement consumes two edits. */
6432
+ const MAX_DIFF_EDIT_LENGTH = 256;
6433
+ /** Derive exact local patches or a whole-fragment replacement when search exceeds the limit. */
6434
+ function localHunks(diff) {
6435
+ const oldLines = contentLines(diff.oldText ?? "");
6436
+ const newLines = contentLines(diff.newText);
6437
+ const normalize = (lines) => lines.map((line) => `${line}\n`).join("");
6438
+ return structuredPatch("", "", normalize(oldLines), normalize(newLines), void 0, void 0, {
6439
+ context: 3,
6440
+ maxEditLength: MAX_DIFF_EDIT_LENGTH
6441
+ })?.hunks ?? [{ lines: [...oldLines.map((line) => `-${line}`), ...newLines.map((line) => `+${line}`)] }];
6442
+ }
6443
+ /**
6444
+ * Count displayed additions and deletions. Exact patches exclude shared context;
6445
+ * comparisons exceeding the edit limit count both complete fragments as replaced.
6446
+ * Text follows {@link contentLines}'s terminator rule.
5798
6447
  * @param diffs - the hunks to count.
5799
- * @returns the +/- totals.
6448
+ * @returns the +/- totals for summaries and the card footer.
5800
6449
  */
5801
6450
  function diffTotals(diffs) {
5802
6451
  let added = 0;
5803
6452
  let removed = 0;
5804
- for (const diff of diffs) {
5805
- if (diff.oldText !== null) removed += contentLines(diff.oldText).length;
5806
- added += contentLines(diff.newText).length;
6453
+ for (const diff of diffs) for (const hunk of localHunks(diff)) for (const line of hunk.lines) {
6454
+ if (line.startsWith("+")) added++;
6455
+ if (line.startsWith("-")) removed++;
5807
6456
  }
5808
6457
  return {
5809
6458
  added,
@@ -5811,12 +6460,9 @@ function diffTotals(diffs) {
5811
6460
  };
5812
6461
  }
5813
6462
  /**
5814
- * Flatten the hunks into the body's rows plus the footer counts. A path header
5815
- * opens each new file; a same-file second hunk (a scattered edit) opens with a
5816
- * `⋯` gap instead of repeating the path. The +/- totals are
5817
- * {@link diffTotals}'s. The file count is of DISTINCT paths, matching the TUI
5818
- * diff card's footer, so two hunks in one file read as `1 file` on both front
5819
- * ends.
6463
+ * Flatten local patches into rows and count only added and removed lines.
6464
+ * A path header opens each new file. A `⋯` gap separates consecutive same-file
6465
+ * fragments and distant patches within a fragment. File counts use distinct paths.
5820
6466
  * @param diffs - the hunks to render.
5821
6467
  * @returns the body rows, the +/- totals, and the distinct-file count.
5822
6468
  */
@@ -5835,18 +6481,24 @@ function buildRows(diffs) {
5835
6481
  text: "⋯"
5836
6482
  });
5837
6483
  prevPath = diff.path;
5838
- if (diff.oldText !== null) for (const line of contentLines(diff.oldText)) rows.push({
5839
- kind: "del",
5840
- text: line
5841
- });
5842
- for (const line of contentLines(diff.newText)) rows.push({
5843
- kind: "add",
5844
- text: line
5845
- });
6484
+ for (const [index, hunk] of localHunks(diff).entries()) {
6485
+ if (index > 0) rows.push({
6486
+ kind: "gap",
6487
+ text: "⋯"
6488
+ });
6489
+ for (const line of hunk.lines) {
6490
+ const kind = line.startsWith("-") ? "del" : line.startsWith("+") ? "add" : "context";
6491
+ rows.push({
6492
+ kind,
6493
+ text: line.slice(1)
6494
+ });
6495
+ }
6496
+ }
5846
6497
  }
5847
6498
  return {
5848
6499
  rows,
5849
- ...diffTotals(diffs),
6500
+ added: rows.filter((row) => row.kind === "add").length,
6501
+ removed: rows.filter((row) => row.kind === "del").length,
5850
6502
  files: paths.size
5851
6503
  };
5852
6504
  }
@@ -5864,9 +6516,8 @@ function contentLines(text) {
5864
6516
  return (text.endsWith("\n") ? text.slice(0, -1) : text).split("\n");
5865
6517
  }
5866
6518
  /**
5867
- * The diff text a reader copies: each row's `-`/`+`/path/gap prefix and its
5868
- * content, exactly what the card shows. The removed and added blocks are the
5869
- * change; the path headers keep a multi-file copy attributable.
6519
+ * Copy the full local diff, including folded rows: removed/added lines have
6520
+ * `- `/`+ ` prefixes, context has two spaces, and paths and gaps stay verbatim.
5870
6521
  * @param rows - the flattened body rows.
5871
6522
  * @returns the diff as plain text.
5872
6523
  */
@@ -5875,6 +6526,7 @@ function copyText$1(rows) {
5875
6526
  switch (row.kind) {
5876
6527
  case "del": return `- ${row.text}`;
5877
6528
  case "add": return `+ ${row.text}`;
6529
+ case "context": return ` ${row.text}`;
5878
6530
  case "path": return row.text;
5879
6531
  case "gap": return row.text;
5880
6532
  /* v8 ignore next -- closed-union backstop; only reached if a row kind is forged */
@@ -5912,37 +6564,37 @@ function DiffBlock({ diffs, labels, maxLines = 16, className }) {
5912
6564
  const head = capped ? rows.slice(0, headLines) : rows;
5913
6565
  const tail = capped ? rows.slice(rows.length - tailLines) : [];
5914
6566
  return jsxs("div", {
5915
- className: clsx(css$20.block, className),
6567
+ className: clsx(css$21.block, className),
5916
6568
  "data-diff": "",
5917
6569
  children: [
5918
6570
  jsx("button", {
5919
6571
  type: "button",
5920
- className: css$20.copyButton,
6572
+ className: css$21.copyButton,
5921
6573
  onClick: onCopy,
5922
6574
  children: copied ? labels.copied : labels.copy
5923
6575
  }),
5924
6576
  jsxs("div", {
5925
- className: css$20.body,
6577
+ className: css$21.body,
5926
6578
  children: [
5927
6579
  head.map((row, index) => jsx("div", {
5928
- className: clsx(css$20.line, ROW_CLASS[row.kind]),
6580
+ className: clsx(css$21.line, ROW_CLASS[row.kind]),
5929
6581
  children: row.text
5930
6582
  }, index)),
5931
6583
  hidden > 0 && jsx(FoldToggle, {
5932
- className: css$20.expand,
6584
+ className: css$21.expand,
5933
6585
  expanded,
5934
6586
  hidden,
5935
6587
  labels,
5936
6588
  onToggle
5937
6589
  }),
5938
6590
  tail.map((row, index) => jsx("div", {
5939
- className: clsx(css$20.line, ROW_CLASS[row.kind]),
6591
+ className: clsx(css$21.line, ROW_CLASS[row.kind]),
5940
6592
  children: row.text
5941
6593
  }, index))
5942
6594
  ]
5943
6595
  }),
5944
6596
  jsxs("div", {
5945
- className: css$20.footer,
6597
+ className: css$21.footer,
5946
6598
  children: [
5947
6599
  "└ +",
5948
6600
  added,
@@ -6078,56 +6730,56 @@ function SearchBlock(props) {
6078
6730
  const tail = tailHeader === void 0 ? naturalTail : naturalTail.slice(1);
6079
6731
  const renderRow = (row) => {
6080
6732
  if (row.type === "path") return jsx("div", {
6081
- className: css$21.line,
6733
+ className: css$22.line,
6082
6734
  children: row.path
6083
6735
  });
6084
6736
  if (row.type === "match") return jsxs("div", {
6085
- className: css$21.line,
6737
+ className: css$22.line,
6086
6738
  children: [jsxs("span", {
6087
- className: css$21.lineNumber,
6739
+ className: css$22.lineNumber,
6088
6740
  children: [row.lineNumber, ": "]
6089
6741
  }), row.line]
6090
6742
  });
6091
6743
  return jsxs("button", {
6092
6744
  type: "button",
6093
- className: css$21.fileHeader,
6745
+ className: css$22.fileHeader,
6094
6746
  "aria-expanded": !row.collapsed,
6095
6747
  onClick: () => {
6096
6748
  toggleFile(row.index);
6097
6749
  },
6098
6750
  children: [jsx("span", {
6099
- className: css$21.filePath,
6751
+ className: css$22.filePath,
6100
6752
  children: row.path
6101
6753
  }), jsx("span", {
6102
- className: css$21.fileCount,
6754
+ className: css$22.fileCount,
6103
6755
  children: row.count
6104
6756
  })]
6105
6757
  });
6106
6758
  };
6107
6759
  return jsxs("div", {
6108
- className: clsx(css$21.block, className),
6760
+ className: clsx(css$22.block, className),
6109
6761
  "data-search": props.kind,
6110
6762
  children: [jsxs("div", {
6111
- className: css$21.header,
6763
+ className: css$22.header,
6112
6764
  children: [jsx("span", {
6113
- className: css$21.summary,
6765
+ className: css$22.summary,
6114
6766
  children: summaryText(props, shown, truncated, total)
6115
6767
  }), !empty && jsx("button", {
6116
6768
  type: "button",
6117
- className: css$21.copyButton,
6769
+ className: css$22.copyButton,
6118
6770
  onClick: onCopy,
6119
6771
  children: copied ? props.labels.copied : props.labels.copy
6120
6772
  })]
6121
6773
  }), empty ? jsx("div", {
6122
- className: css$21.empty,
6774
+ className: css$22.empty,
6123
6775
  children: props.labels.noResults
6124
6776
  }) : jsxs("div", {
6125
- className: css$21.body,
6777
+ className: css$22.body,
6126
6778
  children: [
6127
6779
  head.map((row) => jsx("div", { children: renderRow(row) }, rowKey(row))),
6128
6780
  hidden > 0 && jsx("button", {
6129
6781
  type: "button",
6130
- className: css$21.expand,
6782
+ className: css$22.expand,
6131
6783
  "aria-expanded": expanded,
6132
6784
  "aria-label": expanded ? props.labels.collapseAria : props.labels.expandAria(hidden),
6133
6785
  onClick: onToggle,
@@ -6833,7 +7485,7 @@ function renderLine(line, index) {
6833
7485
  }, spanIndex))
6834
7486
  })] }, index);
6835
7487
  }
6836
- function CodeBlock({ code, lang, streaming, className, contentRef, lineNumbers = false, copyLabel, copiedLabel }) {
7488
+ function CodeBlock({ code, lang, streaming, className, contentRef, lineNumbers = false, showHeader = true, copyLabel, copiedLabel }) {
6837
7489
  const trimmed = code.endsWith("\n") ? code.slice(0, -1) : code;
6838
7490
  const sourceLines = lineNumbers ? trimmed.split("\n") : void 0;
6839
7491
  const rootRef = useRef(null);
@@ -6929,7 +7581,7 @@ function CodeBlock({ code, lang, streaming, className, contentRef, lineNumbers =
6929
7581
  });
6930
7582
  }, [copied, trimmed]);
6931
7583
  const body = streamedBody !== void 0 ? streamedBody : html === void 0 ? jsx("pre", {
6932
- className: css$22.plain,
7584
+ className: css$23.plain,
6933
7585
  children: jsx("code", { children: sourceLines === void 0 ? trimmed : sourceLines.map((line, index) => jsxs(Fragment$1, { children: [index > 0 && "\n", jsx("span", {
6934
7586
  className: "line",
6935
7587
  children: line
@@ -6937,22 +7589,22 @@ function CodeBlock({ code, lang, streaming, className, contentRef, lineNumbers =
6937
7589
  }) : jsx("div", { dangerouslySetInnerHTML: { __html: html } });
6938
7590
  return jsxs("div", {
6939
7591
  ref: rootRef,
6940
- className: clsx(css$22.block, "md-code-block", lineNumbers && css$22.numbered, className),
7592
+ className: clsx(css$23.block, "md-code-block", lineNumbers && css$23.numbered, className),
6941
7593
  "data-line-numbers": lineNumbers || void 0,
6942
7594
  style: sourceLines === void 0 ? void 0 : { "--dsl-code-block-line-number-width": `${Math.max(2, String(sourceLines.length).length)}ch` },
6943
- children: [jsx("div", {
6944
- className: css$22.bannerWrap,
7595
+ children: [showHeader && jsx("div", {
7596
+ className: css$23.bannerWrap,
6945
7597
  children: jsxs("div", {
6946
- className: css$22.banner,
7598
+ className: css$23.banner,
6947
7599
  "data-code-block-banner": true,
6948
7600
  children: [jsx("div", {
6949
- className: css$22.infostring,
7601
+ className: css$23.infostring,
6950
7602
  children: lang ?? ""
6951
7603
  }), jsx("div", {
6952
- className: css$22.action,
7604
+ className: css$23.action,
6953
7605
  children: jsx("button", {
6954
7606
  type: "button",
6955
- className: css$22.copyButton,
7607
+ className: css$23.copyButton,
6956
7608
  onClick: onCopy,
6957
7609
  children: copied ? copiedLabel : copyLabel
6958
7610
  })
@@ -6960,13 +7612,46 @@ function CodeBlock({ code, lang, streaming, className, contentRef, lineNumbers =
6960
7612
  })
6961
7613
  }), jsx("div", {
6962
7614
  ref: contentRef,
6963
- className: css$22.content,
7615
+ className: css$23.content,
6964
7616
  "data-code-block-content": true,
6965
7617
  children: body
6966
7618
  })]
6967
7619
  });
6968
7620
  }
6969
7621
  //#endregion
7622
+ //#region lib/types/markdown/file-link.js
7623
+ /** Local Markdown destinations accepted by the file-preview callback. */
7624
+ /**
7625
+ * Decode a file destination and its optional GitHub-style line fragment.
7626
+ * Literal `?` and `#` in filenames must be percent-encoded.
7627
+ * @param value - Parsed Markdown link destination.
7628
+ * @returns A local path and optional first line, or undefined for URLs,
7629
+ * fragment-only links, queries, malformed escapes, or invalid line ranges.
7630
+ */
7631
+ function parseFileLink(value) {
7632
+ const hash = value.indexOf("#");
7633
+ const destination = hash < 0 ? value : value.slice(0, hash);
7634
+ if (destination.includes("?")) return void 0;
7635
+ let path;
7636
+ try {
7637
+ path = decodeURIComponent(destination);
7638
+ } catch (_error) {
7639
+ return;
7640
+ }
7641
+ if (path.length === 0 || /[\u0000-\u001f\u007f]/.test(path) || /^[\\/]{2}/.test(path) || /^[a-z][a-z\d+.-]*:/i.test(path) && !/^[a-z]:[\\/]/i.test(path)) return void 0;
7642
+ if (hash < 0) return { path };
7643
+ const fragment = value.slice(hash + 1);
7644
+ const match = /^L([1-9]\d*)(?:-L([1-9]\d*))?$/.exec(fragment);
7645
+ if (match === null) return void 0;
7646
+ const line = Number(match[1]);
7647
+ const end = match[2] === void 0 ? line : Number(match[2]);
7648
+ if (!Number.isSafeInteger(line) || !Number.isSafeInteger(end) || end < line) return void 0;
7649
+ return {
7650
+ path,
7651
+ line
7652
+ };
7653
+ }
7654
+ //#endregion
6970
7655
  //#region lib/types/markdown/katex.js
6971
7656
  /**
6972
7657
  * TeX-to-React via KaTeX, replicating the rehype-katex pipeline this renderer
@@ -7048,6 +7733,33 @@ function renderTexToReact(value, displayMode) {
7048
7733
  return [...new DOMParser().parseFromString(html, "text/html").body.childNodes].map(domToReact);
7049
7734
  }
7050
7735
  //#endregion
7736
+ //#region lib/types/markdown/MarkdownDelegate.js
7737
+ /** Consumer-owned navigation for Markdown links. */
7738
+ const MarkdownDelegateContext = createContext({});
7739
+ /**
7740
+ * Scope Markdown navigation without threading callbacks through renderers.
7741
+ * Nested providers replace the enclosing capabilities. Handler changes reach cached links.
7742
+ * @param props - Child tree and its file and HTTP(S) link handlers.
7743
+ * @returns the scoped child tree.
7744
+ */
7745
+ function MarkdownDelegateProvider({ children, openExternalLink, openFile }) {
7746
+ const delegate = useMemo(() => ({
7747
+ openExternalLink,
7748
+ openFile
7749
+ }), [openExternalLink, openFile]);
7750
+ return jsx(MarkdownDelegateContext.Provider, {
7751
+ value: delegate,
7752
+ children
7753
+ });
7754
+ }
7755
+ /**
7756
+ * Read the nearest Markdown navigation capabilities.
7757
+ * @returns Owner callbacks, or an empty delegate outside a provider.
7758
+ */
7759
+ function useMarkdownDelegate() {
7760
+ return useContext(MarkdownDelegateContext);
7761
+ }
7762
+ //#endregion
7051
7763
  //#region lib/types/markdown/render.js
7052
7764
  /**
7053
7765
  * Direct mdast→React markdown renderer. Replaces the react-markdown /
@@ -7055,8 +7767,8 @@ function renderTexToReact(value, displayMode) {
7055
7767
  * cache frozen blocks as React elements; the rendered DOM is pinned
7056
7768
  * byte-for-byte by `tests/fixtures/markdown-dom` and must not drift.
7057
7769
  *
7058
- * Untrusted-output policy (unchanged from the replaced pipeline): link and
7059
- * image destinations pass a protocol allowlist, images additionally require
7770
+ * External link and image destinations pass a protocol allowlist; settled
7771
+ * local file links use an explicit owner callback. Images additionally require
7060
7772
  * absolute HTTP(S), raw HTML renders as literal text (no HTML enters the
7061
7773
  * DOM), and KaTeX runs without trusted commands. Fragment-anchor URLs fail
7062
7774
  * the allowlist, so footnote references and back-references render as plain
@@ -7201,13 +7913,13 @@ function renderNode(node, key, context) {
7201
7913
  const mention = context.inLink === true ? void 0 : context.fileMentions?.resolve(value);
7202
7914
  if (mention !== void 0) return jsx("code", { children: jsxs("button", {
7203
7915
  type: "button",
7204
- className: css$23.fileMention,
7916
+ className: markdownCss.fileMention,
7205
7917
  title: mention.title,
7206
7918
  "aria-label": mention.label,
7207
7919
  onClick: mention.open,
7208
7920
  children: [jsx(LinkIcon, {
7209
7921
  kind: classifyLinkPath(value),
7210
- className: css$23.linkIcon
7922
+ className: markdownCss.linkIcon
7211
7923
  }), value]
7212
7924
  }) }, key);
7213
7925
  return jsx("code", { children: value }, key);
@@ -7222,7 +7934,7 @@ function renderNode(node, key, context) {
7222
7934
  case "link": return renderAnchor(node.url, renderChildren(node.children, {
7223
7935
  ...context,
7224
7936
  inLink: true
7225
- }), key, !anchorWrapsOnlyImages(node.children));
7937
+ }), key, !anchorWrapsOnlyImages(node.children), context.streaming);
7226
7938
  case "linkReference": return renderLinkReference(node, key, context);
7227
7939
  case "image": return renderImage(node.url, node.alt ?? "", key, context);
7228
7940
  case "imageReference": return renderImageReference(node, key, context);
@@ -7299,7 +8011,7 @@ function renderTable(node, key, context) {
7299
8011
  const [headRow, ...bodyRows] = node.children;
7300
8012
  const wide = (align === null ? headRow?.children.length ?? 0 : align.length) >= 4 && context.inBlockquote !== true;
7301
8013
  return jsx("div", {
7302
- className: clsx(css$23.tableScroll, wide ? "md-table-wide" : css$23.tableFill),
8014
+ className: clsx(markdownCss.tableScroll, wide ? "md-table-wide" : markdownCss.tableFill),
7303
8015
  tabIndex: wide ? 0 : void 0,
7304
8016
  children: jsxs("table", { children: [headRow !== void 0 && jsx("thead", { children: renderTableRow(headRow, "th", align, 0, context) }), bodyRows.length > 0 && jsx("tbody", { children: bodyRows.map((row, index) => renderTableRow(row, "td", align, index + 1, context)) })] })
7305
8017
  }, key);
@@ -7325,26 +8037,64 @@ function renderTableRow(row, cellTag, align, key, context) {
7325
8037
  function anchorWrapsOnlyImages(children) {
7326
8038
  return children.length > 0 && children.every((child) => child.type === "image" || child.type === "imageReference");
7327
8039
  }
7328
- /** Anchor over an already-authored href: allowlisted or unwrapped, external links get the safe attributes. */
8040
+ /** Anchor over an already-authored href: allowlisted or unwrapped, with optional owner navigation for HTTP(S). */
7329
8041
  function renderSafeLink(href, children, key, glyph = true) {
7330
8042
  const safeHref = sanitizeUrl(href);
7331
8043
  if (safeHref === "") return jsx(Fragment$1, { children }, key);
7332
- return jsxs("a", {
8044
+ return jsx(MarkdownAnchor, {
7333
8045
  href: safeHref,
7334
- ...["http:", "https:"].includes(new URL(safeHref).protocol) ? {
8046
+ glyph,
8047
+ children
8048
+ }, key);
8049
+ }
8050
+ function MarkdownAnchor({ href, glyph, children }) {
8051
+ const { openExternalLink } = useMarkdownDelegate();
8052
+ const external = ["http:", "https:"].includes(new URL(href).protocol);
8053
+ const open = external ? openExternalLink : void 0;
8054
+ return jsxs("a", {
8055
+ href,
8056
+ ...external ? {
7335
8057
  target: "_blank",
7336
8058
  rel: "noopener noreferrer"
7337
8059
  } : {},
8060
+ onClick: open === void 0 ? void 0 : (event) => {
8061
+ if (event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;
8062
+ event.preventDefault();
8063
+ open(href);
8064
+ },
7338
8065
  children: [glyph && jsx(LinkIcon, {
7339
8066
  kind: "url",
7340
- className: css$23.linkIcon
8067
+ href,
8068
+ className: markdownCss.linkIcon
7341
8069
  }), children]
7342
- }, key);
8070
+ });
7343
8071
  }
7344
- /** Anchor over a parsed markdown destination, which hast normalized before the allowlist saw it. */
7345
- function renderAnchor(url, children, key, glyph = true) {
8072
+ /** Local destinations use the scoped file delegate after settlement. */
8073
+ function renderAnchor(url, children, key, glyph = true, streaming = false) {
8074
+ const file = streaming ? void 0 : parseFileLink(url);
8075
+ if (file !== void 0) return jsx(MarkdownFileLink, {
8076
+ file,
8077
+ glyph,
8078
+ children
8079
+ }, key);
7346
8080
  return renderSafeLink(normalizeUri(url), children, key, glyph);
7347
8081
  }
8082
+ function MarkdownFileLink({ file, glyph, children }) {
8083
+ const { openFile } = useMarkdownDelegate();
8084
+ if (openFile === void 0) return jsx(Fragment, { children });
8085
+ return jsxs("button", {
8086
+ type: "button",
8087
+ className: clsx(markdownCss.fileMention, markdownCss.fileLink),
8088
+ title: file.path,
8089
+ onClick: () => {
8090
+ openFile(file.path, file.line === void 0 ? void 0 : { line: file.line });
8091
+ },
8092
+ children: [glyph && jsx(LinkIcon, {
8093
+ kind: classifyLinkPath(file.path),
8094
+ className: markdownCss.linkIcon
8095
+ }), children]
8096
+ });
8097
+ }
7348
8098
  /**
7349
8099
  * The complete inline-code value when it is exactly an absolute HTTP(S) URL
7350
8100
  * (no surrounding whitespace); anything else stays inert code.
@@ -7361,7 +8111,7 @@ function inlineCodeHttpUrl(value) {
7361
8111
  function renderImage(url, alt, key, context) {
7362
8112
  const imageSrc = imageSource(url, context.pathImages);
7363
8113
  if (imageSrc === void 0) return jsx("span", {
7364
- className: css$23.imageAlt,
8114
+ className: markdownCss.imageAlt,
7365
8115
  children: alt
7366
8116
  }, key);
7367
8117
  return jsx(MarkdownImage, {
@@ -7374,11 +8124,11 @@ function renderImage(url, alt, key, context) {
7374
8124
  function MarkdownImage({ src, alt, destination }) {
7375
8125
  const [failed, setFailed] = useState(false);
7376
8126
  if (failed) return jsx("span", {
7377
- className: css$23.imageAlt,
8127
+ className: markdownCss.imageAlt,
7378
8128
  children: alt || destination
7379
8129
  });
7380
8130
  return jsx("img", {
7381
- className: css$23.image,
8131
+ className: markdownCss.image,
7382
8132
  src,
7383
8133
  alt,
7384
8134
  onError: () => {
@@ -7406,7 +8156,7 @@ function renderLinkReference(node, key, context) {
7406
8156
  ...context,
7407
8157
  inLink: true
7408
8158
  });
7409
- return renderAnchor(definition.url, rendered, key, !anchorWrapsOnlyImages(node.children));
8159
+ return renderAnchor(definition.url, rendered, key, !anchorWrapsOnlyImages(node.children), context.streaming);
7410
8160
  }
7411
8161
  function renderImageReference(node, key, context) {
7412
8162
  const definition = context.targets.definitions.get(node.identifier.toUpperCase());
@@ -7594,15 +8344,21 @@ var StreamingRenderer = class {
7594
8344
  * identity discards the streaming render cache mid-message. `fileMentions`
7595
8345
  * links inline-code tokens its resolver recognizes as real files, and
7596
8346
  * `pathImages` rewrites image destinations that are local file paths into
7597
- * displayable URLs its resolver vouches for; both vocabularies are the
8347
+ * displayable URLs its resolver vouches for. Those two vocabularies are the
7598
8348
  * single streaming gate — they apply to settled renders only, because a
7599
8349
  * streaming message's vocabulary is not final and frozen cached elements
7600
- * must not bake in handlers that could go stale.
7601
- * @returns A GFM document with TeX math rendered through KaTeX; raw HTML,
7602
- * relative links, and unsafe protocols are disabled, while absolute HTTP(S)
7603
- * images render directly.
7604
- */
7605
- const MarkdownText = memo(function MarkdownText({ text, streaming = false, labels, fileMentions, pathImages }) {
8350
+ * must not bake in handlers that could go stale. A surrounding
8351
+ * `MarkdownDelegateProvider` can delegate ordinary HTTP(S) activation while
8352
+ * modified clicks retain native behavior. `variant="compact"` uses secondary
8353
+ * text sizing, uniform bold headings, and tight block spacing; the default
8354
+ * `body` variant uses the full document typography.
8355
+ * The provider's `openFile` enables local Markdown links in settled messages,
8356
+ * including `#L24` and `#L24-L30` destinations (ranges open at their first line).
8357
+ * @returns A GFM document with TeX math rendered through KaTeX; raw HTML and
8358
+ * unsafe protocols are disabled. Local links without an opener remain text;
8359
+ * absolute HTTP(S) images render directly.
8360
+ */
8361
+ const MarkdownText = memo(function MarkdownText({ text, streaming = false, labels, fileMentions, pathImages, variant = "body" }) {
7606
8362
  const streamRef = useRef(null);
7607
8363
  const streamLabelsRef = useRef(labels);
7608
8364
  const children = useMemo(() => {
@@ -7623,7 +8379,8 @@ const MarkdownText = memo(function MarkdownText({ text, streaming = false, label
7623
8379
  pathImages
7624
8380
  ]);
7625
8381
  return jsx("div", {
7626
- className: css$23.markdown,
8382
+ className: clsx(markdownCss.markdown, variant === "compact" && markdownCss.compact),
8383
+ "data-markdown-variant": variant === "compact" ? variant : void 0,
7627
8384
  children
7628
8385
  });
7629
8386
  });
@@ -7686,6 +8443,7 @@ function SafeLink({ url, label, className }) {
7686
8443
  rel: "noopener noreferrer",
7687
8444
  children: [jsx(LinkIcon, {
7688
8445
  kind: "url",
8446
+ href,
7689
8447
  className: css$24.linkIcon
7690
8448
  }), label]
7691
8449
  });
@@ -7903,6 +8661,6 @@ function extractMarkdownPlainText(markdown, options = {}) {
7903
8661
  }
7904
8662
  }
7905
8663
  //#endregion
7906
- export { BrandWordmark, Button, CodeBlock, ConnectionIndicator, DEFAULT_DIFF_MAX_LINES, DEFAULT_READ_MAX_LINES, DEFAULT_SEARCH_MAX_LINES, DEFAULT_TERMINAL_MAX_LINES, DiffBlock, DisclosureRow, FISH_LOGO_PATH, FISH_LOGO_VIEWBOX, FileTypeIcon, FishLogo, HoverCard, IconAgentPresetOutline16, IconAlarmClockOutline16, IconApiOutline14, IconArchiveOutline20, IconBranchOutline16, IconBrowseOutline16, IconCheckOutline14, IconCheckOutline16, IconChecklistOutline14, IconChevronDownOutline14, IconChevronLeftOutline14, IconChevronRightOutline14, IconChevronUpOutline14, IconClockOutline16, IconCloseFill14, IconCloseOutline16, IconCodeOutline16, IconContextInjectionOutline16, IconCopyOutline16, IconCordisPluginOutline14, IconDarkOutline16, IconDataOutline16, IconDatabaseOutline16, IconDislikeFill16, IconDislikeOutline16, IconDownloadOutline16, IconEditOutline16, IconEllipsisOutline16, IconEnhanceOutline16, IconFolderClose16, IconFolderOpen16, IconFolderOpenOutline16, IconFollowsystemOutline16, IconFullscreenOutline16, IconGaugeOutline16, IconGlobeOutline14, IconGoalOutline16, IconInspectOutline12, IconLightOutline16, IconLikeFill16, IconLikeOutline16, IconLinkOutline14, IconLinkOutline16, IconListPenOutline16, IconLoadingOutline16, IconNewChatOutline16, IconPanelLeftOutline16, IconPaperclipOutline16, IconPauseOutline16, IconPersonalizationOutline16, IconPlayOutline16, IconPlusOutline16, IconProjectAddOutline16, IconQuestionOutline14, IconQueueOutline14, IconRefreshOutline14, IconRefreshOutline16, IconRightUpOutline14, IconRightUpOutline16, IconSearchOutline16, IconSendOutline14, IconSendOutline16, IconSettingsOutline14, IconSettingsOutline16, IconShareOutline16, IconSkillOutline16, IconSparkle16, IconStopFill16, IconThinkOutline14, IconThinkOutline16, IconTrashOutline16, IconTreeCorner8x10, IconTriangleRightFill14, IconUserOutline16, IconWarningOutline16, Input, JsonBlock, JsonTree, LinkIcon, MarkdownText, Menu, Modal, OnboardingSurface, Pill, ReadBlock, ReferenceIcon, RiskConfirmation, SearchBlock, StateDot, Switch, Tag, TerminalBlock, Toast, Tooltip, WebBlock, classifyFileType, classifyLinkPath, diffTotals, extractMarkdownPlainText, fileExtension, fileSizeText, projectUserText, rankByName, relativeTime, useAnchoredMaxHeight, useAnchoredPosition, useDismissOnOutsidePointer, writeClipboard };
8664
+ export { BrandWordmark, Button, Checkbox, CodeBlock, ConnectionIndicator, DEFAULT_DIFF_MAX_LINES, DEFAULT_READ_MAX_LINES, DEFAULT_SEARCH_MAX_LINES, DEFAULT_TERMINAL_MAX_LINES, DiffBlock, DisclosureRow, FISH_LOGO_PATH, FISH_LOGO_VIEWBOX, FileTypeIcon, FishLogo, HoverCard, IconAgentPresetOutline16, IconAlarmClockOutline16, IconApiOutline14, IconArchiveOutline20, IconBranchOutline16, IconBrowseOutline16, IconCheckOutline14, IconCheckOutline16, IconChecklistOutline14, IconChevronDownOutline14, IconChevronLeftOutline14, IconChevronRightOutline14, IconChevronUpOutline14, IconClockOutline16, IconCloseFill14, IconCloseOutline16, IconCodeOutline16, IconCompactOutline16, IconContextInjectionOutline16, IconCopyOutline16, IconCordisPluginOutline14, IconDarkOutline16, IconDataOutline16, IconDatabaseOutline16, IconDislikeFill16, IconDislikeOutline16, IconDownloadOutline16, IconEditOutline16, IconEllipsisOutline16, IconEnhanceOutline16, IconFolderClose16, IconFolderOpen16, IconFolderOpenOutline16, IconFollowsystemOutline16, IconFullscreenOutline16, IconGaugeOutline16, IconGlobeOutline14, IconGoalOutline16, IconInfoOutline14, IconInspectOutline12, IconLightOutline16, IconLikeFill16, IconLikeOutline16, IconLinkOutline14, IconLinkOutline16, IconListPenOutline16, IconLoadingOutline16, IconNewChatOutline16, IconPanelLeftOutline16, IconPaperPlaneOutline14, IconPaperclipOutline16, IconPauseOutline16, IconPersonalizationOutline16, IconPlanOutline14, IconPlayOutline16, IconPluginPinwheelOutline16, IconPlusOutline16, IconProjectAddOutline16, IconQuestionOutline14, IconQueueOutline14, IconRefreshOutline14, IconRefreshOutline16, IconRightUpOutline14, IconRightUpOutline16, IconSearchOutline16, IconSendOutline14, IconSettingsOutline14, IconSettingsOutline16, IconShareOutline16, IconShieldOutline16, IconSkillOutline16, IconSparkle16, IconStopFill16, IconThinkOutline14, IconThinkOutline16, IconTrashOutline16, IconTreeCorner8x10, IconTriangleRightFill14, IconUserOutline16, IconWarningOutline16, IconWrapLinesOutline16, Input, JsonBlock, JsonTree, LinkIcon, MarkdownDelegateProvider, MarkdownText, Menu, Modal, OnboardingSurface, Pill, ReadBlock, ReferenceIcon, RiskConfirmation, SHIELD_OUTLINE_PATH, SHIELD_OUTLINE_STROKE, SearchBlock, StateDot, Switch, Tag, TerminalBlock, Toast, Tooltip, WebBlock, classifyFileType, classifyLinkPath, diffTotals, extractMarkdownPlainText, fileExtension, fileSizeText, isDarwinDesktop, projectUserText, rankByName, relativeTime, useAnchoredMaxHeight, useAnchoredPosition, useDismissOnOutsidePointer, writeClipboard };
7907
8665
 
7908
8666
  //# sourceMappingURL=index.js.map