@deepseek-ai/dsh-client-ui-primitives 0.1.7-alpha.2 → 0.1.7-rc.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.
Files changed (58) hide show
  1. package/README.i18n.yaml +60 -5
  2. package/README.md +27 -10
  3. package/README.zh.md +27 -10
  4. package/lib/Button.module.css +6 -8
  5. package/lib/Checkbox.module.css +1 -1
  6. package/lib/CodeCard.module.css +3 -3
  7. package/lib/ConnectionIndicator.module.css +6 -2
  8. package/lib/HoverCard.module.css +17 -8
  9. package/lib/ImageLightbox.module.css +46 -0
  10. package/lib/ImagePreview.module.css +24 -0
  11. package/lib/Input.module.css +2 -2
  12. package/lib/JsonTree.module.css +3 -3
  13. package/lib/Menu.module.css +22 -18
  14. package/lib/MenuSurface.module.css +49 -0
  15. package/lib/Modal.module.css +27 -6
  16. package/lib/Pill.module.css +2 -1
  17. package/lib/RiskConfirmation.module.css +1 -1
  18. package/lib/SearchBlock.module.css +1 -1
  19. package/lib/SegmentedControl.module.css +9 -9
  20. package/lib/SegmentedTabs.module.css +3 -3
  21. package/lib/ShortcutKeys.module.css +33 -0
  22. package/lib/Switch.module.css +3 -8
  23. package/lib/TerminalBlock.module.css +3 -3
  24. package/lib/Toast.module.css +2 -2
  25. package/lib/Tooltip.module.css +9 -1
  26. package/lib/WebBlock.module.css +1 -1
  27. package/lib/index.js +1592 -825
  28. package/lib/markdown/CodeBlock.module.css +1 -1
  29. package/lib/markdown/JsonBlock.module.css +2 -2
  30. package/lib/markdown/MarkdownText.module.css +28 -5
  31. package/lib/settings-form/SettingsForm.module.css +2 -2
  32. package/lib/settings-form/fields.module.css +4 -4
  33. package/lib/types/Button.d.ts +6 -5
  34. package/lib/types/HoverCard.d.ts +5 -2
  35. package/lib/types/ImageLightbox.d.ts +27 -0
  36. package/lib/types/ImagePreview.d.ts +12 -0
  37. package/lib/types/Menu.d.ts +9 -1
  38. package/lib/types/MenuSurface.d.ts +16 -0
  39. package/lib/types/Modal.d.ts +12 -4
  40. package/lib/types/ShortcutKeys.d.ts +11 -0
  41. package/lib/types/Tooltip.d.ts +15 -4
  42. package/lib/types/code-highlighting.d.ts +1 -8
  43. package/lib/types/focus.d.ts +9 -0
  44. package/lib/types/guide-artwork.d.ts +15 -0
  45. package/lib/types/icons/index.d.ts +8 -4
  46. package/lib/types/icons/shared-artwork.d.ts +8 -1
  47. package/lib/types/index.d.ts +9 -1
  48. package/lib/types/input-modality.d.ts +19 -0
  49. package/lib/types/keyboard-composition.d.ts +11 -0
  50. package/lib/types/markdown/MarkdownDelegate.d.ts +11 -1
  51. package/lib/types/markdown/highlight.d.ts +16 -10
  52. package/lib/types/markdown/local-image-syntax.d.ts +10 -0
  53. package/lib/types/plugin-artwork.d.ts +1 -1
  54. package/lib/types/settings-form/fields.d.ts +1 -0
  55. package/lib/types/useModalLayer.d.ts +27 -0
  56. package/package.json +4 -3
  57. package/lib/OnboardingSurface.module.css +0 -28
  58. package/lib/types/OnboardingSurface.d.ts +0 -11
package/lib/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
2
2
  import clsx from "clsx";
3
3
  import css from "./StateDot.module.css";
4
- import { Fragment as Fragment$1, cloneElement, createContext, createElement, memo, useCallback, useContext, useEffect, useId, useLayoutEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
4
+ import { Fragment as Fragment$1, cloneElement, createContext, createElement, forwardRef, memo, useCallback, useContext, useEffect, useId, useLayoutEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
5
5
  import css$1 from "./TextShimmer.module.css";
6
6
  import css$2 from "./DisclosureRow.module.css";
7
7
  import css$3 from "./Button.module.css";
@@ -15,34 +15,36 @@ import css$9 from "./SegmentedControl.module.css";
15
15
  import css$10 from "./Checkbox.module.css";
16
16
  import css$11 from "./Input.module.css";
17
17
  import { createPortal } from "react-dom";
18
- import css$12 from "./Menu.module.css";
19
- import css$13 from "./HoverCard.module.css";
20
- import css$14 from "./Modal.module.css";
21
- import css$15 from "./OnboardingSurface.module.css";
22
- import css$16 from "./RiskConfirmation.module.css";
23
- import css$17 from "./ConnectionIndicator.module.css";
24
- import css$18 from "./FileTypeIcon.module.css";
18
+ import css$12 from "./ShortcutKeys.module.css";
19
+ import css$13 from "./MenuSurface.module.css";
20
+ import css$14 from "./Menu.module.css";
21
+ import css$15 from "./Tooltip.module.css";
22
+ import css$16 from "./HoverCard.module.css";
23
+ import css$17 from "./Modal.module.css";
24
+ import css$18 from "./RiskConfirmation.module.css";
25
+ import css$19 from "./ConnectionIndicator.module.css";
26
+ import css$20 from "./FileTypeIcon.module.css";
25
27
  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";
26
- import css$19 from "./user-text.module.css";
28
+ import css$21 from "./user-text.module.css";
27
29
  import markdownCss from "./markdown/MarkdownText.module.css";
28
- import css$20 from "./Tooltip.module.css";
29
- import css$21 from "./Toast.module.css";
30
- import css$22 from "./settings-form/SettingsForm.module.css";
31
- import css$23 from "./settings-form/fields.module.css";
30
+ import css$22 from "./Toast.module.css";
31
+ import css$23 from "./settings-form/SettingsForm.module.css";
32
+ import css$24 from "./settings-form/fields.module.css";
32
33
  import { createSnapshotStore } from "@deepseek-ai/dsh-client-store";
33
34
  import { createCssVariablesTheme, createHighlighterCoreSync } from "shiki/core";
34
35
  import { createJavaScriptRegexEngine, defaultJavaScriptRegexConstructor } from "shiki/engine/javascript";
35
36
  import langTs from "@shikijs/langs/typescript";
36
37
  import langBash from "@shikijs/langs/shellscript";
37
38
  import langJson from "@shikijs/langs/json";
38
- import css$24 from "./JsonTree.module.css";
39
+ import { CODE_HIGHLIGHT_EXTENSIONS, languageForPath } from "@deepseek-ai/dsh-util-code-language";
40
+ import css$25 from "./JsonTree.module.css";
39
41
  import Anser from "anser";
40
- import css$25 from "./TerminalBlock.module.css";
42
+ import css$26 from "./TerminalBlock.module.css";
41
43
  import cardCss from "./CodeCard.module.css";
42
- import css$26 from "./ReadBlock.module.css";
44
+ import css$27 from "./ReadBlock.module.css";
43
45
  import { structuredPatch } from "diff";
44
- import css$27 from "./DiffBlock.module.css";
45
- import css$28 from "./SearchBlock.module.css";
46
+ import css$28 from "./DiffBlock.module.css";
47
+ import css$29 from "./SearchBlock.module.css";
46
48
  import { fromMarkdown } from "mdast-util-from-markdown";
47
49
  import { gfmFromMarkdown } from "mdast-util-gfm";
48
50
  import { mathFromMarkdown } from "mdast-util-math";
@@ -54,14 +56,16 @@ import { classifyCharacter } from "micromark-util-classify-character";
54
56
  import { codes, constants, types } from "micromark-util-symbol";
55
57
  import { factorySpace } from "micromark-factory-space";
56
58
  import { normalizeUri } from "micromark-util-sanitize-uri";
57
- import css$29 from "./markdown/CodeBlock.module.css";
59
+ import css$30 from "./markdown/CodeBlock.module.css";
58
60
  import katex from "katex";
61
+ import css$31 from "./ImageLightbox.module.css";
62
+ import css$32 from "./ImagePreview.module.css";
59
63
  import "katex/dist/katex.min.css";
60
- import css$30 from "./WebBlock.module.css";
61
- import css$31 from "./markdown/JsonBlock.module.css";
64
+ import css$33 from "./WebBlock.module.css";
65
+ import css$34 from "./markdown/JsonBlock.module.css";
62
66
  //#region lib/types/icons/shared-artwork.js
63
67
  /**
64
- * Render shared new-conversation geometry for product and reference icons.
68
+ * Render new-conversation geometry — the bubble around a plus — for product icons.
65
69
  * @param props - Size, optional CSS class, and inherited stroke width.
66
70
  * @returns The decorative SVG artwork.
67
71
  */
@@ -90,6 +94,36 @@ const NewChatOutlineArtwork = ({ size = 16, className, strokeWidth }) => jsxs("s
90
94
  ]
91
95
  });
92
96
  /**
97
+ * Render shared conversation geometry — the chat bubble around two text
98
+ * lines — for the queue product icon and session reference icons.
99
+ * @param props - Size, optional CSS class, and inherited stroke width.
100
+ * @returns The decorative SVG artwork.
101
+ */
102
+ const ChatLinesOutlineArtwork = ({ size = 16, className, strokeWidth }) => jsxs("svg", {
103
+ width: size,
104
+ height: size,
105
+ className,
106
+ viewBox: "0 0 16 16",
107
+ fill: "none",
108
+ xmlns: "http://www.w3.org/2000/svg",
109
+ "aria-hidden": "true",
110
+ strokeWidth,
111
+ children: [
112
+ jsx("path", {
113
+ d: "M5 6.75H11",
114
+ stroke: "currentColor"
115
+ }),
116
+ jsx("path", {
117
+ d: "M5 9H8",
118
+ stroke: "currentColor"
119
+ }),
120
+ jsx("path", {
121
+ d: "M2.37067 11.2497C1.5872 9.89252 1.32042 8.29798 1.61945 6.7597C1.91847 5.22141 2.76317 3.84293 3.99801 2.87809C5.23285 1.91325 6.7747 1.427 8.33964 1.50888C9.90458 1.59076 11.3873 2.23526 12.5147 3.32369C13.6422 4.41232 14.3384 5.8717 14.4751 7.43304C14.6118 8.99438 14.1797 10.5525 13.2585 11.8205C12.3372 13.0885 10.9889 13.9809 9.4617 14.3334C8.18666 14.6277 6.8587 14.529 5.64964 14.0601C5.17095 13.8745 4.76937 13.4929 4.26509 13.3963C3.67389 13.2832 2.95232 13.5595 2.0377 14.3334",
122
+ stroke: "currentColor"
123
+ })
124
+ ]
125
+ });
126
+ /**
93
127
  * Render shared globe geometry for product and link icons.
94
128
  * @param props - Size, optional CSS class, and inherited stroke width.
95
129
  * @returns The decorative SVG artwork.
@@ -205,6 +239,10 @@ const FolderCloseArtwork = ({ size = 16, className, strokeWidth }) => jsxs("svg"
205
239
  });
206
240
  //#endregion
207
241
  //#region lib/types/icons/index.js
242
+ /**
243
+ * Shared current-color product icons. Names identify the glyph and weight;
244
+ * rendered size remains a prop instead of part of the component name.
245
+ */
208
246
  /** Shared shield contour used by composite icons outside this module. */
209
247
  const SHIELD_OUTLINE_PATH = "M6.80132 2.14853C7.70663 1.80917 8.70422 1.80919 9.60952 2.14859L14.1296 3.84317V7.11961C14.1296 11.6089 10.7615 13.5975 8.20543 14.5779C5.64931 13.5975 2.28052 11.6089 2.28052 7.11961V3.84317L6.80132 2.14853Z";
210
248
  /** Regular stroke width used by the product icon set. */
@@ -1243,10 +1281,10 @@ const IconUserOutlineArtwork = ({ size = 16, className, strokeWidth }) => jsxs("
1243
1281
  "aria-hidden": "true",
1244
1282
  strokeWidth,
1245
1283
  children: [jsx("path", {
1246
- d: "M8 8.5C9.65685 8.5 11 7.15685 11 5.5C11 3.84315 9.65685 2.5 8 2.5C6.34315 2.5 5 3.84315 5 5.5C5 7.15685 6.34315 8.5 8 8.5Z",
1284
+ d: "M8 8.25C9.51878 8.25 10.75 7.01878 10.75 5.5C10.75 3.98122 9.51878 2.75 8 2.75C6.48122 2.75 5.25 3.98122 5.25 5.5C5.25 7.01878 6.48122 8.25 8 8.25Z",
1247
1285
  stroke: "currentColor"
1248
1286
  }), jsx("path", {
1249
- d: "M1.5 14.5C1.5 11.25 4.25 10 8 10C11.75 10 14.5 11.25 14.5 14.5",
1287
+ d: "M2.5 14.5C2.5 11.5 5.25 10.25 8 10.25C10.75 10.25 13.5 11.5 13.5 14.5",
1250
1288
  stroke: "currentColor"
1251
1289
  })]
1252
1290
  });
@@ -1260,6 +1298,44 @@ const IconUserOutlineMedium = (props) => jsx(IconUserOutlineArtwork, {
1260
1298
  ...props,
1261
1299
  strokeWidth: ICON_MEDIUM_STROKE
1262
1300
  });
1301
+ const IconUsersOutlineArtwork = ({ size = 16, className, strokeWidth }) => jsxs("svg", {
1302
+ width: size,
1303
+ height: size,
1304
+ className,
1305
+ viewBox: "0 0 16 16",
1306
+ fill: "none",
1307
+ xmlns: "http://www.w3.org/2000/svg",
1308
+ "aria-hidden": "true",
1309
+ strokeWidth,
1310
+ children: [
1311
+ jsx("path", {
1312
+ d: "M6 8.25C7.51878 8.25 8.75 7.01878 8.75 5.5C8.75 3.98122 7.51878 2.75 6 2.75C4.48122 2.75 3.25 3.98122 3.25 5.5C3.25 7.01878 4.48122 8.25 6 8.25Z",
1313
+ stroke: "currentColor"
1314
+ }),
1315
+ jsx("path", {
1316
+ d: "M1 14.5C1 11.5 3.5 10.25 6 10.25C8.5 10.25 11 11.5 11 14.5",
1317
+ stroke: "currentColor"
1318
+ }),
1319
+ jsx("path", {
1320
+ d: "M10.5 2.9C11.65 3.35 12.45 4.35 12.45 5.5C12.45 6.65 11.65 7.65 10.5 8.1",
1321
+ stroke: "currentColor"
1322
+ }),
1323
+ jsx("path", {
1324
+ d: "M12.4 10.6C13.9 11.3 15 12.6 15 14.5",
1325
+ stroke: "currentColor"
1326
+ })
1327
+ ]
1328
+ });
1329
+ /** Regular one-pixel IconUsersOutline artwork: a front person with a second person behind. */
1330
+ const IconUsersOutlineRegular = (props) => jsx(IconUsersOutlineArtwork, {
1331
+ ...props,
1332
+ strokeWidth: 1
1333
+ });
1334
+ /** Medium IconUsersOutline artwork with a 1.3px stroke. */
1335
+ const IconUsersOutlineMedium = (props) => jsx(IconUsersOutlineArtwork, {
1336
+ ...props,
1337
+ strokeWidth: ICON_MEDIUM_STROKE
1338
+ });
1263
1339
  const IconPaperPlaneOutlineArtwork = ({ size = 14, className, strokeWidth }) => jsxs("svg", {
1264
1340
  width: size,
1265
1341
  height: size,
@@ -1953,10 +2029,10 @@ const IconClockOutlineArtwork = ({ size = 16, className, strokeWidth }) => jsxs(
1953
2029
  "aria-hidden": "true",
1954
2030
  strokeWidth,
1955
2031
  children: [jsx("path", {
1956
- d: "M8 14.5C11.5899 14.5 14.5 11.5899 14.5 8C14.5 4.41015 11.5899 1.5 8 1.5C4.41015 1.5 1.5 4.41015 1.5 8C1.5 11.5899 4.41015 14.5 8 14.5Z",
2032
+ d: "M8 14C11.3137 14 14 11.3137 14 8C14 4.68629 11.3137 2 8 2C4.68629 2 2 4.68629 2 8C2 11.3137 4.68629 14 8 14Z",
1957
2033
  stroke: "currentColor"
1958
2034
  }), jsx("path", {
1959
- d: "M8 4V8.5L11.25 10.25",
2035
+ d: "M8 4.31V8.46L11 10.08",
1960
2036
  stroke: "currentColor"
1961
2037
  })]
1962
2038
  });
@@ -2031,29 +2107,9 @@ const IconSendOutlineMedium = (props) => jsx(IconSendOutlineArtwork, {
2031
2107
  ...props,
2032
2108
  strokeWidth: ICON_MEDIUM_STROKE
2033
2109
  });
2034
- const IconQueueOutlineArtwork = ({ size = 14, className, strokeWidth }) => jsxs("svg", {
2035
- width: size,
2036
- height: size,
2037
- className,
2038
- viewBox: "0 0 16 16",
2039
- fill: "none",
2040
- xmlns: "http://www.w3.org/2000/svg",
2041
- "aria-hidden": "true",
2042
- strokeWidth,
2043
- children: [
2044
- jsx("path", {
2045
- d: "M5 6.75H11",
2046
- stroke: "currentColor"
2047
- }),
2048
- jsx("path", {
2049
- d: "M5 9H8",
2050
- stroke: "currentColor"
2051
- }),
2052
- jsx("path", {
2053
- d: "M2.37067 11.2497C1.5872 9.89252 1.32042 8.29798 1.61945 6.7597C1.91847 5.22141 2.76317 3.84293 3.99801 2.87809C5.23285 1.91325 6.7747 1.427 8.33964 1.50888C9.90458 1.59076 11.3873 2.23526 12.5147 3.32369C13.6422 4.41232 14.3384 5.8717 14.4751 7.43304C14.6118 8.99438 14.1797 10.5525 13.2585 11.8205C12.3372 13.0885 10.9889 13.9809 9.4617 14.3334C8.18666 14.6277 6.8587 14.529 5.64964 14.0601C5.17095 13.8745 4.76937 13.4929 4.26509 13.3963C3.67389 13.2832 2.95232 13.5595 2.0377 14.3334",
2054
- stroke: "currentColor"
2055
- })
2056
- ]
2110
+ const IconQueueOutlineArtwork = ({ size = 14, ...rest }) => jsx(ChatLinesOutlineArtwork, {
2111
+ size,
2112
+ ...rest
2057
2113
  });
2058
2114
  /** Regular one-pixel IconQueueOutline artwork. */
2059
2115
  const IconQueueOutlineRegular = (props) => jsx(IconQueueOutlineArtwork, {
@@ -2826,6 +2882,64 @@ const IconChevronsUpDownOutlineMedium = (props) => jsx(IconChevronsUpDownOutline
2826
2882
  ...props,
2827
2883
  strokeWidth: ICON_MEDIUM_STROKE
2828
2884
  });
2885
+ const IconArchiveOffOutlineArtwork = ({ size = 16, className, strokeWidth }) => {
2886
+ const maskId = `dsh-archive-off-${useId().replaceAll(":", "")}`;
2887
+ return jsxs("svg", {
2888
+ width: size,
2889
+ height: size,
2890
+ className,
2891
+ viewBox: "0 0 16 16",
2892
+ fill: "none",
2893
+ stroke: "currentColor",
2894
+ strokeLinecap: "round",
2895
+ strokeLinejoin: "round",
2896
+ "aria-hidden": "true",
2897
+ strokeWidth,
2898
+ children: [
2899
+ jsxs("mask", {
2900
+ id: maskId,
2901
+ maskUnits: "userSpaceOnUse",
2902
+ x: "0",
2903
+ y: "0",
2904
+ width: "16",
2905
+ height: "16",
2906
+ children: [jsx("rect", {
2907
+ x: "0",
2908
+ y: "0",
2909
+ width: "16",
2910
+ height: "16",
2911
+ fill: "white",
2912
+ stroke: "none"
2913
+ }), jsx("path", {
2914
+ d: "m2.2 1.3 11.6 12.8",
2915
+ stroke: "black",
2916
+ strokeWidth: strokeWidth + 3
2917
+ })]
2918
+ }),
2919
+ jsxs("g", {
2920
+ mask: `url(#${maskId})`,
2921
+ children: [jsx("rect", {
2922
+ x: "1.9",
2923
+ y: "2.1",
2924
+ width: "12.2",
2925
+ height: "3.4",
2926
+ rx: "1.1"
2927
+ }), jsx("path", { d: "M2.95 5.7v4.8a2.9 2.9 0 0 0 2.9 2.9h4.3a2.9 2.9 0 0 0 2.9-2.9V5.7" })]
2928
+ }),
2929
+ jsx("path", { d: "m2.2 1.3 11.6 12.8" })
2930
+ ]
2931
+ });
2932
+ };
2933
+ /** Regular one-pixel IconArchiveOffOutline artwork. */
2934
+ const IconArchiveOffOutlineRegular = (props) => jsx(IconArchiveOffOutlineArtwork, {
2935
+ ...props,
2936
+ strokeWidth: 1
2937
+ });
2938
+ /** Medium IconArchiveOffOutline artwork with a 1.3px stroke. */
2939
+ const IconArchiveOffOutlineMedium = (props) => jsx(IconArchiveOffOutlineArtwork, {
2940
+ ...props,
2941
+ strokeWidth: ICON_MEDIUM_STROKE
2942
+ });
2829
2943
  const IconArchiveCheckOutlineArtwork = ({ size = 16, className, strokeWidth }) => jsxs("svg", {
2830
2944
  width: size,
2831
2945
  height: size,
@@ -3057,12 +3171,14 @@ const DisclosureRow = memo(function DisclosureRow({ icon, title, open, expandabl
3057
3171
  /**
3058
3172
  * Render a button.
3059
3173
  * @param props.variant - visual family (default 'ghost').
3060
- * @param props.size - 'md' 36px capsule (figma Button) or 'sm' 28px compact.
3174
+ * @param props.size - 'md' 36px control with 12px corners or 'sm' 28px control with 8px corners.
3061
3175
  * @param props.icon - optional leading 16px icon node.
3176
+ * @param ref - native button for focus management and overlay anchors.
3062
3177
  * @returns the button element; native button attributes pass through.
3063
3178
  */
3064
- function Button({ variant = "ghost", size = "md", icon, className, children, ...rest }) {
3179
+ const Button = forwardRef(function Button({ variant = "ghost", size = "md", icon, className, children, ...rest }, ref) {
3065
3180
  return jsxs("button", {
3181
+ ref,
3066
3182
  type: "button",
3067
3183
  className: clsx(css$3.button, css$3[variant], css$3[size], className),
3068
3184
  ...rest,
@@ -3071,7 +3187,7 @@ function Button({ variant = "ghost", size = "md", icon, className, children, ...
3071
3187
  children: icon
3072
3188
  }), children]
3073
3189
  });
3074
- }
3190
+ });
3075
3191
  //#endregion
3076
3192
  //#region lib/types/Pill.js
3077
3193
  /**
@@ -3432,6 +3548,234 @@ function usePointerGrace(close) {
3432
3548
  };
3433
3549
  }
3434
3550
  //#endregion
3551
+ //#region lib/types/keyboard-composition.js
3552
+ /** Composition lifetime for local keyboard handlers, including a late closing keydown. */
3553
+ /**
3554
+ * Observe composition until its closing key is released or consumed.
3555
+ * @param document - document whose input events belong to the caller.
3556
+ * @returns an event guard and a disposer for all listeners.
3557
+ */
3558
+ function observeComposition(document) {
3559
+ let composing = false;
3560
+ let ended = false;
3561
+ const start = () => {
3562
+ composing = true;
3563
+ };
3564
+ const end = () => {
3565
+ composing = false;
3566
+ ended = true;
3567
+ };
3568
+ const release = () => {
3569
+ ended = false;
3570
+ };
3571
+ const blur = () => {
3572
+ composing = false;
3573
+ ended = false;
3574
+ };
3575
+ document.addEventListener("compositionstart", start, true);
3576
+ document.addEventListener("compositionend", end, true);
3577
+ document.addEventListener("keyup", release, true);
3578
+ document.defaultView?.addEventListener("blur", blur);
3579
+ return {
3580
+ guards: (event) => {
3581
+ const guarded = composing || ended || event.isComposing || event.keyCode === 229;
3582
+ ended = false;
3583
+ return guarded;
3584
+ },
3585
+ dispose: () => {
3586
+ document.removeEventListener("compositionstart", start, true);
3587
+ document.removeEventListener("compositionend", end, true);
3588
+ document.removeEventListener("keyup", release, true);
3589
+ document.defaultView?.removeEventListener("blur", blur);
3590
+ }
3591
+ };
3592
+ }
3593
+ //#endregion
3594
+ //#region lib/types/focus.js
3595
+ /** Focus presentation for automatic entry and restoration. */
3596
+ const releases = /* @__PURE__ */ new WeakMap();
3597
+ const navigationKeys = new Set([
3598
+ "Tab",
3599
+ "ArrowUp",
3600
+ "ArrowDown",
3601
+ "ArrowLeft",
3602
+ "ArrowRight",
3603
+ "Home",
3604
+ "End"
3605
+ ]);
3606
+ /**
3607
+ * Focus an automatic destination without a focus outline until keyboard navigation or blur.
3608
+ * The theme suppresses outlines while data-dsh-automatic-focus is present; borders and shadows remain intact.
3609
+ * Tab and directional navigation restore normal focus styling.
3610
+ * @param element - control or container receiving automatic focus.
3611
+ * @param options - browser focus options, including scroll preservation.
3612
+ */
3613
+ function focusWithoutRing(element, options) {
3614
+ releases.get(element)?.();
3615
+ const release = () => {
3616
+ element.removeAttribute("data-dsh-automatic-focus");
3617
+ element.removeEventListener("blur", release);
3618
+ element.removeEventListener("keydown", navigate, true);
3619
+ releases.delete(element);
3620
+ };
3621
+ const navigate = (event) => {
3622
+ if (!event.isComposing && !event.ctrlKey && !event.altKey && !event.metaKey && navigationKeys.has(event.key)) release();
3623
+ };
3624
+ releases.set(element, release);
3625
+ element.setAttribute("data-dsh-automatic-focus", "");
3626
+ element.addEventListener("blur", release);
3627
+ element.addEventListener("keydown", navigate, true);
3628
+ element.focus(options);
3629
+ if (!element.matches(":focus")) release();
3630
+ }
3631
+ //#endregion
3632
+ //#region lib/types/useModalLayer.js
3633
+ /** Shared modal keyboard ownership and focus lifetime. */
3634
+ /** Dialog and menu elements whose document order determines foreground shortcut ownership. */
3635
+ const modalSelector = "[role=\"dialog\"][aria-modal=\"true\"], [role=\"menu\"]";
3636
+ const layers = /* @__PURE__ */ new WeakMap();
3637
+ /**
3638
+ * Request closure of the foreground registered modal using its current onClose callback.
3639
+ * A newer menu or unregistered dialog blocks dismissal of the modal behind it.
3640
+ * @param document - product document whose modal owns the close command.
3641
+ */
3642
+ function closeTopModal(document) {
3643
+ const top = layers.get(document)?.at(-1);
3644
+ if (top === void 0) return;
3645
+ if ([...document.querySelectorAll("[role=\"dialog\"][aria-modal=\"true\"], [role=\"menu\"]")].at(-1) === top.element) top.close();
3646
+ }
3647
+ /**
3648
+ * Whether an anchor belongs behind the current modal and must yield keyboard input.
3649
+ * @param anchor - local control owning the input handler.
3650
+ * @returns true when another modal owns the foreground.
3651
+ */
3652
+ function isBehindModal(anchor) {
3653
+ if (anchor === null) return false;
3654
+ const top = layers.get(anchor.ownerDocument)?.at(-1);
3655
+ return top !== void 0 && !top.element.contains(anchor);
3656
+ }
3657
+ const focusable = "button:not(:disabled), input:not(:disabled), textarea:not(:disabled), select:not(:disabled), a[href], [tabindex=\"0\"]";
3658
+ /**
3659
+ * Give only the top modal Escape and Tab ownership, then restore its previous focus.
3660
+ * Automatic entry and return focus omit outlines; keyboard traversal retains its indicators.
3661
+ * Controls mounted with the dialog use data-modal-autofocus for initial focus;
3662
+ * React autoFocus runs before this layer can capture the invoking control.
3663
+ * Local menus handle their Escape during capture before this bubble listener.
3664
+ * @param dialog - mounted dialog element.
3665
+ * @param open - whether this layer is active.
3666
+ * @param onClose - top-layer Escape or application close action.
3667
+ */
3668
+ function useModalLayer(dialog, open, onClose) {
3669
+ const close = useRef(onClose);
3670
+ close.current = onClose;
3671
+ useLayoutEffect(() => {
3672
+ const element = dialog.current;
3673
+ if (!open || element === null) return;
3674
+ const document = element.ownerDocument;
3675
+ const composition = observeComposition(document);
3676
+ const previous = document.activeElement;
3677
+ const stack = layers.get(document) ?? [];
3678
+ layers.set(document, stack);
3679
+ const layer = {
3680
+ element,
3681
+ close: () => {
3682
+ close.current();
3683
+ }
3684
+ };
3685
+ stack.push(layer);
3686
+ const initial = element.querySelector("[data-modal-autofocus]") ?? element.querySelector(focusable) ?? element;
3687
+ if (!element.contains(document.activeElement)) focusWithoutRing(initial);
3688
+ const keydown = (event) => {
3689
+ const composing = composition.guards(event);
3690
+ if (stack.at(-1) !== layer || event.defaultPrevented || composing || event.ctrlKey || event.altKey || event.metaKey) return;
3691
+ if (event.key === "Escape" && !event.shiftKey) {
3692
+ event.preventDefault();
3693
+ if (!event.repeat) close.current();
3694
+ }
3695
+ if (event.key !== "Tab") return;
3696
+ if (document.activeElement?.closest("[role=\"menu\"]")) return;
3697
+ const items = [...element.querySelectorAll(focusable)].filter((item) => !item.closest("[inert], [hidden]"));
3698
+ const first = items[0] ?? element;
3699
+ const last = items.at(-1) ?? element;
3700
+ const atEdge = event.shiftKey ? document.activeElement === first : document.activeElement === last;
3701
+ if (document.activeElement === element || !element.contains(document.activeElement) || atEdge) {
3702
+ event.preventDefault();
3703
+ (event.shiftKey ? last : first).focus();
3704
+ }
3705
+ };
3706
+ document.addEventListener("keydown", keydown);
3707
+ return () => {
3708
+ composition.dispose();
3709
+ const wasTop = stack.at(-1) === layer;
3710
+ stack.splice(stack.indexOf(layer), 1);
3711
+ document.removeEventListener("keydown", keydown);
3712
+ if (stack.length === 0) layers.delete(document);
3713
+ if (wasTop) {
3714
+ const target = previous instanceof HTMLElement && previous.isConnected ? previous : stack.at(-1)?.element;
3715
+ if (target !== void 0) focusWithoutRing(target);
3716
+ }
3717
+ };
3718
+ }, [dialog, open]);
3719
+ }
3720
+ //#endregion
3721
+ //#region lib/types/ShortcutKeys.js
3722
+ /** Shared shortcut keycaps; callers supply the effective platform presentation. */
3723
+ /**
3724
+ * Render one command's keycaps without owning binding defaults or localized copy.
3725
+ * @param props - effective key labels, presentation variant and optional interaction styling.
3726
+ * @returns unboxed keys by default, or tooltip keycaps with plus-separated combinations grouped together.
3727
+ */
3728
+ function ShortcutKeys({ keys, variant = "plain", className }) {
3729
+ return jsx("span", {
3730
+ className: clsx(css$12.keys, variant === "tooltip" && css$12.tooltip, variant === "tooltip" && keys.includes("+") && css$12.joined, className),
3731
+ children: keys.map((key, index) => jsx("kbd", {
3732
+ className: key === "+" ? css$12.separator : css$12.key,
3733
+ children: key
3734
+ }, index))
3735
+ });
3736
+ }
3737
+ //#endregion
3738
+ //#region lib/types/MenuSurface.js
3739
+ /** Shared menu material and the macOS backing that lets Chromium blur transparent windows. */
3740
+ /**
3741
+ * Paint a menu and, on macOS, an opaque backing behind the page content within its bounds.
3742
+ * CSS anchors keep each backing aligned during placement, resizing, and nested-menu movement.
3743
+ * @param props - Div content and placement, and compact geometry.
3744
+ * @param ref - The visible menu div, excluding the non-interactive backing.
3745
+ * @returns Menu content plus a backing portal removed with the menu.
3746
+ */
3747
+ const MenuSurface = forwardRef(function MenuSurface({ compact = false, className, style, children, ...props }, ref) {
3748
+ const id = useId();
3749
+ const backingRef = useRef(null);
3750
+ useLayoutEffect(() => {
3751
+ document.body.appendChild(backingRef.current);
3752
+ }, []);
3753
+ const anchorStyle = { "--dsh-menu-anchor": `--dsh-menu-${id.replaceAll(":", "")}` };
3754
+ return jsxs(Fragment, { children: [jsxs("div", {
3755
+ ...props,
3756
+ ref,
3757
+ "data-menu-material": "translucent",
3758
+ className: clsx(css$13.surface, compact && css$13.compact, className),
3759
+ style: {
3760
+ ...style,
3761
+ ...anchorStyle
3762
+ },
3763
+ children: [jsx("div", {
3764
+ "aria-hidden": "true",
3765
+ className: css$13.material
3766
+ }), children]
3767
+ }), createPortal(jsx("div", {
3768
+ ref: backingRef,
3769
+ "aria-hidden": "true",
3770
+ "data-menu-backing": "",
3771
+ className: clsx(css$13.backing, compact && css$13.compact),
3772
+ style: {
3773
+ ...anchorStyle,
3774
+ visibility: style?.visibility
3775
+ }
3776
+ }), document.body)] });
3777
+ });
3778
+ //#endregion
3435
3779
  //#region lib/types/Menu.js
3436
3780
  /**
3437
3781
  * Render one `role="menuitem"` row for a {@link Menu} whose rows are
@@ -3440,6 +3784,7 @@ function usePointerGrace(close) {
3440
3784
  * without any shared state. Closing the menu stays the owner's decision, as
3441
3785
  * it is for data rows.
3442
3786
  * @param props.children - visible row label.
3787
+ * @param props.shortcut - effective key labels and accessible combination.
3443
3788
  * @param props.icon - optional leading icon.
3444
3789
  * @param props.disabled - whether the row cannot be activated.
3445
3790
  * @param props.danger - whether to use the destructive row colors.
@@ -3447,25 +3792,37 @@ function usePointerGrace(close) {
3447
3792
  * @param props.onSelect - row activation callback.
3448
3793
  * @returns one menu-item row.
3449
3794
  */
3450
- function MenuItemButton({ children, icon, disabled = false, danger = false, separatorBefore = false, onSelect }) {
3795
+ function MenuItemButton({ children, shortcut, icon, disabled = false, danger = false, separatorBefore = false, onSelect }) {
3451
3796
  return jsxs("div", {
3452
- className: css$12.itemWrap,
3797
+ className: css$14.itemWrap,
3453
3798
  children: [separatorBefore && jsx("div", {
3454
- className: css$12.separator,
3799
+ className: css$14.separator,
3455
3800
  role: "separator"
3456
3801
  }), jsxs("button", {
3457
3802
  type: "button",
3458
3803
  role: "menuitem",
3459
- className: clsx(css$12.item, danger && css$12.danger),
3804
+ className: clsx(css$14.item, danger && css$14.danger),
3460
3805
  disabled,
3806
+ "aria-keyshortcuts": shortcut?.aria,
3461
3807
  onClick: onSelect,
3462
- children: [icon !== void 0 && jsx("span", {
3463
- className: css$12.itemIcon,
3464
- children: icon
3465
- }), jsx("span", {
3466
- className: css$12.itemLabel,
3467
- children
3468
- })]
3808
+ children: [
3809
+ icon !== void 0 && jsx("span", {
3810
+ className: css$14.itemIcon,
3811
+ children: icon
3812
+ }),
3813
+ jsx("span", {
3814
+ className: css$14.itemLabel,
3815
+ children
3816
+ }),
3817
+ shortcut !== void 0 && jsx("span", {
3818
+ "aria-hidden": "true",
3819
+ className: css$14.shortcut,
3820
+ children: jsx(ShortcutKeys, {
3821
+ keys: shortcut.keys,
3822
+ className: css$14.shortcutKeys
3823
+ })
3824
+ })
3825
+ ]
3469
3826
  })]
3470
3827
  });
3471
3828
  }
@@ -3543,19 +3900,20 @@ function Menu({ open, anchor, items = [], children, selectedId, selectedIds, onS
3543
3900
  * able to name by position.
3544
3901
  */
3545
3902
  const triggerRef = useRef(null);
3903
+ const selectingWithTab = useRef(false);
3546
3904
  /**
3547
3905
  * Hand the keyboard back to the trigger that opened the menu — or, when the
3548
3906
  * anchor never held it, to the anchor's first button. Focus left on a removed
3549
3907
  * row otherwise falls to the page body, where the next Tab restarts from the
3550
3908
  * top of the page.
3909
+ * @param navigation - whether explicit keyboard traversal should retain its focus indicator.
3551
3910
  */
3552
- const refocusAnchor = () => {
3911
+ const refocusAnchor = (navigation = false) => {
3553
3912
  const trigger = triggerRef.current;
3554
- if (trigger !== null && document.contains(trigger) && !trigger.disabled) {
3555
- trigger.focus();
3556
- return;
3557
- }
3558
- rootRef.current?.querySelector("button:not(:disabled)")?.focus();
3913
+ const target = trigger !== null && document.contains(trigger) && !trigger.disabled ? trigger : rootRef.current?.querySelector("button:not(:disabled)");
3914
+ if (target == null) return;
3915
+ if (navigation) target.focus();
3916
+ else focusWithoutRing(target);
3559
3917
  };
3560
3918
  /**
3561
3919
  * Post-selection focus, for the paths where the rows unmount with the list.
@@ -3565,10 +3923,11 @@ function Menu({ open, anchor, items = [], children, selectedId, selectedIds, onS
3565
3923
  * its removal produced) comes back to the trigger.
3566
3924
  */
3567
3925
  const refocusAfterSelection = () => {
3926
+ const navigation = selectingWithTab.current;
3568
3927
  queueMicrotask(() => {
3569
3928
  if (openRef.current) return;
3570
3929
  const active = document.activeElement;
3571
- if (active === null || active === document.body || listRef.current?.contains(active) === true) refocusAnchor();
3930
+ if (active === null || active === document.body || listRef.current?.contains(active) === true) refocusAnchor(navigation);
3572
3931
  });
3573
3932
  };
3574
3933
  const openRef = useRef(open);
@@ -3645,7 +4004,7 @@ function Menu({ open, anchor, items = [], children, selectedId, selectedIds, onS
3645
4004
  if (!open || !autoFocus) return;
3646
4005
  const first = listRef.current?.querySelector("button:not(:disabled)");
3647
4006
  walkIndex.current = first === void 0 || first === null ? null : 0;
3648
- first?.focus();
4007
+ if (first != null) focusWithoutRing(first);
3649
4008
  }, [open, autoFocus]);
3650
4009
  useEffect(() => {
3651
4010
  if (!open) {
@@ -3653,6 +4012,7 @@ function Menu({ open, anchor, items = [], children, selectedId, selectedIds, onS
3653
4012
  walkIndex.current = null;
3654
4013
  return;
3655
4014
  }
4015
+ const composition = observeComposition(document);
3656
4016
  const onPointerDown = (e) => {
3657
4017
  if (!(e.target instanceof Node)) return;
3658
4018
  if (rootRef.current?.contains(e.target) === true) return;
@@ -3660,10 +4020,13 @@ function Menu({ open, anchor, items = [], children, selectedId, selectedIds, onS
3660
4020
  onClose();
3661
4021
  };
3662
4022
  const onKeyDown = (e) => {
4023
+ if (composition.guards(e) || isBehindModal(rootRef.current) || e.defaultPrevented || e.ctrlKey || e.altKey || e.metaKey) return;
3663
4024
  const focused = document.activeElement;
3664
4025
  const insideList = listRef.current?.contains(focused) === true;
3665
4026
  const anchored = rootRef.current?.contains(focused) === true || insideList;
3666
- if (e.key === "Escape") {
4027
+ if (e.key === "Escape" && !e.shiftKey) {
4028
+ e.preventDefault();
4029
+ if (e.repeat) return;
3667
4030
  onClose();
3668
4031
  if (anchored || autoFocus) refocusAnchor();
3669
4032
  }
@@ -3673,13 +4036,18 @@ function Menu({ open, anchor, items = [], children, selectedId, selectedIds, onS
3673
4036
  if (e.shiftKey) {
3674
4037
  e.preventDefault();
3675
4038
  onClose();
3676
- refocusAnchor();
4039
+ refocusAnchor(true);
3677
4040
  return;
3678
4041
  }
3679
4042
  if (insideList) {
3680
4043
  if (focused instanceof Element && focused.getAttribute("role") === "menuitem") {
3681
4044
  e.preventDefault();
3682
- focused.click();
4045
+ selectingWithTab.current = true;
4046
+ try {
4047
+ focused.click();
4048
+ } finally {
4049
+ selectingWithTab.current = false;
4050
+ }
3683
4051
  }
3684
4052
  return;
3685
4053
  }
@@ -3711,11 +4079,20 @@ function Menu({ open, anchor, items = [], children, selectedId, selectedIds, onS
3711
4079
  if (document.activeElement instanceof HTMLIFrameElement) onClose();
3712
4080
  };
3713
4081
  document.addEventListener("pointerdown", onPointerDown);
3714
- document.addEventListener("keydown", onKeyDown);
4082
+ const onEscape = (event) => {
4083
+ if (event.key === "Escape") onKeyDown(event);
4084
+ };
4085
+ const onOtherKey = (event) => {
4086
+ if (event.key !== "Escape") onKeyDown(event);
4087
+ };
4088
+ document.addEventListener("keydown", onOtherKey);
4089
+ document.addEventListener("keydown", onEscape, true);
3715
4090
  window.addEventListener("blur", onWindowBlur);
3716
4091
  return () => {
4092
+ composition.dispose();
3717
4093
  document.removeEventListener("pointerdown", onPointerDown);
3718
- document.removeEventListener("keydown", onKeyDown);
4094
+ document.removeEventListener("keydown", onOtherKey);
4095
+ document.removeEventListener("keydown", onEscape, true);
3719
4096
  window.removeEventListener("blur", onWindowBlur);
3720
4097
  };
3721
4098
  }, [
@@ -3729,11 +4106,11 @@ function Menu({ open, anchor, items = [], children, selectedId, selectedIds, onS
3729
4106
  const scrollable = !items.some((entry) => !isSeparator(entry) && !isLabel(entry) && entry.submenu !== void 0 && entry.submenu.length > 0);
3730
4107
  const renderEntry = (entry) => {
3731
4108
  if (isSeparator(entry)) return jsx("div", {
3732
- className: css$12.separator,
4109
+ className: css$14.separator,
3733
4110
  role: "separator"
3734
4111
  }, entry.id);
3735
4112
  if (isLabel(entry)) return jsx("div", {
3736
- className: css$12.label,
4113
+ className: css$14.label,
3737
4114
  role: "presentation",
3738
4115
  children: entry.text
3739
4116
  }, entry.id);
@@ -3741,7 +4118,7 @@ function Menu({ open, anchor, items = [], children, selectedId, selectedIds, onS
3741
4118
  const subOpen = hasSub && openSubmenuId === entry.id;
3742
4119
  const selected = entry.id === selectedId || selectedIds?.includes(entry.id) === true;
3743
4120
  return jsxs("div", {
3744
- className: css$12.itemWrap,
4121
+ className: css$14.itemWrap,
3745
4122
  onMouseEnter: hasSub ? () => {
3746
4123
  setOpenSubmenuId(entry.id);
3747
4124
  } : void 0,
@@ -3751,8 +4128,9 @@ function Menu({ open, anchor, items = [], children, selectedId, selectedIds, onS
3751
4128
  children: [jsxs("button", {
3752
4129
  type: "button",
3753
4130
  role: "menuitem",
3754
- className: clsx(css$12.item, selected && (selection === "fill" ? css$12.selectedFill : css$12.selected), entry.danger === true && css$12.danger),
4131
+ className: clsx(css$14.item, selected && (selection === "fill" ? css$14.selectedFill : css$14.selected), entry.danger === true && css$14.danger),
3755
4132
  disabled: entry.disabled,
4133
+ "aria-keyshortcuts": entry.shortcut?.aria,
3756
4134
  "aria-haspopup": hasSub ? "menu" : void 0,
3757
4135
  "aria-expanded": hasSub ? subOpen : void 0,
3758
4136
  onFocus: hasSub ? () => {
@@ -3767,33 +4145,55 @@ function Menu({ open, anchor, items = [], children, selectedId, selectedIds, onS
3767
4145
  },
3768
4146
  children: [
3769
4147
  entry.icon !== void 0 && jsx("span", {
3770
- className: css$12.itemIcon,
4148
+ className: css$14.itemIcon,
3771
4149
  children: entry.icon
3772
4150
  }),
3773
4151
  jsx("span", {
3774
- className: css$12.itemLabel,
4152
+ className: css$14.itemLabel,
3775
4153
  children: entry.label
3776
4154
  }),
3777
- selected && selection === "check" && jsx(IconCheckOutlineRegular, { className: css$12.check })
4155
+ entry.shortcut !== void 0 && jsx("span", {
4156
+ "aria-hidden": "true",
4157
+ className: css$14.shortcut,
4158
+ children: jsx(ShortcutKeys, {
4159
+ keys: entry.shortcut.keys,
4160
+ className: css$14.shortcutKeys
4161
+ })
4162
+ }),
4163
+ selected && selection === "check" && jsx(IconCheckOutlineRegular, { className: css$14.check })
3778
4164
  ]
3779
- }), subOpen && entry.submenu !== void 0 && jsx("div", {
3780
- className: clsx(css$12.submenu, compact && css$12.compactList),
4165
+ }), subOpen && entry.submenu !== void 0 && jsx(MenuSurface, {
4166
+ compact,
4167
+ className: clsx(css$14.submenu, compact && css$14.compactList),
3781
4168
  role: "menu",
3782
4169
  children: entry.submenu.map((sub) => jsxs("button", {
3783
4170
  type: "button",
3784
4171
  role: "menuitem",
3785
- className: css$12.item,
4172
+ className: css$14.item,
3786
4173
  disabled: sub.disabled,
4174
+ "aria-keyshortcuts": sub.shortcut?.aria,
3787
4175
  onClick: () => {
3788
4176
  onSelect?.(sub.id);
4177
+ refocusAfterSelection();
3789
4178
  },
3790
- children: [sub.icon !== void 0 && jsx("span", {
3791
- className: css$12.itemIcon,
3792
- children: sub.icon
3793
- }), jsx("span", {
3794
- className: css$12.itemLabel,
3795
- children: sub.label
3796
- })]
4179
+ children: [
4180
+ sub.icon !== void 0 && jsx("span", {
4181
+ className: css$14.itemIcon,
4182
+ children: sub.icon
4183
+ }),
4184
+ jsx("span", {
4185
+ className: css$14.itemLabel,
4186
+ children: sub.label
4187
+ }),
4188
+ sub.shortcut !== void 0 && jsx("span", {
4189
+ "aria-hidden": "true",
4190
+ className: css$14.shortcut,
4191
+ children: jsx(ShortcutKeys, {
4192
+ keys: sub.shortcut.keys,
4193
+ className: css$14.shortcutKeys
4194
+ })
4195
+ })
4196
+ ]
3797
4197
  }, sub.id))
3798
4198
  })]
3799
4199
  }, entry.id);
@@ -3804,9 +4204,10 @@ function Menu({ open, anchor, items = [], children, selectedId, selectedIds, onS
3804
4204
  if (row.closest("[role=\"menu\"]") !== e.currentTarget) return;
3805
4205
  setOpenSubmenuId(null);
3806
4206
  };
3807
- const list = open && jsxs("div", {
4207
+ const list = open && jsxs(MenuSurface, {
4208
+ compact,
3808
4209
  ref: listRef,
3809
- className: clsx(css$12.list, listClassName, dense && css$12.denseList, compact && css$12.compactList, scrollable && css$12.scrollable, portal && css$12.portal, side === "top" && !portal && css$12.sideTop, align === "end" && !portal && css$12.alignEnd),
4210
+ className: clsx(css$14.list, listClassName, dense && css$14.denseList, compact && css$14.compactList, scrollable && css$14.scrollable, portal && css$14.portal, side === "top" && !portal && css$14.sideTop, align === "end" && !portal && css$14.alignEnd),
3810
4211
  style: portal ? fixedPos ?? MEASURE_STYLE : void 0,
3811
4212
  role: "menu",
3812
4213
  onClick: (e) => {
@@ -3817,18 +4218,18 @@ function Menu({ open, anchor, items = [], children, selectedId, selectedIds, onS
3817
4218
  onMouseOver: collapseSubmenuFrom,
3818
4219
  onFocus: collapseSubmenuFrom,
3819
4220
  children: [jsxs("div", {
3820
- className: css$12.viewport,
4221
+ className: css$14.viewport,
3821
4222
  role: "presentation",
3822
4223
  children: [items.map(renderEntry), children]
3823
4224
  }), footer !== void 0 && footer.length > 0 && jsx("div", {
3824
- className: css$12.footer,
4225
+ className: css$14.footer,
3825
4226
  role: "presentation",
3826
4227
  children: footer.map(renderEntry)
3827
4228
  })]
3828
4229
  });
3829
4230
  return jsxs("span", {
3830
4231
  ref: rootRef,
3831
- className: clsx(css$12.root, className),
4232
+ className: clsx(css$14.root, className),
3832
4233
  onPointerEnter: closeOnPointerLeave ? cancelClose : void 0,
3833
4234
  onPointerLeave: closeOnPointerLeave ? () => {
3834
4235
  if (open) armClose();
@@ -4018,77 +4419,349 @@ async function writeClipboard(text) {
4018
4419
  }
4019
4420
  }
4020
4421
  //#endregion
4021
- //#region lib/types/HoverCard.js
4022
- /** Preview opacity transition and retained lifetime during dismissal. */
4023
- const PREVIEW_FADE_MS = 100;
4024
- const PREVIEW_MAX_HEIGHT = 420;
4025
- const PREVIEW_INSET = 24;
4026
- const ANCHOR_GAP = 8;
4027
- const VIEWPORT_MARGIN = 8;
4422
+ //#region lib/types/input-modality.js
4028
4423
  /**
4029
- * Render an anchor with a hover-triggered preview card.
4030
- * @param props.anchor - the hover target (rendered in place inside a wrapper span).
4031
- * @param props.content - card content; the pointer may rest on it, so it is
4032
- * readable and selectable, but it carries no dismissal affordance of its own.
4033
- * @param props.openDelayMs - hover dwell before the card shows (default 500).
4034
- * @param props.variant - compact card beside the anchor, or a preview above/below it
4035
- * with 24px side insets, a 420px height cap, frame-top clearance, and 100ms opacity transitions.
4036
- * @param props.widthAnchorRef - optional element whose width and horizontal position size the preview.
4037
- * @param props.disabled - suppress opening; turning true dismisses an open card.
4038
- * @param props.copyText - optional primary value copied by activation and
4039
- * included in the card's accessible name.
4040
- * @param props.copyLabel - localized accessible activation-label prefix.
4041
- * @param props.copiedLabel - localized visible success label.
4042
- * @returns anchor wrapper with the conditional portaled card.
4424
+ * Document-wide input tracking shared by tooltips and focus-ring styles.
4425
+ * Tooltips follow the last input; rings follow navigation or a key followed by
4426
+ * focus on a different control. Modifiers and refocusing alone keep rings silent.
4427
+ * Module-level listeners live for the document lifetime; Node imports are inert.
4043
4428
  */
4044
- function HoverCard({ anchor, content, openDelayMs = 500, disabled = false, copyText, copyLabel, copiedLabel, variant = "compact", widthAnchorRef }) {
4045
- const rootRef = useRef(null);
4046
- const cardRef = useRef(null);
4047
- const timerRef = useRef(null);
4048
- const copyTimerRef = useRef(null);
4049
- const copyHeightRef = useRef(null);
4050
- const copyEpochRef = useRef(0);
4051
- const copyingRef = useRef(false);
4052
- const mountedRef = useRef(true);
4053
- const [phase, setPhase] = useState("closed");
4054
- const open = phase !== "closed";
4055
- const closing = phase === "closing";
4056
- const [pos, setPos] = useState(null);
4057
- const positioned = pos !== null;
4058
- const [copied, setCopied] = useState(false);
4059
- const clearCopied = useCallback(() => {
4060
- if (copyTimerRef.current !== null) {
4061
- clearTimeout(copyTimerRef.current);
4062
- copyTimerRef.current = null;
4063
- }
4064
- copyHeightRef.current = null;
4065
- setCopied(false);
4066
- }, []);
4067
- const close = useCallback(() => {
4068
- copyEpochRef.current += 1;
4069
- clearCopied();
4070
- setPhase((current) => variant === "preview" && current !== "closed" ? "closing" : "closed");
4071
- }, [clearCopied, variant]);
4072
- const { arm: armClose, cancel: cancelClose } = usePointerGrace(close);
4073
- const clearTimer = () => {
4074
- if (timerRef.current !== null) {
4075
- clearTimeout(timerRef.current);
4076
- timerRef.current = null;
4429
+ /** Modality values published on the document element. */
4430
+ const INPUT_MODALITY = {
4431
+ pointer: "pointer",
4432
+ keyboard: "keyboard"
4433
+ };
4434
+ /** Attribute carrying whether focus navigation last owned focus. */
4435
+ const INPUT_MODALITY_ATTRIBUTE = "data-input-modality";
4436
+ const FOCUS_NAVIGATION = new Set([
4437
+ "Tab",
4438
+ "Home",
4439
+ "End",
4440
+ "PageUp",
4441
+ "PageDown"
4442
+ ]);
4443
+ let pointer = false;
4444
+ let pointerOwnsFocus = false;
4445
+ let keyFocusOrigin;
4446
+ function publish() {
4447
+ document.documentElement.setAttribute(INPUT_MODALITY_ATTRIBUTE, pointerOwnsFocus ? INPUT_MODALITY.pointer : INPUT_MODALITY.keyboard);
4448
+ }
4449
+ /**
4450
+ * Whether the last input came from a pointer, independently of ring visibility.
4451
+ * @returns True after pointer input; false after any key.
4452
+ */
4453
+ function pointerModality() {
4454
+ return pointer;
4455
+ }
4456
+ if (typeof window !== "undefined") {
4457
+ window.addEventListener("pointerdown", () => {
4458
+ pointer = true;
4459
+ pointerOwnsFocus = true;
4460
+ keyFocusOrigin = void 0;
4461
+ publish();
4462
+ }, true);
4463
+ window.addEventListener("keydown", (event) => {
4464
+ pointer = false;
4465
+ if (event.isComposing) {
4466
+ keyFocusOrigin = void 0;
4467
+ return;
4077
4468
  }
4078
- };
4079
- useEffect(() => {
4080
- if (!closing) return;
4081
- const timer = setTimeout(() => {
4082
- setPhase("closed");
4083
- }, PREVIEW_FADE_MS);
4084
- return () => {
4085
- clearTimeout(timer);
4086
- };
4087
- }, [closing]);
4088
- useEffect(() => {
4089
- if (!disabled) return;
4090
- clearTimer();
4091
- cancelClose();
4469
+ keyFocusOrigin = event.composedPath()[0];
4470
+ if (!FOCUS_NAVIGATION.has(event.key) && !event.key.startsWith("Arrow")) return;
4471
+ pointerOwnsFocus = false;
4472
+ publish();
4473
+ }, true);
4474
+ window.addEventListener("focusin", (event) => {
4475
+ if (keyFocusOrigin === void 0 || event.composedPath()[0] === keyFocusOrigin) return;
4476
+ keyFocusOrigin = void 0;
4477
+ if (!pointerOwnsFocus) return;
4478
+ pointerOwnsFocus = false;
4479
+ publish();
4480
+ }, true);
4481
+ window.addEventListener("blur", () => {
4482
+ keyFocusOrigin = void 0;
4483
+ });
4484
+ }
4485
+ //#endregion
4486
+ //#region lib/types/Tooltip.js
4487
+ /** Anchor-preserving tooltips; an optional body portal escapes clipping containers and stacking contexts that cap the bubble's z-index. */
4488
+ /**
4489
+ * Suppression channel for enclosing tooltip and hover-card anchors: a visible
4490
+ * tooltip within an anchor withdraws the enclosing preview while its bubble is shown.
4491
+ */
4492
+ const TooltipSuppression = createContext(null);
4493
+ /**
4494
+ * Attach a hover/focus tooltip to an anchor element.
4495
+ * @param props.label - bubble text, or a resolver evaluated only while visible; an empty string shows only shortcut keys.
4496
+ * @param props.shortcutKeys - effective key labels rendered as platform-formatted keycaps after optional text.
4497
+ * @param props.side - placement relative to the anchor (default 'right').
4498
+ * @param props.align - horizontal anchor-edge alignment for 'bottom'/'top' bubbles: 'end' pins
4499
+ * the bubble's right edge to the anchor's (for anchors beside other hover surfaces the centered
4500
+ * bubble would overlap); default 'center'. Ignored for side 'right'.
4501
+ * @param props.portal - render the bubble under document.body, so an ancestor's clipping or its
4502
+ * stacking context (which confines the bubble's z-index to that context) cannot hide it.
4503
+ * @param props.delayMs - hover delay in milliseconds; keyboard focus remains immediate.
4504
+ * @param props.gap - anchor-to-bubble distance in pixels for 'bottom'/'top' bubbles (default 8);
4505
+ * ignored for side 'right'.
4506
+ * @param props.disabled - suppress the bubble while true; the anchor renders identically so
4507
+ * toggling never remounts it (which would cut its CSS transitions).
4508
+ * @param props.maxWidth - bubble width cap in pixels, for labels long enough that the default
4509
+ * half-viewport cap would render a slab wider than the surface the anchor sits on.
4510
+ * @param props.children - a single anchor element; its own ref (callback or object) is forwarded alongside the tooltip's.
4511
+ * @returns the cloned anchor plus a fixed-position bubble, optionally portaled to the body.
4512
+ * The bubble stays hidden until ResizeObserver supplies its size for viewport fitting; clicking the
4513
+ * anchor dismisses the bubble until the next trigger, and focus arriving after a pointer
4514
+ * interaction (a closing menu refocusing its trigger) never raises it.
4515
+ */
4516
+ function Tooltip({ label, shortcutKeys, side = "right", align = "center", delayMs = 0, gap = 8, disabled = false, portal = false, maxWidth, children }) {
4517
+ const anchor = useRef(null);
4518
+ const childRef = children.ref;
4519
+ const mergedRef = useCallback((el) => {
4520
+ anchor.current = el;
4521
+ if (typeof childRef === "function") childRef(el);
4522
+ else if (childRef != null) childRef.current = el;
4523
+ }, [childRef]);
4524
+ const [pos, setPos] = useState(null);
4525
+ const bubble = useRef(null);
4526
+ const resolvedLabel = pos === null ? null : typeof label === "function" ? label() : label;
4527
+ const y = pos === null ? 0 : side === "right" ? pos.top + (pos.bottom - pos.top) / 2 : side === "top" ? pos.top - gap : pos.bottom + gap;
4528
+ const showTimer = useRef(null);
4529
+ const triggers = useRef({
4530
+ hover: false,
4531
+ focus: false
4532
+ });
4533
+ const suppressAncestors = useContext(TooltipSuppression);
4534
+ const [suppressed, setSuppressed] = useState(false);
4535
+ const announce = useCallback((active) => {
4536
+ suppressAncestors?.(active);
4537
+ }, [suppressAncestors]);
4538
+ const visible = pos !== null && !disabled;
4539
+ useEffect(() => {
4540
+ const el = bubble.current;
4541
+ if (pos === null || !visible || suppressed || el === null) return;
4542
+ const edgeMargin = 12;
4543
+ let size;
4544
+ let placement = side;
4545
+ const fit = () => {
4546
+ if (size === void 0) return;
4547
+ const { inlineSize: width, blockSize: height } = size;
4548
+ const offset = side === "right" ? 0 : align === "end" ? width : width / 2;
4549
+ const left = Math.max(edgeMargin, Math.min(pos.x - offset, window.innerWidth - edgeMargin - width));
4550
+ const fitsBelow = pos.bottom + gap + height <= window.innerHeight - edgeMargin;
4551
+ const fitsAbove = pos.top - gap - height >= edgeMargin;
4552
+ if (placement === "bottom" && !fitsBelow && fitsAbove) placement = "top";
4553
+ else if (placement === "top" && !fitsAbove && fitsBelow) placement = "bottom";
4554
+ el.style.left = `${left + offset}px`;
4555
+ el.style.top = `${placement === "right" ? (pos.top + pos.bottom) / 2 : placement === "top" ? pos.top - gap : pos.bottom + gap}px`;
4556
+ el.dataset.side = placement;
4557
+ el.style.visibility = "visible";
4558
+ };
4559
+ const observer = new ResizeObserver((entries) => {
4560
+ size = entries[0]?.borderBoxSize[0];
4561
+ fit();
4562
+ });
4563
+ observer.observe(el, { box: "border-box" });
4564
+ window.addEventListener("resize", fit);
4565
+ return () => {
4566
+ observer.disconnect();
4567
+ window.removeEventListener("resize", fit);
4568
+ };
4569
+ }, [
4570
+ align,
4571
+ gap,
4572
+ pos,
4573
+ side,
4574
+ suppressed,
4575
+ visible
4576
+ ]);
4577
+ useEffect(() => {
4578
+ announce(visible);
4579
+ return () => {
4580
+ announce(false);
4581
+ };
4582
+ }, [announce, visible]);
4583
+ const cancelShow = useCallback(() => {
4584
+ if (showTimer.current === null) return;
4585
+ clearTimeout(showTimer.current);
4586
+ showTimer.current = null;
4587
+ }, []);
4588
+ useEffect(() => {
4589
+ if (disabled) {
4590
+ cancelShow();
4591
+ triggers.current = {
4592
+ hover: false,
4593
+ focus: false
4594
+ };
4595
+ setPos(null);
4596
+ }
4597
+ return cancelShow;
4598
+ }, [cancelShow, disabled]);
4599
+ const show = () => {
4600
+ if (disabled) return;
4601
+ const el = anchor.current;
4602
+ /* v8 ignore next -- the ref is attached by event time: events fire on the cloned anchor. */
4603
+ if (el === null) return;
4604
+ const r = el.getBoundingClientRect();
4605
+ setPos({
4606
+ x: side === "right" ? r.right + 10 : align === "end" ? r.right : r.left + r.width / 2,
4607
+ top: r.top,
4608
+ bottom: r.bottom
4609
+ });
4610
+ announce(true);
4611
+ };
4612
+ const showAfterHoverDelay = () => {
4613
+ cancelShow();
4614
+ if (delayMs <= 0) {
4615
+ show();
4616
+ return;
4617
+ }
4618
+ showTimer.current = setTimeout(() => {
4619
+ showTimer.current = null;
4620
+ show();
4621
+ }, delayMs);
4622
+ };
4623
+ const withdraw = () => {
4624
+ setPos(null);
4625
+ announce(false);
4626
+ };
4627
+ const hide = () => {
4628
+ cancelShow();
4629
+ if (!triggers.current.hover && !triggers.current.focus) withdraw();
4630
+ };
4631
+ const content = visible && !suppressed && jsxs("span", {
4632
+ ref: bubble,
4633
+ className: css$15.bubble,
4634
+ "data-side": side,
4635
+ "data-portal": portal || void 0,
4636
+ "data-align": align,
4637
+ "data-has-shortcut": shortcutKeys?.length ? true : void 0,
4638
+ style: {
4639
+ left: pos.x,
4640
+ top: y,
4641
+ visibility: "hidden",
4642
+ ...maxWidth === void 0 ? {} : { maxWidth }
4643
+ },
4644
+ role: "tooltip",
4645
+ "aria-label": shortcutKeys?.length ? [resolvedLabel, shortcutKeys.join(" ")].filter(Boolean).join(" ") : void 0,
4646
+ children: [resolvedLabel && jsx("span", {
4647
+ className: css$15.label,
4648
+ children: resolvedLabel
4649
+ }), shortcutKeys !== void 0 && shortcutKeys.length > 0 && jsx(ShortcutKeys, {
4650
+ keys: shortcutKeys,
4651
+ variant: "tooltip"
4652
+ })]
4653
+ });
4654
+ return jsxs(TooltipSuppression.Provider, {
4655
+ value: setSuppressed,
4656
+ children: [cloneElement(children, {
4657
+ ref: mergedRef,
4658
+ onMouseEnter: (e) => {
4659
+ children.props.onMouseEnter?.(e);
4660
+ triggers.current.hover = true;
4661
+ showAfterHoverDelay();
4662
+ },
4663
+ onMouseLeave: (e) => {
4664
+ children.props.onMouseLeave?.(e);
4665
+ triggers.current.hover = false;
4666
+ cancelShow();
4667
+ withdraw();
4668
+ },
4669
+ onClick: (e) => {
4670
+ children.props.onClick?.(e);
4671
+ triggers.current.focus = false;
4672
+ cancelShow();
4673
+ withdraw();
4674
+ },
4675
+ onFocus: (e) => {
4676
+ children.props.onFocus?.(e);
4677
+ if (pointerModality()) return;
4678
+ triggers.current.focus = true;
4679
+ cancelShow();
4680
+ show();
4681
+ },
4682
+ onBlur: (e) => {
4683
+ children.props.onBlur?.(e);
4684
+ triggers.current.focus = false;
4685
+ hide();
4686
+ }
4687
+ }), portal ? content !== false && createPortal(content, document.body) : content]
4688
+ });
4689
+ }
4690
+ //#endregion
4691
+ //#region lib/types/HoverCard.js
4692
+ /** Preview opacity transition and retained lifetime during dismissal. */
4693
+ const PREVIEW_FADE_MS = 100;
4694
+ const PREVIEW_MAX_HEIGHT = 420;
4695
+ const PREVIEW_INSET = 24;
4696
+ const INLINE_PREVIEW_WIDTH = 300;
4697
+ const ANCHOR_GAP = 8;
4698
+ const VIEWPORT_MARGIN = 8;
4699
+ /**
4700
+ * Render an anchor with a hover-triggered preview card, hidden while a tooltip within the anchor is visible.
4701
+ * @param props.anchor - the hover target (rendered in place inside a wrapper span).
4702
+ * @param props.content - card content; the pointer may rest on it, so it is
4703
+ * readable and selectable, but it carries no dismissal affordance of its own.
4704
+ * @param props.openDelayMs - hover dwell before the card shows (default 500).
4705
+ * @param props.variant - compact card beside the anchor, or a preview above/below it
4706
+ * with 24px side insets, a 420px height cap, frame-top clearance, and 100ms opacity transitions.
4707
+ * @param props.widthAnchorRef - optional element whose width and horizontal position size the preview.
4708
+ * @param props.inline - keep the anchor in prose; show a contained preview on hover or keyboard focus.
4709
+ * @param props.disabled - suppress opening; turning true dismisses an open card.
4710
+ * @param props.copyText - optional primary value copied by activation and
4711
+ * included in the card's accessible name.
4712
+ * @param props.copyLabel - localized accessible activation-label prefix.
4713
+ * @param props.copiedLabel - localized visible success label.
4714
+ * @returns anchor wrapper with the conditional portaled card.
4715
+ */
4716
+ function HoverCard({ anchor, content, openDelayMs = 500, disabled = false, copyText, copyLabel, copiedLabel, variant = "compact", widthAnchorRef, inline = false }) {
4717
+ const rootRef = useRef(null);
4718
+ const cardRef = useRef(null);
4719
+ const timerRef = useRef(null);
4720
+ const copyTimerRef = useRef(null);
4721
+ const copyHeightRef = useRef(null);
4722
+ const copyEpochRef = useRef(0);
4723
+ const copyingRef = useRef(false);
4724
+ const mountedRef = useRef(true);
4725
+ const [phase, setPhase] = useState("closed");
4726
+ const open = phase !== "closed";
4727
+ const closing = phase === "closing";
4728
+ const [pos, setPos] = useState(null);
4729
+ const positioned = pos !== null;
4730
+ const [copied, setCopied] = useState(false);
4731
+ const [suppressed, setSuppressed] = useState(false);
4732
+ const clearCopied = useCallback(() => {
4733
+ if (copyTimerRef.current !== null) {
4734
+ clearTimeout(copyTimerRef.current);
4735
+ copyTimerRef.current = null;
4736
+ }
4737
+ copyHeightRef.current = null;
4738
+ setCopied(false);
4739
+ }, []);
4740
+ const close = useCallback(() => {
4741
+ copyEpochRef.current += 1;
4742
+ clearCopied();
4743
+ setPhase((current) => variant === "preview" && current !== "closed" ? "closing" : "closed");
4744
+ }, [clearCopied, variant]);
4745
+ const { arm: armClose, cancel: cancelClose } = usePointerGrace(close);
4746
+ const clearTimer = () => {
4747
+ if (timerRef.current !== null) {
4748
+ clearTimeout(timerRef.current);
4749
+ timerRef.current = null;
4750
+ }
4751
+ };
4752
+ useEffect(() => {
4753
+ if (!closing) return;
4754
+ const timer = setTimeout(() => {
4755
+ setPhase("closed");
4756
+ }, PREVIEW_FADE_MS);
4757
+ return () => {
4758
+ clearTimeout(timer);
4759
+ };
4760
+ }, [closing]);
4761
+ useEffect(() => {
4762
+ if (!disabled) return;
4763
+ clearTimer();
4764
+ cancelClose();
4092
4765
  close();
4093
4766
  }, [
4094
4767
  disabled,
@@ -4108,19 +4781,22 @@ function HoverCard({ anchor, content, openDelayMs = 500, disabled = false, copyT
4108
4781
  };
4109
4782
  }, []);
4110
4783
  useEffect(() => {
4111
- if (!open || variant !== "preview") return;
4784
+ if (!open || variant !== "preview" && !inline) return;
4112
4785
  const dismiss = (event) => {
4113
4786
  if (event.key !== "Escape") return;
4787
+ if (inline) event.stopPropagation();
4788
+ clearTimer();
4114
4789
  cancelClose();
4115
4790
  close();
4116
4791
  };
4117
- window.addEventListener("keydown", dismiss);
4792
+ window.addEventListener("keydown", dismiss, inline);
4118
4793
  return () => {
4119
- window.removeEventListener("keydown", dismiss);
4794
+ window.removeEventListener("keydown", dismiss, inline);
4120
4795
  };
4121
4796
  }, [
4122
4797
  open,
4123
4798
  variant,
4799
+ inline,
4124
4800
  cancelClose,
4125
4801
  close
4126
4802
  ]);
@@ -4152,6 +4828,23 @@ function HoverCard({ anchor, content, openDelayMs = 500, disabled = false, copyT
4152
4828
  });
4153
4829
  return;
4154
4830
  }
4831
+ if (inline) {
4832
+ const height = Math.max(h, cardRef.current?.scrollHeight ?? 0);
4833
+ const width = Math.max(0, Math.min(INLINE_PREVIEW_WIDTH, window.innerWidth - VIEWPORT_MARGIN * 2));
4834
+ const topMargin = overlayTopMargin(VIEWPORT_MARGIN);
4835
+ const belowTop = Math.max(topMargin, r.bottom + ANCHOR_GAP);
4836
+ const above = Math.max(0, r.top - ANCHOR_GAP - topMargin);
4837
+ const below = Math.max(0, window.innerHeight - belowTop - VIEWPORT_MARGIN);
4838
+ const onTop = height > below && above > below;
4839
+ const maxHeight = onTop ? above : below;
4840
+ setPos({
4841
+ left: Math.max(VIEWPORT_MARGIN, Math.min(r.left, window.innerWidth - width - VIEWPORT_MARGIN)),
4842
+ top: onTop ? r.top - ANCHOR_GAP - Math.min(height, maxHeight) : belowTop,
4843
+ width,
4844
+ maxHeight
4845
+ });
4846
+ return;
4847
+ }
4155
4848
  const top = r.top + h > window.innerHeight - VIEWPORT_MARGIN ? window.innerHeight - h - VIEWPORT_MARGIN : r.top;
4156
4849
  setPos({
4157
4850
  left: r.right + ANCHOR_GAP,
@@ -4159,7 +4852,7 @@ function HoverCard({ anchor, content, openDelayMs = 500, disabled = false, copyT
4159
4852
  });
4160
4853
  };
4161
4854
  place();
4162
- const observer = variant === "preview" && typeof ResizeObserver !== "undefined" ? new ResizeObserver(place) : null;
4855
+ const observer = (variant === "preview" || inline) && typeof ResizeObserver !== "undefined" ? new ResizeObserver(place) : null;
4163
4856
  for (const element of [
4164
4857
  cardRef.current,
4165
4858
  rootRef.current,
@@ -4176,10 +4869,11 @@ function HoverCard({ anchor, content, openDelayMs = 500, disabled = false, copyT
4176
4869
  open,
4177
4870
  variant,
4178
4871
  widthAnchorRef,
4179
- positioned
4872
+ positioned,
4873
+ inline
4180
4874
  ]);
4181
4875
  useLayoutEffect(() => {
4182
- if (!open || pos === null || variant === "preview") return;
4876
+ if (!open || pos === null || variant === "preview" || inline || suppressed) return;
4183
4877
  /* v8 ignore next -- the card is mounted whenever pos is set, so the ref is attached here. */
4184
4878
  const h = cardRef.current?.offsetHeight ?? 0;
4185
4879
  if (pos.top + h > window.innerHeight - VIEWPORT_MARGIN) setPos({
@@ -4189,7 +4883,9 @@ function HoverCard({ anchor, content, openDelayMs = 500, disabled = false, copyT
4189
4883
  }, [
4190
4884
  open,
4191
4885
  pos,
4192
- variant
4886
+ variant,
4887
+ inline,
4888
+ suppressed
4193
4889
  ]);
4194
4890
  const copy = async (text) => {
4195
4891
  if (copied || copyingRef.current) return;
@@ -4211,9 +4907,9 @@ function HoverCard({ anchor, content, openDelayMs = 500, disabled = false, copyT
4211
4907
  cancelClose();
4212
4908
  close();
4213
4909
  };
4214
- const card = open && pos !== null && jsx("div", {
4910
+ const card = open && pos !== null && !suppressed && jsx("div", {
4215
4911
  ref: cardRef,
4216
- className: clsx(css$13.card, variant === "preview" && css$13.preview, copyable && css$13.copyable, copied && css$13.feedback),
4912
+ className: clsx(css$16.card, variant === "preview" && css$16.preview, inline && css$16.media, copyable && css$16.copyable, copied && css$16.feedback),
4217
4913
  "data-closing": closing || void 0,
4218
4914
  style: {
4219
4915
  ...pos,
@@ -4236,16 +4932,29 @@ function HoverCard({ anchor, content, openDelayMs = 500, disabled = false, copyT
4236
4932
  copy(copyText);
4237
4933
  } : void 0,
4238
4934
  children: copied ? jsx("span", {
4239
- className: css$13.copied,
4935
+ className: css$16.copied,
4240
4936
  "aria-hidden": "true",
4241
4937
  children: copiedLabel
4242
4938
  }) : content
4243
4939
  });
4244
4940
  return jsxs("span", {
4245
4941
  ref: rootRef,
4246
- className: css$13.root,
4247
- onPointerEnter: () => {
4248
- if (disabled) return;
4942
+ className: clsx(css$16.root, inline && css$16.inline),
4943
+ onFocus: inline ? (event) => {
4944
+ if (!disabled && event.target.matches(":focus-visible")) {
4945
+ cancelClose();
4946
+ setPhase("open");
4947
+ }
4948
+ } : void 0,
4949
+ onBlur: inline ? (event) => {
4950
+ if (!event.currentTarget.contains(event.relatedTarget)) {
4951
+ clearTimer();
4952
+ cancelClose();
4953
+ close();
4954
+ }
4955
+ } : void 0,
4956
+ onPointerEnter: (event) => {
4957
+ if (disabled || inline && event.pointerType === "touch") return;
4249
4958
  cancelClose();
4250
4959
  if (open) {
4251
4960
  setPhase("open");
@@ -4263,9 +4972,12 @@ function HoverCard({ anchor, content, openDelayMs = 500, disabled = false, copyT
4263
4972
  onPointerDownCapture: dismissFromAnchor,
4264
4973
  onClickCapture: dismissFromAnchor,
4265
4974
  children: [
4266
- anchor,
4267
- open && copyable && jsx("span", {
4268
- className: css$13.status,
4975
+ jsx(TooltipSuppression.Provider, {
4976
+ value: setSuppressed,
4977
+ children: anchor
4978
+ }),
4979
+ open && !suppressed && copyable && jsx("span", {
4980
+ className: css$16.status,
4269
4981
  role: "status",
4270
4982
  children: copied ? copiedLabel : ""
4271
4983
  }),
@@ -4278,104 +4990,77 @@ function HoverCard({ anchor, content, openDelayMs = 500, disabled = false, copyT
4278
4990
  /**
4279
4991
  * Render a centered, body-portaled modal over a blurred page mask.
4280
4992
  * @param props.open - whether the dialog is showing.
4281
- * @param props.onClose - Escape or mask click; while a menu is open inside the
4993
+ * @param props.onClose - application close command, Escape, or mask click; while a menu is open inside the
4282
4994
  * dialog, Escape belongs to that menu first.
4283
4995
  * @param props.title - dialog heading (aria-label in every mode).
4284
4996
  * @param props.closeLabel - localized accessible close-button label.
4285
4997
  * @param props.description - optional supporting sentence under the title.
4286
- * @param props.children - body (inputs, etc.).
4998
+ * @param props.children - dialog body; mark its initial-focus control with
4999
+ * data-modal-autofocus instead of React autoFocus to preserve return focus.
4287
5000
  * @param props.footer - action row (Cancel / Create).
4288
5001
  * @param props.contentClassName - optional class for a scrollable content region.
5002
+ * @param props.backdropBlur - disable when the caller already blurs the page; defaults to true.
5003
+ * @param props.shortcutModal - command scope allowed by shortcut owners; unnamed
5004
+ * dialogs block application commands unless their owner allows the "other" scope.
4289
5005
  * @param props.headless - render children directly in the card (no default
4290
5006
  * header/close/body chrome); mask, card, Escape, and aria-label remain.
5007
+ * @param props.onKeyDownCapture - handle a nested dialog's keys before the document Escape listeners.
4291
5008
  * @returns null when closed; otherwise the overlay tree.
4292
5009
  */
4293
- function Modal({ open, onClose, title, closeLabel, description, children, footer, className, contentClassName, headless = false }) {
4294
- useEffect(() => {
4295
- if (!open) return;
4296
- const onKeyDown = (e) => {
4297
- if (e.key === "Escape") onClose();
4298
- };
4299
- document.addEventListener("keydown", onKeyDown);
4300
- return () => {
4301
- document.removeEventListener("keydown", onKeyDown);
4302
- };
4303
- }, [open, onClose]);
5010
+ function Modal({ open, onClose, title, closeLabel, description, children, footer, className, contentClassName, onKeyDownCapture, headless = false, backdropBlur = true, shortcutModal }) {
5011
+ const dialog = useRef(null);
5012
+ useModalLayer(dialog, open, onClose);
4304
5013
  if (!open) return null;
4305
5014
  return createPortal(jsxs("div", {
4306
- className: css$14.root,
5015
+ className: css$17.root,
4307
5016
  role: "presentation",
5017
+ onKeyDownCapture,
4308
5018
  children: [jsx("div", {
4309
- className: css$14.mask,
5019
+ className: css$17.mask,
5020
+ style: backdropBlur ? void 0 : { backdropFilter: "none" },
4310
5021
  "aria-hidden": "true",
4311
5022
  onClick: onClose
4312
5023
  }), jsx("div", {
4313
- className: clsx(css$14.dialog, className),
5024
+ ref: dialog,
5025
+ tabIndex: -1,
5026
+ "data-shortcut-modal": shortcutModal,
5027
+ className: clsx(css$17.dialog, className),
4314
5028
  role: "dialog",
4315
5029
  "aria-modal": "true",
4316
5030
  "aria-label": title,
4317
5031
  children: headless ? children : jsxs(Fragment, { children: [jsxs("div", {
4318
- className: clsx(css$14.content, contentClassName),
5032
+ className: clsx(css$17.content, contentClassName),
4319
5033
  children: [
4320
5034
  jsxs("div", {
4321
- className: css$14.header,
5035
+ className: css$17.header,
4322
5036
  children: [jsx("h2", {
4323
- className: css$14.title,
5037
+ className: css$17.title,
4324
5038
  children: title
4325
5039
  }), jsx("button", {
4326
5040
  type: "button",
4327
- className: css$14.close,
5041
+ className: css$17.close,
4328
5042
  "aria-label": closeLabel,
4329
5043
  onClick: onClose,
4330
5044
  children: jsx(IconCloseOutlineRegular, { size: 14 })
4331
5045
  })]
4332
5046
  }),
4333
5047
  description !== void 0 && description !== "" && jsx("p", {
4334
- className: css$14.description,
5048
+ className: css$17.description,
4335
5049
  children: description
4336
5050
  }),
4337
5051
  children !== void 0 && jsx("div", {
4338
- className: css$14.body,
5052
+ className: css$17.body,
4339
5053
  children
4340
5054
  })
4341
5055
  ]
4342
5056
  }), footer !== void 0 && jsx("div", {
4343
- className: css$14.footer,
5057
+ className: css$17.footer,
4344
5058
  children: footer
4345
5059
  })] })
4346
5060
  })]
4347
5061
  }), document.body);
4348
5062
  }
4349
5063
  //#endregion
4350
- //#region lib/types/OnboardingSurface.js
4351
- /**
4352
- * Render a body-portaled onboarding stage and keep the application root inert
4353
- * while mounted.
4354
- * @param props.children - the step's page content, centered on the stage.
4355
- * @returns the body-portaled overlay tree.
4356
- */
4357
- function OnboardingSurface({ children }) {
4358
- useEffect(() => {
4359
- const appRoot = document.getElementById("root");
4360
- if (appRoot === null) return;
4361
- appRoot.inert = true;
4362
- return () => {
4363
- appRoot.inert = false;
4364
- };
4365
- }, []);
4366
- return createPortal(jsxs("div", {
4367
- className: css$15.onboardingOverlay,
4368
- role: "presentation",
4369
- children: [jsx("div", {
4370
- className: css$15.onboardingMask,
4371
- "aria-hidden": "true"
4372
- }), jsx("div", {
4373
- className: css$15.onboardingStage,
4374
- children
4375
- })]
4376
- }), document.body);
4377
- }
4378
- //#endregion
4379
5064
  //#region lib/types/RiskConfirmation.js
4380
5065
  /**
4381
5066
  * Controlled risk acknowledgement dialog shared by product surfaces that
@@ -4391,33 +5076,33 @@ function RiskConfirmation({ open, title, description, acknowledgeLabel, cancelLa
4391
5076
  onClose: onCancel,
4392
5077
  title,
4393
5078
  closeLabel,
4394
- className: css$16.confirmation ?? "",
4395
- contentClassName: css$16.confirmationContent ?? "",
5079
+ className: css$18.confirmation ?? "",
5080
+ contentClassName: css$18.confirmationContent ?? "",
4396
5081
  footer: jsxs(Fragment, { children: [jsx(Button, {
4397
5082
  variant: "outline",
4398
- className: css$16.modalAction,
5083
+ className: css$18.modalAction,
4399
5084
  onClick: onCancel,
4400
5085
  children: cancelLabel
4401
5086
  }), jsx(Button, {
4402
5087
  variant: "primary",
4403
- className: css$16.confirmAction,
5088
+ className: css$18.confirmAction,
4404
5089
  disabled: disabled || !acknowledged,
4405
5090
  onClick: onConfirm,
4406
5091
  children: confirmLabel
4407
5092
  })] }),
4408
5093
  children: [jsxs("div", {
4409
- className: css$16.warning,
5094
+ className: css$18.warning,
4410
5095
  children: [jsx(IconWarningOutlineRegular, {
4411
5096
  size: 18,
4412
- className: css$16.warningIcon
5097
+ className: css$18.warningIcon
4413
5098
  }), jsx("p", { children: description })]
4414
5099
  }), jsxs("label", {
4415
- className: css$16.acknowledgement,
5100
+ className: css$18.acknowledgement,
4416
5101
  children: [jsx("input", {
4417
5102
  type: "checkbox",
4418
5103
  checked: acknowledged,
4419
5104
  disabled,
4420
- autoFocus: true,
5105
+ "data-modal-autofocus": true,
4421
5106
  onChange: (event) => {
4422
5107
  onAcknowledgedChange(event.currentTarget.checked);
4423
5108
  }
@@ -4460,44 +5145,44 @@ function ConnectionIndicator({ state, disconnectedLabel, connectingLabel, recove
4460
5145
  };
4461
5146
  }, [state, rendered]);
4462
5147
  if (rendered === void 0) return null;
4463
- const leavingClass = leaving ? ` ${css$17.leaving}` : "";
5148
+ const leavingClass = leaving ? ` ${css$19.leaving}` : "";
4464
5149
  if (rendered === "recovered") return jsxs("div", {
4465
- className: `${css$17.indicator} ${css$17.success}${leavingClass}`,
5150
+ className: `${css$19.indicator} ${css$19.success}${leavingClass}`,
4466
5151
  role: "status",
4467
5152
  "aria-label": recoveredLabel,
4468
5153
  children: [jsx("span", {
4469
- className: css$17.icon,
5154
+ className: css$19.icon,
4470
5155
  "aria-hidden": "true",
4471
5156
  children: jsx(IconCheckOutlineRegular, { size: 14 })
4472
5157
  }), jsx("span", {
4473
- className: css$17.label,
5158
+ className: css$19.label,
4474
5159
  children: recoveredLabel
4475
5160
  })]
4476
5161
  });
4477
5162
  const connecting = rendered === "connecting";
4478
5163
  return jsxs("button", {
4479
5164
  type: "button",
4480
- className: `${css$17.indicator} ${css$17.warning}${leavingClass}`,
5165
+ className: `${css$19.indicator} ${css$19.warning}${leavingClass}`,
4481
5166
  "data-phase": rendered,
4482
5167
  "aria-label": connecting ? restartActionLabel : reconnectActionLabel,
4483
5168
  onClick: onReconnect,
4484
5169
  children: [jsx("span", {
4485
- className: css$17.icon,
5170
+ className: css$19.icon,
4486
5171
  "aria-hidden": "true",
4487
5172
  children: connecting ? jsx(StateDot, { state: "ongoing" }) : jsx(IconRefreshOutlineRegular, { size: 14 })
4488
5173
  }), jsx("span", {
4489
- className: css$17.label,
5174
+ className: css$19.label,
4490
5175
  children: connecting ? jsxs(Fragment, { children: [connectingLabel, jsxs("span", {
4491
- className: css$17.dots,
5176
+ className: css$19.dots,
4492
5177
  "aria-hidden": "true",
4493
5178
  children: [
4494
5179
  jsx("span", { children: "." }),
4495
5180
  jsx("span", {
4496
- className: css$17.secondDot,
5181
+ className: css$19.secondDot,
4497
5182
  children: "."
4498
5183
  }),
4499
5184
  jsx("span", {
4500
- className: css$17.thirdDot,
5185
+ className: css$19.thirdDot,
4501
5186
  children: "."
4502
5187
  })
4503
5188
  ]
@@ -4814,7 +5499,7 @@ function PermissionIconFullAccessMedium(props) {
4814
5499
  */
4815
5500
  function ReferenceIconArtwork({ kind, size = 16, className, strokeWidth }) {
4816
5501
  switch (kind) {
4817
- case "session": return jsx(NewChatOutlineArtwork, {
5502
+ case "session": return jsx(ChatLinesOutlineArtwork, {
4818
5503
  size,
4819
5504
  className,
4820
5505
  strokeWidth
@@ -5400,14 +6085,23 @@ function glyph(type, size, className) {
5400
6085
  case "code": return jsxs(FileGlyph, {
5401
6086
  size,
5402
6087
  className,
5403
- markTransform: FILE_MARK_TRANSFORM,
5404
- children: [jsx("path", {
5405
- d: "M8.61 16.3601L11.76 18.3901V20.1401L7 17.0601V15.6601L11.76 12.5801V14.3301L8.61 16.3601Z",
5406
- fill: "currentColor"
5407
- }), jsx("path", {
5408
- d: "M16.1918 14.3301V12.5801L20.9518 15.6601V17.0601L16.1918 20.1401V18.3901L19.3418 16.3601L16.1918 14.3301Z",
5409
- fill: "currentColor"
5410
- })]
6088
+ children: [
6089
+ jsx("path", {
6090
+ d: "M10.0053 13.126L7.0236 16.3788C6.96052 16.4476 6.96052 16.5532 7.0236 16.622L10.0053 19.8748",
6091
+ stroke: "currentColor",
6092
+ strokeWidth: "1.35"
6093
+ }),
6094
+ jsx("path", {
6095
+ d: "M17.9941 13.126L20.9759 16.3788C21.039 16.4476 21.039 16.5532 20.9759 16.622L17.9941 19.8748",
6096
+ stroke: "currentColor",
6097
+ strokeWidth: "1.35"
6098
+ }),
6099
+ jsx("path", {
6100
+ d: "M15.2652 12.957L12.7344 20.0433",
6101
+ stroke: "currentColor",
6102
+ strokeWidth: "1.35"
6103
+ })
6104
+ ]
5411
6105
  });
5412
6106
  case "excel": return jsx(SpreadsheetGlyph, {
5413
6107
  size,
@@ -5519,7 +6213,7 @@ function FileTypeIcon(props) {
5519
6213
  type: resolvedType,
5520
6214
  size,
5521
6215
  className
5522
- }) : glyph(resolvedType, size, clsx(css$18.icon, css$18[resolvedType], className));
6216
+ }) : glyph(resolvedType, size, clsx(css$20.icon, css$20[resolvedType], className));
5523
6217
  }
5524
6218
  //#endregion
5525
6219
  //#region lib/types/SiteGlyph.js
@@ -5817,284 +6511,82 @@ function projectUserText(text, sessionLabels, slashNames = [], slashKind = "skil
5817
6511
  for (const rawLabel of [...new Set(sessionLabels)].sort((a, b) => b.length - a.length)) {
5818
6512
  const label = `@${rawLabel}`;
5819
6513
  let start = text.indexOf(label);
5820
- while (start >= 0) {
5821
- ranges.push({
5822
- start,
5823
- end: start + label.length,
5824
- label,
5825
- kind: "session"
5826
- });
5827
- start = text.indexOf(label, start + label.length);
5828
- }
5829
- }
5830
- const re = /(^|\s)(\/[\w-]+(?=\s|$)|@"[^"\n]+"|@[^\s]+)/gu;
5831
- let m;
5832
- while ((m = re.exec(text)) !== null) {
5833
- const tokenStart = m.index + m[1].length;
5834
- const rawLabel = m[2];
5835
- const label = rawLabel.startsWith("@\"") ? rawLabel : rawLabel.replace(TRAILING_PUNCTUATION_RE, "");
5836
- if (label.length <= 1) continue;
5837
- if (label.startsWith("/") && !slashNames.includes(label.slice(1))) continue;
5838
- ranges.push({
5839
- start: tokenStart,
5840
- end: tokenStart + label.length,
5841
- label,
5842
- kind: "plain"
5843
- });
5844
- }
5845
- const rankOf = (range) => range.kind === "session" ? 0 : 1;
5846
- ranges.sort((a, b) => a.start - b.start || rankOf(a) - rankOf(b) || b.end - a.end);
5847
- const parts = [];
5848
- let cursor = 0;
5849
- const pushPlain = (from, to) => {
5850
- parts.push(jsx("span", {
5851
- className: css$19.plainRun,
5852
- children: text.slice(from, to)
5853
- }, `t${from}`));
5854
- };
5855
- for (const range of ranges) {
5856
- if (range.start < cursor) continue;
5857
- const { start: tokenStart, end, label, kind } = range;
5858
- if (tokenStart > cursor) pushPlain(cursor, tokenStart);
5859
- const referenceKind = kind === "session" ? "session" : label.startsWith("@") ? label.replace(/^@"|"$/gu, "").endsWith("/") ? "folder" : "file" : void 0;
5860
- 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));
5861
- const contents = jsxs(Fragment, { children: [referenceKind !== void 0 && jsx(ReferenceIconRegular, {
5862
- kind: referenceKind,
5863
- size: 16,
5864
- className: css$19.refIcon
5865
- }), displayLabel] });
5866
- const open = references === void 0 ? void 0 : referenceKind === "file" ? () => {
5867
- references.openFile(label.slice(1).replace(/^"|"$/gu, ""));
5868
- } : referenceKind === void 0 && slashKind === "skill" ? () => {
5869
- references.openSkill(label.slice(1));
5870
- } : void 0;
5871
- const className = clsx(css$19.refChip, referenceKind === void 0 && css$19.slashChip);
5872
- parts.push(open === void 0 ? jsx("span", {
5873
- className,
5874
- "data-ref-chip": referenceKind ?? slashKind,
5875
- title: label,
5876
- children: contents
5877
- }, tokenStart) : jsx("button", {
5878
- type: "button",
5879
- className: clsx(className, markdownCss.fileMention),
5880
- "data-ref-chip": referenceKind ?? slashKind,
5881
- title: label,
5882
- onClick: (event) => {
5883
- if (event.detail > 1 || event.detail !== 0 && event.currentTarget.ownerDocument.getSelection()?.isCollapsed === false) return;
5884
- open();
5885
- },
5886
- children: contents
5887
- }, tokenStart));
5888
- cursor = end;
5889
- }
5890
- if (parts.length === 0) return jsx("span", {
5891
- className: css$19.plainRun,
5892
- children: text
5893
- });
5894
- if (cursor < text.length) pushPlain(cursor, text.length);
5895
- return jsx(Fragment, { children: parts });
5896
- }
5897
- //#endregion
5898
- //#region lib/types/Tooltip.js
5899
- /** Anchor-preserving tooltips with optional body portals for clipping containers. */
5900
- /**
5901
- * Suppression channel from a tooltip to the tooltips above it: a tooltip hands
5902
- * this setter to its own descendants, and a visible descendant bubble calls it
5903
- * so the ancestor withdraws its bubble for as long as the descendant shows one.
5904
- */
5905
- const TooltipSuppression = createContext(null);
5906
- let pointerModality = false;
5907
- if (typeof window !== "undefined") {
5908
- window.addEventListener("pointerdown", () => {
5909
- pointerModality = true;
5910
- }, true);
5911
- window.addEventListener("keydown", () => {
5912
- pointerModality = false;
5913
- }, true);
5914
- }
5915
- /**
5916
- * Attach a hover/focus tooltip to an anchor element.
5917
- * @param props.label - bubble text, or a resolver evaluated only while the bubble is visible.
5918
- * @param props.side - placement relative to the anchor (default 'right').
5919
- * @param props.align - horizontal anchor-edge alignment for 'bottom'/'top' bubbles: 'end' pins
5920
- * the bubble's right edge to the anchor's (for anchors beside other hover surfaces the centered
5921
- * bubble would overlap); default 'center'. Ignored for side 'right'.
5922
- * @param props.portal - render the bubble under document.body to escape containing blocks and clipping ancestors.
5923
- * @param props.delayMs - hover delay in milliseconds; keyboard focus remains immediate.
5924
- * @param props.disabled - suppress the bubble while true; the anchor renders identically so
5925
- * toggling never remounts it (which would cut its CSS transitions).
5926
- * @param props.maxWidth - bubble width cap in pixels, for labels long enough that the default
5927
- * half-viewport cap would render a slab wider than the surface the anchor sits on.
5928
- * @param props.children - a single anchor element; its own ref (callback or object) is forwarded alongside the tooltip's.
5929
- * @returns the cloned anchor plus a fixed-position bubble, optionally portaled to the body.
5930
- * The bubble stays hidden until ResizeObserver supplies its size for viewport fitting; clicking the
5931
- * anchor dismisses the bubble until the next trigger, and focus arriving after a pointer
5932
- * interaction (a closing menu refocusing its trigger) never raises it.
5933
- */
5934
- function Tooltip({ label, side = "right", align = "center", delayMs = 0, disabled = false, portal = false, maxWidth, children }) {
5935
- const anchor = useRef(null);
5936
- const childRef = children.ref;
5937
- const mergedRef = useCallback((el) => {
5938
- anchor.current = el;
5939
- if (typeof childRef === "function") childRef(el);
5940
- else if (childRef != null) childRef.current = el;
5941
- }, [childRef]);
5942
- const [pos, setPos] = useState(null);
5943
- const bubble = useRef(null);
5944
- const resolvedLabel = pos === null ? null : typeof label === "function" ? label() : label;
5945
- const y = pos === null ? 0 : side === "right" ? pos.top + (pos.bottom - pos.top) / 2 : side === "top" ? pos.top - 8 : pos.bottom + 8;
5946
- const showTimer = useRef(null);
5947
- const triggers = useRef({
5948
- hover: false,
5949
- focus: false
5950
- });
5951
- const suppressAncestors = useContext(TooltipSuppression);
5952
- const [suppressed, setSuppressed] = useState(false);
5953
- const announce = useCallback((active) => {
5954
- suppressAncestors?.(active);
5955
- }, [suppressAncestors]);
5956
- const visible = pos !== null && !disabled;
5957
- useEffect(() => {
5958
- const el = bubble.current;
5959
- if (pos === null || !visible || suppressed || el === null) return;
5960
- const edgeMargin = 12;
5961
- let size;
5962
- let placement = side;
5963
- const fit = () => {
5964
- if (size === void 0) return;
5965
- const { inlineSize: width, blockSize: height } = size;
5966
- const offset = side === "right" ? 0 : align === "end" ? width : width / 2;
5967
- const left = Math.max(edgeMargin, Math.min(pos.x - offset, window.innerWidth - edgeMargin - width));
5968
- const fitsBelow = pos.bottom + 8 + height <= window.innerHeight - edgeMargin;
5969
- const fitsAbove = pos.top - 8 - height >= edgeMargin;
5970
- if (placement === "bottom" && !fitsBelow && fitsAbove) placement = "top";
5971
- else if (placement === "top" && !fitsAbove && fitsBelow) placement = "bottom";
5972
- el.style.left = `${left + offset}px`;
5973
- el.style.top = `${placement === "right" ? (pos.top + pos.bottom) / 2 : placement === "top" ? pos.top - 8 : pos.bottom + 8}px`;
5974
- el.dataset.side = placement;
5975
- el.style.visibility = "visible";
5976
- };
5977
- const observer = new ResizeObserver((entries) => {
5978
- size = entries[0]?.borderBoxSize[0];
5979
- fit();
5980
- });
5981
- observer.observe(el, { box: "border-box" });
5982
- window.addEventListener("resize", fit);
5983
- return () => {
5984
- observer.disconnect();
5985
- window.removeEventListener("resize", fit);
5986
- };
5987
- }, [
5988
- align,
5989
- pos,
5990
- side,
5991
- suppressed,
5992
- visible
5993
- ]);
5994
- useEffect(() => {
5995
- announce(visible);
5996
- return () => {
5997
- announce(false);
5998
- };
5999
- }, [announce, visible]);
6000
- const cancelShow = useCallback(() => {
6001
- if (showTimer.current === null) return;
6002
- clearTimeout(showTimer.current);
6003
- showTimer.current = null;
6004
- }, []);
6005
- useEffect(() => {
6006
- if (disabled) {
6007
- cancelShow();
6008
- triggers.current = {
6009
- hover: false,
6010
- focus: false
6011
- };
6012
- setPos(null);
6514
+ while (start >= 0) {
6515
+ ranges.push({
6516
+ start,
6517
+ end: start + label.length,
6518
+ label,
6519
+ kind: "session"
6520
+ });
6521
+ start = text.indexOf(label, start + label.length);
6013
6522
  }
6014
- return cancelShow;
6015
- }, [cancelShow, disabled]);
6016
- const show = () => {
6017
- if (disabled) return;
6018
- const el = anchor.current;
6019
- /* v8 ignore next -- the ref is attached by event time: events fire on the cloned anchor. */
6020
- if (el === null) return;
6021
- const r = el.getBoundingClientRect();
6022
- setPos({
6023
- x: side === "right" ? r.right + 10 : align === "end" ? r.right : r.left + r.width / 2,
6024
- top: r.top,
6025
- bottom: r.bottom
6523
+ }
6524
+ const re = /(^|\s)(\/[\w-]+(?=\s|$)|@"[^"\n]+"|@[^\s]+)/gu;
6525
+ let m;
6526
+ while ((m = re.exec(text)) !== null) {
6527
+ const tokenStart = m.index + m[1].length;
6528
+ const rawLabel = m[2];
6529
+ const label = rawLabel.startsWith("@\"") ? rawLabel : rawLabel.replace(TRAILING_PUNCTUATION_RE, "");
6530
+ if (label.length <= 1) continue;
6531
+ if (label.startsWith("/") && !slashNames.includes(label.slice(1))) continue;
6532
+ ranges.push({
6533
+ start: tokenStart,
6534
+ end: tokenStart + label.length,
6535
+ label,
6536
+ kind: "plain"
6026
6537
  });
6027
- announce(true);
6028
- };
6029
- const showAfterHoverDelay = () => {
6030
- cancelShow();
6031
- if (delayMs <= 0) {
6032
- show();
6033
- return;
6034
- }
6035
- showTimer.current = setTimeout(() => {
6036
- showTimer.current = null;
6037
- show();
6038
- }, delayMs);
6039
- };
6040
- const withdraw = () => {
6041
- setPos(null);
6042
- announce(false);
6043
- };
6044
- const hide = () => {
6045
- cancelShow();
6046
- if (!triggers.current.hover && !triggers.current.focus) withdraw();
6538
+ }
6539
+ const rankOf = (range) => range.kind === "session" ? 0 : 1;
6540
+ ranges.sort((a, b) => a.start - b.start || rankOf(a) - rankOf(b) || b.end - a.end);
6541
+ const parts = [];
6542
+ let cursor = 0;
6543
+ const pushPlain = (from, to) => {
6544
+ parts.push(jsx("span", {
6545
+ className: css$21.plainRun,
6546
+ children: text.slice(from, to)
6547
+ }, `t${from}`));
6047
6548
  };
6048
- const content = visible && !suppressed && jsx("span", {
6049
- ref: bubble,
6050
- className: css$20.bubble,
6051
- "data-side": side,
6052
- "data-portal": portal || void 0,
6053
- "data-align": align,
6054
- style: {
6055
- left: pos.x,
6056
- top: y,
6057
- visibility: "hidden",
6058
- ...maxWidth === void 0 ? {} : { maxWidth }
6059
- },
6060
- role: "tooltip",
6061
- children: resolvedLabel
6062
- });
6063
- return jsxs(TooltipSuppression.Provider, {
6064
- value: setSuppressed,
6065
- children: [cloneElement(children, {
6066
- ref: mergedRef,
6067
- onMouseEnter: (e) => {
6068
- children.props.onMouseEnter?.(e);
6069
- triggers.current.hover = true;
6070
- showAfterHoverDelay();
6071
- },
6072
- onMouseLeave: (e) => {
6073
- children.props.onMouseLeave?.(e);
6074
- triggers.current.hover = false;
6075
- cancelShow();
6076
- withdraw();
6077
- },
6078
- onClick: (e) => {
6079
- children.props.onClick?.(e);
6080
- triggers.current.focus = false;
6081
- cancelShow();
6082
- withdraw();
6083
- },
6084
- onFocus: (e) => {
6085
- children.props.onFocus?.(e);
6086
- if (pointerModality) return;
6087
- triggers.current.focus = true;
6088
- cancelShow();
6089
- show();
6549
+ for (const range of ranges) {
6550
+ if (range.start < cursor) continue;
6551
+ const { start: tokenStart, end, label, kind } = range;
6552
+ if (tokenStart > cursor) pushPlain(cursor, tokenStart);
6553
+ const referenceKind = kind === "session" ? "session" : label.startsWith("@") ? label.replace(/^@"|"$/gu, "").endsWith("/") ? "folder" : "file" : void 0;
6554
+ 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));
6555
+ const contents = jsxs(Fragment, { children: [referenceKind !== void 0 && jsx(ReferenceIconRegular, {
6556
+ kind: referenceKind,
6557
+ size: 16,
6558
+ className: css$21.refIcon
6559
+ }), displayLabel] });
6560
+ const open = references === void 0 ? void 0 : referenceKind === "file" ? () => {
6561
+ references.openFile(label.slice(1).replace(/^"|"$/gu, ""));
6562
+ } : referenceKind === void 0 && slashKind === "skill" ? () => {
6563
+ references.openSkill(label.slice(1));
6564
+ } : void 0;
6565
+ const className = clsx(css$21.refChip, referenceKind === void 0 && css$21.slashChip);
6566
+ parts.push(open === void 0 ? jsx("span", {
6567
+ className,
6568
+ "data-ref-chip": referenceKind ?? slashKind,
6569
+ title: label,
6570
+ children: contents
6571
+ }, tokenStart) : jsx("button", {
6572
+ type: "button",
6573
+ className: clsx(className, markdownCss.fileMention),
6574
+ "data-ref-chip": referenceKind ?? slashKind,
6575
+ title: label,
6576
+ onClick: (event) => {
6577
+ if (event.detail > 1 || event.detail !== 0 && event.currentTarget.ownerDocument.getSelection()?.isCollapsed === false) return;
6578
+ open();
6090
6579
  },
6091
- onBlur: (e) => {
6092
- children.props.onBlur?.(e);
6093
- triggers.current.focus = false;
6094
- hide();
6095
- }
6096
- }), portal ? content !== false && createPortal(content, document.body) : content]
6580
+ children: contents
6581
+ }, tokenStart));
6582
+ cursor = end;
6583
+ }
6584
+ if (parts.length === 0) return jsx("span", {
6585
+ className: css$21.plainRun,
6586
+ children: text
6097
6587
  });
6588
+ if (cursor < text.length) pushPlain(cursor, text.length);
6589
+ return jsx(Fragment, { children: parts });
6098
6590
  }
6099
6591
  //#endregion
6100
6592
  //#region lib/types/Toast.js
@@ -6163,25 +6655,25 @@ function Toast({ text, icon, tone, anchor, holdMs = HOLD_MS, actions, onDone })
6163
6655
  };
6164
6656
  }, [anchor]);
6165
6657
  return createPortal(jsxs("div", {
6166
- className: css$21.toast,
6658
+ className: css$22.toast,
6167
6659
  role: "alert",
6168
6660
  style: {
6169
6661
  ...left === null ? {} : { left },
6170
6662
  "--dsh-toast-hold": `${String(holdMs)}ms`
6171
6663
  },
6172
6664
  children: [tone === "success" ? jsx("span", {
6173
- className: `${css$21.icon} ${css$21.success}`,
6665
+ className: `${css$22.icon} ${css$22.success}`,
6174
6666
  "aria-hidden": true,
6175
6667
  children: jsx(IconCheckCircleOutlineRegular, {})
6176
6668
  }) : icon !== void 0 && jsx("span", {
6177
- className: css$21.icon,
6669
+ className: css$22.icon,
6178
6670
  "aria-hidden": true,
6179
6671
  children: icon
6180
6672
  }), jsxs("span", {
6181
- className: css$21.text,
6673
+ className: css$22.text,
6182
6674
  children: [text, actions?.map((action) => jsxs(Fragment$1, { children: [action.prefix, jsx("button", {
6183
6675
  type: "button",
6184
- className: css$21.action,
6676
+ className: css$22.action,
6185
6677
  onClick: action.onClick,
6186
6678
  children: action.label
6187
6679
  })] }, action.label))]
@@ -6231,29 +6723,29 @@ function SettingsForm(props) {
6231
6723
  discard.current();
6232
6724
  }, []);
6233
6725
  if (!state.available) return jsx("p", {
6234
- className: css$22.unavailable,
6726
+ className: css$23.unavailable,
6235
6727
  role: "status",
6236
6728
  children: labels.unavailable
6237
6729
  });
6238
6730
  const blocked = !state.dirty || state.invalid || state.saving;
6239
6731
  return jsxs("div", {
6240
- className: css$22.form,
6732
+ className: css$23.form,
6241
6733
  children: [
6242
6734
  !state.writable ? jsx("p", {
6243
- className: css$22.readOnly,
6735
+ className: css$23.readOnly,
6244
6736
  role: "status",
6245
6737
  children: labels.readOnly
6246
6738
  }) : null,
6247
6739
  props.children,
6248
6740
  jsxs("div", {
6249
- className: css$22.footer,
6741
+ className: css$23.footer,
6250
6742
  children: [state.failed ? jsx("p", {
6251
- className: css$22.failed,
6743
+ className: css$23.failed,
6252
6744
  role: "status",
6253
6745
  children: labels.saveFailed
6254
6746
  }) : null, jsx("button", {
6255
6747
  type: "button",
6256
- className: css$22.save,
6748
+ className: css$23.save,
6257
6749
  disabled: blocked,
6258
6750
  onClick: props.onSave,
6259
6751
  children: state.saving ? labels.saving : labels.save
@@ -6285,19 +6777,19 @@ function SettingsValueField(props) {
6285
6777
  const hasMessage = props.invalid || Boolean(props.hint);
6286
6778
  const description = [hasMessage ? messageId : "", helpOpen ? helpId : ""].filter(Boolean).join(" ");
6287
6779
  return jsxs("div", {
6288
- className: css$23.field,
6780
+ className: css$24.field,
6289
6781
  children: [
6290
6782
  jsxs("div", {
6291
- className: css$23.head,
6783
+ className: css$24.head,
6292
6784
  children: [jsxs("div", {
6293
- className: css$23.labelGroup,
6785
+ className: css$24.labelGroup,
6294
6786
  children: [jsx("label", {
6295
- className: css$23.label,
6787
+ className: css$24.label,
6296
6788
  htmlFor: props.id,
6297
6789
  children: props.label
6298
6790
  }), props.help !== void 0 ? jsx("button", {
6299
6791
  type: "button",
6300
- className: css$23.helpButton,
6792
+ className: css$24.helpButton,
6301
6793
  "aria-label": props.help.label,
6302
6794
  "aria-expanded": helpOpen,
6303
6795
  "aria-controls": helpId,
@@ -6307,13 +6799,13 @@ function SettingsValueField(props) {
6307
6799
  children: jsx(IconInfoOutlineRegular, { size: 12 })
6308
6800
  }) : null]
6309
6801
  }), props.overridden ? jsxs("span", {
6310
- className: css$23.badges,
6802
+ className: css$24.badges,
6311
6803
  children: [jsx(Tag, {
6312
6804
  tone: "neutral",
6313
6805
  children: props.overriddenLabel
6314
6806
  }), jsx("button", {
6315
6807
  type: "button",
6316
- className: css$23.reset,
6808
+ className: css$24.reset,
6317
6809
  disabled: props.disabled,
6318
6810
  onClick: props.onReset,
6319
6811
  children: props.resetLabel
@@ -6322,7 +6814,7 @@ function SettingsValueField(props) {
6322
6814
  }),
6323
6815
  jsx("input", {
6324
6816
  id: props.id,
6325
- className: css$23.input,
6817
+ className: css$24.input,
6326
6818
  type: "text",
6327
6819
  ...props.numeric === true ? { inputMode: "numeric" } : {},
6328
6820
  ...props.invalid ? { "aria-invalid": true } : {},
@@ -6336,12 +6828,12 @@ function SettingsValueField(props) {
6336
6828
  }),
6337
6829
  hasMessage ? jsx("p", {
6338
6830
  id: messageId,
6339
- className: props.invalid ? css$23.invalid : css$23.hint,
6831
+ className: props.invalid ? css$24.invalid : css$24.hint,
6340
6832
  children: props.invalid ? props.invalidLabel : props.hint
6341
6833
  }) : null,
6342
6834
  props.help !== void 0 && helpOpen ? jsx("div", {
6343
6835
  id: helpId,
6344
- className: css$23.help,
6836
+ className: css$24.help,
6345
6837
  role: "region",
6346
6838
  "aria-label": props.help.label,
6347
6839
  children: props.help.content
@@ -6353,21 +6845,22 @@ function SettingsValueField(props) {
6353
6845
  * A write-only credential control. The value never rides a response, so the
6354
6846
  * control reports only whether one is configured and starts blank; a blank
6355
6847
  * draft writes nothing, which keeps the stored key rather than clearing it.
6848
+ * The control asks browsers not to autofill saved login passwords.
6356
6849
  * @param props - the field's copy, its staged text, and the configured state.
6357
6850
  * @returns the labelled control.
6358
6851
  */
6359
6852
  function SettingsSecretField(props) {
6360
6853
  return jsxs("div", {
6361
- className: css$23.field,
6854
+ className: css$24.field,
6362
6855
  children: [
6363
6856
  jsxs("div", {
6364
- className: css$23.head,
6857
+ className: css$24.head,
6365
6858
  children: [jsx("label", {
6366
- className: css$23.label,
6859
+ className: css$24.label,
6367
6860
  htmlFor: props.id,
6368
6861
  children: props.label
6369
6862
  }), jsx("span", {
6370
- className: css$23.badges,
6863
+ className: css$24.badges,
6371
6864
  children: jsx(Tag, {
6372
6865
  tone: props.configured ? "neutral" : "quiet",
6373
6866
  children: props.stateLabel
@@ -6376,9 +6869,9 @@ function SettingsSecretField(props) {
6376
6869
  }),
6377
6870
  jsx("input", {
6378
6871
  id: props.id,
6379
- className: css$23.input,
6872
+ className: css$24.input,
6380
6873
  type: "password",
6381
- autoComplete: "off",
6874
+ autoComplete: "new-password",
6382
6875
  value: props.text,
6383
6876
  disabled: props.disabled,
6384
6877
  onChange: (event) => {
@@ -6386,7 +6879,7 @@ function SettingsSecretField(props) {
6386
6879
  }
6387
6880
  }),
6388
6881
  jsx("p", {
6389
- className: css$23.hint,
6882
+ className: css$24.hint,
6390
6883
  children: props.hint
6391
6884
  })
6392
6885
  ]
@@ -6692,16 +7185,16 @@ var SettingsFormModel = class {
6692
7185
  * dark blocks), never here — the repo's tokens-only styling rule.
6693
7186
  *
6694
7187
  * Only the three markdown-fence and `run_code` grammars (TypeScript, shell,
6695
- * JSON) load into the singleton at boot — the set every session renders. The
6696
- * read card's wider extension set (the file-extension language hints the read
6697
- * tool's `langFromPath` emits — `packages/fs/tool-fs`: python, rust, yaml,
6698
- * markup, …) is imported lazily and registered the first time such a language
6699
- * is requested, so a session that never opens a read card in one of those
6700
- * languages pays neither the ~1.6 MB of grammar modules nor their synchronous
6701
- * init. The first render of a lazy language falls back to plain text while its
6702
- * grammar loads, then {@link onGrammarLoaded} notifies subscribers to re-render
6703
- * with highlighting. An unknown or absent language falls back to plain text (no
6704
- * highlighting, still monospace) — never an error.
7188
+ * JSON) load into the singleton at boot — the set every session renders. Every
7189
+ * other language in the shared extension table
7190
+ * (`@deepseek-ai/dsh-util-code-language`: python, rust, yaml, markup, …) is
7191
+ * imported lazily and registered the first time such a language is requested,
7192
+ * so a session that never opens a code surface in one of those languages pays
7193
+ * neither the grammar modules nor their synchronous init. The first render of a
7194
+ * lazy language falls back to plain text while its grammar loads, then
7195
+ * {@link onGrammarLoaded} notifies subscribers to re-render with highlighting.
7196
+ * An unknown or absent language falls back to plain text (no highlighting, still
7197
+ * monospace) — never an error.
6705
7198
  */
6706
7199
  /**
6707
7200
  * Grammars the singleton loads at boot; each entry's own `name` is the id
@@ -6709,8 +7202,8 @@ var SettingsFormModel = class {
6709
7202
  * resolve to the TypeScript grammar rather than a separate one: it tokenizes
6710
7203
  * plain TS/JS exactly, and JSX/TSX approximately (shiki's TS grammar is not the
6711
7204
  * dedicated TSX grammar, so JSX elements tokenize imperfectly) — an accepted
6712
- * trade to keep the boot set to one JS-family grammar. The read card's wider
6713
- * set loads lazily through {@link LAZY_GRAMMARS}.
7205
+ * trade to keep the boot set to one JS-family grammar. Every other language in
7206
+ * the shared extension table loads lazily through {@link LAZY_GRAMMARS}.
6714
7207
  */
6715
7208
  const LANGS = [
6716
7209
  langTs,
@@ -6718,8 +7211,8 @@ const LANGS = [
6718
7211
  langJson
6719
7212
  ];
6720
7213
  /**
6721
- * The read card's extension grammars, each behind a dynamic import so its
6722
- * module stays out of the boot chunk until a read of that language renders.
7214
+ * The non-boot extension grammars, each behind a dynamic import so its module
7215
+ * stays out of the boot chunk until a code surface renders that language.
6723
7216
  * Keyed by the grammar id (`LanguageRegistration.name`) the aliases resolve to.
6724
7217
  * `@shikijs/langs`' default export is a `LanguageRegistration[]`; the loader
6725
7218
  * hands the whole array to `loadLanguageSync`, which registers each entry
@@ -6749,16 +7242,51 @@ const LAZY_GRAMMARS = new Map([
6749
7242
  ["less", () => import("@shikijs/langs/less")],
6750
7243
  ["sql", () => import("@shikijs/langs/sql")],
6751
7244
  ["xml", () => import("@shikijs/langs/xml")],
6752
- ["lua", () => import("@shikijs/langs/lua")]
7245
+ ["lua", () => import("@shikijs/langs/lua")],
7246
+ ["bat", () => import("@shikijs/langs/bat")],
7247
+ ["powershell", () => import("@shikijs/langs/powershell")],
7248
+ ["fish", () => import("@shikijs/langs/fish")],
7249
+ ["dotenv", () => import("@shikijs/langs/dotenv")],
7250
+ ["log", () => import("@shikijs/langs/log")],
7251
+ ["csv", () => import("@shikijs/langs/csv")],
7252
+ ["diff", () => import("@shikijs/langs/diff")],
7253
+ ["http", () => import("@shikijs/langs/http")],
7254
+ ["rst", () => import("@shikijs/langs/rst")],
7255
+ ["latex", () => import("@shikijs/langs/latex")],
7256
+ ["bibtex", () => import("@shikijs/langs/bibtex")],
7257
+ ["asciidoc", () => import("@shikijs/langs/asciidoc")],
7258
+ ["r", () => import("@shikijs/langs/r")],
7259
+ ["julia", () => import("@shikijs/langs/julia")],
7260
+ ["dart", () => import("@shikijs/langs/dart")],
7261
+ ["scala", () => import("@shikijs/langs/scala")],
7262
+ ["clojure", () => import("@shikijs/langs/clojure")],
7263
+ ["erlang", () => import("@shikijs/langs/erlang")],
7264
+ ["elixir", () => import("@shikijs/langs/elixir")],
7265
+ ["haskell", () => import("@shikijs/langs/haskell")],
7266
+ ["fsharp", () => import("@shikijs/langs/fsharp")],
7267
+ ["vb", () => import("@shikijs/langs/vb")],
7268
+ ["perl", () => import("@shikijs/langs/perl")],
7269
+ ["verilog", () => import("@shikijs/langs/verilog")],
7270
+ ["system-verilog", () => import("@shikijs/langs/system-verilog")],
7271
+ ["graphql", () => import("@shikijs/langs/graphql")],
7272
+ ["proto", () => import("@shikijs/langs/proto")],
7273
+ ["hcl", () => import("@shikijs/langs/hcl")],
7274
+ ["nix", () => import("@shikijs/langs/nix")],
7275
+ ["vue", () => import("@shikijs/langs/vue")],
7276
+ ["svelte", () => import("@shikijs/langs/svelte")],
7277
+ ["make", () => import("@shikijs/langs/make")],
7278
+ ["cmake", () => import("@shikijs/langs/cmake")],
7279
+ ["groovy", () => import("@shikijs/langs/groovy")]
6753
7280
  ]);
6754
7281
  /**
6755
7282
  * Language ids (and aliases) the highlighter accepts; everything else renders
6756
7283
  * plain. A Map, not an object: fence info strings are assistant-authored, so
6757
7284
  * a label like `constructor` or `__proto__` must miss instead of resolving an
6758
7285
  * inherited property and crashing the renderer inside shiki. Keys cover both
6759
- * the markdown-fence aliases `CodeBlock` uses and the file-extension hint ids
6760
- * the read tool's `langFromPath` emits, so both callers resolve the same
6761
- * grammars. The JS family maps to the TypeScript grammar (see {@link LANGS} for
7286
+ * the markdown-fence aliases `CodeBlock` uses, the file-extension language ids
7287
+ * `@deepseek-ai/dsh-util-code-language` resolves, and the short ids
7288
+ * `readLangHintForPath` persists, so every caller resolves the same grammars.
7289
+ * A new short name in the shared table must be aliased here too. The JS family maps to the TypeScript grammar (see {@link LANGS} for
6762
7290
  * the JSX/TSX approximation). A value not in {@link LANGS} names a
6763
7291
  * {@link LAZY_GRAMMARS} entry loaded on first use.
6764
7292
  */
@@ -6804,15 +7332,89 @@ const LANG_ALIASES = new Map([
6804
7332
  ["less", "less"],
6805
7333
  ["sql", "sql"],
6806
7334
  ["xml", "xml"],
6807
- ["lua", "lua"]
7335
+ ["lua", "lua"],
7336
+ ["bat", "bat"],
7337
+ ["batch", "bat"],
7338
+ ["powershell", "powershell"],
7339
+ ["ps1", "powershell"],
7340
+ ["ps", "powershell"],
7341
+ ["fish", "fish"],
7342
+ ["properties", "ini"],
7343
+ ["dotenv", "dotenv"],
7344
+ ["env", "dotenv"],
7345
+ ["log", "log"],
7346
+ ["csv", "csv"],
7347
+ ["diff", "diff"],
7348
+ ["patch", "diff"],
7349
+ ["http", "http"],
7350
+ ["rst", "rst"],
7351
+ ["latex", "latex"],
7352
+ ["tex", "latex"],
7353
+ ["bibtex", "bibtex"],
7354
+ ["bib", "bibtex"],
7355
+ ["asciidoc", "asciidoc"],
7356
+ ["adoc", "asciidoc"],
7357
+ ["r", "r"],
7358
+ ["julia", "julia"],
7359
+ ["jl", "julia"],
7360
+ ["dart", "dart"],
7361
+ ["scala", "scala"],
7362
+ ["clojure", "clojure"],
7363
+ ["clj", "clojure"],
7364
+ ["erlang", "erlang"],
7365
+ ["erl", "erlang"],
7366
+ ["elixir", "elixir"],
7367
+ ["ex", "elixir"],
7368
+ ["exs", "elixir"],
7369
+ ["haskell", "haskell"],
7370
+ ["hs", "haskell"],
7371
+ ["fsharp", "fsharp"],
7372
+ ["fs", "fsharp"],
7373
+ ["fsi", "fsharp"],
7374
+ ["fsx", "fsharp"],
7375
+ ["vb", "vb"],
7376
+ ["vbnet", "vb"],
7377
+ ["perl", "perl"],
7378
+ ["pl", "perl"],
7379
+ ["pm", "perl"],
7380
+ ["verilog", "verilog"],
7381
+ ["v", "verilog"],
7382
+ ["system-verilog", "system-verilog"],
7383
+ ["systemverilog", "system-verilog"],
7384
+ ["sv", "system-verilog"],
7385
+ ["svh", "system-verilog"],
7386
+ ["graphql", "graphql"],
7387
+ ["gql", "graphql"],
7388
+ ["proto", "proto"],
7389
+ ["protobuf", "proto"],
7390
+ ["hcl", "hcl"],
7391
+ ["tf", "hcl"],
7392
+ ["tfvars", "hcl"],
7393
+ ["nix", "nix"],
7394
+ ["vue", "vue"],
7395
+ ["svelte", "svelte"],
7396
+ ["make", "make"],
7397
+ ["makefile", "make"],
7398
+ ["mk", "make"],
7399
+ ["cmake", "cmake"],
7400
+ ["groovy", "groovy"],
7401
+ ["gradle", "groovy"]
6808
7402
  ]);
6809
7403
  /**
7404
+ * Resolve a language hint to the grammar id {@link LANG_ALIASES} selects.
7405
+ * @param lang - Language hint from a code surface: a canonical grammar id or the read card's persisted short id.
7406
+ * @returns The resolved grammar id, or `undefined` when the table aliases no grammar.
7407
+ */
7408
+ function grammarForHint(lang) {
7409
+ return lang === void 0 ? void 0 : LANG_ALIASES.get(lang.toLowerCase());
7410
+ }
7411
+ /**
6810
7412
  * Whether a language hint can use the shared syntax highlighter.
6811
7413
  * @param lang - Language hint from a code surface.
6812
7414
  * @returns Whether the hint resolves to a supported grammar.
6813
7415
  */
6814
7416
  function supportsHighlighting(lang) {
6815
- return lang !== void 0 && LANG_ALIASES.has(lang.toLowerCase());
7417
+ return grammarForHint(lang) !== void 0;
6816
7418
  }
6817
7419
  /** All token colors resolve through `--shiki-*` custom properties (theme package sheets). */
6818
7420
  const cssVariablesTheme = createCssVariablesTheme({
@@ -6933,7 +7535,7 @@ setTimeout(() => {
6933
7535
  * @returns the highlighted HTML, or `undefined` for unknown or not-yet-loaded languages.
6934
7536
  */
6935
7537
  function highlightToHtml(code, lang) {
6936
- const resolved = lang === void 0 ? void 0 : LANG_ALIASES.get(lang.toLowerCase());
7538
+ const resolved = grammarForHint(lang);
6937
7539
  if (resolved === void 0) return void 0;
6938
7540
  if (!ensureGrammar(resolved)) return void 0;
6939
7541
  return highlighter().codeToHtml(code, {
@@ -7045,7 +7647,7 @@ var StreamingHighlightSession = class {
7045
7647
  this.lastCode = code;
7046
7648
  this.lastLang = lang;
7047
7649
  this.lastResult = void 0;
7048
- const resolved = lang === void 0 ? void 0 : LANG_ALIASES.get(lang.toLowerCase());
7650
+ const resolved = grammarForHint(lang);
7049
7651
  if (resolved === void 0 || !ensureGrammar(resolved)) {
7050
7652
  this.reset(void 0);
7051
7653
  return;
@@ -7105,7 +7707,7 @@ var StreamingHighlightSession = class {
7105
7707
  * @returns one entry per source line (each an array of runs), or `undefined` for unknown or not-yet-loaded languages.
7106
7708
  */
7107
7709
  function highlightLines(code, lang) {
7108
- const resolved = lang === void 0 ? void 0 : LANG_ALIASES.get(lang.toLowerCase());
7710
+ const resolved = grammarForHint(lang);
7109
7711
  if (resolved === void 0) return void 0;
7110
7712
  if (!ensureGrammar(resolved)) return void 0;
7111
7713
  const { tokens } = highlighter().codeToTokens(code, {
@@ -7121,89 +7723,6 @@ function highlightLines(code, lang) {
7121
7723
  //#endregion
7122
7724
  //#region lib/types/code-highlighting.js
7123
7725
  /** Shared filename-to-grammar selection and lazy line highlighter for source views. */
7124
- const LANGUAGES = new Map(Object.entries({
7125
- typescript: [
7126
- "ts",
7127
- "tsx",
7128
- "mts",
7129
- "cts"
7130
- ],
7131
- javascript: [
7132
- "js",
7133
- "jsx",
7134
- "mjs",
7135
- "cjs"
7136
- ],
7137
- shellscript: [
7138
- "sh",
7139
- "bash",
7140
- "zsh"
7141
- ],
7142
- json: [
7143
- "json",
7144
- "jsonc",
7145
- "jsonl",
7146
- "ndjson"
7147
- ],
7148
- python: [
7149
- "py",
7150
- "pyw",
7151
- "pyi"
7152
- ],
7153
- ruby: [
7154
- "rb",
7155
- "rake",
7156
- "gemspec"
7157
- ],
7158
- go: ["go"],
7159
- rust: ["rs"],
7160
- java: ["java"],
7161
- c: ["c", "h"],
7162
- cpp: [
7163
- "cc",
7164
- "cpp",
7165
- "cxx",
7166
- "hh",
7167
- "hpp",
7168
- "hxx"
7169
- ],
7170
- csharp: ["cs"],
7171
- kotlin: ["kt", "kts"],
7172
- swift: ["swift"],
7173
- php: ["php"],
7174
- yaml: ["yaml", "yml"],
7175
- toml: ["toml"],
7176
- ini: ["ini"],
7177
- markdown: ["md", "markdown"],
7178
- mdx: ["mdx"],
7179
- html: [
7180
- "html",
7181
- "htm",
7182
- "xhtml"
7183
- ],
7184
- css: ["css"],
7185
- scss: ["scss"],
7186
- less: ["less"],
7187
- sql: ["sql"],
7188
- xml: [
7189
- "xml",
7190
- "xsd",
7191
- "xsl",
7192
- "xslt"
7193
- ],
7194
- lua: ["lua"]
7195
- }).flatMap(([language, extensions]) => extensions.map((extension) => [extension, language])));
7196
- /** Recognized filename suffixes whose source can use the shared syntax highlighter. */
7197
- const CODE_HIGHLIGHT_EXTENSIONS = [...LANGUAGES.keys()];
7198
- /**
7199
- * Select the shared syntax highlighter's grammar from a filename.
7200
- * @param path - decoded source filename or path.
7201
- * @returns a supported grammar hint, or `undefined` for other suffixes.
7202
- */
7203
- function languageForPath(path) {
7204
- const extension = /\.([^./]+)$/u.exec(path.replaceAll("\\", "/"))?.[1]?.toLowerCase();
7205
- return extension === void 0 ? void 0 : LANGUAGES.get(extension);
7206
- }
7207
7726
  /**
7208
7727
  * Bind the shared lazy highlighter to one language and refresh after its grammar loads.
7209
7728
  * @param language - grammar hint selected from the source filename.
@@ -7426,7 +7945,7 @@ function JsonCopyAction({ store, target, persistent, labels, onCopy, onClose })
7426
7945
  const object = typeof target.value === "object" && target.value !== null;
7427
7946
  const copyTitle = state === "copied" ? labels.copied : state === "failed" ? labels.copyFailed : object ? labels.copyPrettyJson : labels.copyValue;
7428
7947
  return jsx("span", {
7429
- className: css$24.copySlot,
7948
+ className: css$25.copySlot,
7430
7949
  children: (persistent || snapshot !== void 0) && jsx(Menu, {
7431
7950
  open: snapshot?.menuOpen === true,
7432
7951
  compact: true,
@@ -7435,7 +7954,7 @@ function JsonCopyAction({ store, target, persistent, labels, onCopy, onClose })
7435
7954
  anchor: jsx("button", {
7436
7955
  ref: buttonRef,
7437
7956
  type: "button",
7438
- className: css$24.actionButton,
7957
+ className: css$25.actionButton,
7439
7958
  "data-json-copy-button": true,
7440
7959
  "data-state": state,
7441
7960
  "aria-label": copyTitle,
@@ -7474,35 +7993,35 @@ function bracketOf(value) {
7474
7993
  }
7475
7994
  function previewPrimitive(value) {
7476
7995
  if (value === null) return jsx("span", {
7477
- className: css$24.keywordValue,
7996
+ className: css$25.keywordValue,
7478
7997
  children: "null"
7479
7998
  });
7480
7999
  if (typeof value === "string") return jsx("span", {
7481
- className: css$24.stringValue,
8000
+ className: css$25.stringValue,
7482
8001
  children: JSON.stringify(value)
7483
8002
  });
7484
8003
  if (typeof value === "number") return jsx("span", {
7485
- className: css$24.numberValue,
8004
+ className: css$25.numberValue,
7486
8005
  children: String(value)
7487
8006
  });
7488
8007
  if (typeof value === "boolean") return jsx("span", {
7489
- className: css$24.keywordValue,
8008
+ className: css$25.keywordValue,
7490
8009
  children: String(value)
7491
8010
  });
7492
8011
  if (typeof value === "bigint") return jsx("span", {
7493
- className: css$24.otherValue,
8012
+ className: css$25.otherValue,
7494
8013
  children: value.toString()
7495
8014
  });
7496
8015
  if (typeof value === "undefined") return jsx("span", {
7497
- className: css$24.otherValue,
8016
+ className: css$25.otherValue,
7498
8017
  children: "undefined"
7499
8018
  });
7500
8019
  if (typeof value === "symbol") return jsx("span", {
7501
- className: css$24.otherValue,
8020
+ className: css$25.otherValue,
7502
8021
  children: value.description ?? "Symbol"
7503
8022
  });
7504
8023
  if (typeof value === "function") return jsx("span", {
7505
- className: css$24.otherValue,
8024
+ className: css$25.otherValue,
7506
8025
  children: value.name || "Function"
7507
8026
  });
7508
8027
  return null;
@@ -7516,71 +8035,71 @@ function previewValue(value, depth) {
7516
8035
  const [open, close] = bracketOf(value);
7517
8036
  return jsxs(Fragment, { children: [
7518
8037
  jsx("span", {
7519
- className: css$24.punctuation,
8038
+ className: css$25.punctuation,
7520
8039
  children: open
7521
8040
  }),
7522
8041
  depth >= PREVIEW_DEPTH_LIMIT ? jsx("span", {
7523
- className: css$24.previewEllipsis,
8042
+ className: css$25.previewEllipsis,
7524
8043
  children: "…"
7525
8044
  }) : visible.map(([key, item], index) => jsxs("span", { children: [
7526
8045
  index > 0 && jsx("span", {
7527
- className: css$24.punctuation,
8046
+ className: css$25.punctuation,
7528
8047
  children: ", "
7529
8048
  }),
7530
8049
  !array && jsxs(Fragment, { children: [jsx("span", {
7531
- className: css$24.previewProperty,
8050
+ className: css$25.previewProperty,
7532
8051
  children: key
7533
8052
  }), jsx("span", {
7534
- className: css$24.punctuation,
8053
+ className: css$25.punctuation,
7535
8054
  children: ": "
7536
8055
  })] }),
7537
8056
  previewValue(item, depth + 1)
7538
8057
  ] }, key)),
7539
8058
  depth < PREVIEW_DEPTH_LIMIT && entries.length > limit && jsx("span", {
7540
- className: css$24.previewEllipsis,
8059
+ className: css$25.previewEllipsis,
7541
8060
  children: ", …"
7542
8061
  }),
7543
8062
  jsx("span", {
7544
- className: css$24.punctuation,
8063
+ className: css$25.punctuation,
7545
8064
  children: close
7546
8065
  })
7547
8066
  ] });
7548
8067
  }
7549
8068
  function primitiveValue(value) {
7550
8069
  if (value === null) return jsx("span", {
7551
- className: css$24.keywordValue,
8070
+ className: css$25.keywordValue,
7552
8071
  children: "null"
7553
8072
  });
7554
8073
  if (typeof value === "string") return jsx("span", {
7555
- className: css$24.stringValue,
8074
+ className: css$25.stringValue,
7556
8075
  children: JSON.stringify(value)
7557
8076
  });
7558
8077
  if (typeof value === "boolean") return jsx("span", {
7559
- className: css$24.keywordValue,
8078
+ className: css$25.keywordValue,
7560
8079
  children: String(value)
7561
8080
  });
7562
8081
  if (typeof value === "number") return jsx("span", {
7563
- className: css$24.numberValue,
8082
+ className: css$25.numberValue,
7564
8083
  children: String(value)
7565
8084
  });
7566
8085
  if (typeof value === "bigint") return jsx("span", {
7567
- className: css$24.numberValue,
8086
+ className: css$25.numberValue,
7568
8087
  children: `${value.toString()}n`
7569
8088
  });
7570
8089
  if (value instanceof Date) return jsx("span", {
7571
- className: css$24.otherValue,
8090
+ className: css$25.otherValue,
7572
8091
  children: value.toISOString()
7573
8092
  });
7574
8093
  if (typeof value === "function") return jsxs("span", {
7575
- className: css$24.otherValue,
8094
+ className: css$25.otherValue,
7576
8095
  children: ["function() ", "{ }"]
7577
8096
  });
7578
8097
  if (typeof value === "undefined") return jsx("span", {
7579
- className: css$24.otherValue,
8098
+ className: css$25.otherValue,
7580
8099
  children: "undefined"
7581
8100
  });
7582
8101
  return jsx("span", {
7583
- className: css$24.otherValue,
8102
+ className: css$25.otherValue,
7584
8103
  children: value.toString()
7585
8104
  });
7586
8105
  }
@@ -7608,7 +8127,7 @@ function moveFocus(button, direction) {
7608
8127
  function NodeField({ field, expandable, onToggle }) {
7609
8128
  if (field === void 0) return null;
7610
8129
  return jsxs("span", {
7611
- className: clsx(css$24.label, expandable && css$24.clickableLabel),
8130
+ className: clsx(css$25.label, expandable && css$25.clickableLabel),
7612
8131
  onClick: expandable ? onToggle : void 0,
7613
8132
  children: [fieldText(field), ":"]
7614
8133
  });
@@ -7645,7 +8164,7 @@ function JsonString({ collapsedStringLines, stringWrapping, field, labels, lastE
7645
8164
  if (!expanded) return;
7646
8165
  const raw = rawRef.current;
7647
8166
  const clips = [];
7648
- const tree = raw.closest(`.${css$24.root}`);
8167
+ const tree = raw.closest(`.${css$25.root}`);
7649
8168
  for (let parent = tree.parentElement; parent !== null; parent = parent.parentElement) if (/auto|scroll|hidden|clip/.test(getComputedStyle(parent).overflowY)) clips.push(parent);
7650
8169
  const measure = () => {
7651
8170
  let top = 0;
@@ -7674,33 +8193,33 @@ function JsonString({ collapsedStringLines, stringWrapping, field, labels, lastE
7674
8193
  if (expanded) {
7675
8194
  const fieldId = `${contentsId}-field`;
7676
8195
  return jsxs("div", {
7677
- className: css$24.stringField,
8196
+ className: css$25.stringField,
7678
8197
  "data-expanded": true,
7679
8198
  children: [
7680
8199
  field !== void 0 && jsxs("span", {
7681
8200
  id: fieldId,
7682
- className: css$24.label,
8201
+ className: css$25.label,
7683
8202
  children: [fieldText(field), ":"]
7684
8203
  }),
7685
8204
  jsx("pre", {
7686
8205
  ref: rawRef,
7687
8206
  id: contentsId,
7688
- className: css$24.stringRaw,
8207
+ className: css$25.stringRaw,
7689
8208
  "data-wrap": wrapped,
7690
8209
  tabIndex: 0,
7691
8210
  "aria-labelledby": field === void 0 ? void 0 : fieldId,
7692
8211
  children: value
7693
8212
  }),
7694
8213
  !lastElement && jsx("span", {
7695
- className: css$24.punctuation,
8214
+ className: css$25.punctuation,
7696
8215
  children: ","
7697
8216
  }),
7698
8217
  jsxs("div", {
7699
- className: css$24.stringActions,
8218
+ className: css$25.stringActions,
7700
8219
  children: [
7701
8220
  stringWrapping !== void 0 && jsx("button", {
7702
8221
  type: "button",
7703
- className: css$24.actionButton,
8222
+ className: css$25.actionButton,
7704
8223
  "aria-label": stringWrapping.label,
7705
8224
  title: stringWrapping.label,
7706
8225
  "aria-pressed": wrapped,
@@ -7714,7 +8233,7 @@ function JsonString({ collapsedStringLines, stringWrapping, field, labels, lastE
7714
8233
  }),
7715
8234
  jsx("button", {
7716
8235
  type: "button",
7717
- className: css$24.actionButton,
8236
+ className: css$25.actionButton,
7718
8237
  "aria-label": labels.collapseNode,
7719
8238
  title: labels.collapseNode,
7720
8239
  "aria-expanded": true,
@@ -7739,18 +8258,18 @@ function JsonString({ collapsedStringLines, stringWrapping, field, labels, lastE
7739
8258
  });
7740
8259
  }
7741
8260
  return jsxs(Fragment, { children: [renderCopy?.(), jsx("span", {
7742
- className: css$24.stringField,
8261
+ className: css$25.stringField,
7743
8262
  "data-expanded": expanded,
7744
8263
  children: jsxs("span", {
7745
8264
  ref: contentRef,
7746
8265
  id: contentsId,
7747
- className: css$24.stringText,
8266
+ className: css$25.stringText,
7748
8267
  children: [
7749
8268
  truncated && jsx("span", {
7750
- className: css$24.stringToggleSlot,
8269
+ className: css$25.stringToggleSlot,
7751
8270
  children: jsxs("button", {
7752
8271
  type: "button",
7753
- className: css$24.stringToggle,
8272
+ className: css$25.stringToggle,
7754
8273
  "aria-label": labels.expandNode,
7755
8274
  "aria-expanded": false,
7756
8275
  "aria-controls": contentsId,
@@ -7765,12 +8284,12 @@ function JsonString({ collapsedStringLines, stringWrapping, field, labels, lastE
7765
8284
  })
7766
8285
  }),
7767
8286
  field !== void 0 && jsxs("span", {
7768
- className: css$24.label,
8287
+ className: css$25.label,
7769
8288
  children: [fieldText(field), ":"]
7770
8289
  }),
7771
8290
  primitiveValue(value),
7772
8291
  !lastElement && jsx("span", {
7773
- className: css$24.punctuation,
8292
+ className: css$25.punctuation,
7774
8293
  children: ","
7775
8294
  })
7776
8295
  ]
@@ -7801,7 +8320,7 @@ function JsonTreeNode({ collapsedStringLines, stringWrapping, field, initialExpa
7801
8320
  }
7802
8321
  };
7803
8322
  const row = (children, ariaExpanded) => jsxs("div", {
7804
- className: css$24.row,
8323
+ className: css$25.row,
7805
8324
  role: "treeitem",
7806
8325
  "aria-expanded": ariaExpanded,
7807
8326
  onMouseOver: (event) => {
@@ -7836,7 +8355,7 @@ function JsonTreeNode({ collapsedStringLines, stringWrapping, field, initialExpa
7836
8355
  }),
7837
8356
  primitiveValue(value),
7838
8357
  !lastElement && jsx("span", {
7839
- className: css$24.punctuation,
8358
+ className: css$25.punctuation,
7840
8359
  children: ","
7841
8360
  })
7842
8361
  ] }));
@@ -7848,22 +8367,22 @@ function JsonTreeNode({ collapsedStringLines, stringWrapping, field, initialExpa
7848
8367
  onToggle: toggle
7849
8368
  }),
7850
8369
  jsx("span", {
7851
- className: css$24.punctuation,
8370
+ className: css$25.punctuation,
7852
8371
  children: open
7853
8372
  }),
7854
8373
  jsx("span", {
7855
- className: css$24.punctuation,
8374
+ className: css$25.punctuation,
7856
8375
  children: close
7857
8376
  }),
7858
8377
  !lastElement && jsx("span", {
7859
- className: css$24.punctuation,
8378
+ className: css$25.punctuation,
7860
8379
  children: ","
7861
8380
  })
7862
8381
  ] }));
7863
8382
  return row(jsxs(Fragment, { children: [
7864
8383
  jsx("span", {
7865
8384
  ref: expanderRef,
7866
- className: clsx(css$24.expander, expanded ? css$24.collapseIcon : css$24.expandIcon),
8385
+ className: clsx(css$25.expander, expanded ? css$25.collapseIcon : css$25.expandIcon),
7867
8386
  "data-json-expander": true,
7868
8387
  role: "button",
7869
8388
  "aria-label": expanded ? labels.collapseNode : labels.expandNode,
@@ -7877,7 +8396,7 @@ function JsonTreeNode({ collapsedStringLines, stringWrapping, field, initialExpa
7877
8396
  onKeyDown: onExpanderKeyDown
7878
8397
  }),
7879
8398
  jsxs("span", {
7880
- className: css$24.summary,
8399
+ className: css$25.summary,
7881
8400
  children: [
7882
8401
  jsx(NodeField, {
7883
8402
  field,
@@ -7885,11 +8404,11 @@ function JsonTreeNode({ collapsedStringLines, stringWrapping, field, initialExpa
7885
8404
  onToggle: toggle
7886
8405
  }),
7887
8406
  jsx("span", {
7888
- className: css$24.preview,
8407
+ className: css$25.preview,
7889
8408
  children: previewValue(value, 0)
7890
8409
  }),
7891
8410
  !lastElement && jsx("span", {
7892
- className: css$24.punctuation,
8411
+ className: css$25.punctuation,
7893
8412
  children: ","
7894
8413
  })
7895
8414
  ]
@@ -7897,7 +8416,7 @@ function JsonTreeNode({ collapsedStringLines, stringWrapping, field, initialExpa
7897
8416
  expanded && jsx("ul", {
7898
8417
  id: contentsId,
7899
8418
  role: "group",
7900
- className: css$24.children,
8419
+ className: css$25.children,
7901
8420
  children: entries.map(([key, item], index) => jsx(JsonTreeNode, {
7902
8421
  collapsedStringLines,
7903
8422
  stringWrapping,
@@ -8029,17 +8548,17 @@ function JsonTree({ data, label, className, collapsedStringLines = 3, stringWrap
8029
8548
  onClose: clearCopyTarget
8030
8549
  }) : void 0;
8031
8550
  return jsx("div", {
8032
- className: clsx(css$24.root, className),
8551
+ className: clsx(css$25.root, className),
8033
8552
  style: { "--json-tree-collapsed-lines": collapsedStringLines },
8034
8553
  onMouseOver: handleRootMouseOver,
8035
8554
  onMouseLeave: () => {
8036
8555
  if (!copyStore.get()?.menuOpen) clearCopyTarget();
8037
8556
  },
8038
8557
  children: expandTopLevel ? jsxs("div", {
8039
- className: css$24.expandedTopLevel,
8558
+ className: css$25.expandedTopLevel,
8040
8559
  children: [
8041
8560
  jsxs("div", {
8042
- className: clsx(css$24.row, css$24.topLevelBracket),
8561
+ className: clsx(css$25.row, css$25.topLevelBracket),
8043
8562
  "data-json-root-row": true,
8044
8563
  onMouseOver: (event) => {
8045
8564
  event.stopPropagation();
@@ -8052,13 +8571,13 @@ function JsonTree({ data, label, className, collapsedStringLines = 3, stringWrap
8052
8571
  path: [],
8053
8572
  value: data
8054
8573
  }), jsx("span", {
8055
- className: css$24.punctuation,
8574
+ className: css$25.punctuation,
8056
8575
  children: rootOpen
8057
8576
  })]
8058
8577
  }),
8059
8578
  jsx("div", {
8060
8579
  "aria-label": label,
8061
- className: clsx(css$24.container, css$24.expandedTopLevelContainer),
8580
+ className: clsx(css$25.container, css$25.expandedTopLevelContainer),
8062
8581
  role: "tree",
8063
8582
  children: rootEntries.map(([key, value], index) => jsx(JsonTreeNode, {
8064
8583
  collapsedStringLines,
@@ -8076,16 +8595,16 @@ function JsonTree({ data, label, className, collapsedStringLines = 3, stringWrap
8076
8595
  }, key))
8077
8596
  }),
8078
8597
  jsx("div", {
8079
- className: clsx(css$24.row, css$24.topLevelBracket),
8598
+ className: clsx(css$25.row, css$25.topLevelBracket),
8080
8599
  children: jsx("span", {
8081
- className: css$24.punctuation,
8600
+ className: css$25.punctuation,
8082
8601
  children: rootClose
8083
8602
  })
8084
8603
  })
8085
8604
  ]
8086
8605
  }) : jsx("div", {
8087
8606
  "aria-label": label,
8088
- className: css$24.container,
8607
+ className: css$25.container,
8089
8608
  role: "tree",
8090
8609
  children: jsx(JsonTreeNode, {
8091
8610
  collapsedStringLines,
@@ -8648,67 +9167,67 @@ function TerminalBlock({ command, cwd, home, output, exitCode, signal, running =
8648
9167
  const { hidden, capped, headLines, tailLines } = headTailCap(lines.length, maxLines, expanded);
8649
9168
  const body = !running || !empty;
8650
9169
  return jsxs("div", {
8651
- className: clsx(css$25.block, className),
9170
+ className: clsx(css$26.block, className),
8652
9171
  "data-terminal": "",
8653
9172
  "data-running": running ? "" : void 0,
8654
9173
  "data-body": body ? "" : void 0,
8655
9174
  children: [jsxs("div", {
8656
- className: css$25.header,
9175
+ className: css$26.header,
8657
9176
  children: [
8658
9177
  jsxs("div", {
8659
- className: css$25.prompt,
9178
+ className: css$26.prompt,
8660
9179
  children: [runStateDot && jsx("span", {
8661
- className: css$25.runStateLabel,
9180
+ className: css$26.runStateLabel,
8662
9181
  children: state.label
8663
9182
  }), commandLines.map((line, index) => jsxs("div", {
8664
- className: css$25.promptLine,
9183
+ className: css$26.promptLine,
8665
9184
  children: [
8666
9185
  index === 0 && runStateDot && jsx(StateDot, {
8667
9186
  state: state.state,
8668
- className: css$25.runState
9187
+ className: css$26.runState
8669
9188
  }),
8670
9189
  jsx("span", {
8671
- className: css$25.cwd,
9190
+ className: css$26.cwd,
8672
9191
  children: index > 0 || cwd === void 0 ? "$" : promptLabel(cwd, home)
8673
9192
  }),
8674
9193
  jsx("span", {
8675
- className: css$25.command,
9194
+ className: css$26.command,
8676
9195
  children: line
8677
9196
  })
8678
9197
  ]
8679
9198
  }, index))]
8680
9199
  }),
8681
9200
  status !== void 0 && jsx(Pill, {
8682
- className: css$25.status,
9201
+ className: css$26.status,
8683
9202
  children: status
8684
9203
  }),
8685
9204
  (copyText !== void 0 || !running && !empty) && jsx("button", {
8686
9205
  type: "button",
8687
- className: css$25.copyButton,
9206
+ className: css$26.copyButton,
8688
9207
  onClick: onCopy,
8689
9208
  children: copied ? copy.copied : copy.copy
8690
9209
  })
8691
9210
  ]
8692
9211
  }), body && (empty ? jsx("div", {
8693
- className: css$25.empty,
9212
+ className: css$26.empty,
8694
9213
  children: copy.noOutput
8695
9214
  }) : jsxs("div", {
8696
- className: css$25.output,
9215
+ className: css$26.output,
8697
9216
  children: [
8698
9217
  (capped ? lines.slice(0, headLines) : lines).map((line, index) => jsx("div", {
8699
- className: css$25.line,
9218
+ className: css$26.line,
8700
9219
  children: renderLine$1(line)
8701
9220
  }, index)),
8702
9221
  hidden > 0 && jsx("button", {
8703
9222
  type: "button",
8704
- className: css$25.expand,
9223
+ className: css$26.expand,
8705
9224
  "aria-expanded": expanded,
8706
9225
  "aria-label": expanded ? copy.collapseAria : copy.expandAria(hidden),
8707
9226
  onClick: onToggle,
8708
9227
  children: expanded ? copy.collapse : copy.expand(hidden)
8709
9228
  }),
8710
9229
  capped && lines.slice(lines.length - tailLines).map((line, index) => jsx("div", {
8711
- className: css$25.line,
9230
+ className: css$26.line,
8712
9231
  children: renderLine$1(line)
8713
9232
  }, index))
8714
9233
  ]
@@ -8911,13 +9430,13 @@ function ReadBlock({ label, labels, lines, totalLines, lang, maxLines = 16, clas
8911
9430
  const tailLines = maxLines - headLines;
8912
9431
  const windowed = lines.length < totalLines;
8913
9432
  const rows = (slice) => slice.map(([line, spans]) => jsxs("div", {
8914
- className: css$26.line,
9433
+ className: css$27.line,
8915
9434
  children: [jsx("span", {
8916
- className: css$26.gutter,
9435
+ className: css$27.gutter,
8917
9436
  "aria-hidden": true,
8918
9437
  children: line.number
8919
9438
  }), jsx("span", {
8920
- className: css$26.content,
9439
+ className: css$27.content,
8921
9440
  children: spans === void 0 ? line.text : renderSpans(spans)
8922
9441
  })]
8923
9442
  }, line.number));
@@ -8925,7 +9444,7 @@ function ReadBlock({ label, labels, lines, totalLines, lang, maxLines = 16, clas
8925
9444
  const paired = lines.map((line, index) => [line, highlighted?.[index]]);
8926
9445
  return jsxs("div", {
8927
9446
  ref: rootRef,
8928
- className: clsx(cardCss.card, css$26.block, className),
9447
+ className: clsx(cardCss.card, css$27.block, className),
8929
9448
  "data-read": "",
8930
9449
  "data-code-wrap": wrapped,
8931
9450
  style: gutterStyle,
@@ -8947,7 +9466,7 @@ function ReadBlock({ label, labels, lines, totalLines, lang, maxLines = 16, clas
8947
9466
  children: [
8948
9467
  rows(capped ? paired.slice(0, headLines) : paired),
8949
9468
  hidden > 0 && jsx(FoldToggle, {
8950
- className: css$26.expand,
9469
+ className: css$27.expand,
8951
9470
  expanded,
8952
9471
  hidden,
8953
9472
  labels,
@@ -8969,11 +9488,11 @@ function assertNever(value) {
8969
9488
  }
8970
9489
  /** The dim class per row kind (path/gap chrome vs the diff's own +/- colors). */
8971
9490
  const ROW_CLASS = {
8972
- path: css$27.path,
8973
- del: css$27.del,
8974
- add: css$27.add,
8975
- context: css$27.context,
8976
- gap: css$27.gap
9491
+ path: css$28.path,
9492
+ del: css$28.del,
9493
+ add: css$28.add,
9494
+ context: css$28.context,
9495
+ gap: css$28.gap
8977
9496
  };
8978
9497
  /** Bound synchronous edit-graph search; one replacement consumes two edits. */
8979
9498
  const MAX_DIFF_EDIT_LENGTH = 256;
@@ -9107,7 +9626,7 @@ function DiffBlock({ diffs, labels, maxLines = 16, className }) {
9107
9626
  const head = capped ? rows.slice(0, headLines) : rows;
9108
9627
  const tail = capped ? rows.slice(rows.length - tailLines) : [];
9109
9628
  return jsxs("div", {
9110
- className: clsx(cardCss.card, css$27.block, className),
9629
+ className: clsx(cardCss.card, css$28.block, className),
9111
9630
  "data-diff": "",
9112
9631
  "data-code-wrap": wrapped,
9113
9632
  children: [jsx(CodeToolbar, {
@@ -9122,21 +9641,21 @@ function DiffBlock({ diffs, labels, maxLines = 16, className }) {
9122
9641
  setWrapped((value) => !value);
9123
9642
  }
9124
9643
  }), jsxs("div", {
9125
- className: css$27.body,
9644
+ className: css$28.body,
9126
9645
  children: [
9127
9646
  head.map((row, index) => jsx("div", {
9128
- className: clsx(css$27.line, ROW_CLASS[row.kind]),
9647
+ className: clsx(css$28.line, ROW_CLASS[row.kind]),
9129
9648
  children: row.text
9130
9649
  }, index)),
9131
9650
  hidden > 0 && jsx(FoldToggle, {
9132
- className: css$27.expand,
9651
+ className: css$28.expand,
9133
9652
  expanded,
9134
9653
  hidden,
9135
9654
  labels,
9136
9655
  onToggle
9137
9656
  }),
9138
9657
  tail.map((row, index) => jsx("div", {
9139
- className: clsx(css$27.line, ROW_CLASS[row.kind]),
9658
+ className: clsx(css$28.line, ROW_CLASS[row.kind]),
9140
9659
  children: row.text
9141
9660
  }, index))
9142
9661
  ]
@@ -9266,56 +9785,56 @@ function SearchBlock(props) {
9266
9785
  const tail = tailHeader === void 0 ? naturalTail : naturalTail.slice(1);
9267
9786
  const renderRow = (row) => {
9268
9787
  if (row.type === "path") return jsx("div", {
9269
- className: css$28.line,
9788
+ className: css$29.line,
9270
9789
  children: row.path
9271
9790
  });
9272
9791
  if (row.type === "match") return jsxs("div", {
9273
- className: css$28.line,
9792
+ className: css$29.line,
9274
9793
  children: [jsxs("span", {
9275
- className: css$28.lineNumber,
9794
+ className: css$29.lineNumber,
9276
9795
  children: [row.lineNumber, ": "]
9277
9796
  }), row.line]
9278
9797
  });
9279
9798
  return jsxs("button", {
9280
9799
  type: "button",
9281
- className: css$28.fileHeader,
9800
+ className: css$29.fileHeader,
9282
9801
  "aria-expanded": !row.collapsed,
9283
9802
  onClick: () => {
9284
9803
  toggleFile(row.index);
9285
9804
  },
9286
9805
  children: [jsx("span", {
9287
- className: css$28.filePath,
9806
+ className: css$29.filePath,
9288
9807
  children: row.path
9289
9808
  }), jsx("span", {
9290
- className: css$28.fileCount,
9809
+ className: css$29.fileCount,
9291
9810
  children: row.count
9292
9811
  })]
9293
9812
  });
9294
9813
  };
9295
9814
  return jsxs("div", {
9296
- className: clsx(css$28.block, className),
9815
+ className: clsx(css$29.block, className),
9297
9816
  "data-search": props.kind,
9298
9817
  children: [jsxs("div", {
9299
- className: css$28.header,
9818
+ className: css$29.header,
9300
9819
  children: [jsx("span", {
9301
- className: css$28.summary,
9820
+ className: css$29.summary,
9302
9821
  children: summaryText(props, shown, truncated, total)
9303
9822
  }), !empty && jsx("button", {
9304
9823
  type: "button",
9305
- className: css$28.copyButton,
9824
+ className: css$29.copyButton,
9306
9825
  onClick: onCopy,
9307
9826
  children: copied ? props.labels.copied : props.labels.copy
9308
9827
  })]
9309
9828
  }), empty ? jsx("div", {
9310
- className: css$28.empty,
9829
+ className: css$29.empty,
9311
9830
  children: props.labels.noResults
9312
9831
  }) : jsxs("div", {
9313
- className: css$28.body,
9832
+ className: css$29.body,
9314
9833
  children: [
9315
9834
  head.map((row) => jsx("div", { children: renderRow(row) }, rowKey(row))),
9316
9835
  hidden > 0 && jsx("button", {
9317
9836
  type: "button",
9318
- className: css$28.expand,
9837
+ className: css$29.expand,
9319
9838
  "aria-expanded": expanded,
9320
9839
  "aria-label": expanded ? props.labels.collapseAria : props.labels.expandAria(hidden),
9321
9840
  onClick: onToggle,
@@ -9626,6 +10145,32 @@ var IncrementalMarkdownParser = class {
9626
10145
  }
9627
10146
  };
9628
10147
  //#endregion
10148
+ //#region lib/types/markdown/local-image-syntax.js
10149
+ /**
10150
+ * Recover only unambiguous, unescaped image-only paragraphs containing a local path with spaces.
10151
+ * @param root - Parsed Markdown tree, modified in place.
10152
+ * @param source - Original source used to distinguish authored syntax from escaped examples.
10153
+ * @returns The same root with recovered image nodes.
10154
+ */
10155
+ function recoverLocalImages(root, source) {
10156
+ const visit = (node) => {
10157
+ if (node.type === "paragraph" && node.children.length === 1) {
10158
+ const [child] = node.children;
10159
+ if (child.type === "text" && child.position !== void 0 && source.slice(child.position.start.offset, child.position.end.offset) === child.value) {
10160
+ const match = /^!\[([^\]\n]*)\]\(((?:\/(?!\/)|\.{1,2}\/|[a-z]:[\\/])[^\n<>()[\]"']+\.[a-z\d]+)\)$/iu.exec(child.value);
10161
+ if (match !== null && match[2].includes(" ") && classifyFileType(match[2]) === "image") node.children = [{
10162
+ type: "image",
10163
+ alt: match[1],
10164
+ url: match[2],
10165
+ position: child.position
10166
+ }];
10167
+ }
10168
+ } else if ("children" in node) for (const child of node.children) visit(child);
10169
+ };
10170
+ visit(root);
10171
+ return root;
10172
+ }
10173
+ //#endregion
9629
10174
  //#region lib/types/markdown/cjkFriendlyStrong.js
9630
10175
  /** Let asterisk strong emphasis close after punctuation when CJK prose continues without whitespace. */
9631
10176
  const cjkCharacter = new RegExp([
@@ -9972,10 +10517,10 @@ function mathCompatibility() {
9972
10517
  * @returns The mdast root.
9973
10518
  */
9974
10519
  function parseGfm(text) {
9975
- return fromMarkdown(text, {
10520
+ return recoverLocalImages(fromMarkdown(text, {
9976
10521
  extensions: [gfm(), cjkFriendlyStrong()],
9977
10522
  mdastExtensions: [gfmFromMarkdown()]
9978
- });
10523
+ }), text);
9979
10524
  }
9980
10525
  /**
9981
10526
  * Parse GFM markdown plus TeX math with the compatibility delimiters
@@ -9984,7 +10529,7 @@ function parseGfm(text) {
9984
10529
  * @returns The mdast root.
9985
10530
  */
9986
10531
  function parseGfmWithMath(text) {
9987
- return fromMarkdown(text, {
10532
+ return recoverLocalImages(fromMarkdown(text, {
9988
10533
  extensions: [
9989
10534
  gfm(),
9990
10535
  cjkFriendlyStrong(),
@@ -9992,7 +10537,7 @@ function parseGfmWithMath(text) {
9992
10537
  math()
9993
10538
  ],
9994
10539
  mdastExtensions: [gfmFromMarkdown(), mathFromMarkdown()]
9995
- });
10540
+ }), text);
9996
10541
  }
9997
10542
  //#endregion
9998
10543
  //#region lib/types/markdown/CodeBlock.js
@@ -10119,7 +10664,7 @@ function CodeBlock({ code, lang, streaming, className, contentRef, lineNumbers =
10119
10664
  });
10120
10665
  }, [copied, trimmed]);
10121
10666
  const body = streamedBody !== void 0 ? streamedBody : html === void 0 ? jsx("pre", {
10122
- className: css$29.plain,
10667
+ className: css$30.plain,
10123
10668
  children: jsx("code", { children: sourceLines === void 0 ? trimmed : sourceLines.map((line, index) => jsxs(Fragment$1, { children: [index > 0 && "\n", jsx("span", {
10124
10669
  className: "line",
10125
10670
  children: line
@@ -10127,12 +10672,12 @@ function CodeBlock({ code, lang, streaming, className, contentRef, lineNumbers =
10127
10672
  }) : jsx("div", { dangerouslySetInnerHTML: { __html: html } });
10128
10673
  return jsxs("div", {
10129
10674
  ref: rootRef,
10130
- className: clsx(css$29.block, "md-code-block", lineNumbers && css$29.numbered, toolbarLabels !== void 0 && css$29.card, className),
10675
+ className: clsx(css$30.block, "md-code-block", lineNumbers && css$30.numbered, toolbarLabels !== void 0 && css$30.card, className),
10131
10676
  "data-line-numbers": lineNumbers || void 0,
10132
10677
  "data-code-wrap": toolbarLabels === void 0 ? void 0 : wrapped,
10133
10678
  style: sourceLines === void 0 ? void 0 : { "--dsl-code-block-line-number-width": `${Math.max(2, String(sourceLines.length).length)}ch` },
10134
10679
  children: [showHeader && jsx("div", {
10135
- className: css$29.bannerWrap,
10680
+ className: css$30.bannerWrap,
10136
10681
  children: toolbarLabels !== void 0 ? jsx(CodeToolbar, {
10137
10682
  lang,
10138
10683
  labels: toolbarLabels,
@@ -10145,16 +10690,16 @@ function CodeBlock({ code, lang, streaming, className, contentRef, lineNumbers =
10145
10690
  setWrapped((value) => !value);
10146
10691
  } : void 0
10147
10692
  }) : jsxs("div", {
10148
- className: css$29.banner,
10693
+ className: css$30.banner,
10149
10694
  "data-code-block-banner": true,
10150
10695
  children: [jsx("div", {
10151
- className: css$29.infostring,
10696
+ className: css$30.infostring,
10152
10697
  children: lang ?? ""
10153
10698
  }), jsx("div", {
10154
- className: css$29.action,
10699
+ className: css$30.action,
10155
10700
  children: jsx("button", {
10156
10701
  type: "button",
10157
- className: css$29.copyButton,
10702
+ className: css$30.copyButton,
10158
10703
  onClick: onCopy,
10159
10704
  children: copied ? copiedLabel : copyLabel
10160
10705
  })
@@ -10162,7 +10707,7 @@ function CodeBlock({ code, lang, streaming, className, contentRef, lineNumbers =
10162
10707
  })
10163
10708
  }), jsx("div", {
10164
10709
  ref: contentRef,
10165
- className: css$29.content,
10710
+ className: css$30.content,
10166
10711
  "data-code-block-content": true,
10167
10712
  children: body
10168
10713
  })]
@@ -10292,11 +10837,16 @@ const MarkdownDelegateContext = createContext({});
10292
10837
  * @param props - Child tree and its file and HTTP(S) link handlers.
10293
10838
  * @returns the scoped child tree.
10294
10839
  */
10295
- function MarkdownDelegateProvider({ children, openExternalLink, openFile }) {
10840
+ function MarkdownDelegateProvider({ children, openExternalLink, openFile, fileImages }) {
10296
10841
  const delegate = useMemo(() => ({
10297
10842
  openExternalLink,
10298
- openFile
10299
- }), [openExternalLink, openFile]);
10843
+ openFile,
10844
+ fileImages
10845
+ }), [
10846
+ openExternalLink,
10847
+ openFile,
10848
+ fileImages
10849
+ ]);
10300
10850
  return jsx(MarkdownDelegateContext.Provider, {
10301
10851
  value: delegate,
10302
10852
  children
@@ -10310,6 +10860,111 @@ function useMarkdownDelegate() {
10310
10860
  return useContext(MarkdownDelegateContext);
10311
10861
  }
10312
10862
  //#endregion
10863
+ //#region lib/types/ImageLightbox.js
10864
+ /**
10865
+ * Document-level original-image preview opened by clicking a thumbnail.
10866
+ * Closes on Escape, backdrop press, or the close control, and restores focus
10867
+ * to the opener on unmount. Rendered through a body portal: an opener inside
10868
+ * a transformed or filtered ancestor would otherwise trap the fixed backdrop
10869
+ * in that ancestor's box instead of covering the viewport.
10870
+ *
10871
+ * @param props.src - the original image URL.
10872
+ * @param props.alt - the image's alt text.
10873
+ * @param props.labels - dialog and close-control strings.
10874
+ * @param props.onClose - dismiss callback owned by the opener.
10875
+ * @returns the modal preview dialog.
10876
+ */
10877
+ function ImageLightbox({ src, alt, labels, onClose }) {
10878
+ const closeRef = useRef(null);
10879
+ const restoreRef = useRef(null);
10880
+ useEffect(() => {
10881
+ restoreRef.current = document.activeElement instanceof HTMLElement ? document.activeElement : null;
10882
+ closeRef.current?.focus();
10883
+ const onKeyDown = (event) => {
10884
+ if (event.key === "Escape") {
10885
+ event.stopPropagation();
10886
+ onClose();
10887
+ }
10888
+ if (event.key === "Tab") {
10889
+ event.preventDefault();
10890
+ closeRef.current?.focus();
10891
+ }
10892
+ };
10893
+ window.addEventListener("keydown", onKeyDown, true);
10894
+ return () => {
10895
+ window.removeEventListener("keydown", onKeyDown, true);
10896
+ restoreRef.current?.focus();
10897
+ };
10898
+ }, [onClose]);
10899
+ return createPortal(jsxs("div", {
10900
+ className: css$31.backdrop,
10901
+ role: "dialog",
10902
+ "aria-modal": "true",
10903
+ "aria-label": labels.dialog,
10904
+ children: [
10905
+ jsx("div", {
10906
+ className: css$31.mask,
10907
+ "aria-hidden": "true",
10908
+ onMouseDown: onClose
10909
+ }),
10910
+ jsx("img", {
10911
+ className: css$31.image,
10912
+ src,
10913
+ alt
10914
+ }),
10915
+ jsx("button", {
10916
+ ref: closeRef,
10917
+ type: "button",
10918
+ className: css$31.close,
10919
+ "aria-label": labels.close,
10920
+ onClick: onClose,
10921
+ children: jsx(IconCloseOutlineRegular, { size: 16 })
10922
+ })
10923
+ ]
10924
+ }), document.body);
10925
+ }
10926
+ //#endregion
10927
+ //#region lib/types/ImagePreview.js
10928
+ /** Passive, contained image preview for Markdown image links. */
10929
+ /**
10930
+ * Render an image without introducing a second activation target.
10931
+ * @param props - Source, accessible description, localized status.
10932
+ * @returns A contained preview with loading or failure status.
10933
+ */
10934
+ function ImagePreview({ src, alt, loadingLabel, failedLabel }) {
10935
+ return jsx(Preview, {
10936
+ src,
10937
+ alt,
10938
+ loadingLabel,
10939
+ failedLabel
10940
+ }, src);
10941
+ }
10942
+ function Preview({ src, alt, loadingLabel, failedLabel }) {
10943
+ const [state, setState] = useState("loading");
10944
+ return jsxs("span", {
10945
+ className: css$32.frame,
10946
+ children: [state !== "failed" && jsx("img", {
10947
+ src,
10948
+ alt,
10949
+ loading: "lazy",
10950
+ decoding: "async",
10951
+ referrerPolicy: "no-referrer",
10952
+ className: css$32.image,
10953
+ "data-ready": state === "ready" || void 0,
10954
+ onLoad: () => {
10955
+ setState("ready");
10956
+ },
10957
+ onError: () => {
10958
+ setState("failed");
10959
+ }
10960
+ }), state !== "ready" && jsxs("span", {
10961
+ className: css$32.status,
10962
+ role: "status",
10963
+ children: [state === "loading" && jsx(IconLoadingOutlineRegular, { size: 16 }), jsx("span", { children: state === "loading" ? loadingLabel : failedLabel })]
10964
+ })]
10965
+ });
10966
+ }
10967
+ //#endregion
10313
10968
  //#region lib/types/markdown/render.js
10314
10969
  /**
10315
10970
  * Direct mdast→React markdown renderer. Replaces the react-markdown /
@@ -10348,11 +11003,11 @@ function remoteImageUrl(url) {
10348
11003
  return;
10349
11004
  }
10350
11005
  }
10351
- /** Protocols a vocabulary-rewritten image destination may carry. */
11006
+ /** Rewritten images may use Web media protocols or the Desktop application's file route. */
10352
11007
  function vocabularyImageUrl(url) {
10353
11008
  try {
10354
11009
  const protocol = new URL(url).protocol;
10355
- return protocol === "http:" || protocol === "https:" || protocol === "blob:" || protocol === "data:" ? url : void 0;
11010
+ return protocol === "http:" || protocol === "https:" || protocol === "blob:" || protocol === "data:" || url.startsWith("dsh-app://app/api/file?") ? url : void 0;
10356
11011
  } catch {
10357
11012
  return;
10358
11013
  }
@@ -10631,12 +11286,14 @@ function renderAnchor(url, children, key, glyph = true, streaming = false) {
10631
11286
  return renderSafeLink(normalizeUri(url), children, key, glyph);
10632
11287
  }
10633
11288
  function MarkdownFileLink({ file, glyph, children }) {
10634
- const { openFile } = useMarkdownDelegate();
11289
+ const { openFile, fileImages } = useMarkdownDelegate();
10635
11290
  if (openFile === void 0) return jsx(Fragment, { children });
10636
- return jsxs("button", {
11291
+ const preview = glyph && classifyLinkPath(file.path) === "image" ? fileImages : void 0;
11292
+ const src = preview?.resolve(file.path);
11293
+ const anchor = jsxs("button", {
10637
11294
  type: "button",
10638
11295
  className: clsx(markdownCss.fileMention, markdownCss.fileLink),
10639
- title: file.path,
11296
+ title: src === void 0 ? file.path : void 0,
10640
11297
  onClick: () => {
10641
11298
  openFile(file.path, file.line === void 0 ? void 0 : { line: file.line });
10642
11299
  },
@@ -10645,6 +11302,20 @@ function MarkdownFileLink({ file, glyph, children }) {
10645
11302
  className: markdownCss.linkIcon
10646
11303
  }), children]
10647
11304
  });
11305
+ if (src === void 0 || preview === void 0) return anchor;
11306
+ return jsx(HoverCard, {
11307
+ inline: true,
11308
+ anchor,
11309
+ content: jsxs(Fragment, { children: [jsx(ImagePreview, {
11310
+ src,
11311
+ alt: file.path,
11312
+ loadingLabel: preview.labels.loading,
11313
+ failedLabel: preview.labels.failed
11314
+ }), jsx("span", {
11315
+ className: markdownCss.previewName,
11316
+ children: file.path.split(/[\\/]/u).pop()
11317
+ })] })
11318
+ });
10648
11319
  }
10649
11320
  /**
10650
11321
  * The complete inline-code value when it is exactly an absolute HTTP(S) URL
@@ -10660,25 +11331,40 @@ function inlineCodeHttpUrl(value) {
10660
11331
  }
10661
11332
  }
10662
11333
  function renderImage(url, alt, key, context) {
10663
- const imageSrc = imageSource(url, context.pathImages);
10664
- if (imageSrc === void 0) return jsx("span", {
11334
+ return jsx(MarkdownImage, {
11335
+ destination: url,
11336
+ alt,
11337
+ pathImages: context.pathImages,
11338
+ streaming: context.streaming,
11339
+ inLink: context.inLink === true
11340
+ }, `${key}:${url}`);
11341
+ }
11342
+ function MarkdownImage({ destination, alt, pathImages, streaming, inLink }) {
11343
+ const { fileImages } = useMarkdownDelegate();
11344
+ const file = streaming ? void 0 : parseFileLink(destination);
11345
+ const src = (file === void 0 ? void 0 : fileImages?.resolve(file.path)) ?? imageSource(destination, pathImages);
11346
+ if (src === void 0) return jsx("span", {
10665
11347
  className: markdownCss.imageAlt,
10666
11348
  children: alt
10667
- }, key);
10668
- return jsx(MarkdownImage, {
10669
- src: imageSrc,
11349
+ });
11350
+ return jsx(LoadedMarkdownImage, {
11351
+ src,
10670
11352
  alt,
10671
- destination: url
10672
- }, `${key}:${imageSrc}`);
11353
+ destination,
11354
+ preview: inLink ? void 0 : fileImages
11355
+ }, src);
10673
11356
  }
10674
- /** Failed loads retain the authored alt or destination; a new source remounts the image. */
10675
- function MarkdownImage({ src, alt, destination }) {
11357
+ function LoadedMarkdownImage({ src, alt, destination, preview }) {
10676
11358
  const [failed, setFailed] = useState(false);
10677
- if (failed) return jsx("span", {
11359
+ const [open, setOpen] = useState(false);
11360
+ const close = useCallback(() => {
11361
+ setOpen(false);
11362
+ }, []);
11363
+ if (failed) return jsxs("span", {
10678
11364
  className: markdownCss.imageAlt,
10679
- children: alt || destination
11365
+ children: [preview === void 0 ? "" : `${preview.labels.failed} · `, alt || destination]
10680
11366
  });
10681
- return jsx("img", {
11367
+ const img = jsx("img", {
10682
11368
  className: markdownCss.image,
10683
11369
  src,
10684
11370
  alt,
@@ -10689,6 +11375,22 @@ function MarkdownImage({ src, alt, destination }) {
10689
11375
  decoding: "async",
10690
11376
  referrerPolicy: "no-referrer"
10691
11377
  });
11378
+ if (preview === void 0) return img;
11379
+ return jsxs(Fragment, { children: [jsx("button", {
11380
+ type: "button",
11381
+ className: markdownCss.imageButton,
11382
+ title: preview.labels.open,
11383
+ "aria-label": alt ? `${preview.labels.open}: ${alt}` : preview.labels.open,
11384
+ onClick: () => {
11385
+ setOpen(true);
11386
+ },
11387
+ children: img
11388
+ }), open && jsx(ImageLightbox, {
11389
+ src,
11390
+ alt,
11391
+ labels: preview.labels,
11392
+ onClose: close
11393
+ })] });
10692
11394
  }
10693
11395
  /** The bracketed source text a reference reverts to when its definition is missing. */
10694
11396
  function referenceSuffix(node) {
@@ -10995,7 +11697,7 @@ function SafeLink({ url, label, className }) {
10995
11697
  children: [jsx(LinkIconMedium, {
10996
11698
  kind: "url",
10997
11699
  href,
10998
- className: css$30.linkIcon
11700
+ className: css$33.linkIcon
10999
11701
  }), label]
11000
11702
  });
11001
11703
  }
@@ -11010,20 +11712,20 @@ function SafeLink({ url, label, className }) {
11010
11712
  */
11011
11713
  function SourceItem({ source, ordinal }) {
11012
11714
  return jsxs("li", {
11013
- className: css$30.source,
11715
+ className: css$33.source,
11014
11716
  value: ordinal,
11015
11717
  children: [
11016
11718
  jsx(SafeLink, {
11017
11719
  url: source.url,
11018
11720
  label: linkLabel(source.url, source.title),
11019
- className: css$30.sourceLink
11721
+ className: css$33.sourceLink
11020
11722
  }),
11021
11723
  source.snippet !== void 0 && source.snippet !== "" && jsx("div", {
11022
- className: css$30.snippet,
11724
+ className: css$33.snippet,
11023
11725
  children: source.snippet
11024
11726
  }),
11025
11727
  source.publishedAt !== void 0 && source.publishedAt !== "" && jsx("div", {
11026
- className: css$30.published,
11728
+ className: css$33.published,
11027
11729
  children: source.publishedAt
11028
11730
  })
11029
11731
  ]
@@ -11038,28 +11740,28 @@ function SourceItem({ source, ordinal }) {
11038
11740
  function WebSearchBlock({ answer, sources, truncated, labels, className }) {
11039
11741
  const empty = (answer === void 0 || answer === "") && sources.length === 0;
11040
11742
  return jsxs("div", {
11041
- className: clsx(css$30.block, className),
11743
+ className: clsx(css$33.block, className),
11042
11744
  "data-web": "search",
11043
11745
  children: [
11044
11746
  answer !== void 0 && answer !== "" && jsx("div", {
11045
- className: css$30.answer,
11747
+ className: css$33.answer,
11046
11748
  children: jsx(MarkdownText, {
11047
11749
  text: answer,
11048
11750
  labels: labels.markdown
11049
11751
  })
11050
11752
  }),
11051
11753
  empty ? jsx("div", {
11052
- className: css$30.empty,
11754
+ className: css$33.empty,
11053
11755
  children: labels.noResults
11054
11756
  }) : jsx("ol", {
11055
- className: css$30.sources,
11757
+ className: css$33.sources,
11056
11758
  children: sources.map((source, index) => jsx(SourceItem, {
11057
11759
  source,
11058
11760
  ordinal: index + 1
11059
11761
  }, index))
11060
11762
  }),
11061
11763
  truncated && jsx("div", {
11062
- className: css$30.truncated,
11764
+ className: css$33.truncated,
11063
11765
  children: labels.sourcesTruncated
11064
11766
  })
11065
11767
  ]
@@ -11072,23 +11774,23 @@ function WebSearchBlock({ answer, sources, truncated, labels, className }) {
11072
11774
  */
11073
11775
  function WebFetchBlock({ url, statusCode, truncated, labels, className }) {
11074
11776
  return jsxs("div", {
11075
- className: clsx(css$30.block, css$30.fetch, className),
11777
+ className: clsx(css$33.block, css$33.fetch, className),
11076
11778
  "data-web": "fetch",
11077
11779
  children: [jsx(SafeLink, {
11078
11780
  url,
11079
11781
  label: url,
11080
- className: css$30.fetchUrl
11782
+ className: css$33.fetchUrl
11081
11783
  }), jsxs("div", {
11082
- className: css$30.fetchMeta,
11784
+ className: css$33.fetchMeta,
11083
11785
  children: [jsxs("span", {
11084
- className: css$30.status,
11786
+ className: css$33.status,
11085
11787
  children: [
11086
11788
  labels.http,
11087
11789
  " ",
11088
11790
  statusCode
11089
11791
  ]
11090
11792
  }), truncated && jsx("span", {
11091
- className: css$30.truncated,
11793
+ className: css$33.truncated,
11092
11794
  children: labels.contentTruncated
11093
11795
  })]
11094
11796
  })]
@@ -11122,10 +11824,10 @@ function JsonBlock({ label, payload, defaultOpen = false, truncatedLabel }) {
11122
11824
  truncatedLabel
11123
11825
  ]);
11124
11826
  return jsxs("div", {
11125
- className: css$31.root,
11827
+ className: css$34.root,
11126
11828
  children: [jsxs("button", {
11127
11829
  type: "button",
11128
- className: css$31.toggle,
11830
+ className: css$34.toggle,
11129
11831
  onClick: () => {
11130
11832
  setOpen((v) => !v);
11131
11833
  },
@@ -11135,7 +11837,7 @@ function JsonBlock({ label, payload, defaultOpen = false, truncatedLabel }) {
11135
11837
  label
11136
11838
  ]
11137
11839
  }), open && jsx("pre", {
11138
- className: css$31.body,
11840
+ className: css$34.body,
11139
11841
  children: body
11140
11842
  })]
11141
11843
  });
@@ -11223,7 +11925,7 @@ function extractMarkdownPlainText(markdown, options = {}) {
11223
11925
  * and duplicate document ids would make every `url(#…)` resolve to the first instance.
11224
11926
  */
11225
11927
  const useArtworkId = () => `dsh_plugin_art_${useId().replaceAll(":", "")}`;
11226
- /** Terminal plugin artwork (prompt chevron and cursor bar). */
11928
+ /** Light-blue terminal artwork shared by plugin cards and sidebar guide entries. */
11227
11929
  const PluginArtworkTerminal = ({ size = 36, className }) => jsxs("svg", {
11228
11930
  width: size,
11229
11931
  height: size,
@@ -11233,11 +11935,11 @@ const PluginArtworkTerminal = ({ size = 36, className }) => jsxs("svg", {
11233
11935
  xmlns: "http://www.w3.org/2000/svg",
11234
11936
  children: [jsx("path", {
11235
11937
  d: "M10 11L16.606 17.606C16.6841 17.6841 16.6841 17.8107 16.606 17.8888L10 24.4948",
11236
- stroke: "#145AF3",
11938
+ stroke: "#679EFE",
11237
11939
  strokeWidth: "3.5"
11238
11940
  }), jsx("path", {
11239
11941
  d: "M20.1211 24.4946H26.8685",
11240
- stroke: "#145AF3",
11942
+ stroke: "#679EFE",
11241
11943
  strokeWidth: "3.5"
11242
11944
  })]
11243
11945
  });
@@ -11411,6 +12113,71 @@ const PluginArtworkDefault = ({ size = 36, className }) => {
11411
12113
  });
11412
12114
  };
11413
12115
  //#endregion
11414
- export { BrandWordmark, Button, CODE_HIGHLIGHT_EXTENSIONS, 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, ICON_MEDIUM_STROKE, ICON_REGULAR_STROKE, IconAgentPresetOutlineMedium, IconAgentPresetOutlineRegular, IconAlarmClockOutlineMedium, IconAlarmClockOutlineRegular, IconApiOutlineMedium, IconApiOutlineRegular, IconArchiveCheckOutlineMedium, IconArchiveCheckOutlineRegular, IconArchiveOutlineMedium, IconArchiveOutlineRegular, IconBranchOutlineMedium, IconBranchOutlineRegular, IconBrowseOutlineMedium, IconBrowseOutlineRegular, IconCheckCircleFillMedium, IconCheckCircleFillRegular, IconCheckCircleOutlineMedium, IconCheckCircleOutlineRegular, IconCheckOutlineMedium, IconCheckOutlineRegular, IconChecklistOutlineMedium, IconChecklistOutlineRegular, IconChevronDownOutlineMedium, IconChevronDownOutlineRegular, IconChevronLeftOutlineMedium, IconChevronLeftOutlineRegular, IconChevronRightOutlineMedium, IconChevronRightOutlineRegular, IconChevronUpOutlineMedium, IconChevronUpOutlineRegular, IconChevronsUpDownOutlineMedium, IconChevronsUpDownOutlineRegular, IconClockOutlineMedium, IconClockOutlineRegular, IconCloseCircleFillMedium, IconCloseCircleFillRegular, IconCloseFillMedium, IconCloseFillRegular, IconCloseOutlineMedium, IconCloseOutlineRegular, IconCodeOutlineMedium, IconCodeOutlineRegular, IconCompactOutlineMedium, IconCompactOutlineRegular, IconCompareSplitOutlineMedium, IconCompareSplitOutlineRegular, IconContextInjectionOutlineMedium, IconContextInjectionOutlineRegular, IconCopyOutlineMedium, IconCopyOutlineRegular, IconCordisPluginOutlineMedium, IconCordisPluginOutlineRegular, IconDarkOutlineMedium, IconDarkOutlineRegular, IconDataOutlineMedium, IconDataOutlineRegular, IconDatabaseOutlineMedium, IconDatabaseOutlineRegular, IconDeliverDocMedium, IconDeliverDocRegular, IconDislikeFillMedium, IconDislikeFillRegular, IconDislikeOutlineMedium, IconDislikeOutlineRegular, IconDownloadOutlineMedium, IconDownloadOutlineRegular, IconEditOutlineMedium, IconEditOutlineRegular, IconEllipsisOutlineMedium, IconEllipsisOutlineRegular, IconEnhanceOutlineMedium, IconEnhanceOutlineRegular, IconFlatListOutlineMedium, IconFlatListOutlineRegular, IconFolderCloseMedium, IconFolderCloseRegular, IconFolderOpenMedium, IconFolderOpenOutlineMedium, IconFolderOpenOutlineRegular, IconFolderOpenRegular, IconFollowsystemOutlineMedium, IconFollowsystemOutlineRegular, IconFullscreenOutlineMedium, IconFullscreenOutlineRegular, IconGaugeOutlineMedium, IconGaugeOutlineRegular, IconGlobeOutlineMedium, IconGlobeOutlineRegular, IconGoalOutlineMedium, IconGoalOutlineRegular, IconInfoOutlineMedium, IconInfoOutlineRegular, IconInspectOutlineMedium, IconInspectOutlineRegular, IconLightOutlineMedium, IconLightOutlineRegular, IconLikeFillMedium, IconLikeFillRegular, IconLikeOutlineMedium, IconLikeOutlineRegular, IconLinkOutlineMedium, IconLinkOutlineRegular, IconListPenOutlineMedium, IconListPenOutlineRegular, IconLoadingOutlineMedium, IconLoadingOutlineRegular, IconMicrophoneOutlineMedium, IconMicrophoneOutlineRegular, IconNewChatOutlineMedium, IconNewChatOutlineRegular, IconNowrapFillMedium, IconNowrapFillRegular, IconPanelLeftOutlineMedium, IconPanelLeftOutlineRegular, IconPaperPlaneOutlineMedium, IconPaperPlaneOutlineRegular, IconPaperclipOutlineMedium, IconPaperclipOutlineRegular, IconPauseOutlineMedium, IconPauseOutlineRegular, IconPersonalizationOutlineMedium, IconPersonalizationOutlineRegular, IconPinFillMedium, IconPinFillRegular, IconPinOutlineMedium, IconPinOutlineRegular, IconPlanOutlineMedium, IconPlanOutlineRegular, IconPlayOutlineMedium, IconPlayOutlineRegular, IconPluginPinwheelOutlineMedium, IconPluginPinwheelOutlineRegular, IconPlusOutlineMedium, IconPlusOutlineRegular, IconProjectAddOutlineMedium, IconProjectAddOutlineRegular, IconQuestionOutlineMedium, IconQuestionOutlineRegular, IconQueueOutlineMedium, IconQueueOutlineRegular, IconRefreshOutlineMedium, IconRefreshOutlineRegular, IconRightUpOutlineMedium, IconRightUpOutlineRegular, IconSearchOutlineMedium, IconSearchOutlineRegular, IconSendOutlineMedium, IconSendOutlineRegular, IconSettingsOutlineMedium, IconSettingsOutlineRegular, IconShareOutlineMedium, IconShareOutlineRegular, IconShieldOutlineMedium, IconShieldOutlineRegular, IconSkillOutlineMedium, IconSkillOutlineRegular, IconSlidersTwoOutlineMedium, IconSlidersTwoOutlineRegular, IconSparkleMedium, IconSparkleRegular, IconStopFillMedium, IconStopFillRegular, IconThinkOutlineMedium, IconThinkOutlineRegular, IconTrashOutlineMedium, IconTrashOutlineRegular, IconTreeCornerMedium, IconTreeCornerRegular, IconTriangleRightFillMedium, IconTriangleRightFillRegular, IconUnarchiveOutlineMedium, IconUnarchiveOutlineRegular, IconUserOutlineMedium, IconUserOutlineRegular, IconWarningOutlineMedium, IconWarningOutlineRegular, IconWarningTriangleOutlineMedium, IconWarningTriangleOutlineRegular, IconWorkspaceTreeOutlineMedium, IconWorkspaceTreeOutlineRegular, IconWrapFillMedium, IconWrapFillRegular, IconWrapLinesOutlineMedium, IconWrapLinesOutlineRegular, Input, JsonBlock, JsonTree, LinkIconMedium, LinkIconRegular, MarkdownDelegateProvider, MarkdownText, Menu, MenuItemButton, Modal, OnboardingSurface, PathLabel, PermissionIconFullAccessMedium, PermissionIconFullAccessRegular, PermissionIconReadOnlyMedium, PermissionIconReadOnlyRegular, PermissionIconWorkspaceWriteMedium, PermissionIconWorkspaceWriteRegular, Pill, PluginArtworkDefault, PluginArtworkLoop, PluginArtworkSearch, PluginArtworkSubagent, PluginArtworkTerminal, ReadBlock, ReferenceIconMedium, ReferenceIconRegular, RiskConfirmation, SHIELD_OUTLINE_PATH, SearchBlock, SegmentedControl, SegmentedTabs, SettingsForm, SettingsFormModel, SettingsSecretField, SettingsValueField, StateDot, Switch, Tag, TerminalBlock, TextShimmer, Toast, Tooltip, WebBlock, classifyFileType, classifyLinkPath, diffTotals, extractMarkdownPlainText, fileExtension, fileSizeText, isDarwinDesktop, languageForPath, projectUserText, rankByName, relativeTime, settingsNumberField, settingsTextField, useAnchoredMaxHeight, useAnchoredPosition, useCodeHighlighter, useDismissOnOutsidePointer, writeClipboard };
12116
+ //#region lib/types/guide-artwork.js
12117
+ /**
12118
+ * Render fixed-palette artwork for a sidebar guide entry.
12119
+ * @param props - canvas size and layout class supplied by the guide.
12120
+ * @returns an ornamental SVG hidden from assistive technology.
12121
+ */
12122
+ function GuideArtworkBrowser({ size = 36, className }) {
12123
+ return jsxs("svg", {
12124
+ width: size,
12125
+ height: size,
12126
+ className,
12127
+ "aria-hidden": "true",
12128
+ viewBox: "0 0 36 36",
12129
+ fill: "none",
12130
+ xmlns: "http://www.w3.org/2000/svg",
12131
+ children: [
12132
+ jsx("path", {
12133
+ d: "M17.9995 28.1465C23.6034 28.1465 28.1461 23.6038 28.1461 18C28.1461 12.3963 23.6034 7.85352 17.9995 7.85352C12.3958 7.85352 7.85303 12.3963 7.85303 18C7.85303 23.6038 12.3958 28.1465 17.9995 28.1465Z",
12134
+ stroke: "#539CFA",
12135
+ strokeWidth: "2"
12136
+ }),
12137
+ jsx("path", {
12138
+ d: "M8.57764 18H27.4211",
12139
+ stroke: "#539CFA",
12140
+ strokeWidth: "2",
12141
+ strokeLinecap: "square"
12142
+ }),
12143
+ jsx("path", {
12144
+ d: "M17.999 28.1467C20.0576 28.1467 21.6228 23.6039 21.6228 18C21.6228 12.3963 20.0576 7.85352 17.999 7.85352",
12145
+ stroke: "#539CFA",
12146
+ strokeWidth: "2"
12147
+ }),
12148
+ jsx("path", {
12149
+ d: "M17.9992 28.1467C15.9407 28.1467 14.3755 23.6039 14.3755 18C14.3755 12.3963 15.9407 7.85352 17.9992 7.85352",
12150
+ stroke: "#539CFA",
12151
+ strokeWidth: "2"
12152
+ })
12153
+ ]
12154
+ });
12155
+ }
12156
+ /**
12157
+ * Render fixed-palette artwork for a sidebar guide entry.
12158
+ * @param props - canvas size and layout class supplied by the guide.
12159
+ * @returns an ornamental SVG hidden from assistive technology.
12160
+ */
12161
+ function GuideArtworkFiles({ size = 36, className }) {
12162
+ return jsxs("svg", {
12163
+ width: size,
12164
+ height: size,
12165
+ className,
12166
+ "aria-hidden": "true",
12167
+ viewBox: "0 0 36 36",
12168
+ fill: "none",
12169
+ xmlns: "http://www.w3.org/2000/svg",
12170
+ children: [jsx("path", {
12171
+ d: "M10.7603 27.922H24.6817C26.3441 27.922 27.1753 27.922 27.8102 27.5984C28.3687 27.3139 28.8228 26.8598 29.1074 26.3012C29.4309 25.6663 29.4309 24.8351 29.4309 23.1727V15.4936",
12172
+ stroke: "#FFCD78",
12173
+ strokeWidth: "1.97886"
12174
+ }), jsx("path", {
12175
+ d: "M13.1597 8.07812C13.4182 8.07818 13.6727 8.14336 13.8989 8.26855L16.7554 9.84961C16.9817 9.97485 17.2369 10.041 17.4956 10.041H26.106C26.9492 10.0412 27.6323 10.7251 27.6323 11.5684V24.5371C27.6323 25.3805 26.9483 26.0645 26.105 26.0645H8.09619C7.25281 26.0645 6.56884 25.3805 6.56885 24.5371V9.60449C6.56909 8.76133 7.25297 8.07812 8.09619 8.07812H13.1597ZM9.81592 14.5508V16.5293H24.3999V14.5508H9.81592Z",
12176
+ fill: "#FFBC4D"
12177
+ })]
12178
+ });
12179
+ }
12180
+ //#endregion
12181
+ export { BrandWordmark, Button, CODE_HIGHLIGHT_EXTENSIONS, 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, GuideArtworkBrowser, GuideArtworkFiles, HoverCard, ICON_MEDIUM_STROKE, ICON_REGULAR_STROKE, IconAgentPresetOutlineMedium, IconAgentPresetOutlineRegular, IconAlarmClockOutlineMedium, IconAlarmClockOutlineRegular, IconApiOutlineMedium, IconApiOutlineRegular, IconArchiveCheckOutlineMedium, IconArchiveCheckOutlineRegular, IconArchiveOffOutlineMedium, IconArchiveOffOutlineRegular, IconArchiveOutlineMedium, IconArchiveOutlineRegular, IconBranchOutlineMedium, IconBranchOutlineRegular, IconBrowseOutlineMedium, IconBrowseOutlineRegular, IconCheckCircleFillMedium, IconCheckCircleFillRegular, IconCheckCircleOutlineMedium, IconCheckCircleOutlineRegular, IconCheckOutlineMedium, IconCheckOutlineRegular, IconChecklistOutlineMedium, IconChecklistOutlineRegular, IconChevronDownOutlineMedium, IconChevronDownOutlineRegular, IconChevronLeftOutlineMedium, IconChevronLeftOutlineRegular, IconChevronRightOutlineMedium, IconChevronRightOutlineRegular, IconChevronUpOutlineMedium, IconChevronUpOutlineRegular, IconChevronsUpDownOutlineMedium, IconChevronsUpDownOutlineRegular, IconClockOutlineMedium, IconClockOutlineRegular, IconCloseCircleFillMedium, IconCloseCircleFillRegular, IconCloseFillMedium, IconCloseFillRegular, IconCloseOutlineMedium, IconCloseOutlineRegular, IconCodeOutlineMedium, IconCodeOutlineRegular, IconCompactOutlineMedium, IconCompactOutlineRegular, IconCompareSplitOutlineMedium, IconCompareSplitOutlineRegular, IconContextInjectionOutlineMedium, IconContextInjectionOutlineRegular, IconCopyOutlineMedium, IconCopyOutlineRegular, IconCordisPluginOutlineMedium, IconCordisPluginOutlineRegular, IconDarkOutlineMedium, IconDarkOutlineRegular, IconDataOutlineMedium, IconDataOutlineRegular, IconDatabaseOutlineMedium, IconDatabaseOutlineRegular, IconDeliverDocMedium, IconDeliverDocRegular, IconDislikeFillMedium, IconDislikeFillRegular, IconDislikeOutlineMedium, IconDislikeOutlineRegular, IconDownloadOutlineMedium, IconDownloadOutlineRegular, IconEditOutlineMedium, IconEditOutlineRegular, IconEllipsisOutlineMedium, IconEllipsisOutlineRegular, IconEnhanceOutlineMedium, IconEnhanceOutlineRegular, IconFlatListOutlineMedium, IconFlatListOutlineRegular, IconFolderCloseMedium, IconFolderCloseRegular, IconFolderOpenMedium, IconFolderOpenOutlineMedium, IconFolderOpenOutlineRegular, IconFolderOpenRegular, IconFollowsystemOutlineMedium, IconFollowsystemOutlineRegular, IconFullscreenOutlineMedium, IconFullscreenOutlineRegular, IconGaugeOutlineMedium, IconGaugeOutlineRegular, IconGlobeOutlineMedium, IconGlobeOutlineRegular, IconGoalOutlineMedium, IconGoalOutlineRegular, IconInfoOutlineMedium, IconInfoOutlineRegular, IconInspectOutlineMedium, IconInspectOutlineRegular, IconLightOutlineMedium, IconLightOutlineRegular, IconLikeFillMedium, IconLikeFillRegular, IconLikeOutlineMedium, IconLikeOutlineRegular, IconLinkOutlineMedium, IconLinkOutlineRegular, IconListPenOutlineMedium, IconListPenOutlineRegular, IconLoadingOutlineMedium, IconLoadingOutlineRegular, IconMicrophoneOutlineMedium, IconMicrophoneOutlineRegular, IconNewChatOutlineMedium, IconNewChatOutlineRegular, IconNowrapFillMedium, IconNowrapFillRegular, IconPanelLeftOutlineMedium, IconPanelLeftOutlineRegular, IconPaperPlaneOutlineMedium, IconPaperPlaneOutlineRegular, IconPaperclipOutlineMedium, IconPaperclipOutlineRegular, IconPauseOutlineMedium, IconPauseOutlineRegular, IconPersonalizationOutlineMedium, IconPersonalizationOutlineRegular, IconPinFillMedium, IconPinFillRegular, IconPinOutlineMedium, IconPinOutlineRegular, IconPlanOutlineMedium, IconPlanOutlineRegular, IconPlayOutlineMedium, IconPlayOutlineRegular, IconPluginPinwheelOutlineMedium, IconPluginPinwheelOutlineRegular, IconPlusOutlineMedium, IconPlusOutlineRegular, IconProjectAddOutlineMedium, IconProjectAddOutlineRegular, IconQuestionOutlineMedium, IconQuestionOutlineRegular, IconQueueOutlineMedium, IconQueueOutlineRegular, IconRefreshOutlineMedium, IconRefreshOutlineRegular, IconRightUpOutlineMedium, IconRightUpOutlineRegular, IconSearchOutlineMedium, IconSearchOutlineRegular, IconSendOutlineMedium, IconSendOutlineRegular, IconSettingsOutlineMedium, IconSettingsOutlineRegular, IconShareOutlineMedium, IconShareOutlineRegular, IconShieldOutlineMedium, IconShieldOutlineRegular, IconSkillOutlineMedium, IconSkillOutlineRegular, IconSlidersTwoOutlineMedium, IconSlidersTwoOutlineRegular, IconSparkleMedium, IconSparkleRegular, IconStopFillMedium, IconStopFillRegular, IconThinkOutlineMedium, IconThinkOutlineRegular, IconTrashOutlineMedium, IconTrashOutlineRegular, IconTreeCornerMedium, IconTreeCornerRegular, IconTriangleRightFillMedium, IconTriangleRightFillRegular, IconUnarchiveOutlineMedium, IconUnarchiveOutlineRegular, IconUserOutlineMedium, IconUserOutlineRegular, IconUsersOutlineMedium, IconUsersOutlineRegular, IconWarningOutlineMedium, IconWarningOutlineRegular, IconWarningTriangleOutlineMedium, IconWarningTriangleOutlineRegular, IconWorkspaceTreeOutlineMedium, IconWorkspaceTreeOutlineRegular, IconWrapFillMedium, IconWrapFillRegular, IconWrapLinesOutlineMedium, IconWrapLinesOutlineRegular, ImageLightbox, Input, JsonBlock, JsonTree, LinkIconMedium, LinkIconRegular, MarkdownDelegateProvider, MarkdownText, Menu, MenuItemButton, MenuSurface, Modal, PathLabel, PermissionIconFullAccessMedium, PermissionIconFullAccessRegular, PermissionIconReadOnlyMedium, PermissionIconReadOnlyRegular, PermissionIconWorkspaceWriteMedium, PermissionIconWorkspaceWriteRegular, Pill, PluginArtworkDefault, PluginArtworkLoop, PluginArtworkSearch, PluginArtworkSubagent, PluginArtworkTerminal, ReadBlock, ReferenceIconMedium, ReferenceIconRegular, RiskConfirmation, SHIELD_OUTLINE_PATH, SearchBlock, SegmentedControl, SegmentedTabs, SettingsForm, SettingsFormModel, SettingsSecretField, SettingsValueField, ShortcutKeys, StateDot, Switch, Tag, TerminalBlock, TextShimmer, Toast, Tooltip, WebBlock, classifyFileType, classifyLinkPath, closeTopModal, diffTotals, extractMarkdownPlainText, fileExtension, fileSizeText, focusWithoutRing, isBehindModal, isDarwinDesktop, languageForPath, modalSelector, observeComposition, projectUserText, rankByName, relativeTime, settingsNumberField, settingsTextField, useAnchoredMaxHeight, useAnchoredPosition, useCodeHighlighter, useDismissOnOutsidePointer, useModalLayer, writeClipboard };
11415
12182
 
11416
12183
  //# sourceMappingURL=index.js.map