@qilin-agent/client-ui-primitives 3.1.7 → 3.1.9

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 (55) hide show
  1. package/README.i18n.yaml +8 -8
  2. package/README.md +11 -4
  3. package/README.zh.md +12 -4
  4. package/lib/AnimatedRows.module.css +7 -0
  5. package/lib/Button.module.css +6 -3
  6. package/lib/DetailDocument.module.css +67 -0
  7. package/lib/HoverCard.module.css +3 -3
  8. package/lib/ImageLightbox.module.css +1 -1
  9. package/lib/InlineEditor.module.css +1 -1
  10. package/lib/Input.module.css +2 -2
  11. package/lib/Menu.module.css +127 -78
  12. package/lib/MenuSurface.module.css +23 -7
  13. package/lib/QuotePreview.module.css +89 -0
  14. package/lib/RowDragPreview.module.css +12 -0
  15. package/lib/SegmentedTabs.module.css +4 -1
  16. package/lib/Tag.module.css +1 -1
  17. package/lib/TerminalBlock.module.css +1 -1
  18. package/lib/Toast.module.css +28 -0
  19. package/lib/UserMessageBubble.module.css +11 -0
  20. package/lib/index.js +2094 -1115
  21. package/lib/markdown/CodeBlock.module.css +23 -0
  22. package/lib/markdown/JsonBlock.module.css +1 -1
  23. package/lib/markdown/MarkdownText.module.css +11 -7
  24. package/lib/settings-form/fields.module.css +2 -2
  25. package/lib/types/AnimatedRows.d.ts +46 -0
  26. package/lib/types/Button.d.ts +13 -4
  27. package/lib/types/DetailDocument.d.ts +79 -0
  28. package/lib/types/ImageLightbox.d.ts +3 -1
  29. package/lib/types/Menu.d.ts +25 -9
  30. package/lib/types/MenuSurface.d.ts +9 -0
  31. package/lib/types/QuotePreview.d.ts +26 -0
  32. package/lib/types/SegmentedTabs.d.ts +3 -1
  33. package/lib/types/Toast.d.ts +19 -1
  34. package/lib/types/Tooltip.d.ts +13 -8
  35. package/lib/types/UserMessageBubble.d.ts +11 -0
  36. package/lib/types/code-highlighting.d.ts +12 -0
  37. package/lib/types/focus.d.ts +8 -0
  38. package/lib/types/icons/index.d.ts +8 -0
  39. package/lib/types/icons/shared-artwork.d.ts +1 -0
  40. package/lib/types/index.d.ts +20 -4
  41. package/lib/types/markdown/MarkdownDelegate.d.ts +19 -2
  42. package/lib/types/markdown/MarkdownText.d.ts +3 -1
  43. package/lib/types/markdown/file-link.d.ts +13 -3
  44. package/lib/types/markdown/mathCompatibility.d.ts +12 -5
  45. package/lib/types/markdown/render.d.ts +2 -1
  46. package/lib/types/markdown/windows-path-destinations.d.ts +12 -0
  47. package/lib/types/motion.d.ts +13 -0
  48. package/lib/types/overlay-top-margin.d.ts +10 -2
  49. package/lib/types/quote-text.d.ts +69 -0
  50. package/lib/types/use-clipboard-copy.d.ts +14 -0
  51. package/lib/types/useAnchoredPosition.d.ts +31 -18
  52. package/lib/types/useRowDragPreview.d.ts +20 -0
  53. package/lib/types/user-text.d.ts +24 -2
  54. package/package.json +8 -6
  55. package/lib/types/use-copy-feedback.d.ts +0 -14
@@ -104,6 +104,29 @@
104
104
  color: var(--qilin-alias-label-primary);
105
105
  }
106
106
 
107
+ /* Line-level hover: the source line under the pointer takes the interactive
108
+ wash so a reader can follow one line across a wide block. The rule paints a
109
+ background only — selection, the copy button's textContent read, and the
110
+ gutter's counters are untouched. Shiki, the plain arm, and the streaming arm
111
+ all emit one `span.line` per source line, so the selector covers every arm. */
112
+ .block :where(pre) code > :global(.line) {
113
+ border-radius: 4px;
114
+ transition: background-color 120ms ease;
115
+ }
116
+
117
+ @media (hover: hover) {
118
+ .block :where(pre) code > :global(.line):hover {
119
+ background-color: var(--qilin-alias-interactive-bg-hover);
120
+ }
121
+ }
122
+
123
+ /* A reader who asked for less motion gets the wash without the fade. */
124
+ @media (prefers-reduced-motion: reduce) {
125
+ .block :where(pre) code > :global(.line) {
126
+ transition: none;
127
+ }
128
+ }
129
+
107
130
  .numbered :where(pre) code {
108
131
  display: block;
109
132
  counter-reset: source-line;
@@ -23,7 +23,7 @@
23
23
  max-height: 200px;
24
24
  overflow: auto;
25
25
  background: var(--qilin-alias-markdown-code-block);
26
- border: 0.5px solid var(--qilin-alias-border-l1);
26
+ border: 1px solid var(--qilin-alias-border-l1);
27
27
  border-radius: 6px;
28
28
  font-family: var(--ds-font-family-code);
29
29
  font-size: 11px;
@@ -143,7 +143,7 @@
143
143
  .markdown hr {
144
144
  display: block;
145
145
  border: none;
146
- height: 0.5px;
146
+ height: 1px;
147
147
  margin: 32px 0;
148
148
  background: var(--qilin-alias-border-l2);
149
149
  }
@@ -168,7 +168,7 @@
168
168
  font-family: var(--ds-font-family-code);
169
169
  font-size: 0.875em !important;
170
170
  background-color: var(--qilin-alias-markdown-inline-code);
171
- border: 0.5px solid var(--qilin-alias-border-l1);
171
+ border: 1px solid var(--qilin-alias-border-l1);
172
172
  border-radius: 6px;
173
173
  padding: 0 5px;
174
174
  }
@@ -244,7 +244,7 @@
244
244
  .tableScroll th {
245
245
  text-align: start;
246
246
  padding: 10px 16px;
247
- border-bottom: 0.5px solid var(--qilin-alias-border-l3);
247
+ border-bottom: 1px solid var(--qilin-alias-border-l3);
248
248
  border-top: none;
249
249
  font: var(--qilin-font-markdown-table-head);
250
250
  max-width: 320px;
@@ -254,7 +254,7 @@
254
254
 
255
255
  .tableScroll td {
256
256
  padding: 10px 16px;
257
- border-bottom: 0.5px solid var(--qilin-alias-border-l2);
257
+ border-bottom: 1px solid var(--qilin-alias-border-l2);
258
258
  font: var(--qilin-font-markdown-table);
259
259
  max-width: 320px;
260
260
  max-width: min(30vw, 320px);
@@ -305,10 +305,14 @@
305
305
  on hover/focus. An underline at rest reads badly inside the code chip, where
306
306
  it collides with monospace descenders and the pill background. */
307
307
  .fileMention {
308
- margin: 0;
309
- padding: 0;
308
+ /* A mention of a file the agent produced highlights its code chip: the
309
+ negative margins fill the chip's own padding so the accent pill covers
310
+ the whole chip rather than nesting inside it. */
311
+ margin: 0 -5px;
312
+ padding: 0 5px;
310
313
  border: none;
311
- background: none;
314
+ border-radius: 6px;
315
+ background: var(--qilin-specific-bubble-highlight);
312
316
  font: inherit;
313
317
  font-weight: 500;
314
318
  color: var(--qilin-alias-link);
@@ -8,7 +8,7 @@
8
8
  }
9
9
 
10
10
  .field + .field {
11
- border-top: 0.5px solid var(--qilin-alias-border-l2);
11
+ border-top: 1px solid var(--qilin-alias-border-l2);
12
12
  }
13
13
 
14
14
  .head {
@@ -107,7 +107,7 @@
107
107
  .input {
108
108
  height: 34px;
109
109
  padding: 0 12px;
110
- border: 0.5px solid var(--qilin-alias-border-l4);
110
+ border: 1px solid var(--qilin-alias-border-l4);
111
111
  border-radius: var(--qilin-radius-md);
112
112
  background: var(--qilin-alias-bg-layer-3);
113
113
  font: inherit;
@@ -0,0 +1,46 @@
1
+ /** React-commit-driven movement and entry/exit fades for keyed rows. */
2
+ import { Component, type AriaRole, type ReactNode } from 'react';
3
+ /** Keyed row content, list semantics, and animation readiness. */
4
+ export interface AnimatedRowsProps {
5
+ children: ReactNode;
6
+ className: string;
7
+ role?: AriaRole;
8
+ label?: string;
9
+ /** Unique DOM-order keys matching the rendered data-row-key attributes. */
10
+ rowKeys: readonly string[];
11
+ ready: boolean;
12
+ /** Changes that replace the view or reveal hidden rows settle immediately. */
13
+ resetKey: string;
14
+ }
15
+ interface RowPosition {
16
+ element: HTMLElement;
17
+ rect: DOMRect;
18
+ opacity: number;
19
+ }
20
+ interface RowSnapshot {
21
+ positions: Map<string, RowPosition>;
22
+ removed: Map<string, RowPosition>;
23
+ }
24
+ /**
25
+ * Animates keyed rows only when their rendered membership or order changes.
26
+ * Motion starts after the first pointer or keyboard input inside the mounted list.
27
+ * The parent supplies a positioned container for the inert exit overlay.
28
+ */
29
+ export declare class AnimatedRows extends Component<AnimatedRowsProps> {
30
+ private armed;
31
+ private readonly list;
32
+ private readonly overlay;
33
+ private readonly movements;
34
+ private readonly exits;
35
+ getSnapshotBeforeUpdate(previous: AnimatedRowsProps): RowSnapshot | null;
36
+ componentDidUpdate(previous: AnimatedRowsProps, _state: unknown, snapshot: RowSnapshot | null): void;
37
+ componentWillUnmount(): void;
38
+ private readPositions;
39
+ private move;
40
+ private cancelMovements;
41
+ private removeExit;
42
+ private clear;
43
+ render(): ReactNode;
44
+ }
45
+ export {};
46
+ //# sourceMappingURL=AnimatedRows.d.ts.map
@@ -1,18 +1,27 @@
1
- import type { ButtonHTMLAttributes, ReactNode } from 'react';
1
+ import { type ButtonHTMLAttributes, type ReactNode } from 'react';
2
2
  /** Visual variant, each backed by its --qilin-alias-button-* token family. */
3
3
  export type ButtonVariant = 'primary' | 'ghost' | 'outline' | 'toolbar';
4
+ /** Props accepted by {@link Button}. */
5
+ export type ButtonProps = {
6
+ variant?: ButtonVariant;
7
+ size?: 'md' | 'sm';
8
+ icon?: ReactNode;
9
+ className?: string | undefined;
10
+ children?: ReactNode;
11
+ } & ButtonHTMLAttributes<HTMLButtonElement>;
4
12
  /**
5
13
  * Render a button.
6
14
  * @param props.variant - visual family (default 'ghost').
7
15
  * @param props.size - 'md' 36px capsule (figma Button) or 'sm' 28px compact.
8
16
  * @param props.icon - optional leading 16px icon node.
17
+ * @param ref - native button for focus management and overlay anchors.
9
18
  * @returns the button element; native button attributes pass through.
10
19
  */
11
- export declare function Button({ variant, size, icon, className, children, ...rest }: {
20
+ export declare const Button: import("react").ForwardRefExoticComponent<{
12
21
  variant?: ButtonVariant;
13
- size?: 'md' | 'sm';
22
+ size?: "md" | "sm";
14
23
  icon?: ReactNode;
15
24
  className?: string | undefined;
16
25
  children?: ReactNode;
17
- } & ButtonHTMLAttributes<HTMLButtonElement>): import("react").JSX.Element;
26
+ } & ButtonHTMLAttributes<HTMLButtonElement> & import("react").RefAttributes<HTMLButtonElement>>;
18
27
  //# sourceMappingURL=Button.d.ts.map
@@ -0,0 +1,79 @@
1
+ import { type ReactNode } from 'react';
2
+ import { type MarkdownLabels, type MarkdownPathImages } from './markdown/MarkdownText.tsx';
3
+ /** One read of a long Markdown description, such as a plugin's detail document. */
4
+ export type DetailDocumentState = {
5
+ readonly status: 'loading';
6
+ } | {
7
+ readonly status: 'found';
8
+ readonly markdown: string;
9
+ } | {
10
+ readonly status: 'none';
11
+ } | {
12
+ readonly status: 'error';
13
+ readonly message: string;
14
+ };
15
+ /** A read as owners track it: in flight, settled with a document or none, or failed with a localized message. */
16
+ export type DetailDocumentRead = undefined | {
17
+ readonly status: 'loading';
18
+ } | {
19
+ readonly status: 'ready';
20
+ readonly markdown: string | undefined;
21
+ } | {
22
+ readonly status: 'error';
23
+ readonly message: string;
24
+ };
25
+ /**
26
+ * The state {@link DetailDocument} shows for an owner's read: loading until it settles, then the document, none, or the
27
+ * failure.
28
+ * @param read - the owner's read; undefined before it starts.
29
+ * @returns the state to show.
30
+ */
31
+ export declare function detailDocumentState(read: DetailDocumentRead): DetailDocumentState;
32
+ /** Translated strings of a detail document's chrome, as an owner's dictionary supplies them. */
33
+ export interface DetailDocumentCopy {
34
+ readonly title: string;
35
+ readonly loading: string;
36
+ readonly retry: string;
37
+ readonly copy: string;
38
+ readonly copied: string;
39
+ /** Alt text naming an on-page Mermaid diagram. */
40
+ readonly diagram: string;
41
+ /** Alt text naming an enlarged Mermaid diagram. */
42
+ readonly enlarged: string;
43
+ readonly footnotes: string;
44
+ }
45
+ /**
46
+ * Assemble reference-stable labels for {@link DetailDocument} from translated strings.
47
+ * @param copy - the translated strings.
48
+ * @returns labels that keep their identity while the strings stay equal.
49
+ */
50
+ export declare function useDetailDocumentLabels(copy: DetailDocumentCopy): DetailDocumentLabels;
51
+ /** Localized chrome of a detail document. */
52
+ export interface DetailDocumentLabels {
53
+ /** Accessible name of the scrollable document region; the section shows no heading of its own. */
54
+ readonly title: string;
55
+ /** Accessible name of the loading placeholder. */
56
+ readonly loading: string;
57
+ /** Retry button text. */
58
+ readonly retry: string;
59
+ readonly markdown: MarkdownLabels;
60
+ }
61
+ /**
62
+ * A section showing a long Markdown description, without a heading of its own: three skeleton lines while it loads, the
63
+ * document once found, or the owner's failure message with a retry. Nothing renders for `none`. A document taller than
64
+ * the section's height limit scrolls inside it, and the scroll region, named by `labels.title`, takes keyboard focus.
65
+ * Raw HTML is omitted, relative links stay text, and only images the owner's `images` resolver accepts, or absolute
66
+ * HTTP(S) images, load.
67
+ * @param props.state - the read to show.
68
+ * @param props.labels - localized region name, placeholder name, retry text, and Markdown chrome; keep `markdown` reference-stable.
69
+ * @param props.images - resolves the document's relative image destinations to displayable URLs.
70
+ * @param props.onRetry - reads the document again after a failure.
71
+ * @returns the section, or null.
72
+ */
73
+ export declare function DetailDocument({ state, labels, images, onRetry }: {
74
+ readonly state: DetailDocumentState;
75
+ readonly labels: DetailDocumentLabels;
76
+ readonly images?: MarkdownPathImages | undefined;
77
+ readonly onRetry: () => void;
78
+ }): ReactNode;
79
+ //# sourceMappingURL=DetailDocument.d.ts.map
@@ -1,3 +1,4 @@
1
+ import type { HTMLAttributes } from 'react';
1
2
  /** Lightbox strings the owner resolves from its own locale namespace. */
2
3
  export interface ImageLightboxLabels {
3
4
  /** Accessible name of the preview dialog. */
@@ -18,10 +19,11 @@ export interface ImageLightboxLabels {
18
19
  * @param props.onClose - dismiss callback owned by the opener.
19
20
  * @returns the modal preview dialog.
20
21
  */
21
- export declare function ImageLightbox({ src, alt, labels, onClose }: {
22
+ export declare function ImageLightbox({ src, alt, labels, onClose, contextMenuProps }: {
22
23
  src: string;
23
24
  alt: string;
24
25
  labels: ImageLightboxLabels;
25
26
  onClose: () => void;
27
+ contextMenuProps?: Pick<HTMLAttributes<HTMLElement>, 'onContextMenu' | 'onKeyDown' | 'tabIndex'> | undefined;
26
28
  }): import("react").ReactPortal;
27
29
  //# sourceMappingURL=ImageLightbox.d.ts.map
@@ -14,7 +14,7 @@ export interface MenuItem {
14
14
  /** Destructive row: error-colored text/icon and danger hover fill. */
15
15
  danger?: boolean;
16
16
  /** Nested card opened to the right on hover/focus. */
17
- submenu?: readonly MenuItem[];
17
+ submenu?: readonly MenuEntry[];
18
18
  }
19
19
  /** Hairline between item groups (not selectable). */
20
20
  export interface MenuSeparator {
@@ -41,6 +41,12 @@ export interface MenuItemButtonProps {
41
41
  current?: boolean;
42
42
  /** Whether the row cannot be activated. */
43
43
  disabled?: boolean;
44
+ /**
45
+ * Why the disabled row cannot be activated. The row then stays hoverable and
46
+ * focusable, shows the reason in a tooltip, and exposes it as the row's
47
+ * description; an enabled row ignores it.
48
+ */
49
+ disabledReason?: string | undefined;
44
50
  /** Destructive row: error-colored text/icon and danger hover fill. */
45
51
  danger?: boolean;
46
52
  /**
@@ -64,12 +70,13 @@ export interface MenuItemButtonProps {
64
70
  * @param props.icon - optional leading icon.
65
71
  * @param props.current - whether the row identifies the currently displayed page or view.
66
72
  * @param props.disabled - whether the row cannot be activated.
73
+ * @param props.disabledReason - why a disabled row cannot be activated, shown in a tooltip and read as its description.
67
74
  * @param props.danger - whether to use the destructive row colors.
68
75
  * @param props.separatorBefore - whether this row starts a new group (hairline above it).
69
76
  * @param props.onSelect - row activation callback.
70
77
  * @returns one menu-item row.
71
78
  */
72
- export declare function MenuItemButton({ children, shortcut, icon, current, disabled, danger, separatorBefore, onSelect, }: MenuItemButtonProps): import("react").JSX.Element;
79
+ export declare function MenuItemButton({ children, shortcut, icon, current, disabled, disabledReason, danger, separatorBefore, onSelect, }: MenuItemButtonProps): import("react").JSX.Element;
73
80
  /**
74
81
  * Render an anchored dropdown menu. While the list is open its keys mirror the
75
82
  * composer's: Tab settles the focused row — from the trigger, Tab enters the
@@ -77,6 +84,9 @@ export declare function MenuItemButton({ children, shortcut, icon, current, disa
77
84
  * anchor's first button, and selecting a row does the same — the rows unmount
78
85
  * with the list. Only a keyboard on the trigger or inside the list is
79
86
  * intercepted; Tab presses elsewhere on the page stay the browser's.
87
+ * @param props.contextual - cursor positioning, context dismissal, and per-divider icon alignment.
88
+ * @param props.exiting - retain an inert popup during dismissal or replacement fade.
89
+ * @param props.returnFocus - original control captured before asynchronous menu preparation.
80
90
  * @param props.autoFocus - focus the first item on open; the arrow keys walk the list either way.
81
91
  * @param props.open - whether the list is showing (owner-controlled).
82
92
  * @param props.anchor - the trigger element (rendered in place).
@@ -90,9 +100,9 @@ export declare function MenuItemButton({ children, shortcut, icon, current, disa
90
100
  * @param props.align - list alignment against the anchor (default 'start').
91
101
  * @param props.side - open below (`bottom`, default) or above (`top`) the anchor.
92
102
  * @param props.portal - render the list into document.body, fixed-positioned
93
- * from the anchor rect (follows movement and resizing while open). Use when an
94
- * ancestor's overflow clipping would crop the in-place list; default false
95
- * keeps the pure-CSS in-place behavior.
103
+ * synchronously (follows movement, resizing, and browser zoom while open)
104
+ * and clamped inside the visual viewport. Use when an ancestor's overflow clipping would crop the
105
+ * in-place list; default false keeps the pure-CSS in-place behavior.
96
106
  * @param props.closeOnPointerLeave - close the list once the pointer has left
97
107
  * both trigger and list for the pointer grace (default false keeps it open
98
108
  * until outside click/Escape/selection). The grace makes the 4px trigger->list
@@ -103,11 +113,11 @@ export declare function MenuItemButton({ children, shortcut, icon, current, disa
103
113
  * directly (e.g. from a host-owned trigger button) instead of measuring the
104
114
  * Menu's own wrapper span. Required when the wrapper isn't itself laid out at
105
115
  * the trigger (render-prop anchors, effect-positioned proxies — measuring the
106
- * wrapper there races the host's layout effects). Called on open, each animation
107
- * frame, and scroll/resize; return null to skip placement for that frame.
116
+ * wrapper there races the host's layout effects). Called while tracking the
117
+ * anchor and on scroll/resize; return null to defer placement.
108
118
  * @param props.minWidthFromAnchor - in portal mode, keep the menu at least as wide as its anchor, bounded by viewport margins.
109
119
  * @param props.footer - rows pinned below the scrolling items area, separated
110
- * by a hairline; they stay visible while the items above scroll.
120
+ * by a hairline; contextual menus reuse an explicit leading separator. These rows stay visible while the items above scroll.
111
121
  * @param props.children - component rows rendered after `items` in the same
112
122
  * list, each a `role="menuitem"` button such as {@link MenuItemButton}; they
113
123
  * share the keyboard walk, the submenu exclusivity, and the post-selection
@@ -122,8 +132,14 @@ export declare function MenuItemButton({ children, shortcut, icon, current, disa
122
132
  * document.body outside the owner's DOM subtree.
123
133
  * @returns anchor wrapper with the conditional list.
124
134
  */
125
- export declare function Menu({ open, anchor, items, children, selectedId, selectedIds, onSelect, onClose, align, side, portal, closeOnPointerLeave, dense, compact, autoFocus, selection, getAnchorRect, minWidthFromAnchor, footer, className, listClassName }: {
135
+ export declare function Menu({ open, anchor, items, children, selectedId, selectedIds, onSelect, onClose, align, side, portal, closeOnPointerLeave, dense, compact, autoFocus, selection, getAnchorRect, minWidthFromAnchor, footer, className, listClassName, contextual, exiting, returnFocus }: {
126
136
  open: boolean;
137
+ /** Pointer-positioned popup: cursor clearance, grouped icons, and context gestures. */
138
+ contextual?: boolean;
139
+ /** Resolve the original control for focus restoration and inert/hidden dismissal. */
140
+ returnFocus?: (() => HTMLElement | null) | undefined;
141
+ /** Retain a dismissed context popup during its short fade. */
142
+ exiting?: 'dismiss' | 'replace' | undefined;
127
143
  autoFocus?: boolean;
128
144
  anchor: ReactNode;
129
145
  items?: readonly MenuEntry[];
@@ -1,9 +1,18 @@
1
1
  /** Shared menu material and the macOS backing that lets Chromium blur transparent windows. */
2
2
  import { type ComponentPropsWithoutRef } from 'react';
3
+ /** Context-menu transition durations in milliseconds, shared with its lifecycle owner. */
4
+ export declare const MENU_TRANSITION_MS: Readonly<{
5
+ enter: 40;
6
+ dismiss: 80;
7
+ replace: 10;
8
+ prepare: 20;
9
+ }>;
3
10
  /** Menu containers preserve native div props and refs. */
4
11
  export interface MenuSurfaceProps extends ComponentPropsWithoutRef<'div'> {
5
12
  /** Match the shared compact menu's smaller outer radius. */
6
13
  compact?: boolean;
14
+ /** Fade direct content, material, and native backing without isolating the material's backdrop. */
15
+ phase?: 'enter' | 'dismiss' | 'replace' | undefined;
7
16
  }
8
17
  /**
9
18
  * Paint a menu and, on macOS, an opaque backing behind the page content within its bounds.
@@ -0,0 +1,26 @@
1
+ import type { JSX } from 'react';
2
+ /** Localized names of the quote controls; a caller owns every string. */
3
+ export interface QuotePreviewLabels {
4
+ /** Accessible name of the copy control. */
5
+ readonly copy: string;
6
+ /** Accessible name after an accepted copy. */
7
+ readonly copied: string;
8
+ /** Accessible name of the jump control. */
9
+ readonly jump: string;
10
+ }
11
+ /** Presentation inputs for one quoted excerpt. */
12
+ export interface QuotePreviewProps {
13
+ /** Excerpt text as decoded from the message, without its source marker line. */
14
+ readonly text: string;
15
+ /** Localized control names. */
16
+ readonly labels: QuotePreviewLabels;
17
+ /** Reveal the quoted source; omission hides the jump control. */
18
+ readonly onJump?: (() => void) | undefined;
19
+ }
20
+ /**
21
+ * Render one quoted excerpt with its copy and optional jump controls.
22
+ * @param props - excerpt text, control names, and the reveal action.
23
+ * @returns the quote surface, with the full text on hover while clipped.
24
+ */
25
+ export declare function QuotePreview({ text, labels, onJump }: QuotePreviewProps): JSX.Element;
26
+ //# sourceMappingURL=QuotePreview.d.ts.map
@@ -14,13 +14,15 @@ export interface SegmentedTab<Value extends string = string> {
14
14
  * Keyboard selection also moves focus; only the selected tab is a tab stop.
15
15
  * @param props.label - localized accessible name for the tab list.
16
16
  * @param props.className - layout placement; panels remain caller-owned.
17
+ * @param props.size - `sm` draws 30px tabs with a 2px inset (34px tall); the default `md` draws 34px tabs with a 4px inset (42px tall).
17
18
  * @returns the tab list, without its panels.
18
19
  */
19
- export declare function SegmentedTabs<Value extends string>({ items, value, onChange, label, className }: {
20
+ export declare function SegmentedTabs<Value extends string>({ items, value, onChange, label, className, size }: {
20
21
  items: readonly [SegmentedTab<Value>, ...SegmentedTab<Value>[]];
21
22
  value: Value;
22
23
  onChange: (value: Value) => void;
23
24
  label: string;
24
25
  className?: string | undefined;
26
+ size?: 'sm' | 'md' | undefined;
25
27
  }): ReactNode;
26
28
  //# sourceMappingURL=SegmentedTabs.d.ts.map
@@ -7,6 +7,16 @@ import type { ReactNode } from 'react';
7
7
  * transformed or filtered ancestor cannot trap the fixed banner in that
8
8
  * ancestor's box.
9
9
  *
10
+ * Actions extend the sentence with inline choices: each renders its plain-text
11
+ * `prefix` (a connective such as "or") followed by its localized `label` as
12
+ * clickable text inside the banner. Each press is the owner's to handle (undo
13
+ * the reported change, then unmount the toast). The banner surface stays
14
+ * click-through — only the action text takes the pointer — and a fully faded
15
+ * banner takes no input at all.
16
+ *
17
+ * With an unchanged holdMs, a parent rerender does not extend the lifetime:
18
+ * the completion call reaches the latest onDone handler.
19
+ *
10
20
  * The hold is the owner's to set, because how long a banner has to stay
11
21
  * depends on how much there is to read: a one-line limit lands in the default
12
22
  * window, while a failure that names what broke does not. One value drives
@@ -18,6 +28,9 @@ import type { ReactNode } from 'react';
18
28
  * under `tone="success"`, which brings its own glyph.
19
29
  * @param props.tone - 'success' renders the success check as the leading
20
30
  * glyph; omitted, the icon seat keeps its warning tint.
31
+ * @param props.actions - optional inline actions continuing the sentence;
32
+ * each renders its plain-text `prefix` followed by its localized `label`
33
+ * as clickable text flowing after `text` as one sentence.
21
34
  * @param props.holdMs - full-opacity hold before the fade; defaults to 3000.
22
35
  * @param props.anchor - optional element whose horizontal center the banner
23
36
  * follows (e.g. the composer card, so the banner centers over the chat column
@@ -25,12 +38,17 @@ import type { ReactNode } from 'react';
25
38
  * @param props.onDone - called once the fade completes; unmount the toast here.
26
39
  * @returns the floating banner.
27
40
  */
28
- export declare function Toast({ text, icon, tone, anchor, holdMs, onDone }: {
41
+ export declare function Toast({ text, icon, tone, anchor, holdMs, actions, onDone }: {
29
42
  text: string;
30
43
  icon?: ReactNode;
31
44
  tone?: 'success';
32
45
  anchor?: HTMLElement | null;
33
46
  holdMs?: number;
47
+ actions?: readonly {
48
+ label: string;
49
+ prefix?: string;
50
+ onClick: () => void;
51
+ }[];
34
52
  onDone: () => void;
35
53
  }): import("react").ReactPortal;
36
54
  //# sourceMappingURL=Toast.d.ts.map
@@ -1,5 +1,5 @@
1
1
  /** Anchor-preserving tooltips; an optional body portal escapes clipping containers and stacking contexts that cap the bubble's z-index. */
2
- import type { FocusEventHandler, MouseEventHandler, ReactElement, Ref } from 'react';
2
+ import type { FocusEventHandler, MouseEventHandler, PointerEventHandler, ReactElement, Ref } from 'react';
3
3
  import { type TooltipSide } from './tooltip-placement.ts';
4
4
  export type { TooltipSide } from './tooltip-placement.ts';
5
5
  /**
@@ -11,8 +11,9 @@ export declare const TooltipSuppression: import("react").Context<((suppressed: b
11
11
  interface AnchorProps {
12
12
  'aria-describedby'?: string | undefined;
13
13
  ref?: Ref<HTMLElement> | undefined;
14
- onMouseEnter?: MouseEventHandler | undefined;
15
- onMouseLeave?: MouseEventHandler | undefined;
14
+ onPointerEnter?: PointerEventHandler | undefined;
15
+ onPointerLeave?: PointerEventHandler | undefined;
16
+ onPointerDown?: PointerEventHandler | undefined;
16
17
  onClick?: MouseEventHandler | undefined;
17
18
  onFocus?: FocusEventHandler | undefined;
18
19
  onBlur?: FocusEventHandler | undefined;
@@ -37,17 +38,20 @@ type TooltipLabel = string | (() => string);
37
38
  * toggling never remounts it (which would cut its CSS transitions).
38
39
  * @param props.maxWidth - bubble width cap in pixels, for labels long enough that the default
39
40
  * half-viewport cap would render a slab wider than the surface the anchor sits on; always bounded by viewport margins.
40
- * @param props.openOnClick - clicking also pins the bubble for reading; another click, Escape,
41
+ * @param props.openOnClick - clicking or tapping also pins the bubble for reading; another click, Escape,
41
42
  * Tab, or an outside pointerdown dismisses it. Defaults to false for ordinary action tooltips.
43
+ * @param props.onlyWhenClipped - show the bubble only when the anchor or an HTML element inside it
44
+ * overflows horizontally, measured when the bubble is about to show; for labels that repeat
45
+ * text an ellipsis may cut off. Defaults to false.
42
46
  * @param props.children - a single anchor element; its own ref (callback or object) is forwarded alongside the tooltip's.
43
- * An anchor that accepts no ref still positions the bubble: a raise records the element the event ran on.
44
47
  * @returns the cloned anchor plus a fixed-position bubble, optionally portaled to the body.
45
48
  * The bubble stays hidden until ResizeObserver supplies its size for viewport fitting; clicking the
46
49
  * anchor dismisses the bubble unless openOnClick is enabled, and focus arriving after a pointer
47
- * interaction (a closing menu refocusing its trigger) never raises it. When no side fits, the
48
- * viewport-bounded fallback may overlap the anchor; oversized content is scrollable when pinned.
50
+ * interaction (a closing menu refocusing its trigger) never raises it. Touch cannot trigger hover
51
+ * tooltips; tapping still invokes the child's action and can open an openOnClick explanation.
52
+ * When no side fits, the viewport-bounded fallback may overlap the anchor; oversized content is scrollable when pinned.
49
53
  */
50
- export declare function Tooltip({ label, shortcutKeys, side, align, delayMs, focusDelayMs, gap, disabled, portal, maxWidth, openOnClick, children }: {
54
+ export declare function Tooltip({ label, shortcutKeys, side, align, delayMs, focusDelayMs, gap, disabled, portal, maxWidth, openOnClick, onlyWhenClipped, children }: {
51
55
  label: TooltipLabel;
52
56
  shortcutKeys?: readonly string[] | undefined;
53
57
  side?: TooltipSide;
@@ -59,6 +63,7 @@ export declare function Tooltip({ label, shortcutKeys, side, align, delayMs, foc
59
63
  portal?: boolean;
60
64
  maxWidth?: number;
61
65
  openOnClick?: boolean;
66
+ onlyWhenClipped?: boolean;
62
67
  children: ReactElement<AnchorProps>;
63
68
  }): import("react").JSX.Element;
64
69
  //# sourceMappingURL=Tooltip.d.ts.map
@@ -0,0 +1,11 @@
1
+ /** Shared user-message background, typography, and content inset. */
2
+ import type { ComponentPropsWithoutRef } from 'react';
3
+ /** Native div attributes for user-authored message content. */
4
+ export type UserMessageBubbleProps = ComponentPropsWithoutRef<'div'>;
5
+ /**
6
+ * Render user-style message content without row placement or actions.
7
+ * @param props - Native div attributes and the caller-owned content.
8
+ * @returns A theme-aware message surface that wraps its content.
9
+ */
10
+ export declare function UserMessageBubble({ className, ...props }: UserMessageBubbleProps): import("react").JSX.Element;
11
+ //# sourceMappingURL=UserMessageBubble.d.ts.map
@@ -0,0 +1,12 @@
1
+ import { type HighlightSpan } from './markdown/highlight.ts';
2
+ export { CODE_HIGHLIGHT_EXTENSIONS, languageForPath } from '@qilin-agent/util-code-language';
3
+ /** Highlight one source fragment into one token list per line. */
4
+ export type CodeHighlighter = (code: string) => HighlightSpan[][] | undefined;
5
+ /**
6
+ * Bind the shared lazy highlighter to one language and refresh after its grammar loads.
7
+ * @param language - grammar hint selected from the source filename.
8
+ * @returns a stable fragment highlighter; unknown and loading grammars return `undefined` for plain-text fallback.
9
+ */
10
+ export declare function useCodeHighlighter(language: string | undefined): CodeHighlighter;
11
+ export type { HighlightSpan };
12
+ //# sourceMappingURL=code-highlighting.d.ts.map
@@ -6,4 +6,12 @@
6
6
  * @param options - browser focus options, including scroll preservation.
7
7
  */
8
8
  export declare function focusWithoutRing(element: HTMLElement, options?: FocusOptions): void;
9
+ /**
10
+ * Whether this browser runs on a touch-first mobile device.
11
+ *
12
+ * The answer is cached, so a caller must not expect it to follow a viewport
13
+ * resize. It drives input-shape choices only; layout reacts to container width.
14
+ * @returns `true` on a mobile browser, `false` on a desktop one.
15
+ */
16
+ export declare function isMobileDevice(): boolean;
9
17
  //# sourceMappingURL=focus.d.ts.map
@@ -178,6 +178,10 @@ export declare const IconPaperclipOutlineMedium: (props: IconProps) => import("r
178
178
  export declare const IconLoadingOutlineRegular: (props: IconProps) => import("react").JSX.Element;
179
179
  /** Medium IconLoadingOutline artwork with a 1.3px stroke. */
180
180
  export declare const IconLoadingOutlineMedium: (props: IconProps) => import("react").JSX.Element;
181
+ /** Regular one-pixel IconSaveOutline artwork. */
182
+ export declare const IconSaveOutlineRegular: (props: IconProps) => import("react").JSX.Element;
183
+ /** Medium IconSaveOutline artwork with a 1.3px stroke. */
184
+ export declare const IconSaveOutlineMedium: (props: IconProps) => import("react").JSX.Element;
181
185
  /** Regular one-pixel IconDownloadOutline artwork. */
182
186
  export declare const IconDownloadOutlineRegular: (props: IconProps) => import("react").JSX.Element;
183
187
  /** Medium IconDownloadOutline artwork with a 1.3px stroke. */
@@ -270,6 +274,10 @@ export declare const IconQueueOutlineMedium: (props: IconProps) => import("react
270
274
  export declare const IconChecklistOutlineRegular: (props: IconProps) => import("react").JSX.Element;
271
275
  /** Medium IconChecklistOutline artwork with a 1.3px stroke. */
272
276
  export declare const IconChecklistOutlineMedium: (props: IconProps) => import("react").JSX.Element;
277
+ /** Regular one-pixel IconDragHandleOutline artwork. */
278
+ export declare const IconDragHandleOutlineRegular: (props: IconProps) => import("react").JSX.Element;
279
+ /** Medium IconDragHandleOutline artwork with a 1.3px stroke. */
280
+ export declare const IconDragHandleOutlineMedium: (props: IconProps) => import("react").JSX.Element;
273
281
  /** Regular one-pixel IconListPenOutline artwork. */
274
282
  export declare const IconListPenOutlineRegular: (props: IconProps) => import("react").JSX.Element;
275
283
  /** Medium IconListPenOutline artwork with a 1.3px stroke. */
@@ -38,6 +38,7 @@ export declare const BrowseOutlineArtwork: ({ size, className, strokeWidth }: We
38
38
  * @param props - Size, optional CSS class, and inherited stroke width.
39
39
  * @returns The decorative SVG artwork.
40
40
  */
41
+ export declare const DragHandleOutlineArtwork: ({ size, className, strokeWidth }: WeightedArtworkProps) => import("react").JSX.Element;
41
42
  export declare const FolderCloseArtwork: ({ size, className, strokeWidth }: WeightedArtworkProps) => import("react").JSX.Element;
42
43
  export {};
43
44
  //# sourceMappingURL=shared-artwork.d.ts.map