@parallelworks/ui 0.51.0 → 0.53.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (250) hide show
  1. package/README.md +197 -17
  2. package/dist/ai/agent/toolStrings.d.ts +2 -0
  3. package/dist/ai/agent/toolStrings.js +20 -18
  4. package/dist/ai/ai.css +17 -3
  5. package/dist/ai/components/AllocationSelector.js +5 -1
  6. package/dist/ai/components/AttachmentManager.js +51 -42
  7. package/dist/ai/components/AttachmentUpload.d.ts +4 -2
  8. package/dist/ai/components/AttachmentUpload.js +9 -9
  9. package/dist/ai/components/BlockedGroupBanner.js +8 -14
  10. package/dist/ai/components/BranchNavigator.js +11 -12
  11. package/dist/ai/components/ChatEmptyState.js +12 -19
  12. package/dist/ai/components/ChatInput.d.ts +4 -3
  13. package/dist/ai/components/ChatInput.js +27 -13
  14. package/dist/ai/components/ChatMessage.js +45 -121
  15. package/dist/ai/components/ChatMessageList.d.ts +5 -2
  16. package/dist/ai/components/ChatMessageList.js +89 -51
  17. package/dist/ai/components/ChatSidebar.js +11 -7
  18. package/dist/ai/components/ChatThread.js +1 -13
  19. package/dist/ai/components/DragOverlay.js +3 -3
  20. package/dist/ai/components/MessageAttachments.d.ts +10 -0
  21. package/dist/ai/components/MessageAttachments.js +78 -0
  22. package/dist/ai/components/ModelSelector.js +3 -3
  23. package/dist/ai/components/PastedText.js +2 -2
  24. package/dist/ai/components/ProviderIssueBanner.js +19 -23
  25. package/dist/ai/components/ShareDialog.js +2 -2
  26. package/dist/ai/components/agent/ActivityLine.d.ts +0 -2
  27. package/dist/ai/components/agent/ActivityLine.js +8 -51
  28. package/dist/ai/components/agent/AgentMessageParts.js +114 -53
  29. package/dist/ai/core/ChatProvider.d.ts +16 -5
  30. package/dist/ai/core/ChatProvider.js +41 -8
  31. package/dist/ai/core/config.js +4 -3
  32. package/dist/ai/index.d.ts +1 -1
  33. package/dist/ai/index.js +3 -3
  34. package/dist/ai/strings.d.ts +29 -3
  35. package/dist/ai/strings.js +80 -26
  36. package/dist/ai/types.d.ts +4 -0
  37. package/dist/ai/ui/Notice.d.ts +2 -2
  38. package/dist/ai/ui/Notice.js +29 -62
  39. package/dist/ai/ui/sidebar.d.ts +5 -3
  40. package/dist/ai/ui/sidebar.js +22 -10
  41. package/dist/components/ActionMenu.d.ts +1 -0
  42. package/dist/components/ActionMenu.js +6 -1
  43. package/dist/components/AppLink.d.ts +26 -0
  44. package/dist/components/AppLink.js +51 -0
  45. package/dist/components/Avatar.d.ts +2 -1
  46. package/dist/components/Avatar.js +4 -2
  47. package/dist/components/Button.d.ts +15 -5
  48. package/dist/components/Button.js +47 -40
  49. package/dist/components/Callout.d.ts +31 -7
  50. package/dist/components/Callout.js +50 -19
  51. package/dist/components/Card.d.ts +11 -6
  52. package/dist/components/Card.js +14 -8
  53. package/dist/components/Checkbox.js +2 -1
  54. package/dist/components/ColorInput.js +5 -3
  55. package/dist/components/Combobox.js +49 -19
  56. package/dist/components/ConfirmModal.d.ts +19 -8
  57. package/dist/components/ConfirmModal.js +31 -53
  58. package/dist/components/CopyButton.d.ts +38 -0
  59. package/dist/components/CopyButton.js +68 -0
  60. package/dist/components/CreateModal.js +118 -111
  61. package/dist/components/DatePicker.js +12 -8
  62. package/dist/components/DescriptionList.d.ts +25 -5
  63. package/dist/components/DescriptionList.js +56 -22
  64. package/dist/components/Disclosure.js +1 -1
  65. package/dist/components/Drawer.js +10 -16
  66. package/dist/components/Field.d.ts +16 -5
  67. package/dist/components/Field.js +63 -8
  68. package/dist/components/IconButton.d.ts +14 -5
  69. package/dist/components/IconButton.js +25 -14
  70. package/dist/components/InfoTip.d.ts +12 -1
  71. package/dist/components/InfoTip.js +13 -2
  72. package/dist/components/Input.d.ts +14 -8
  73. package/dist/components/Input.js +46 -31
  74. package/dist/components/Loader.js +1 -1
  75. package/dist/components/MarkdownImpl.d.ts +1 -0
  76. package/dist/components/MarkdownImpl.js +1 -0
  77. package/dist/components/MenuEntries.d.ts +4 -0
  78. package/dist/components/MenuEntries.js +11 -12
  79. package/dist/components/Meter.js +5 -4
  80. package/dist/components/Modal.d.ts +28 -4
  81. package/dist/components/Modal.js +37 -14
  82. package/dist/components/Overview.d.ts +20 -12
  83. package/dist/components/Overview.js +14 -9
  84. package/dist/components/PageHeader.d.ts +47 -65
  85. package/dist/components/PageHeader.js +275 -153
  86. package/dist/components/PageHeaderAction.d.ts +67 -0
  87. package/dist/components/PageHeaderAction.js +107 -0
  88. package/dist/components/Pagination.js +6 -16
  89. package/dist/components/Popover.d.ts +4 -1
  90. package/dist/components/Popover.js +5 -3
  91. package/dist/components/Provider.d.ts +79 -19
  92. package/dist/components/Provider.js +140 -130
  93. package/dist/components/Radio.js +2 -1
  94. package/dist/components/RefreshButton.js +7 -8
  95. package/dist/components/RequiredMark.js +1 -1
  96. package/dist/components/Section.js +1 -1
  97. package/dist/components/Select.d.ts +4 -9
  98. package/dist/components/Select.js +24 -11
  99. package/dist/components/SettingModeOption.d.ts +1 -1
  100. package/dist/components/SettingModeOption.js +7 -1
  101. package/dist/components/SettingsCard.d.ts +5 -3
  102. package/dist/components/SettingsCard.js +27 -20
  103. package/dist/components/Slider.js +1 -1
  104. package/dist/components/StatusBadge.js +2 -2
  105. package/dist/components/Switch.js +5 -4
  106. package/dist/components/Table.d.ts +30 -20
  107. package/dist/components/Table.js +50 -73
  108. package/dist/components/Tabs.d.ts +3 -7
  109. package/dist/components/Tabs.js +6 -10
  110. package/dist/components/ThemeEditor.js +18 -8
  111. package/dist/components/Toast.d.ts +11 -3
  112. package/dist/components/Toast.js +45 -20
  113. package/dist/components/buttonClasses.js +1 -1
  114. package/dist/components/comboboxItems.d.ts +4 -0
  115. package/dist/components/comboboxItems.js +6 -1
  116. package/dist/components/dismiss.d.ts +12 -0
  117. package/dist/components/dismiss.js +19 -0
  118. package/dist/components/fieldBox.d.ts +4 -0
  119. package/dist/components/fieldBox.js +7 -3
  120. package/dist/components/floating.d.ts +3 -3
  121. package/dist/components/floating.js +1 -1
  122. package/dist/components/focus.d.ts +5 -0
  123. package/dist/components/focus.js +21 -1
  124. package/dist/components/markdownCodeBreaks.js +3 -4
  125. package/dist/components/pageHeaderStore.d.ts +41 -0
  126. package/dist/components/pageHeaderStore.js +67 -0
  127. package/dist/components/pathBreaks.d.ts +8 -0
  128. package/dist/components/pathBreaks.js +28 -0
  129. package/dist/components/segmented.js +1 -1
  130. package/dist/components/shortcutKey.d.ts +2 -1
  131. package/dist/components/shortcutKey.js +19 -2
  132. package/dist/components/splitter.js +1 -1
  133. package/dist/components/useCssIsDark.js +40 -18
  134. package/dist/components/useTheme.js +9 -10
  135. package/dist/dev.d.ts +9 -0
  136. package/dist/dev.js +14 -0
  137. package/dist/editor/Editor.d.ts +3 -2
  138. package/dist/editor/Editor.js +24 -5
  139. package/dist/editor/EditorField.d.ts +2 -1
  140. package/dist/editor/EditorField.js +11 -29
  141. package/dist/editor/FormCodePanel.d.ts +7 -3
  142. package/dist/editor/FormCodePanel.js +14 -7
  143. package/dist/editor/Monaco.d.ts +22 -6
  144. package/dist/editor/Monaco.js +177 -113
  145. package/dist/editor/editorModel.d.ts +1 -1
  146. package/dist/editor/editorModel.js +1 -1
  147. package/dist/editor/index.d.ts +6 -5
  148. package/dist/editor/index.js +5 -4
  149. package/dist/editor/languages.d.ts +24 -0
  150. package/dist/editor/languages.js +31 -0
  151. package/dist/editor/monacoFeatures.d.ts +7 -0
  152. package/dist/editor/monacoFeatures.js +19 -0
  153. package/dist/editor/themes.d.ts +8 -5
  154. package/dist/editor/themes.js +65 -32
  155. package/dist/editor/workers.d.ts +3 -3
  156. package/dist/editor/workers.js +8 -4
  157. package/dist/editor/yaml.d.ts +6 -2
  158. package/dist/editor/yaml.js +11 -2
  159. package/dist/editor/yamlLanguage.js +55 -38
  160. package/dist/file-explorer/FileExplorer.d.ts +18 -4
  161. package/dist/file-explorer/FileExplorer.js +204 -142
  162. package/dist/file-explorer/FileExplorerContext.js +1 -1
  163. package/dist/file-explorer/FilePreview/StreamedBadge.js +3 -1
  164. package/dist/file-explorer/FilePreview/Viewer.js +14 -10
  165. package/dist/file-explorer/FilePreview/index.js +4 -4
  166. package/dist/file-explorer/FileRow.d.ts +6 -2
  167. package/dist/file-explorer/FileRow.js +35 -17
  168. package/dist/file-explorer/FileRowsWindow.d.ts +2 -1
  169. package/dist/file-explorer/FileRowsWindow.js +36 -4
  170. package/dist/file-explorer/FileTable.d.ts +3 -1
  171. package/dist/file-explorer/FileTable.js +6 -3
  172. package/dist/file-explorer/TreeView.d.ts +5 -1
  173. package/dist/file-explorer/TreeView.js +86 -39
  174. package/dist/file-explorer/helpers.d.ts +2 -1
  175. package/dist/file-explorer/helpers.js +24 -23
  176. package/dist/file-explorer/lib/utils.d.ts +3 -0
  177. package/dist/file-explorer/lib/utils.js +6 -1
  178. package/dist/file-explorer/toasts.js +27 -19
  179. package/dist/file-explorer/treeRows.d.ts +6 -0
  180. package/dist/file-explorer/treeRows.js +19 -1
  181. package/dist/file-explorer/useNearEnd.js +3 -6
  182. package/dist/form/Fields/CheckboxGroupField.js +12 -5
  183. package/dist/form/Fields/DurationField.js +1 -1
  184. package/dist/form/Fields/LabelField.js +1 -1
  185. package/dist/form/Fields/ObjectField.d.ts +1 -1
  186. package/dist/form/Fields/ObjectField.js +3 -2
  187. package/dist/form/Fields/StringField.js +16 -29
  188. package/dist/form/Fields/TextAreaField.js +3 -4
  189. package/dist/form/Form.js +3 -8
  190. package/dist/form/Wizard/WizardNavigation.js +0 -3
  191. package/dist/form/Wizard/utils.js +2 -1
  192. package/dist/form/fieldFrame.d.ts +1 -1
  193. package/dist/form/fieldFrame.js +2 -2
  194. package/dist/form/types/fieldComponentTypes.d.ts +2 -1
  195. package/dist/graph/AnnotationBanner.d.ts +1 -2
  196. package/dist/graph/AnnotationBanner.js +30 -32
  197. package/dist/graph/Connectors.js +4 -1
  198. package/dist/graph/DependencyGraph.js +2 -2
  199. package/dist/graph/GraphEditor.js +11 -9
  200. package/dist/graph/GraphEditorDialogs.js +1 -1
  201. package/dist/graph/GraphNode.js +7 -4
  202. package/dist/graph/InputDialog.js +1 -1
  203. package/dist/graph/InputsEditor.js +14 -11
  204. package/dist/graph/MatrixGroup.js +9 -3
  205. package/dist/graph/Reveal.js +18 -10
  206. package/dist/graph/ShortcutsButton.d.ts +11 -2
  207. package/dist/graph/ShortcutsButton.js +14 -3
  208. package/dist/graph/TreeView.js +35 -18
  209. package/dist/graph/WorkflowSettingsDialog.js +1 -1
  210. package/dist/graph/editorFields.js +1 -1
  211. package/dist/graph/editorRows.js +16 -10
  212. package/dist/graph/editorStrings.d.ts +7 -0
  213. package/dist/graph/editorStrings.js +8 -1
  214. package/dist/graph/inputRefs.js +1 -1
  215. package/dist/graph/settingsViews.js +11 -18
  216. package/dist/graph/stepWith.js +1 -1
  217. package/dist/icons.d.ts +2 -0
  218. package/dist/icons.js +4 -2
  219. package/dist/index.d.ts +12 -12
  220. package/dist/index.js +16 -16
  221. package/dist/list/ActionToolbar.d.ts +42 -6
  222. package/dist/list/ActionToolbar.js +298 -101
  223. package/dist/list/ListPage.d.ts +15 -23
  224. package/dist/list/ListPage.js +42 -43
  225. package/dist/list/ListTable.d.ts +37 -7
  226. package/dist/list/ListTable.js +52 -12
  227. package/dist/list/ListViewControls.js +1 -1
  228. package/dist/list/UserHoverCard.js +7 -15
  229. package/dist/list/index.d.ts +2 -2
  230. package/dist/list/index.js +2 -2
  231. package/dist/list/listSearch.js +12 -10
  232. package/dist/list/listView.d.ts +10 -1
  233. package/dist/list/listView.js +15 -1
  234. package/dist/logviewer/LogViewer.d.ts +4 -2
  235. package/dist/logviewer/LogViewer.js +87 -69
  236. package/dist/logviewer/logviewer.css +3 -2
  237. package/dist/reducedMotion.d.ts +5 -0
  238. package/dist/reducedMotion.js +20 -0
  239. package/dist/safeUrl.d.ts +1 -1
  240. package/dist/safeUrl.js +4 -2
  241. package/dist/styles.css +1 -1
  242. package/package.json +5 -2
  243. package/dist/components/Breadcrumbs.d.ts +0 -37
  244. package/dist/components/Breadcrumbs.js +0 -90
  245. package/dist/components/CopyToClipboard.d.ts +0 -17
  246. package/dist/components/CopyToClipboard.js +0 -66
  247. package/dist/components/Sort.d.ts +0 -9
  248. package/dist/components/Sort.js +0 -42
  249. package/dist/file-explorer/FilePreview/CodePreviewEditor.d.ts +0 -6
  250. package/dist/file-explorer/FilePreview/CodePreviewEditor.js +0 -64
@@ -1,13 +1,28 @@
1
1
  import { cx } from "../cx.js";
2
- import { CloseIcon, ErrorIcon, InfoIcon, SuccessIcon, WarningIcon } from "../icons.js";
3
2
  import { buttonClasses } from "./buttonClasses.js";
3
+ import { CloseIcon, ErrorIcon, InfoIcon, SuccessIcon, WarningIcon } from "../icons.js";
4
4
  import { Loader } from "./Loader.js";
5
5
  import { toneIcon } from "./tone.js";
6
6
  import { useStrings } from "./Provider.js";
7
+ import { AppLink } from "./AppLink.js";
7
8
  import { jsx, jsxs } from "react/jsx-runtime";
8
9
  import { useEffect, useId, useSyncExternalStore } from "react";
9
10
  import { Toast } from "@base-ui/react/toast";
10
11
  //#region src/components/Toast.tsx
12
+ function actionFields(action) {
13
+ if (action.href === void 0) return {
14
+ actionProps: {
15
+ children: action.label,
16
+ onClick: action.onClick
17
+ },
18
+ link: void 0
19
+ };
20
+ const { onClick: _, ...link } = action;
21
+ return {
22
+ actionProps: void 0,
23
+ link
24
+ };
25
+ }
11
26
  var manager = Toast.createToastManager();
12
27
  var TIMEOUT = {
13
28
  info: 5e3,
@@ -18,21 +33,19 @@ var TIMEOUT = {
18
33
  };
19
34
  /** Everything a toast shows, from its kind's defaults and the options that override them. */
20
35
  function toastOptions(kind, message, options) {
21
- const { action } = options;
36
+ const action = options.action && actionFields(options.action);
22
37
  return {
23
38
  type: kind,
24
39
  title: options.title,
25
40
  description: message,
26
41
  timeout: options.timeout === false ? 0 : options.timeout ?? TIMEOUT[kind],
27
42
  priority: kind === "error" ? "high" : "low",
28
- actionProps: action && {
29
- children: action.label,
30
- onClick: action.onClick
31
- },
43
+ actionProps: action?.actionProps,
32
44
  data: {
33
45
  kind,
34
46
  icon: options.icon ?? true,
35
- dismissible: options.dismissible ?? kind !== "loading"
47
+ dismissible: options.dismissible ?? kind !== "loading",
48
+ link: action?.link
36
49
  }
37
50
  };
38
51
  }
@@ -59,18 +72,17 @@ var toast = {
59
72
  icon: true,
60
73
  dismissible: true
61
74
  };
75
+ const action = options.action && actionFields(options.action);
62
76
  return {
63
77
  ...message === void 0 ? {} : { description: message },
64
78
  ..."title" in options ? { title: options.title } : {},
65
79
  ...options.timeout === void 0 ? {} : { timeout: options.timeout === false ? 0 : options.timeout },
66
- ...options.action === void 0 ? {} : { actionProps: {
67
- children: options.action.label,
68
- onClick: options.action.onClick
69
- } },
80
+ ...action ? { actionProps: action.actionProps } : {},
70
81
  data: {
71
82
  kind: data.kind,
72
83
  icon: options.icon ?? data.icon,
73
- dismissible: options.dismissible ?? data.dismissible
84
+ dismissible: options.dismissible ?? data.dismissible,
85
+ link: action ? action.link : data.link
74
86
  }
75
87
  };
76
88
  });
@@ -152,15 +164,19 @@ function ToneIcon({ tone }) {
152
164
  className: cx("mt-0.5 size-4 shrink-0", toneIcon[tone])
153
165
  });
154
166
  }
167
+ var actionClasses = buttonClasses({
168
+ variant: "outline",
169
+ size: "xs"
170
+ });
155
171
  function ToastList() {
156
172
  const { toasts } = Toast.useToastManager();
157
173
  const { common } = useStrings();
158
174
  return toasts.map((item) => {
159
- const { kind = "info", icon = true, dismissible = true } = item.data ?? {};
175
+ const { kind = "info", icon = true, dismissible = true, link } = item.data ?? {};
160
176
  return /* @__PURE__ */ jsx(Toast.Root, {
161
177
  toast: item,
162
178
  swipeDirection: ["up", "right"],
163
- className: cx("[--gap:0.5rem] [--peek:0.5rem] [--scale:calc(max(0,1-(var(--toast-index)*0.05)))] [--height:var(--toast-frontmost-height,var(--toast-height))]", "[--offset-y:calc(var(--toast-offset-y)+var(--toast-index)*var(--gap)+var(--toast-swipe-movement-y))]", "absolute inset-x-0 top-0 z-[calc(100-var(--toast-index))] h-(--height) origin-top select-none", "[transform:translateX(var(--toast-swipe-movement-x))_translateY(calc(var(--toast-swipe-movement-y)+var(--toast-index)*var(--peek)+(1-var(--scale))*var(--height)))_scale(var(--scale))]", "data-expanded:h-(--toast-height) data-expanded:[transform:translateX(var(--toast-swipe-movement-x))_translateY(var(--offset-y))]", "data-starting-style:[transform:translateY(-150%)] data-ending-style:opacity-0 data-limited:opacity-0", "[&[data-ending-style]:not([data-limited]):not([data-swipe-direction])]:[transform:translateY(-150%)]", "data-ending-style:data-[swipe-direction=up]:[transform:translateY(calc(var(--toast-swipe-movement-y)-150%))]", "data-ending-style:data-[swipe-direction=right]:[transform:translateX(calc(var(--toast-swipe-movement-x)+150%))_translateY(var(--offset-y))]", "rounded-lg border border-(--theme-border) bg-(--theme-app-bg) text-(--theme-app) shadow-(--theme-shadow-lg) outline-none focus-visible:outline-2 focus-visible:outline-(--theme-link)", "[transition:transform_0.4s_cubic-bezier(0.22,1,0.36,1),opacity_0.4s,height_0.15s] motion-reduce:transition-none", "after:absolute after:inset-x-0 after:top-full after:h-[calc(var(--gap)+1px)] after:content-['']"),
179
+ className: cx("[--gap:0.5rem] [--peek:0.5rem] [--scale:calc(max(0,1-(var(--toast-index)*0.05)))] [--height:var(--toast-frontmost-height,var(--toast-height))]", "[--offset-y:calc(var(--toast-offset-y)+var(--toast-index)*var(--gap)+var(--toast-swipe-movement-y))]", "absolute inset-x-0 top-0 z-[calc(100-var(--toast-index))] h-(--height) origin-top select-none", "[transform:translateX(var(--toast-swipe-movement-x))_translateY(calc(var(--toast-swipe-movement-y)+var(--toast-index)*var(--peek)+(1-var(--scale))*var(--height)))_scale(var(--scale))]", "data-expanded:h-(--toast-height) data-expanded:[transform:translateX(var(--toast-swipe-movement-x))_translateY(var(--offset-y))]", "data-starting-style:[transform:translateY(-150%)] data-ending-style:opacity-0 data-limited:opacity-0", "[&[data-ending-style]:not([data-limited]):not([data-swipe-direction])]:[transform:translateY(-150%)]", "data-ending-style:data-[swipe-direction=up]:[transform:translateY(calc(var(--toast-swipe-movement-y)-150%))]", "data-ending-style:data-[swipe-direction=right]:[transform:translateX(calc(var(--toast-swipe-movement-x)+150%))_translateY(var(--offset-y))]", "rounded-lg border border-(--theme-border) bg-(--theme-app-bg) text-(--theme-app) shadow-(--theme-shadow-lg) outline-hidden focus-visible:outline-2 focus-visible:outline-solid focus-visible:outline-(--theme-focus)", "[transition:transform_0.4s_cubic-bezier(0.22,1,0.36,1),opacity_0.4s,height_0.15s] motion-reduce:transition-none", "after:absolute after:inset-x-0 after:top-full after:h-[calc(var(--gap)+1px)] after:content-['']"),
164
180
  children: /* @__PURE__ */ jsxs(Toast.Content, {
165
181
  className: "flex items-start gap-3 overflow-hidden p-3 transition-opacity duration-200 data-behind:opacity-0 data-expanded:opacity-100",
166
182
  children: [
@@ -184,15 +200,24 @@ function ToastList() {
184
200
  }),
185
201
  item.actionProps && /* @__PURE__ */ jsx(Toast.Action, {
186
202
  onClick: () => manager.close(item.id),
187
- className: buttonClasses({
188
- variant: "outline",
189
- size: "xs"
190
- })
203
+ className: actionClasses
204
+ }),
205
+ link && /* @__PURE__ */ jsx(AppLink, {
206
+ href: link.href,
207
+ reloadDocument: link.reloadDocument,
208
+ target: link.target,
209
+ onClick: () => manager.close(item.id),
210
+ className: actionClasses,
211
+ children: link.label
191
212
  }),
192
213
  dismissible && /* @__PURE__ */ jsx(Toast.Close, {
193
214
  "aria-label": common.close,
194
- className: "-m-1 shrink-0 cursor-pointer rounded-md p-1 text-(--theme-muted-text-color) transition-colors hover:bg-(--theme-muted-panel-bg) hover:text-(--theme-app)",
195
- children: /* @__PURE__ */ jsx(CloseIcon, { className: "size-4" })
215
+ className: cx(buttonClasses({
216
+ variant: "ghost",
217
+ size: "sm",
218
+ iconOnly: true
219
+ }), "-m-1.5"),
220
+ children: /* @__PURE__ */ jsx(CloseIcon, {})
196
221
  })
197
222
  ]
198
223
  })
@@ -1,6 +1,6 @@
1
1
  import { cx } from "../cx.js";
2
2
  //#region src/components/buttonClasses.ts
3
- var base = "relative inline-flex shrink-0 cursor-pointer items-center justify-center gap-1.5 whitespace-nowrap rounded-md font-medium no-underline transition-colors focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-(--theme-link) disabled:cursor-not-allowed disabled:opacity-50 aria-disabled:cursor-not-allowed aria-disabled:opacity-50";
3
+ var base = "relative inline-flex shrink-0 cursor-pointer items-center justify-center gap-1.5 whitespace-nowrap rounded-md font-medium no-underline transition-colors focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-(--theme-focus) disabled:cursor-not-allowed disabled:opacity-50 aria-disabled:cursor-not-allowed aria-disabled:opacity-50";
4
4
  var variants = {
5
5
  primary: "bg-(--theme-accent) text-(--theme-accent-text) hover:not-disabled:not-aria-disabled:bg-(--theme-accent-hover)",
6
6
  secondary: "border border-(--theme-border) bg-(--theme-panel-bg) text-(--theme-app) hover:not-disabled:not-aria-disabled:bg-(--theme-hover)",
@@ -1,4 +1,8 @@
1
1
  import type { ReactNode } from 'react';
2
+ /** An option's row in a Select's or a Combobox's list, so both lists look alike: the icon, the
3
+ * label over its description, and the check at the end. */
4
+ export declare const pickerItemClassName: string;
5
+ /** An option of a Select or a Combobox. */
2
6
  export interface ComboboxItem<Value = string> {
3
7
  value: Value;
4
8
  label: string;
@@ -1,4 +1,9 @@
1
+ import { cx } from "../cx.js";
2
+ import { panelItemClassName } from "./floating.js";
1
3
  //#region src/components/comboboxItems.ts
4
+ /** An option's row in a Select's or a Combobox's list, so both lists look alike: the icon, the
5
+ * label over its description, and the check at the end. */
6
+ var pickerItemClassName = cx(panelItemClassName, "data-selected:font-medium");
2
7
  function isComboboxGroup(item) {
3
8
  return typeof item === "object" && "items" in item;
4
9
  }
@@ -7,4 +12,4 @@ function comboboxLeaves(items) {
7
12
  return items.flatMap((item) => isComboboxGroup(item) ? item.items : [item]);
8
13
  }
9
14
  //#endregion
10
- export { comboboxLeaves, isComboboxGroup };
15
+ export { comboboxLeaves, isComboboxGroup, pickerItemClassName };
@@ -0,0 +1,12 @@
1
+ interface CloseDetails {
2
+ reason: string;
3
+ event: Event;
4
+ cancel: () => void;
5
+ }
6
+ /** The `onOpenChange` of a modal or drawer that the host opens and closes:
7
+ * Escape and a press outside close it unless it's not `dismissible`, and a
8
+ * press on a toast never does, since the toast sits above the backdrop. */
9
+ export declare function closeHandler(dismissible: boolean, onClose: () => void): (next: boolean, details: CloseDetails) => void;
10
+ /** The row of a dialog's actions along its bottom edge, ruled off from its body. */
11
+ export declare const dialogFooterClassName = "flex shrink-0 items-center justify-end gap-2 border-t border-(--theme-border) px-5 py-3";
12
+ export {};
@@ -0,0 +1,19 @@
1
+ import { pressedToast } from "./Toast.js";
2
+ //#region src/components/dismiss.ts
3
+ /** The `onOpenChange` of a modal or drawer that the host opens and closes:
4
+ * Escape and a press outside close it unless it's not `dismissible`, and a
5
+ * press on a toast never does, since the toast sits above the backdrop. */
6
+ function closeHandler(dismissible, onClose) {
7
+ return (next, details) => {
8
+ if (next) return;
9
+ if (!dismissible && details.reason === "escape-key" || details.reason === "outside-press" && pressedToast(details.event)) {
10
+ details.cancel();
11
+ return;
12
+ }
13
+ onClose();
14
+ };
15
+ }
16
+ /** The row of a dialog's actions along its bottom edge, ruled off from its body. */
17
+ var dialogFooterClassName = "flex shrink-0 items-center justify-end gap-2 border-t border-(--theme-border) px-5 py-3";
18
+ //#endregion
19
+ export { closeHandler, dialogFooterClassName };
@@ -9,6 +9,10 @@ export interface FieldBoxOptions {
9
9
  * buttons beside it: focus, invalid and disabled then follow what is inside. */
10
10
  within?: boolean | undefined;
11
11
  }
12
+ /** The keyboard focus ring of a control that isn't a box, such as a checkbox, switch or slider
13
+ * thumb: an outline in the theme's focus color, which is solved for 3:1 on every surface a
14
+ * control sits on, so it shows on a checked control as well as an empty one. */
15
+ export declare const focusOutline = "focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-(--theme-focus)";
12
16
  /** The box every input-like control draws: text inputs, textareas, and the triggers of
13
17
  * pickers. Invalid follows `aria-invalid`, disabled follows `disabled` or `data-disabled`. */
14
18
  export declare function fieldBox({ size, multiline, padded, within, }?: FieldBoxOptions): string;
@@ -16,9 +16,13 @@ var sizes = {
16
16
  text: "text-sm"
17
17
  }
18
18
  };
19
+ /** The keyboard focus ring of a control that isn't a box, such as a checkbox, switch or slider
20
+ * thumb: an outline in the theme's focus color, which is solved for 3:1 on every surface a
21
+ * control sits on, so it shows on a checked control as well as an empty one. */
22
+ var focusOutline = "focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-(--theme-focus)";
19
23
  var base = "w-full min-w-0 rounded-md border border-(--theme-border) bg-(--theme-input-bg) text-(--theme-app) outline-none transition-colors placeholder:text-(--theme-placeholder)";
20
- var own = cx("focus-visible:border-(--theme-accent) focus-visible:ring-1 focus-visible:ring-(--theme-accent)", "aria-invalid:border-(--theme-error) aria-invalid:focus-visible:ring-(--theme-error)", "disabled:cursor-not-allowed disabled:bg-(--theme-input-disabled-bg) disabled:text-(--theme-placeholder)", "data-disabled:cursor-not-allowed data-disabled:bg-(--theme-input-disabled-bg) data-disabled:text-(--theme-placeholder)");
21
- var inside = cx("has-[:focus-visible]:border-(--theme-accent) has-[:focus-visible]:ring-1 has-[:focus-visible]:ring-(--theme-accent)", "has-[[aria-invalid=true]]:border-(--theme-error) has-[[aria-invalid=true]:focus-visible]:ring-(--theme-error)", "has-[input:disabled]:cursor-not-allowed has-[input:disabled]:bg-(--theme-input-disabled-bg) has-[input:disabled]:text-(--theme-placeholder)", "data-disabled:cursor-not-allowed data-disabled:bg-(--theme-input-disabled-bg) data-disabled:text-(--theme-placeholder)");
24
+ var own = cx("focus-visible:border-(--theme-focus) focus-visible:ring-1 focus-visible:ring-(--theme-focus)", "aria-invalid:border-(--theme-error) aria-invalid:focus-visible:ring-(--theme-error)", "disabled:cursor-not-allowed disabled:bg-(--theme-input-disabled-bg) disabled:text-(--theme-placeholder)", "data-disabled:cursor-not-allowed data-disabled:bg-(--theme-input-disabled-bg) data-disabled:text-(--theme-placeholder)");
25
+ var inside = cx("has-[:focus-visible]:border-(--theme-focus) has-[:focus-visible]:ring-1 has-[:focus-visible]:ring-(--theme-focus)", "has-[[aria-invalid=true]]:border-(--theme-error) has-[[aria-invalid=true]:focus-visible]:ring-(--theme-error)", "has-[input:disabled]:cursor-not-allowed has-[input:disabled]:bg-(--theme-input-disabled-bg) has-[input:disabled]:text-(--theme-placeholder)", "data-disabled:cursor-not-allowed data-disabled:bg-(--theme-input-disabled-bg) data-disabled:text-(--theme-placeholder)");
22
26
  /** The box every input-like control draws: text inputs, textareas, and the triggers of
23
27
  * pickers. Invalid follows `aria-invalid`, disabled follows `disabled` or `data-disabled`. */
24
28
  function fieldBox({ size = "md", multiline = false, padded = true, within = false } = {}) {
@@ -26,4 +30,4 @@ function fieldBox({ size = "md", multiline = false, padded = true, within = fals
26
30
  return cx(base, box.text, multiline ? box.minHeight : box.height, padded && box.px, padded && multiline && box.py, within ? inside : own);
27
31
  }
28
32
  //#endregion
29
- export { fieldBox };
33
+ export { fieldBox, focusOutline };
@@ -1,14 +1,14 @@
1
1
  /** Above dialogs and sticky chrome; hidden while its anchor is scrolled out of view. */
2
2
  export declare const positionerClassName = "z-(--layer-popover) outline-none data-anchor-hidden:invisible";
3
3
  /** A panel of choices: a menu, a select's list, a combobox's list. */
4
- export declare const panelClassName = "rounded-lg border border-(--theme-border) bg-(--theme-app-bg) text-sm text-(--theme-app) shadow-(--theme-shadow-lg) outline-none origin-(--transform-origin) transition-[transform,opacity] duration-100 data-starting-style:scale-95 data-starting-style:opacity-0 data-ending-style:scale-95 data-ending-style:opacity-0";
4
+ export declare const panelClassName = "rounded-lg border border-(--theme-border) bg-(--theme-app-bg) text-sm text-(--theme-app) shadow-(--theme-shadow-lg) outline-none origin-(--transform-origin) transition-[transform,opacity] duration-100 data-starting-style:scale-95 data-starting-style:opacity-0 data-ending-style:scale-95 data-ending-style:opacity-0 motion-reduce:data-starting-style:scale-100 motion-reduce:data-ending-style:scale-100";
5
5
  /** A row in a panel. */
6
6
  export declare const panelItemClassName = "flex w-full cursor-pointer select-none items-center gap-2 px-3 py-1.5 text-left outline-none data-highlighted:bg-(--theme-hover) data-highlighted:text-(--theme-app) data-disabled:cursor-not-allowed data-disabled:opacity-50";
7
7
  /** A tooltip's or infotip's small inverted surface. */
8
- export declare const tipClassName = "rounded-md bg-(--theme-app) px-2 py-1 text-xs text-(--theme-app-bg) shadow-(--theme-shadow) whitespace-pre-line break-words data-instant:transition-none origin-(--transform-origin) transition-[transform,opacity] duration-100 data-starting-style:scale-95 data-starting-style:opacity-0 data-ending-style:scale-95 data-ending-style:opacity-0";
8
+ export declare const tipClassName = "rounded-md bg-(--theme-app) px-2 py-1 text-xs text-(--theme-app-bg) shadow-(--theme-shadow) whitespace-pre-line break-words data-instant:transition-none origin-(--transform-origin) transition-[transform,opacity] duration-100 data-starting-style:scale-95 data-starting-style:opacity-0 data-ending-style:scale-95 data-ending-style:opacity-0 motion-reduce:data-starting-style:scale-100 motion-reduce:data-ending-style:scale-100";
9
9
  export declare const tipArrowClassName = "size-2 rotate-45 bg-(--theme-app) data-[side=top]:-bottom-1 data-[side=bottom]:-top-1 data-[side=left]:-right-1 data-[side=right]:-left-1";
10
10
  /** A hover card's surface; the card sets its own width and padding. */
11
- export declare const cardClassName = "overflow-hidden rounded-xl border border-(--theme-border) bg-(--theme-panel-bg) text-(--theme-app) shadow-(--theme-shadow-lg) outline-none origin-(--transform-origin) transition-[transform,opacity] duration-100 data-starting-style:scale-95 data-starting-style:opacity-0 data-ending-style:scale-95 data-ending-style:opacity-0";
11
+ export declare const cardClassName = "overflow-hidden rounded-xl border border-(--theme-border) bg-(--theme-panel-bg) text-(--theme-app) shadow-(--theme-shadow-lg) outline-none origin-(--transform-origin) transition-[transform,opacity] duration-100 data-starting-style:scale-95 data-starting-style:opacity-0 data-ending-style:scale-95 data-ending-style:opacity-0 motion-reduce:data-starting-style:scale-100 motion-reduce:data-ending-style:scale-100";
12
12
  /** Where a popup opens: under an element, such as the button that opened it, or
13
13
  * at a pointer event's point, which then moves with the element it landed on. */
14
14
  export type FloatingAnchor = Element | {
@@ -1,7 +1,7 @@
1
1
  //#region src/components/floating.ts
2
2
  /** Above dialogs and sticky chrome; hidden while its anchor is scrolled out of view. */
3
3
  var positionerClassName = "z-(--layer-popover) outline-none data-anchor-hidden:invisible";
4
- var enterExit = "origin-(--transform-origin) transition-[transform,opacity] duration-100 data-starting-style:scale-95 data-starting-style:opacity-0 data-ending-style:scale-95 data-ending-style:opacity-0";
4
+ var enterExit = "origin-(--transform-origin) transition-[transform,opacity] duration-100 data-starting-style:scale-95 data-starting-style:opacity-0 data-ending-style:scale-95 data-ending-style:opacity-0 motion-reduce:data-starting-style:scale-100 motion-reduce:data-ending-style:scale-100";
5
5
  /** A panel of choices: a menu, a select's list, a combobox's list. */
6
6
  var panelClassName = `rounded-lg border border-(--theme-border) bg-(--theme-app-bg) text-sm text-(--theme-app) shadow-(--theme-shadow-lg) outline-none ${enterExit}`;
7
7
  /** A row in a panel. */
@@ -1,2 +1,7 @@
1
1
  /** A ref that focuses a field once, when it mounts; stable so re-renders never move focus back. */
2
2
  export declare function focusOnMount(element: HTMLElement | null): void;
3
+ /** Starts telling keyboard focus from pointer focus, for `focusCameFromKeyboard`. Call it once
4
+ * something needs to know, such as when a component that asks mounts. */
5
+ export declare function trackFocusModality(): void;
6
+ /** Whether the focus that just landed came from the keyboard rather than a press. */
7
+ export declare function focusCameFromKeyboard(): boolean;
@@ -3,5 +3,25 @@
3
3
  function focusOnMount(element) {
4
4
  element?.focus();
5
5
  }
6
+ var fromKeyboard = false;
7
+ var listening = false;
8
+ function onKeyDown(event) {
9
+ if (!event.metaKey && !event.ctrlKey && !event.altKey) fromKeyboard = true;
10
+ }
11
+ function onPointerDown() {
12
+ fromKeyboard = false;
13
+ }
14
+ /** Starts telling keyboard focus from pointer focus, for `focusCameFromKeyboard`. Call it once
15
+ * something needs to know, such as when a component that asks mounts. */
16
+ function trackFocusModality() {
17
+ if (listening || typeof document === "undefined") return;
18
+ listening = true;
19
+ document.addEventListener("keydown", onKeyDown, true);
20
+ document.addEventListener("pointerdown", onPointerDown, true);
21
+ }
22
+ /** Whether the focus that just landed came from the keyboard rather than a press. */
23
+ function focusCameFromKeyboard() {
24
+ return fromKeyboard;
25
+ }
6
26
  //#endregion
7
- export { focusOnMount };
27
+ export { focusCameFromKeyboard, focusOnMount, trackFocusModality };
@@ -1,16 +1,15 @@
1
+ import { pathPieces } from "./pathBreaks.js";
1
2
  //#region src/components/markdownCodeBreaks.ts
2
- var BREAK_AFTER = /(?<=[/])/;
3
3
  function withBreaks(text) {
4
- const pieces = text.split(BREAK_AFTER);
5
4
  const out = [];
6
- pieces.forEach((piece, i) => {
5
+ pathPieces(text).forEach((piece, i) => {
7
6
  if (i > 0) out.push({
8
7
  type: "element",
9
8
  tagName: "wbr",
10
9
  properties: {},
11
10
  children: []
12
11
  });
13
- if (piece !== "") out.push({
12
+ out.push({
14
13
  type: "text",
15
14
  value: piece
16
15
  });
@@ -0,0 +1,41 @@
1
+ /** One step of the trail to the current page. */
2
+ export interface Breadcrumb {
3
+ /** Empty while the name is still loading: the bar shows a placeholder. */
4
+ label: string;
5
+ href?: string | undefined;
6
+ }
7
+ export type PageHeaderRegion = 'titleActions' | 'actions' | 'toolbar';
8
+ /** The nodes a declaration portals its regions into. They are created with the
9
+ * declaration, before any bar exists, so its content renders on the first pass
10
+ * and keeps its state when the bar unmounts or moves. */
11
+ export type RegionNodes = Readonly<Record<PageHeaderRegion, HTMLElement>>;
12
+ export interface Declaration {
13
+ breadcrumbs: readonly Breadcrumb[] | undefined;
14
+ hidden: boolean;
15
+ nodes: RegionNodes | null;
16
+ fills: Readonly<Record<PageHeaderRegion, boolean>>;
17
+ }
18
+ export interface PageHeaderSnapshot {
19
+ breadcrumbs: readonly Breadcrumb[];
20
+ hidden: boolean;
21
+ titleActions: readonly HTMLElement[];
22
+ actions: readonly HTMLElement[];
23
+ toolbar: readonly HTMLElement[];
24
+ }
25
+ export declare function sameCrumbs(a: readonly Breadcrumb[] | undefined, b: readonly Breadcrumb[] | undefined): boolean;
26
+ /**
27
+ * Holds what each mounted PageHeader declares, keyed by its render order. The
28
+ * bar reads one merged snapshot through useSyncExternalStore, so a page
29
+ * changing its header re-renders the bar alone, never the app under the
30
+ * provider.
31
+ */
32
+ export declare class PageHeaderStore {
33
+ private declarations;
34
+ private listeners;
35
+ private snapshot;
36
+ subscribe: (listener: () => void) => (() => void);
37
+ getSnapshot: () => PageHeaderSnapshot;
38
+ set(order: number, declaration: Declaration): void;
39
+ delete(order: number): void;
40
+ private publish;
41
+ }
@@ -0,0 +1,67 @@
1
+ //#region src/components/pageHeaderStore.ts
2
+ var NO_NODES = [];
3
+ var NO_CRUMBS = [];
4
+ var EMPTY = {
5
+ breadcrumbs: NO_CRUMBS,
6
+ hidden: false,
7
+ titleActions: NO_NODES,
8
+ actions: NO_NODES,
9
+ toolbar: NO_NODES
10
+ };
11
+ function sameCrumbs(a, b) {
12
+ if (a === b) return true;
13
+ if (!a || !b || a.length !== b.length) return false;
14
+ return a.every((crumb, i) => crumb.label === b[i]?.label && crumb.href === b[i]?.href);
15
+ }
16
+ function sameDeclaration(a, b) {
17
+ return sameCrumbs(a.breadcrumbs, b.breadcrumbs) && a.hidden === b.hidden && a.nodes === b.nodes && a.fills.titleActions === b.fills.titleActions && a.fills.actions === b.fills.actions && a.fills.toolbar === b.fills.toolbar;
18
+ }
19
+ function sameNodes(a, b) {
20
+ return a.length === b.length && a.every((node, i) => node === b[i]);
21
+ }
22
+ /**
23
+ * Holds what each mounted PageHeader declares, keyed by its render order. The
24
+ * bar reads one merged snapshot through useSyncExternalStore, so a page
25
+ * changing its header re-renders the bar alone, never the app under the
26
+ * provider.
27
+ */
28
+ var PageHeaderStore = class {
29
+ declarations = /* @__PURE__ */ new Map();
30
+ listeners = /* @__PURE__ */ new Set();
31
+ snapshot = EMPTY;
32
+ subscribe = (listener) => {
33
+ this.listeners.add(listener);
34
+ return () => {
35
+ this.listeners.delete(listener);
36
+ };
37
+ };
38
+ getSnapshot = () => this.snapshot;
39
+ set(order, declaration) {
40
+ const prev = this.declarations.get(order);
41
+ if (prev && sameDeclaration(prev, declaration)) return;
42
+ this.declarations.set(order, declaration);
43
+ this.publish();
44
+ }
45
+ delete(order) {
46
+ if (this.declarations.delete(order)) this.publish();
47
+ }
48
+ publish() {
49
+ const ordered = [...this.declarations.entries()].sort(([a], [b]) => a - b).map(([, declaration]) => declaration);
50
+ const regionNodes = (region, prev) => {
51
+ const next = ordered.flatMap((d) => d.nodes && d.fills[region] ? [d.nodes[region]] : []);
52
+ return sameNodes(next, prev) ? prev : next;
53
+ };
54
+ const trail = ordered.findLast((d) => d.breadcrumbs !== void 0)?.breadcrumbs ?? NO_CRUMBS;
55
+ const prev = this.snapshot;
56
+ this.snapshot = {
57
+ breadcrumbs: sameCrumbs(trail, prev.breadcrumbs) ? prev.breadcrumbs : trail,
58
+ hidden: ordered.some((d) => d.hidden),
59
+ titleActions: regionNodes("titleActions", prev.titleActions),
60
+ actions: regionNodes("actions", prev.actions),
61
+ toolbar: regionNodes("toolbar", prev.toolbar)
62
+ };
63
+ for (const listener of this.listeners) listener();
64
+ }
65
+ };
66
+ //#endregion
67
+ export { PageHeaderStore, sameCrumbs };
@@ -0,0 +1,8 @@
1
+ import { type ReactNode } from 'react';
2
+ /** The pieces of `text` with a break allowed after each, split after each path
3
+ * separator; one piece when it holds none. */
4
+ export declare function pathPieces(text: string): string[];
5
+ /** `node` with a place to wrap after each path separator in its text: a string,
6
+ * or an element, such as a link, whose child is one. Deeper content is left as
7
+ * it is. */
8
+ export declare function withPathBreaks(node: ReactNode): ReactNode;
@@ -0,0 +1,28 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { Children, Fragment as Fragment$1, cloneElement, isValidElement } from "react";
3
+ //#region src/components/pathBreaks.tsx
4
+ var SEPARATOR = /(?<=[/\\])(?![/\\])/;
5
+ /** The pieces of `text` with a break allowed after each, split after each path
6
+ * separator; one piece when it holds none. */
7
+ function pathPieces(text) {
8
+ return text.split(SEPARATOR).filter((piece) => piece !== "");
9
+ }
10
+ function breakText(text) {
11
+ const pieces = pathPieces(text);
12
+ if (pieces.length < 2) return text;
13
+ return pieces.map((piece, i) => /* @__PURE__ */ jsxs(Fragment$1, { children: [piece, i < pieces.length - 1 && /* @__PURE__ */ jsx("wbr", {})] }, i));
14
+ }
15
+ /** `node` with a place to wrap after each path separator in its text: a string,
16
+ * or an element, such as a link, whose child is one. Deeper content is left as
17
+ * it is. */
18
+ function withPathBreaks(node) {
19
+ if (typeof node === "string") return breakText(node);
20
+ if (isValidElement(node)) {
21
+ const inner = node.props.children;
22
+ return typeof inner === "string" ? cloneElement(node, void 0, breakText(inner)) : node;
23
+ }
24
+ if (Array.isArray(node)) return Children.map(node, (child) => typeof child === "string" ? breakText(child) : child);
25
+ return node;
26
+ }
27
+ //#endregion
28
+ export { pathPieces, withPathBreaks };
@@ -5,7 +5,7 @@ function segmentGroupClassName({ fill, iconOnly }) {
5
5
  return cx("gap-0.5", iconOnly ? "flex flex-col items-center" : cx("rounded-lg theme-muted-panel p-0.5", fill ? "grid auto-cols-fr grid-flow-col" : "inline-flex"));
6
6
  }
7
7
  function segmentClassName(current, { fill, iconOnly }) {
8
- return cx("relative flex min-w-0 cursor-pointer items-center justify-center rounded-md font-medium transition-colors duration-150 outline-none focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-(--theme-link) data-disabled:cursor-not-allowed data-disabled:opacity-50", current ? "bg-(--theme-panel-bg) theme-text shadow-sm ring-1 ring-(--theme-border)" : "theme-muted-text hover:theme-text hover:bg-(--theme-hover)", iconOnly ? "h-8 w-8" : cx("h-7 gap-1.5 text-[13px]", fill ? "px-1.5" : "px-2.5"));
8
+ return cx("relative flex min-w-0 cursor-pointer items-center justify-center rounded-md font-medium transition-colors duration-150 focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-(--theme-focus) data-disabled:cursor-not-allowed data-disabled:opacity-50", current ? "bg-(--theme-panel-bg) theme-text shadow-sm ring-1 ring-(--theme-border)" : "theme-muted-text hover:theme-text hover:bg-(--theme-hover)", iconOnly ? "h-8 w-8" : cx("h-7 gap-1.5 text-[13px]", fill ? "px-1.5" : "px-2.5"));
9
9
  }
10
10
  function SegmentContent({ label, icon, badge, iconOnly }) {
11
11
  return /* @__PURE__ */ jsxs(Fragment, { children: [
@@ -1,4 +1,5 @@
1
- /** Guards page shortcuts from firing inside inputs, terminals, editors, and dialogs. */
1
+ /** Guards page shortcuts from firing inside text fields, editors, dialogs, and
2
+ * widgets with typeahead such as menus, lists and trees. */
2
3
  export declare function isTextEntryContext(target: EventTarget | null): boolean;
3
4
  /** Calls `onPress` on a single key, such as F to filter, unless the key goes to a text field. */
4
5
  export declare function useShortcutKey(key: string | undefined, onPress: () => void): void;
@@ -1,11 +1,27 @@
1
1
  import { useEffect, useRef } from "react";
2
2
  //#region src/components/shortcutKey.ts
3
- /** Guards page shortcuts from firing inside inputs, terminals, editors, and dialogs. */
3
+ var KEYS_ARE_INPUT = [
4
+ ".xterm",
5
+ ".monaco-editor",
6
+ ".cm-editor",
7
+ "select",
8
+ "[role=\"dialog\"]",
9
+ "[role=\"alertdialog\"]",
10
+ "[role=\"textbox\"]",
11
+ "[role=\"combobox\"]",
12
+ "[role=\"listbox\"]",
13
+ "[role=\"menu\"]",
14
+ "[role=\"menubar\"]",
15
+ "[role=\"tree\"]",
16
+ "[role=\"grid\"]"
17
+ ].join(", ");
18
+ /** Guards page shortcuts from firing inside text fields, editors, dialogs, and
19
+ * widgets with typeahead such as menus, lists and trees. */
4
20
  function isTextEntryContext(target) {
5
21
  const el = target;
6
22
  if (!el) return false;
7
23
  if (el.tagName === "INPUT" || el.tagName === "TEXTAREA" || el.isContentEditable) return true;
8
- return Boolean(el.closest?.(".xterm, .monaco-editor, .cm-editor, [role=\"dialog\"]"));
24
+ return Boolean(el.closest?.(KEYS_ARE_INPUT));
9
25
  }
10
26
  /** Calls `onPress` on a single key, such as F to filter, unless the key goes to a text field. */
11
27
  function useShortcutKey(key, onPress) {
@@ -14,6 +30,7 @@ function useShortcutKey(key, onPress) {
14
30
  useEffect(() => {
15
31
  if (!key) return;
16
32
  const onKey = (e) => {
33
+ if (e.defaultPrevented || e.repeat || e.isComposing) return;
17
34
  if (e.metaKey || e.ctrlKey || e.altKey || e.key.toLowerCase() !== key.toLowerCase()) return;
18
35
  if (isTextEntryContext(e.target)) return;
19
36
  e.preventDefault();
@@ -67,7 +67,7 @@ function useSplitter({ width, onWidthChange, min, max, side, defaultWidth, label
67
67
  onPointerCancel: onPointerUp,
68
68
  onKeyDown,
69
69
  ...defaultWidth === void 0 ? {} : { onDoubleClick: () => onWidthChange(defaultWidth) },
70
- className: cx("absolute inset-y-0 z-10 w-1.5 cursor-col-resize touch-none transition-colors focus-visible:bg-(--theme-link) focus-visible:outline-none", side === "left" ? "-left-0.75" : "-right-0.75", live === null ? "hover:bg-(--theme-border)" : "bg-(--theme-accent)")
70
+ className: cx("absolute inset-y-0 z-10 w-1.5 cursor-col-resize touch-none transition-colors focus-visible:bg-(--theme-focus) focus-visible:outline-none", side === "left" ? "-left-0.75" : "-right-0.75", live === null ? "hover:bg-(--theme-border)" : "bg-(--theme-accent)")
71
71
  }
72
72
  };
73
73
  }
@@ -1,25 +1,32 @@
1
- import { useEffect, useState } from "react";
1
+ import { useSyncExternalStore } from "react";
2
2
  //#region src/components/useCssIsDark.ts
3
+ var DARK = "(prefers-color-scheme: dark)";
3
4
  /** Whether the host's CSS color-scheme reads dark, falling back to the system preference. */
4
5
  function readIsDark() {
6
+ if (typeof document === "undefined") return false;
5
7
  const scheme = getComputedStyle(document.documentElement).colorScheme;
6
8
  const dark = scheme.includes("dark");
7
9
  if (dark !== scheme.includes("light")) return dark;
8
10
  if (typeof window.matchMedia !== "function") return false;
9
- return window.matchMedia("(prefers-color-scheme: dark)").matches;
11
+ return window.matchMedia(DARK).matches;
10
12
  }
11
- /**
12
- * Follows the host's CSS color-scheme, the same signal the log viewer's
13
- * light-dark() palette uses — no provider API involved. For the rare concrete
14
- * pick (editor themes, image variants) the tokens cannot express.
15
- */
16
- function useCssIsDark() {
17
- const [isDark, setIsDark] = useState(readIsDark);
18
- useEffect(() => {
19
- const update = () => setIsDark(readIsDark());
20
- const media = typeof window.matchMedia === "function" ? window.matchMedia("(prefers-color-scheme: dark)") : void 0;
21
- media?.addEventListener("change", update);
22
- const observer = new MutationObserver(update);
13
+ var listeners = /* @__PURE__ */ new Set();
14
+ var current = false;
15
+ var stop;
16
+ function refresh() {
17
+ const next = readIsDark();
18
+ if (next !== current) {
19
+ current = next;
20
+ for (const listener of listeners) listener();
21
+ }
22
+ }
23
+ function subscribe(listener) {
24
+ listeners.add(listener);
25
+ if (!stop) {
26
+ current = readIsDark();
27
+ const media = typeof window.matchMedia === "function" ? window.matchMedia(DARK) : void 0;
28
+ media?.addEventListener("change", refresh);
29
+ const observer = new MutationObserver(refresh);
23
30
  observer.observe(document.documentElement, {
24
31
  attributes: true,
25
32
  attributeFilter: [
@@ -28,12 +35,27 @@ function useCssIsDark() {
28
35
  "data-theme"
29
36
  ]
30
37
  });
31
- return () => {
32
- media?.removeEventListener("change", update);
38
+ stop = () => {
39
+ media?.removeEventListener("change", refresh);
33
40
  observer.disconnect();
34
41
  };
35
- }, []);
36
- return isDark;
42
+ }
43
+ return () => {
44
+ listeners.delete(listener);
45
+ if (listeners.size === 0) {
46
+ stop?.();
47
+ stop = void 0;
48
+ }
49
+ };
50
+ }
51
+ var snapshot = () => stop ? current : readIsDark();
52
+ /**
53
+ * Follows the host's CSS color-scheme, the same signal the log viewer's
54
+ * light-dark() palette uses — no provider API involved. For the rare concrete
55
+ * pick (editor themes, image variants) the tokens cannot express.
56
+ */
57
+ function useCssIsDark() {
58
+ return useSyncExternalStore(subscribe, snapshot, () => false);
37
59
  }
38
60
  //#endregion
39
61
  export { readIsDark, useCssIsDark };