@parallelworks/ui 0.80.0 → 0.81.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 (42) hide show
  1. package/README.md +7 -1
  2. package/dist/ai/adapter/types.d.ts +1 -0
  3. package/dist/ai/ai.css +3 -92
  4. package/dist/ai/components/agent/AgentMessageParts.js +1 -0
  5. package/dist/ai/core/ChatProvider.js +10 -5
  6. package/dist/ai/strings.d.ts +3 -0
  7. package/dist/ai/strings.js +2 -0
  8. package/dist/ai/types.d.ts +5 -0
  9. package/dist/components/Card.d.ts +16 -1
  10. package/dist/components/Card.js +13 -2
  11. package/dist/components/CodeBlock.d.ts +40 -0
  12. package/dist/components/CodeBlock.js +159 -0
  13. package/dist/components/CreateModal.d.ts +3 -1
  14. package/dist/components/CreateModal.js +15 -8
  15. package/dist/components/DescriptionList.js +1 -1
  16. package/dist/components/Drawer.js +1 -1
  17. package/dist/components/Input.d.ts +5 -0
  18. package/dist/components/Input.js +4 -3
  19. package/dist/components/MarkdownImpl.js +89 -2
  20. package/dist/components/MenuEntries.d.ts +3 -0
  21. package/dist/components/MenuEntries.js +19 -6
  22. package/dist/components/Modal.d.ts +1 -1
  23. package/dist/components/Modal.js +6 -5
  24. package/dist/components/PageHeader.d.ts +4 -0
  25. package/dist/components/PageHeader.js +6 -3
  26. package/dist/components/Provider.d.ts +3 -0
  27. package/dist/components/Provider.js +1 -0
  28. package/dist/components/Select.js +1 -1
  29. package/dist/components/ToggleGroup.d.ts +3 -1
  30. package/dist/components/ToggleGroup.js +2 -2
  31. package/dist/components/codeHighlight.d.ts +9 -0
  32. package/dist/components/codeHighlight.js +29 -0
  33. package/dist/components/pageHeaderStore.d.ts +4 -0
  34. package/dist/components/pageHeaderStore.js +5 -2
  35. package/dist/file-explorer/FileExplorer.js +1 -1
  36. package/dist/index.d.ts +2 -1
  37. package/dist/index.js +3 -2
  38. package/dist/styles/theme.css +1 -0
  39. package/dist/styles.css +1 -1
  40. package/dist/theme/derive.d.ts +1 -1
  41. package/dist/theme/derive.js +3 -1
  42. package/package.json +1 -1
@@ -168,7 +168,7 @@ function TitleInput({ value, onCommit, autoSelect = false, size = "xl", readOnly
168
168
  setShown(value);
169
169
  if (!editing.current) setDraft(value);
170
170
  }
171
- const { resize } = useAutosize(true, draft);
171
+ const { element: sizer, resize } = useAutosize(true, draft);
172
172
  useEffect(() => {
173
173
  if (autoSelect && element.current) {
174
174
  element.current.focus();
@@ -177,9 +177,10 @@ function TitleInput({ value, onCommit, autoSelect = false, size = "xl", readOnly
177
177
  }, []);
178
178
  const setRef = useCallback((el) => {
179
179
  element.current = el;
180
+ sizer.current = el;
180
181
  if (typeof ref === "function") ref(el);
181
182
  else if (ref) ref.current = el;
182
- }, [ref]);
183
+ }, [ref, sizer]);
183
184
  return /* @__PURE__ */ jsx("textarea", {
184
185
  ...props,
185
186
  ref: setRef,
@@ -218,4 +219,4 @@ function TitleInput({ value, onCommit, autoSelect = false, size = "xl", readOnly
218
219
  });
219
220
  }
220
221
  //#endregion
221
- export { Input$1 as Input, Textarea, TitleInput, framed };
222
+ export { Input$1 as Input, Textarea, TitleInput, framed, useAutosize };
@@ -1,15 +1,16 @@
1
1
  import { cx } from "../cx.js";
2
+ import { CodeBlock } from "./CodeBlock.js";
2
3
  import { ExternalLink } from "./ExternalLink.js";
3
4
  import { readIsDark } from "./useCssIsDark.js";
4
5
  import { CODE_THEMES } from "./codeTheme.js";
5
6
  import { rehypeHttpsImagesOnly } from "./httpsImages.js";
6
7
  import { rehypeCodeBreaks } from "./markdownCodeBreaks.js";
7
- import { useEffect, useState } from "react";
8
+ import { useEffect, useMemo, useState } from "react";
8
9
  import { jsx } from "react/jsx-runtime";
9
10
  import { code } from "@streamdown/code";
10
11
  import { math } from "@streamdown/math";
11
12
  import "katex/dist/katex.min.css";
12
- import { Streamdown, defaultRehypePlugins } from "streamdown";
13
+ import { Streamdown, defaultComponents, defaultRehypePlugins, useIsCodeFenceIncomplete } from "streamdown";
13
14
  //#region src/components/MarkdownImpl.tsx
14
15
  var basePlugins = {
15
16
  code,
@@ -35,6 +36,81 @@ function loadMermaid() {
35
36
  }
36
37
  var rehypePlugins = [...Object.values(defaultRehypePlugins), rehypeCodeBreaks];
37
38
  var httpsImagesRehypePlugins = [...Object.entries(defaultRehypePlugins).flatMap(([name, plugin]) => name === "harden" ? [rehypeHttpsImagesOnly, plugin] : [plugin]), rehypeCodeBreaks];
39
+ function codeControls(controls) {
40
+ if (controls === false) return {
41
+ copy: false,
42
+ download: null
43
+ };
44
+ const code = controls === void 0 || controls === true ? true : controls.code;
45
+ if (code === false) return {
46
+ copy: false,
47
+ download: null
48
+ };
49
+ if (code === void 0 || code === true) return {
50
+ copy: true,
51
+ download: "file"
52
+ };
53
+ const { download } = code;
54
+ return {
55
+ copy: code.copy !== false,
56
+ download: download === false ? null : typeof download === "object" ? download.filename : "file"
57
+ };
58
+ }
59
+ var EXTENSIONS = {
60
+ javascript: "js",
61
+ typescript: "ts",
62
+ python: "py",
63
+ ruby: "rb",
64
+ rust: "rs",
65
+ golang: "go",
66
+ bash: "sh",
67
+ shell: "sh",
68
+ shellscript: "sh",
69
+ zsh: "sh",
70
+ console: "sh",
71
+ markdown: "md",
72
+ text: "txt",
73
+ plaintext: "txt",
74
+ csharp: "cs",
75
+ kotlin: "kt",
76
+ powershell: "ps1",
77
+ dockerfile: "Dockerfile"
78
+ };
79
+ function extensionFor(language) {
80
+ const lang = language.toLowerCase();
81
+ return EXTENSIONS[lang] ?? (/^[a-z0-9+]{1,10}$/.test(lang) ? lang : "txt");
82
+ }
83
+ var LANGUAGE = /language-([^\s]+)/;
84
+ var START_LINE = /startLine=(\d+)/;
85
+ var NO_LINE_NUMBERS = /\bnoLineNumbers\b/;
86
+ var DefaultCode = defaultComponents.code;
87
+ function textOf(children) {
88
+ if (typeof children === "string") return children;
89
+ if (Array.isArray(children)) return children.map(textOf).join("");
90
+ if (children && typeof children === "object" && "props" in children) return textOf(children.props.children);
91
+ return "";
92
+ }
93
+ function makeCode(controls, diagrams) {
94
+ return function MarkdownCode(props) {
95
+ const incomplete = useIsCodeFenceIncomplete();
96
+ const language = props.className?.match(LANGUAGE)?.[1] ?? "";
97
+ if (!("data-block" in props) || diagrams && language === "mermaid") return /* @__PURE__ */ jsx(DefaultCode, { ...props });
98
+ const meta = props.node?.properties?.["metastring"];
99
+ const metastring = typeof meta === "string" ? meta : "";
100
+ const start = Number.parseInt(metastring.match(START_LINE)?.[1] ?? "", 10);
101
+ return /* @__PURE__ */ jsx(CodeBlock, {
102
+ className: "my-3.5",
103
+ code: textOf(props.children),
104
+ language,
105
+ copy: controls.copy,
106
+ filename: controls.download === null ? void 0 : `${controls.download}.${extensionFor(language)}`,
107
+ lineNumbers: NO_LINE_NUMBERS.test(metastring) ? false : void 0,
108
+ startLine: start >= 1 ? start : 1,
109
+ maxHeight: 400,
110
+ streaming: incomplete
111
+ });
112
+ };
113
+ }
38
114
  function MarkdownImpl({ children, isStreaming, controls, components, httpsImagesOnly, compact }) {
39
115
  const needsMermaid = !!children && children.includes("```mermaid");
40
116
  const [, setMermaidReady] = useState(() => !!pluginsWithMermaid);
@@ -49,6 +125,16 @@ function MarkdownImpl({ children, isStreaming, controls, components, httpsImages
49
125
  };
50
126
  }, [needsMermaid]);
51
127
  const plugins = needsMermaid && pluginsWithMermaid ? pluginsWithMermaid : basePlugins;
128
+ const { copy, download } = codeControls(controls);
129
+ const diagrams = plugins === pluginsWithMermaid;
130
+ const code = useMemo(() => makeCode({
131
+ copy,
132
+ download
133
+ }, diagrams), [
134
+ copy,
135
+ download,
136
+ diagrams
137
+ ]);
52
138
  return /* @__PURE__ */ jsx("div", {
53
139
  className: cx("markdown whitespace-normal text-start w-full", compact && "markdown-compact"),
54
140
  children: /* @__PURE__ */ jsx(Streamdown, {
@@ -63,6 +149,7 @@ function MarkdownImpl({ children, isStreaming, controls, components, httpsImages
63
149
  rehypePlugins: httpsImagesOnly ? httpsImagesRehypePlugins : rehypePlugins,
64
150
  shikiTheme: CODE_THEMES,
65
151
  components: {
152
+ code,
66
153
  a: ({ node, ...props }) => /* @__PURE__ */ jsx(ExternalLink, {
67
154
  ...props,
68
155
  className: cx("link wrap-break-word", props.className)
@@ -9,6 +9,9 @@ interface MenuActionBase {
9
9
  /** The key that does the same from the page, such as `S` or `⌘K`, in plain text at the
10
10
  * row's end. The menu doesn't bind it; the host does. */
11
11
  shortcut?: string | undefined;
12
+ /** Muted text at the row's end that tells rows apart, such as a team's key `CORE`. It is
13
+ * read with the label, and a menu's search matches it. For a key to press, use `shortcut`. */
14
+ hint?: string | undefined;
12
15
  /** A small mark at the row's end, such as a dot for something unseen. It is hidden from
13
16
  * screen readers, so say what it means in `label`. */
14
17
  badge?: ReactNode;
@@ -121,7 +121,7 @@ function SearchedEntries({ entries, search, query }) {
121
121
  const matches = useQueryMatch();
122
122
  if (!search) return /* @__PURE__ */ jsx(EntryRows, { entries });
123
123
  const needle = query.trim();
124
- const shown = needle ? searchMatches(entries, (a) => matches(needle, a.label, a.description, a.keywords), (submenu) => matches(needle, submenu.label, submenu.keywords)) : entries;
124
+ const shown = needle ? searchMatches(entries, (a) => matches(needle, a.label, a.description, a.keywords, a.hint), (submenu) => matches(needle, submenu.label, submenu.keywords)) : entries;
125
125
  return /* @__PURE__ */ jsxs(Fragment$1, { children: [
126
126
  /* @__PURE__ */ jsx("div", {
127
127
  className: "sticky top-0 z-[1] bg-(--theme-app-bg) px-1 pt-0.5 pb-1",
@@ -183,6 +183,7 @@ function RowEnd({ children, className }) {
183
183
  children
184
184
  });
185
185
  }
186
+ var hintClassName = "ps-3 text-xs text-(--theme-muted-text-color) in-data-[size=sm]:text-[11px]";
186
187
  var shortcutClassName = "ps-3 text-xs text-(--theme-muted-text-color) tabular-nums in-data-[size=sm]:text-[11px]";
187
188
  /** A row's icon, label, second line and end details, laid out alike in every kind of row. */
188
189
  function RowContent({ icon, label, description, end }) {
@@ -290,11 +291,19 @@ function ActionRow({ action }) {
290
291
  const runAction = useRunAction();
291
292
  const className = cx(action.description && "items-start", action.destructive && destructiveClassName, action.current && "font-medium");
292
293
  const keyshortcuts = ariaKeyShortcut(action.shortcut);
294
+ const heard = action.hint && /* @__PURE__ */ jsxs("span", {
295
+ className: "sr-only",
296
+ children: [". ", action.hint]
297
+ });
293
298
  const end = (trailing) => /* @__PURE__ */ jsxs(Fragment$1, { children: [
294
299
  action.badge !== void 0 && action.badge !== null && action.badge !== false && /* @__PURE__ */ jsx(RowEnd, {
295
300
  className: "ps-2",
296
301
  children: action.badge
297
302
  }),
303
+ action.hint && /* @__PURE__ */ jsx(RowEnd, {
304
+ className: hintClassName,
305
+ children: action.hint
306
+ }),
298
307
  action.shortcut && /* @__PURE__ */ jsx(RowEnd, {
299
308
  className: shortcutClassName,
300
309
  children: action.shortcut
@@ -314,7 +323,7 @@ function ActionRow({ action }) {
314
323
  className,
315
324
  children: /* @__PURE__ */ jsx(RowContent, {
316
325
  icon: action.icon,
317
- label: action.label,
326
+ label: /* @__PURE__ */ jsxs(Fragment$1, { children: [action.label, heard] }),
318
327
  description: action.description,
319
328
  end: end(action.current ? /* @__PURE__ */ jsx(RowEnd, {
320
329
  className: "ps-2",
@@ -330,10 +339,14 @@ function ActionRow({ action }) {
330
339
  const reason = action.disabled ? action.disabledReason : void 0;
331
340
  const content = (trailing) => /* @__PURE__ */ jsx(RowContent, {
332
341
  icon: action.icon,
333
- label: /* @__PURE__ */ jsxs(Fragment$1, { children: [action.label, reason && /* @__PURE__ */ jsxs("span", {
334
- className: "sr-only",
335
- children: [". ", reason]
336
- })] }),
342
+ label: /* @__PURE__ */ jsxs(Fragment$1, { children: [
343
+ action.label,
344
+ heard,
345
+ reason && /* @__PURE__ */ jsxs("span", {
346
+ className: "sr-only",
347
+ children: [". ", reason]
348
+ })
349
+ ] }),
337
350
  description: action.description,
338
351
  end: end(trailing)
339
352
  });
@@ -1,7 +1,7 @@
1
1
  import { Dialog } from '@base-ui/react/dialog';
2
2
  import { type FormEvent, type ReactNode } from 'react';
3
3
  /** The panel a modal draws by default; pass `panel={false}` to paint your own. */
4
- export declare const modalPanelClassName = "rounded-xl border border-(--theme-border) bg-(--theme-app-bg) text-(--theme-app) shadow-2xl";
4
+ export declare const modalPanelClassName = "rounded-xl border border-(--theme-border) bg-(--theme-panel-bg) text-(--theme-app) shadow-2xl";
5
5
  /** Names the modal for assistive technology; renders an `<h2>`. */
6
6
  export declare const ModalTitle: import("react").ForwardRefExoticComponent<Omit<import("@base-ui/react").DialogTitleProps, "ref"> & import("react").RefAttributes<HTMLHeadingElement>>;
7
7
  /** Describes the modal for assistive technology; renders a `<p>`. */
@@ -10,7 +10,8 @@ import { AlertDialog } from "@base-ui/react/alert-dialog";
10
10
  import { Dialog } from "@base-ui/react/dialog";
11
11
  //#region src/components/Modal.tsx
12
12
  /** The panel a modal draws by default; pass `panel={false}` to paint your own. */
13
- var modalPanelClassName = "rounded-xl border border-(--theme-border) bg-(--theme-app-bg) text-(--theme-app) shadow-2xl";
13
+ var modalPanelClassName = "rounded-xl border border-(--theme-border) bg-(--theme-panel-bg) text-(--theme-app) shadow-2xl";
14
+ var phoneFooterClassName = "max-sm:flex-wrap max-sm:[&>:is(button,a)]:h-10 max-sm:[&>:is(button,a)]:flex-1 max-sm:[&>:not(button,a)]:basis-full";
14
15
  /** Names the modal for assistive technology; renders an `<h2>`. */
15
16
  var ModalTitle = Dialog.Title;
16
17
  /** Describes the modal for assistive technology; renders a `<p>`. */
@@ -32,7 +33,7 @@ function firstControlIn(panel) {
32
33
  * and focus returns to where it was on close. Give it a `title`, a `description`
33
34
  * and a `footer` for the standard layout, as a Drawer has, or lay out the whole
34
35
  * panel with the children. */
35
- function Modal({ open, onClose, title, description, footer, size, alert = false, dismissible = true, align = "top", panel = true, backdropClassName = "bg-black/40", closeButton = false, onSubmit, className, children, initialFocus, ...popupProps }) {
36
+ function Modal({ open, onClose, title, description, footer, size, alert = false, dismissible = true, align = "top", panel = true, backdropClassName = "bg-(--theme-backdrop)", closeButton = false, onSubmit, className, children, initialFocus, ...popupProps }) {
36
37
  const portal = usePopupContainer();
37
38
  const { common } = useStrings();
38
39
  const panelElement = useRef(null);
@@ -67,14 +68,14 @@ function Modal({ open, onClose, title, description, footer, size, alert = false,
67
68
  children
68
69
  }),
69
70
  footer !== void 0 && /* @__PURE__ */ jsx("div", {
70
- className: "flex shrink-0 items-center justify-end gap-2 border-t border-(--theme-border) px-4 py-3",
71
+ className: cx("flex shrink-0 items-center justify-end gap-2 border-t border-(--theme-border) px-4 py-3", phoneFooterClassName),
71
72
  children: footer
72
73
  })
73
74
  ] });
74
75
  const content = /* @__PURE__ */ jsxs(Dialog.Portal, {
75
76
  ...portal,
76
77
  children: [/* @__PURE__ */ jsx(Dialog.Backdrop, { className: cx("fixed inset-0 z-(--layer-modal)", backdropMotion, backdropClassName) }), /* @__PURE__ */ jsx(Dialog.Viewport, {
77
- className: cx("fixed inset-0 z-(--layer-modal) flex justify-center px-4 pt-20 pb-8", align === "center" ? "items-center" : "items-start sm:pt-[max(14vh,5rem)]"),
78
+ className: cx("fixed inset-0 z-(--layer-modal) flex justify-center px-4 pt-20 pb-8 max-sm:px-2 max-sm:pb-[max(8px,env(safe-area-inset-bottom))]", align === "center" ? "items-center" : "items-start sm:pt-[max(14vh,5rem)]"),
78
79
  children: /* @__PURE__ */ jsx(Dialog.Popup, {
79
80
  ...popupProps,
80
81
  ref: (element) => {
@@ -84,7 +85,7 @@ function Modal({ open, onClose, title, description, footer, size, alert = false,
84
85
  else if (forward) forward.current = element;
85
86
  },
86
87
  initialFocus: initialFocus ?? (closeButton ? () => firstControlIn(panelElement.current) : void 0),
87
- className: cx(panel && "rounded-xl border border-(--theme-border) bg-(--theme-app-bg) text-(--theme-app) shadow-2xl", size && [sizeClasses[size], "max-w-full"], laidOut && "flex max-h-full flex-col", "outline-none", popupMotion, className),
88
+ className: cx(panel && "rounded-xl border border-(--theme-border) bg-(--theme-panel-bg) text-(--theme-app) shadow-2xl", size && [sizeClasses[size], "max-w-full"], panel && (size || laidOut) && "max-sm:w-full", laidOut && "flex max-h-full flex-col", "outline-none", popupMotion, className),
88
89
  children: !laidOut ? children : onSubmit ? /* @__PURE__ */ jsx("form", {
89
90
  className: "flex min-h-0 flex-1 flex-col",
90
91
  onSubmit: (event) => {
@@ -13,6 +13,10 @@ export interface PageHeaderProps {
13
13
  * `h1`. A crumb with an empty label, such as a name still loading, shows a
14
14
  * placeholder. */
15
15
  breadcrumbs?: readonly Breadcrumb[] | undefined;
16
+ /** False keeps the last crumb out of the page's outline, for a page whose `h1` is in its
17
+ * body, such as a title field or a section's heading: the crumb still says it is the
18
+ * current page, and the page has one `h1`. */
19
+ heading?: boolean | undefined;
16
20
  /** Beside the title: actions on the page's subject, such as edit, rename or
17
21
  * favorite, or its `PageHeaderMenu`. */
18
22
  titleActions?: ReactNode;
@@ -52,6 +52,7 @@ function PageHeader(props) {
52
52
  const store = use(StoreContext);
53
53
  if (!store) return props.hidden ? null : /* @__PURE__ */ jsx(HeaderLayout, {
54
54
  breadcrumbs: props.breadcrumbs ?? [],
55
+ heading: props.heading ?? true,
55
56
  titleActions: props.titleActions,
56
57
  actions: props.actions,
57
58
  toolbar: props.toolbar
@@ -61,7 +62,7 @@ function PageHeader(props) {
61
62
  ...props
62
63
  });
63
64
  }
64
- function DeclaredHeader({ store, breadcrumbs, titleActions, actions, toolbar, hidden = false }) {
65
+ function DeclaredHeader({ store, breadcrumbs, heading = true, titleActions, actions, toolbar, hidden = false }) {
65
66
  const [order] = useState(() => ++renderOrder);
66
67
  const [nodes] = useState(createRegionNodes);
67
68
  const client = useSyncExternalStore(subscribeNever, () => true, () => false);
@@ -73,6 +74,7 @@ function DeclaredHeader({ store, breadcrumbs, titleActions, actions, toolbar, hi
73
74
  useLayoutEffect(() => {
74
75
  store.set(order, {
75
76
  breadcrumbs,
77
+ heading,
76
78
  hidden,
77
79
  nodes,
78
80
  fills
@@ -105,6 +107,7 @@ function PageHeaderBar({ compact = false, persistent = false, className }) {
105
107
  if (header.hidden || empty && !persistent) return null;
106
108
  return /* @__PURE__ */ jsx(HeaderLayout, {
107
109
  breadcrumbs: header.breadcrumbs,
110
+ heading: header.heading,
108
111
  titleActions: header.titleActions.length > 0 && /* @__PURE__ */ jsx(Mounts, { nodes: header.titleActions }),
109
112
  actions: header.actions.length > 0 && /* @__PURE__ */ jsx(Mounts, { nodes: header.actions }),
110
113
  toolbar: header.toolbar.length > 0 && /* @__PURE__ */ jsx(Mounts, { nodes: header.toolbar }),
@@ -137,7 +140,7 @@ function Mount({ node }) {
137
140
  className: "contents"
138
141
  });
139
142
  }
140
- function HeaderLayout({ breadcrumbs, titleActions, actions, toolbar, fit: sharedFit, compact = false, className }) {
143
+ function HeaderLayout({ breadcrumbs, heading, titleActions, actions, toolbar, fit: sharedFit, compact = false, className }) {
141
144
  const { breadcrumbPin, navToggle } = useSlots();
142
145
  const { list: t } = useStrings();
143
146
  const [ownFit] = useState(() => new HeaderActionsFit());
@@ -161,7 +164,7 @@ function HeaderLayout({ breadcrumbs, titleActions, actions, toolbar, fit: shared
161
164
  navToggle?.(),
162
165
  hasTrail && /* @__PURE__ */ jsx(Breadcrumbs, {
163
166
  crumbs: breadcrumbs,
164
- heading: true
167
+ heading
165
168
  }),
166
169
  present(titleActions) && /* @__PURE__ */ jsx("div", {
167
170
  className: "flex shrink-0 items-center gap-0.5",
@@ -102,6 +102,9 @@ export interface UIStrings {
102
102
  opensInNewTab: string;
103
103
  /** Names a code editor that has no label of its own. */
104
104
  codeEditor: string;
105
+ /** Names a block of code that has no title of its own, by its language,
106
+ * such as "yaml code"; `language` is empty when it has none. */
107
+ codeOf: (language: string) => string;
105
108
  /** Shown when someone types in a read-only code editor. */
106
109
  readOnlyText: string;
107
110
  /** Names the handle that resizes a panel. */
@@ -148,6 +148,7 @@ var DEFAULTS = {
148
148
  no: "No",
149
149
  opensInNewTab: "(opens in a new tab)",
150
150
  codeEditor: "Code editor",
151
+ codeOf: (language) => language ? `${language} code` : "Code",
151
152
  readOnlyText: "This text is read-only.",
152
153
  resizePanel: "Resize panel",
153
154
  panelWidth: (px) => `${px} pixels wide`,
@@ -59,7 +59,7 @@ function Select$1({ items, placeholder, className, "aria-label": ariaLabel, "ari
59
59
  content: tip,
60
60
  children: /* @__PURE__ */ jsxs(Select.Trigger, {
61
61
  ...triggerProps,
62
- className: cx(fieldBox({ size }), triggerClassName, content && "w-fit max-w-full", explained && explainedDisabledClassName, className),
62
+ className: cx(fieldBox({ size }), triggerClassName, content && "w-fit max-w-full shrink-0", explained && explainedDisabledClassName, className),
63
63
  children: [/* @__PURE__ */ jsx(Select.Value, {
64
64
  placeholder,
65
65
  className: cx("truncate data-placeholder:text-(--theme-placeholder)", content && !rootProps.multiple && "grid"),
@@ -25,6 +25,8 @@ export interface ToggleGroupProps<T extends string> {
25
25
  all?: boolean | {
26
26
  count?: number | undefined;
27
27
  } | undefined;
28
+ /** The All option's words, for one that names what it covers, such as "All types". */
29
+ allLabel?: ReactNode;
28
30
  size?: 'sm' | 'md' | undefined;
29
31
  disabled?: boolean | undefined;
30
32
  /** Names an element that says more about the group, such as a live summary
@@ -42,4 +44,4 @@ export interface ToggleGroupProps<T extends string> {
42
44
  * value, such as a time range, what to group by, or which view of the same
43
45
  * data to show. A filter that may be off, or takes several values, is a
44
46
  * `ToggleGroup`. */
45
- export declare function ToggleGroup<T extends string>({ label, options, value, onValueChange, multiple, all, size, disabled, 'aria-describedby': describedBy, testId, }: ToggleGroupProps<T>): import("react").JSX.Element;
47
+ export declare function ToggleGroup<T extends string>({ label, options, value, onValueChange, multiple, all, allLabel, size, disabled, 'aria-describedby': describedBy, testId, }: ToggleGroupProps<T>): import("react").JSX.Element;
@@ -18,7 +18,7 @@ var ALL = "\0all";
18
18
  * value, such as a time range, what to group by, or which view of the same
19
19
  * data to show. A filter that may be off, or takes several values, is a
20
20
  * `ToggleGroup`. */
21
- function ToggleGroup$1({ label, options, value, onValueChange, multiple = true, all = false, size = "sm", disabled = false, "aria-describedby": describedBy, testId }) {
21
+ function ToggleGroup$1({ label, options, value, onValueChange, multiple = true, all = false, allLabel, size = "sm", disabled = false, "aria-describedby": describedBy, testId }) {
22
22
  const strings = useStrings();
23
23
  const allCount = typeof all === "object" ? all.count : void 0;
24
24
  const pressed = all && value.length === 0 ? [ALL] : value;
@@ -40,7 +40,7 @@ function ToggleGroup$1({ label, options, value, onValueChange, multiple = true,
40
40
  children: [
41
41
  all && /* @__PURE__ */ jsx(Chip, {
42
42
  value: ALL,
43
- label: strings.common.all,
43
+ label: allLabel ?? strings.common.all,
44
44
  count: allCount,
45
45
  size
46
46
  }),
@@ -0,0 +1,9 @@
1
+ export interface CodeToken {
2
+ content: string;
3
+ color?: string;
4
+ fontStyle?: 'italic';
5
+ }
6
+ /** Tokens for each line of `source`, at once when they are cached, otherwise
7
+ * null now and `onReady` once the language has loaded. A language the
8
+ * highlighter doesn't know reads as plain text. */
9
+ export declare function highlightCode(source: string, language: string, incomplete: boolean, onReady: (lines: CodeToken[][]) => void): CodeToken[][] | null;
@@ -0,0 +1,29 @@
1
+ import { CODE_THEMES } from "./codeTheme.js";
2
+ import { code } from "@streamdown/code";
3
+ //#region src/components/codeHighlight.ts
4
+ var ITALIC = 1;
5
+ function toLines(tokens) {
6
+ return tokens.map((line) => line.map((token) => {
7
+ const color = token.htmlStyle?.["color"] ?? token.color;
8
+ const italic = token.htmlStyle?.["font-style"] === "italic" || ((token.fontStyle ?? 0) & ITALIC) === ITALIC;
9
+ return {
10
+ content: token.content,
11
+ ...color ? { color } : {},
12
+ ...italic ? { fontStyle: "italic" } : {}
13
+ };
14
+ }));
15
+ }
16
+ /** Tokens for each line of `source`, at once when they are cached, otherwise
17
+ * null now and `onReady` once the language has loaded. A language the
18
+ * highlighter doesn't know reads as plain text. */
19
+ function highlightCode(source, language, incomplete, onReady) {
20
+ const result = code.highlight({
21
+ code: source,
22
+ language: language || "text",
23
+ themes: CODE_THEMES,
24
+ isIncomplete: incomplete
25
+ }, (later) => onReady(toLines(later.tokens)));
26
+ return result ? toLines(result.tokens) : null;
27
+ }
28
+ //#endregion
29
+ export { highlightCode };
@@ -7,12 +7,16 @@ export type PageHeaderRegion = 'titleActions' | 'actions' | 'toolbar';
7
7
  export type RegionNodes = Readonly<Record<PageHeaderRegion, HTMLElement>>;
8
8
  export interface Declaration {
9
9
  breadcrumbs: readonly Breadcrumb[] | undefined;
10
+ /** Whether the trail's last crumb is the page's `h1`. */
11
+ heading: boolean;
10
12
  hidden: boolean;
11
13
  nodes: RegionNodes | null;
12
14
  fills: Readonly<Record<PageHeaderRegion, boolean>>;
13
15
  }
14
16
  export interface PageHeaderSnapshot {
15
17
  breadcrumbs: readonly Breadcrumb[];
18
+ /** Whether the trail's last crumb is the page's `h1`, as the page whose trail it is says. */
19
+ heading: boolean;
16
20
  hidden: boolean;
17
21
  titleActions: readonly HTMLElement[];
18
22
  actions: readonly HTMLElement[];
@@ -4,6 +4,7 @@ var NO_NODES = [];
4
4
  var NO_CRUMBS = [];
5
5
  var EMPTY = {
6
6
  breadcrumbs: NO_CRUMBS,
7
+ heading: true,
7
8
  hidden: false,
8
9
  titleActions: NO_NODES,
9
10
  actions: NO_NODES,
@@ -15,7 +16,7 @@ function sameCrumbs(a, b) {
15
16
  return a.every((crumb, i) => crumb.label === b[i]?.label && crumb.href === b[i]?.href);
16
17
  }
17
18
  function sameDeclaration(a, b) {
18
- 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;
19
+ return sameCrumbs(a.breadcrumbs, b.breadcrumbs) && a.heading === b.heading && 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;
19
20
  }
20
21
  function sameNodes(a, b) {
21
22
  return a.length === b.length && a.every((node, i) => node === b[i]);
@@ -54,10 +55,12 @@ var PageHeaderStore = class {
54
55
  const next = ordered.flatMap((d) => d.nodes && d.fills[region] ? [d.nodes[region]] : []);
55
56
  return sameNodes(next, prev) ? prev : next;
56
57
  };
57
- const trail = ordered.findLast((d) => d.breadcrumbs !== void 0)?.breadcrumbs ?? NO_CRUMBS;
58
+ const owner = ordered.findLast((d) => d.breadcrumbs !== void 0);
59
+ const trail = owner?.breadcrumbs ?? NO_CRUMBS;
58
60
  const prev = this.snapshot;
59
61
  this.snapshot = {
60
62
  breadcrumbs: sameCrumbs(trail, prev.breadcrumbs) ? prev.breadcrumbs : trail,
63
+ heading: owner?.heading ?? true,
61
64
  hidden: ordered.some((d) => d.hidden),
62
65
  titleActions: regionNodes("titleActions", prev.titleActions),
63
66
  actions: regionNodes("actions", prev.actions),
@@ -13,9 +13,9 @@ import { useCopy } from "../components/useCopy.js";
13
13
  import { Breadcrumbs } from "../components/Breadcrumbs.js";
14
14
  import { Button } from "../components/Button.js";
15
15
  import { Callout } from "../components/Callout.js";
16
+ import { CopyButton, CopyCodeBlock } from "../components/CopyButton.js";
16
17
  import { InfoTip } from "../components/InfoTip.js";
17
18
  import { ConfirmModal } from "../components/ConfirmModal.js";
18
- import { CopyButton, CopyCodeBlock } from "../components/CopyButton.js";
19
19
  import { CreateModal } from "../components/CreateModal.js";
20
20
  import { useSplitter } from "../components/splitter.js";
21
21
  import { formatBytes, formatDateTime } from "../format.js";
package/dist/index.d.ts CHANGED
@@ -5,8 +5,9 @@ export { type Breadcrumb, Breadcrumbs, type BreadcrumbsProps } from './component
5
5
  export { Button, type ButtonProps, MethodButton } from './components/Button.js';
6
6
  export { type ButtonSize, type ButtonStyle, type ButtonVariant, buttonClasses, } from './components/buttonClasses.js';
7
7
  export { Callout, type CalloutProps, type CalloutTone } from './components/Callout.js';
8
- export { Card, CardHeader, type CardHeaderProps, type CardPadding, type CardProps, type CardSize, } from './components/Card.js';
8
+ export { Card, CardHeader, type CardHeaderProps, type CardPadding, type CardProps, type CardSize, type CardTone, } from './components/Card.js';
9
9
  export { Checkbox, CheckboxGroup, type CheckboxProps } from './components/Checkbox.js';
10
+ export { CodeBlock, type CodeBlockProps } from './components/CodeBlock.js';
10
11
  export { ColorInput, type ColorInputProps } from './components/ColorInput.js';
11
12
  export { Autocomplete, type AutocompleteProps, Combobox, type ComboboxGroup, type ComboboxItem, type ComboboxItems, type ComboboxProps, } from './components/Combobox.js';
12
13
  export { ConfirmModal, type ConfirmModalProps, type ConfirmRequest, useConfirm, } from './components/ConfirmModal.js';
package/dist/index.js CHANGED
@@ -23,6 +23,8 @@ import { Callout } from "./components/Callout.js";
23
23
  import { Card, CardHeader } from "./components/Card.js";
24
24
  import { fieldBox } from "./components/fieldBox.js";
25
25
  import { Checkbox, CheckboxGroup } from "./components/Checkbox.js";
26
+ import { CopyButton, CopyCodeBlock } from "./components/CopyButton.js";
27
+ import { CodeBlock } from "./components/CodeBlock.js";
26
28
  import { JND, composite, deltaE, parseColor, toHex, toP3 } from "./theme/color.js";
27
29
  import { apcaContrast, contrastRatio, isDarkColor } from "./theme/contrast.js";
28
30
  import { CODE_STYLES, DEFAULT_CONTRAST, DEFAULT_PRESET, DEFAULT_THEME, THEME_PRESETS, modeOf, parseTheme, resolveMode, schemeFor, serializeTheme } from "./theme/model.js";
@@ -40,7 +42,6 @@ import { Tag, TagList, normalizeTags, tagClasses } from "./components/Tag.js";
40
42
  import { Autocomplete, Combobox } from "./components/Combobox.js";
41
43
  import { Modal, ModalDescription, ModalTitle, modalPanelClassName } from "./components/Modal.js";
42
44
  import { ConfirmModal, useConfirm } from "./components/ConfirmModal.js";
43
- import { CopyButton, CopyCodeBlock } from "./components/CopyButton.js";
44
45
  import { Disclosure, DisclosurePanel, collapsePanelClassName } from "./components/Disclosure.js";
45
46
  import { Switch } from "./components/Switch.js";
46
47
  import { CreateModal, CreateModalSelect, CreateModalSwitch } from "./components/CreateModal.js";
@@ -83,4 +84,4 @@ import { ToggleGroup } from "./components/ToggleGroup.js";
83
84
  import { useClosingValue } from "./components/useClosingValue.js";
84
85
  import { useCssIsDark } from "./components/useCssIsDark.js";
85
86
  import { useApplyTheme, useColorMode } from "./components/useTheme.js";
86
- export { ActionMenu, AppLink, Autocomplete, Avatar, AvatarGroup, Breadcrumbs, Button, CODE_STYLES, Calendar, Callout, Card, CardHeader, ChartLegendList, ChartTooltipContent, Checkbox, CheckboxGroup, ColorInput, Combobox, ConfirmModal, ContextMenuProvider, CopyButton, CopyCodeBlock, CountBadge, CreateModal, CreateModalSelect, CreateModalSwitch, DEFAULT_CONTRAST, DEFAULT_PRESET, DEFAULT_THEME, DatePicker, DescriptionList, DescriptionListGroup, DescriptionListItem, Disclosure, DisclosurePanel, Drawer, Elapsed, EmptyState, ErrorPage, ErrorText, ExternalLink, Field, FieldOption, FieldValue, FileButton, HoverCard, IconButton, InfoTip, Input, JND, Kbd, LetterBadge, ListboxGroup, ListboxOption, ListboxPanel, Loader, LoadingRegion, Markdown, MenuCheckboxItem, MenuEntries, MenuFilter, MenuGroupLabel, MenuItem, MenuLinkItem, MenuPopup, MenuRadioItem, MenuSeparator, MenuSubmenuTrigger, Meter, MethodButton, Modal, ModalDescription, ModalTitle, NumberInput, OneTimeCodeInput, Overview, PAGE_SIZES, PageHeader, PageHeaderAction, PageHeaderAddButton, PageHeaderBar, PageHeaderMenu, PageHeaderProvider, Pagination, Popover, PopoverClose, ProgressBar, Radio, RadioGroup, RefreshButton, RelativeTime, RequiredMark, RevealToggle, RunStatusIcon, SIDEBAR_TOKENS, SearchInput, Section, SegmentedControl, Select, SelectionCopy, SeriesSwatch, SettingModeOption, SettingRow, SettingsCard, SettingsHeading, Skeleton, Slider, StatusBadge, StatusDot, StickyBar, Switch, THEME_PRESETS, THEME_TOKENS, TabPanel, Table, Tabs, Tag, TagList, Textarea, ThemeEditor, ThemePreview, TitleInput, ToggleGroup, Tooltip, TooltipProvider, UIProvider, apcaContrast, applyTheme, ariaKeyShortcuts, avatarInitials, buttonClasses, categoryClasses, categoryOf, collapsePanelClassName, composite, contrastRatio, cx, deltaE, deriveTheme, errorPageKind, fieldBox, fitSeed, formatBytes, formatCount, formatDateTime, formatDuration, formatInfoTipContent, formatShortcut, formatTimeLeft, generateTheme, isApplePlatform, isDarkColor, isMenuDivider, isMenuGroup, isMenuSubmenu, isPlainClick, isTextEntryContext, levelTone, menuDivider, modalPanelClassName, modeOf, normalizeTags, parseColor, parseTheme, prefersWideGamut, resolveMode, runStatusTone, schemeFor, serializeTheme, seriesColor, seriesColors, seriesOpacity, seriesSlotColor, shortcutText, statusTextClass, tagClasses, themeStylesheet, toHex, toP3, toast, useApplyTheme, useAvatarColoring, useClock, useCloseMenu, useClosePopover, useClosingValue, useColorMode, useConfirm, useContextMenu, useCopy, useCssIsDark, useElapsed, useFieldControl, useFieldLabelledControl, useListboxNavigation, useLocale, useMenuOpenedBy, useNavigation, useOpenMenu, useOptionalWorkflowEngine, usePersistedState, useProblemText, useProvisionCorsRules, useRelativeTime, useRunFile, useSeriesHighlight, useShortcutFormat, useShortcutKey, useSlots, useStrings, useWorkflowActions, useWorkflowEditing, useWorkflowEngine, useWorkflowEngineLoader };
87
+ export { ActionMenu, AppLink, Autocomplete, Avatar, AvatarGroup, Breadcrumbs, Button, CODE_STYLES, Calendar, Callout, Card, CardHeader, ChartLegendList, ChartTooltipContent, Checkbox, CheckboxGroup, CodeBlock, ColorInput, Combobox, ConfirmModal, ContextMenuProvider, CopyButton, CopyCodeBlock, CountBadge, CreateModal, CreateModalSelect, CreateModalSwitch, DEFAULT_CONTRAST, DEFAULT_PRESET, DEFAULT_THEME, DatePicker, DescriptionList, DescriptionListGroup, DescriptionListItem, Disclosure, DisclosurePanel, Drawer, Elapsed, EmptyState, ErrorPage, ErrorText, ExternalLink, Field, FieldOption, FieldValue, FileButton, HoverCard, IconButton, InfoTip, Input, JND, Kbd, LetterBadge, ListboxGroup, ListboxOption, ListboxPanel, Loader, LoadingRegion, Markdown, MenuCheckboxItem, MenuEntries, MenuFilter, MenuGroupLabel, MenuItem, MenuLinkItem, MenuPopup, MenuRadioItem, MenuSeparator, MenuSubmenuTrigger, Meter, MethodButton, Modal, ModalDescription, ModalTitle, NumberInput, OneTimeCodeInput, Overview, PAGE_SIZES, PageHeader, PageHeaderAction, PageHeaderAddButton, PageHeaderBar, PageHeaderMenu, PageHeaderProvider, Pagination, Popover, PopoverClose, ProgressBar, Radio, RadioGroup, RefreshButton, RelativeTime, RequiredMark, RevealToggle, RunStatusIcon, SIDEBAR_TOKENS, SearchInput, Section, SegmentedControl, Select, SelectionCopy, SeriesSwatch, SettingModeOption, SettingRow, SettingsCard, SettingsHeading, Skeleton, Slider, StatusBadge, StatusDot, StickyBar, Switch, THEME_PRESETS, THEME_TOKENS, TabPanel, Table, Tabs, Tag, TagList, Textarea, ThemeEditor, ThemePreview, TitleInput, ToggleGroup, Tooltip, TooltipProvider, UIProvider, apcaContrast, applyTheme, ariaKeyShortcuts, avatarInitials, buttonClasses, categoryClasses, categoryOf, collapsePanelClassName, composite, contrastRatio, cx, deltaE, deriveTheme, errorPageKind, fieldBox, fitSeed, formatBytes, formatCount, formatDateTime, formatDuration, formatInfoTipContent, formatShortcut, formatTimeLeft, generateTheme, isApplePlatform, isDarkColor, isMenuDivider, isMenuGroup, isMenuSubmenu, isPlainClick, isTextEntryContext, levelTone, menuDivider, modalPanelClassName, modeOf, normalizeTags, parseColor, parseTheme, prefersWideGamut, resolveMode, runStatusTone, schemeFor, serializeTheme, seriesColor, seriesColors, seriesOpacity, seriesSlotColor, shortcutText, statusTextClass, tagClasses, themeStylesheet, toHex, toP3, toast, useApplyTheme, useAvatarColoring, useClock, useCloseMenu, useClosePopover, useClosingValue, useColorMode, useConfirm, useContextMenu, useCopy, useCssIsDark, useElapsed, useFieldControl, useFieldLabelledControl, useListboxNavigation, useLocale, useMenuOpenedBy, useNavigation, useOpenMenu, useOptionalWorkflowEngine, usePersistedState, useProblemText, useProvisionCorsRules, useRelativeTime, useRunFile, useSeriesHighlight, useShortcutFormat, useShortcutKey, useSlots, useStrings, useWorkflowActions, useWorkflowEditing, useWorkflowEngine, useWorkflowEngineLoader };
@@ -61,6 +61,7 @@
61
61
  --theme-shadow-xl:
62
62
  0 25px 80px -20px light-dark(rgba(0, 0, 0, 0.2), rgba(0, 0, 0, 0.6)),
63
63
  0 10px 30px -10px light-dark(rgba(0, 0, 0, 0.1), rgba(0, 0, 0, 0.4));
64
+ --theme-backdrop: light-dark(rgba(0, 0, 0, 0.4), rgba(0, 0, 0, 0.65));
64
65
  --theme-code-bg: light-dark(#e7eaee, #1c242c);
65
66
  --theme-code-text: light-dark(#1c2330, #e1e9f2);
66
67
  --theme-code-comment: light-dark(#555d6c, #c0c8d1);