@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
@@ -13,6 +13,10 @@ export { StateDot } from './StateDot.tsx';
13
13
  export type { StateDotState } from './StateDot.tsx';
14
14
  export { DisclosureRow } from './DisclosureRow.tsx';
15
15
  export type { DisclosureRowProps } from './DisclosureRow.tsx';
16
+ export { AnimatedRows } from './AnimatedRows.tsx';
17
+ export type { AnimatedRowsProps } from './AnimatedRows.tsx';
18
+ export { prefersReducedMotion } from './motion.ts';
19
+ export { useRowDragPreview } from './useRowDragPreview.ts';
16
20
  export { Button } from './Button.tsx';
17
21
  export type { ButtonVariant } from './Button.tsx';
18
22
  export { Pill } from './Pill.tsx';
@@ -53,7 +57,11 @@ export type { LinkIconKind, LinkIconProps } from './LinkIcon.tsx';
53
57
  export { FileTypeIcon, classifyFileType, fileExtension } from './FileTypeIcon.tsx';
54
58
  export type { CodeFileType, FileType, FileTypeIconProps, FileTypeKind, FileTypeProjectContext, } from './FileTypeIcon.tsx';
55
59
  export { PathLabel } from './PathLabel.tsx';
56
- export { projectUserText, type UserTextReferences, type UserTextSecretLabels } from './user-text.tsx';
60
+ export { projectUserText, type UserTextQuotes, type UserTextReferences, type UserTextSecretLabels, } from './user-text.tsx';
61
+ export { QuotePreview } from './QuotePreview.tsx';
62
+ export type { QuotePreviewLabels, QuotePreviewProps } from './QuotePreview.tsx';
63
+ export { QUOTE_WIRE_SCHEME, messageQuoteRanges, quoteBlockTextOf, quoteInsertionOf, quoteTextOf, quotedExcerptOf, } from './quote-text.ts';
64
+ export type { MessageQuoteRange, QuoteFragment, QuotedExcerpt } from './quote-text.ts';
57
65
  export { Tooltip } from './Tooltip.tsx';
58
66
  export type { TooltipSide } from './Tooltip.tsx';
59
67
  export { pointerModality } from './input-modality.ts';
@@ -63,8 +71,10 @@ export { closeTopModal, isBehindModal, modalSelector, useModalLayer } from './us
63
71
  export { Toast } from './Toast.tsx';
64
72
  export { fileSizeText, fileSizeRoundedText } from './file-size.ts';
65
73
  export { writeClipboard } from './clipboard.ts';
66
- export { useCopyFeedback } from './use-copy-feedback.ts';
67
- export type { CopyFeedback } from './use-copy-feedback.ts';
74
+ export { useClipboardCopy } from './use-clipboard-copy.ts';
75
+ export type { ClipboardCopyState } from './use-clipboard-copy.ts';
76
+ export { UserMessageBubble } from './UserMessageBubble.tsx';
77
+ export type { UserMessageBubbleProps } from './UserMessageBubble.tsx';
68
78
  export { relativeTime } from './relative-time.ts';
69
79
  export { rankByName } from './rank-by-name.ts';
70
80
  export { isDarwinDesktop, watchDarwinDesktop } from './darwin-desktop.ts';
@@ -81,6 +91,8 @@ export { SearchBlock, DEFAULT_SEARCH_MAX_LINES } from './SearchBlock.tsx';
81
91
  export type { SearchBlockProps, SearchMatchesBlockProps, SearchPathsBlockProps, SearchFileGroup, SearchBlockLineMatch, SearchBlockLabels, } from './SearchBlock.tsx';
82
92
  export { WebBlock } from './WebBlock.tsx';
83
93
  export type { WebBlockProps, WebSearchBlockProps, WebFetchBlockProps, WebSourceView, WebBlockLabels, } from './WebBlock.tsx';
94
+ export { DetailDocument, detailDocumentState, useDetailDocumentLabels } from './DetailDocument.tsx';
95
+ export type { DetailDocumentCopy, DetailDocumentLabels, DetailDocumentRead, DetailDocumentState, } from './DetailDocument.tsx';
84
96
  export { CodeBlock } from './markdown/CodeBlock.tsx';
85
97
  export type { CodeBlockProps } from './markdown/CodeBlock.tsx';
86
98
  export { JsonBlock } from './markdown/JsonBlock.tsx';
@@ -90,9 +102,13 @@ export { MarkdownText } from './markdown/MarkdownText.tsx';
90
102
  export type { MarkdownCodeLabels, MarkdownFileMentions, MarkdownLabels, MarkdownPathImages } from './markdown/MarkdownText.tsx';
91
103
  export { extractMarkdownPlainText } from './markdown/plain-text.ts';
92
104
  export type { MarkdownPlainTextMode, MarkdownPlainTextOptions } from './markdown/plain-text.ts';
105
+ export { CODE_HIGHLIGHT_EXTENSIONS, languageForPath, useCodeHighlighter } from './code-highlighting.ts';
106
+ export type { CodeHighlighter, HighlightSpan } from './code-highlighting.ts';
93
107
  export * from './icons/index.tsx';
94
108
  export { ImageLightbox } from './ImageLightbox.tsx';
95
109
  export type { ImageLightboxLabels } from './ImageLightbox.tsx';
96
110
  export { MenuGroup, observeStickyMenuGroups } from './MenuGroup.tsx';
97
- export { MenuSurface } from './MenuSurface.tsx';
111
+ export { MenuSurface, MENU_TRANSITION_MS } from './MenuSurface.tsx';
112
+ export { isMobileDevice } from './focus.ts';
113
+ export type { MarkdownMenuTarget } from './markdown/MarkdownDelegate.tsx';
98
114
  //# sourceMappingURL=index.d.ts.map
@@ -1,12 +1,29 @@
1
- import type { ReactNode } from 'react';
1
+ import type { HTMLAttributes, ReactNode } from 'react';
2
2
  import type { ImageLightboxLabels } from '../ImageLightbox.tsx';
3
3
  /**
4
4
  * Handle one sanitized absolute HTTP(S) URL selected from Markdown.
5
5
  * @param href - destination URL.
6
6
  */
7
7
  export type MarkdownExternalLinkHandler = (href: string) => void;
8
+ /** Explicit object and viewport coordinates from the element owning a context gesture. */
9
+ export type MarkdownMenuTarget = {
10
+ kind: 'file';
11
+ path: string;
12
+ line?: number;
13
+ } | {
14
+ kind: 'url';
15
+ url: string;
16
+ } | {
17
+ kind: 'image';
18
+ url: string;
19
+ path?: string;
20
+ name?: string;
21
+ mediaType?: string;
22
+ };
8
23
  /** Navigation capabilities supplied by the nearest Markdown owner. */
9
24
  export interface MarkdownDelegate {
25
+ /** Open an object menu without executing its normal activation. */
26
+ readonly getTriggerProps?: ((target: MarkdownMenuTarget) => Pick<HTMLAttributes<HTMLElement>, 'onContextMenu' | 'onKeyDown' | 'tabIndex'>) | undefined;
10
27
  /** Image previews for decoded local paths in this owner's workspace. */
11
28
  readonly fileImages?: {
12
29
  resolve: (path: string) => string | undefined;
@@ -37,7 +54,7 @@ export interface MarkdownDelegateProviderProps extends MarkdownDelegate {
37
54
  * @param props - Child tree and its file and HTTP(S) link handlers.
38
55
  * @returns the scoped child tree.
39
56
  */
40
- export declare function MarkdownDelegateProvider({ children, openExternalLink, openFile, fileImages, }: MarkdownDelegateProviderProps): ReactNode;
57
+ export declare function MarkdownDelegateProvider({ children, openExternalLink, openFile, fileImages, getTriggerProps, }: MarkdownDelegateProviderProps): ReactNode;
41
58
  /**
42
59
  * Read the nearest Markdown navigation capabilities.
43
60
  * @returns Owner callbacks, or an empty delegate outside a provider.
@@ -36,7 +36,9 @@ export type { MarkdownCodeLabels, MarkdownFileMentions, MarkdownHtmlRenderer, Ma
36
36
  * text sizing, uniform bold headings, and tight block spacing; the default
37
37
  * `body` variant uses the full document typography.
38
38
  * The provider's `openFile` enables local Markdown links in settled messages,
39
- * including `#L24` and `#L24-L30` destinations (ranges open at their first line).
39
+ * including `#L24` / `#L24-L30` fragments and `:24` / `:24-30` / `:24:5` suffixes
40
+ * (ranges open at their first line, columns are ignored, and invalid locations
41
+ * open only the file; see the README for URL/path classification).
40
42
  * @returns A GFM document with TeX math rendered through KaTeX; raw HTML
41
43
  * stays literal unless `html` supplies a renderer, and unsafe protocols are
42
44
  * disabled. Local links without an opener remain text; absolute HTTP(S)
@@ -1,10 +1,20 @@
1
1
  /** Local Markdown destinations accepted by the file-preview callback. */
2
2
  /**
3
- * Decode a file destination and its optional GitHub-style line fragment.
4
- * Literal `?` and `#` in filenames must be percent-encoded.
3
+ * Recognize a relative, rooted, or absolute Windows path before URL parsing.
4
+ * @param value - A decoded or URI-normalized destination.
5
+ * @returns Whether a filesystem prefix makes the destination unambiguous.
6
+ */
7
+ export declare function isExplicitFilePath(value: string): boolean;
8
+ /**
9
+ * Decode a file destination with a fragment, line range, or line/column suffix.
10
+ * Explicit relative, slash/backslash-rooted, and Windows drive paths take
11
+ * precedence over URLs. Other parseable absolute URLs are not file links.
12
+ * Fragments take precedence over colon suffixes; invalid locations open the
13
+ * file without selecting a line. Columns are consumed but not selected.
14
+ * Percent-encode literal `?`, `#`, and numeric colon suffixes in filenames.
5
15
  * @param value - Parsed Markdown link destination.
6
16
  * @returns A local path and optional first line, or undefined for URLs,
7
- * fragment-only links, queries, malformed escapes, or invalid line ranges.
17
+ * fragment-only links, queries, malformed escapes, or control characters.
8
18
  */
9
19
  export declare function parseFileLink(value: string): {
10
20
  path: string;
@@ -1,10 +1,17 @@
1
- /** Extend upstream dollar-only math syntax with TeX delimiters while reusing its token vocabulary. */
1
+ /**
2
+ * Extend upstream dollar-only math syntax with TeX delimiters while reusing its
3
+ * token vocabulary. The dollar construct claims every `$$` flow fence, so text
4
+ * on the opening line is math content instead of the upstream construct's fence
5
+ * meta, which the renderer does not read.
6
+ */
2
7
  import type { Extension } from 'micromark-util-types';
3
8
  /**
4
- * TeX backslash delimiters and same-line display-dollar blocks as a micromark
5
- * syntax extension reusing `micromark-extension-math`'s token vocabulary; the
6
- * caller must also register `math()` on the same parse so the emitted tokens
7
- * compile to standard math nodes.
9
+ * TeX backslash delimiters and display-dollar blocks as a micromark syntax
10
+ * extension reusing `micromark-extension-math`'s token vocabulary; the caller
11
+ * must also register `math()` on the same parse so the emitted tokens compile
12
+ * to standard math nodes. Both `$$<math>$$` on one line and a `$$<math>` block
13
+ * closed by a trailing `$$` parse as one math node, and a closing `$$` may sit
14
+ * at the end of a content line.
8
15
  * @returns The micromark syntax extension.
9
16
  */
10
17
  export declare function mathCompatibility(): Extension;
@@ -15,7 +15,7 @@
15
15
  * nothing) rather than ending in assertNever: grammars registered elsewhere
16
16
  * may add node types this renderer has no mapping for.
17
17
  */
18
- import type { ReactNode } from 'react';
18
+ import type { HTMLAttributes, ReactNode } from 'react';
19
19
  import type * as Md from 'mdast';
20
20
  import type { PositionedBlock } from './incremental.ts';
21
21
  /** Copy-button labels forwarded to fence CodeBlocks (this package is cordis-free, so copy arrives via props). */
@@ -88,6 +88,7 @@ export interface MarkdownFileMentions {
88
88
  */
89
89
  resolve(value: string): {
90
90
  open: () => void;
91
+ contextMenuProps?: Pick<HTMLAttributes<HTMLElement>, 'onContextMenu' | 'onKeyDown' | 'tabIndex'>;
91
92
  label: string;
92
93
  title: string;
93
94
  } | undefined;
@@ -0,0 +1,12 @@
1
+ /** Keep backslash separators in Markdown destinations written as Windows paths. */
2
+ import type { Extension } from 'mdast-util-from-markdown';
3
+ /**
4
+ * CommonMark reads `\.` or `\_` in a destination as an escaped character, so
5
+ * `D:\repo\.github` would parse as `D:\repo.github`. For a destination that
6
+ * is a Windows path, this fromMarkdown extension keeps every backslash
7
+ * literally, including one before punctuation such as `\(`, while character
8
+ * references such as `&amp;` still decode; other destinations parse unchanged.
9
+ * @returns The mdast extension to add to `mdastExtensions`.
10
+ */
11
+ export declare function windowsPathDestinations(): Extension;
12
+ //# sourceMappingURL=windows-path-destinations.d.ts.map
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Motion preference for the browser-driven animations in this package.
3
+ * Animation owners ask before starting movement, so one query decides the
4
+ * answer for list glides, fades, and marquees alike.
5
+ */
6
+ /**
7
+ * Whether the platform asked for reduced motion.
8
+ * A platform without the query (Node, a test DOM that implements no matcher)
9
+ * has expressed no preference, so motion stays allowed.
10
+ * @returns true when the reader prefers reduced motion.
11
+ */
12
+ export declare function prefersReducedMotion(): boolean;
13
+ //# sourceMappingURL=motion.d.ts.map
@@ -2,7 +2,15 @@
2
2
  /**
3
3
  * Resolve the top margin an overlay keeps from the viewport edge.
4
4
  * @param min - the overlay's own viewport margin in px, used as the floor.
5
- * @returns the larger of `min` and the frame clearance plus 20px; fullscreen keeps only the 20px gap.
5
+ * @param anchor - the overlay's anchor rect in viewport coordinates. While the
6
+ * anchor's top edge is visible, the margin never exceeds `anchor.bottom + gap`,
7
+ * so an overlay opened from a control inside the top strip stays next to it.
8
+ * @param gap - the distance the overlay keeps below `anchor`.
9
+ * @returns the larger of `min` and the frame clearance plus 20px (20px in
10
+ * fullscreen), capped by a visible `anchor`, but never below `min`.
6
11
  */
7
- export declare function overlayTopMargin(min: number): number;
12
+ export declare function overlayTopMargin(min: number, anchor?: {
13
+ readonly top: number;
14
+ readonly bottom: number;
15
+ }, gap?: number): number;
8
16
  //# sourceMappingURL=overlay-top-margin.d.ts.map
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Explicit Markdown blockquote recognition and its source token, shared by the
3
+ * user-text projection and any composer-side quote gesture.
4
+ *
5
+ * A quote is ordinary message text: consecutive lines that start with up to
6
+ * three spaces and one `>` marker, plus one optional trailing space or tab.
7
+ * Inline greater-than signs stay ordinary text. The first decoded line may name
8
+ * the message the quote came from as `[quoted](qilin-quote:<node-key>)`, which
9
+ * the projection consumes for a jump control and never shows as excerpt text.
10
+ * Line endings normalize to `\n` on decode, so a message sent from any editor
11
+ * prints the same excerpt.
12
+ *
13
+ * This module is the single home for both directions of that form: the
14
+ * projection's scanner and decoder, and the serializer a quote gesture hands
15
+ * the composer. Adding the composer's insertion seat needs no second copy.
16
+ */
17
+ /** One blockquote span in the original UTF-16 text, excluding its trailing line break. */
18
+ export interface MessageQuoteRange {
19
+ readonly start: number;
20
+ readonly end: number;
21
+ }
22
+ /** Scheme of the optional first line naming the quoted message's Chat node key. */
23
+ export declare const QUOTE_WIRE_SCHEME = "qilin-quote:";
24
+ /** One decoded quote block: its excerpt text and the source it names, when any. */
25
+ export interface QuotedExcerpt {
26
+ /** Excerpt shown and copied, without the source marker line. */
27
+ readonly text: string;
28
+ /** Chat node key the quote names, when its first line carries one. */
29
+ readonly source: string | undefined;
30
+ }
31
+ /**
32
+ * Remove one blockquote marker from each line and normalize line endings.
33
+ * @param text - consecutive blockquote lines, with up to three leading spaces.
34
+ * @returns excerpt text with original line endings normalized.
35
+ */
36
+ export declare function quoteTextOf(text: string): string;
37
+ /**
38
+ * Recognize consecutive explicitly marked Markdown blockquote lines.
39
+ * @param text - complete original message text.
40
+ * @returns ordered nonblank quote spans; inline greater-than signs remain ordinary text.
41
+ */
42
+ export declare function messageQuoteRanges(text: string): readonly MessageQuoteRange[];
43
+ /**
44
+ * Split one decoded quote block into the source it names and its excerpt.
45
+ * @param decoded - block text already stripped of its quote markers.
46
+ * @returns the excerpt shown to the reader and the named source, if present.
47
+ */
48
+ export declare function quotedExcerptOf(decoded: string): QuotedExcerpt;
49
+ /**
50
+ * Serialize one excerpt as the blockquote text a quote gesture inserts.
51
+ * @param text - excerpt to quote, as selected.
52
+ * @param source - Chat node key of the quoted message, when the caller knows it.
53
+ * @returns blockquote lines, one per excerpt line, with the source marker first.
54
+ */
55
+ export declare function quoteBlockTextOf(text: string, source?: string): string;
56
+ /** One quoted excerpt as a selection gesture hands it over. */
57
+ export interface QuoteFragment {
58
+ /** Excerpt text as selected, without quote markers. */
59
+ readonly text: string;
60
+ /** Chat node key of the quoted message, when the caller knows it. */
61
+ readonly source?: string | undefined;
62
+ }
63
+ /**
64
+ * Serialize one selected excerpt into the message text a quote gesture inserts.
65
+ * @param fragment - excerpt text and the source it names, if any.
66
+ * @returns blockquote lines the composer receives as ordinary draft text.
67
+ */
68
+ export declare function quoteInsertionOf(fragment: QuoteFragment): string;
69
+ //# sourceMappingURL=quote-text.d.ts.map
@@ -0,0 +1,14 @@
1
+ /** Clipboard action and its temporary success state. */
2
+ export interface ClipboardCopyState {
3
+ /** True for one second after an accepted clipboard write. */
4
+ readonly copied: boolean;
5
+ /** Write the current text unless a write or success indication is already active. */
6
+ readonly onCopy: () => void;
7
+ }
8
+ /**
9
+ * Keep clipboard feedback local to the mounted control and release its reset timer on unmount.
10
+ * @param text - Exact text to copy when the action is invoked.
11
+ * @returns Success feedback and a callback stable while text and feedback are unchanged.
12
+ */
13
+ export declare function useClipboardCopy(text: string): ClipboardCopyState;
14
+ //# sourceMappingURL=use-clipboard-copy.d.ts.map
@@ -1,36 +1,49 @@
1
1
  /**
2
- * Keep a fixed-position floating element anchored to a trigger.
2
+ * Synchronous placement for body-portaled panels fixed to the visual viewport.
3
3
  *
4
- * A portaled panel is positioned from its anchor's viewport rect, which stops
5
- * being true the moment anything scrolls or the window resizes. This owns that
6
- * one concern: measure the anchor, offset the panel below or above it, clamp
7
- * the result inside the viewport, and re-run on scroll (capture phase, so
8
- * scrollers nested inside the page are caught too), on resize, and on the
9
- * panel's own size changes while the element is open.
4
+ * A panel is placed from its anchor's current client rect, and that fact stops
5
+ * being true on scroll, resize, browser zoom, the on-screen keyboard, or an
6
+ * ancestor transform. This owns the whole placement concern: it measures the
7
+ * anchor, keeps the panel inside the visible viewport, publishes the panel's
8
+ * size limits as CSS variables, and tracks the anchor while the panel is open.
9
+ * The placement runs in a layout effect before paint, so the first open frame
10
+ * already carries the final coordinates.
10
11
  * @module @qilin-agent/client-ui-primitives/useAnchoredPosition
11
12
  */
12
13
  import { type CSSProperties, type RefObject } from 'react';
13
14
  /** Inputs for {@link useAnchoredPosition}. */
14
15
  export interface AnchoredPositionOptions {
15
- /** Whether the floating element is mounted and should track its anchor. */
16
+ /** Whether the panel is mounted and should track its anchor. */
16
17
  open: boolean;
17
- /** The element the panel is placed from. */
18
+ /** Element whose viewport rect anchors the panel. */
18
19
  anchorRef: RefObject<HTMLElement | null>;
19
- /** The floating element, measured so the clamp uses real dimensions. */
20
+ /** Body-portaled, fixed-position panel; its ancestors must not establish a containing block. */
20
21
  panelRef: RefObject<HTMLElement | null>;
21
- /** Which anchor edge the panel hangs from: below it (`bottom`, the default) or above it (`top`). */
22
- side?: 'top' | 'bottom';
23
- /** Which anchor edge the panel lines up with: its left edge (`start`, the default) or its right edge (`end`). */
22
+ /** Preferred anchor edge; right-side panels align their top edges. */
23
+ side?: 'top' | 'bottom' | 'right';
24
+ /** Horizontal alignment for panels above or below the anchor. */
24
25
  align?: 'start' | 'end';
25
- /** Distance kept between the anchor edge named by `side` and the panel. */
26
+ /** Distance from the preferred anchor edge in CSS pixels. */
26
27
  gap: number;
27
- /** Distance kept between the panel and each viewport edge. */
28
+ /** Minimum viewport inset; desktop frame clearance can increase the top inset. */
28
29
  margin: number;
30
+ /** Reserve desktop frame clearance in addition to the margin; false keeps only the viewport margin. Defaults to true. */
31
+ frameClearance?: boolean;
32
+ /** Host-supplied client rect; null hides the panel until the anchor is available. */
33
+ getAnchorRect?: () => DOMRect | null;
34
+ /** Re-measure a committed content change before paint. */
35
+ layoutKey?: string | number;
36
+ /** Minimum panel width follows the anchor, bounded by the visible viewport. */
37
+ minWidthFromAnchor?: boolean;
29
38
  }
30
39
  /**
31
- * Track an anchor and return the panel's fixed coordinates.
32
- * @param options - the open state, the two refs, the placement side and alignment, and the gap/margin distances.
33
- * @returns `left`/`top` for the panel, or `null` before the first measurement.
40
+ * Keep a fixed panel aligned and constrained to the visible viewport.
41
+ * Panel CSS consumes `--qilin-anchored-max-width` and
42
+ * `--qilin-anchored-max-height` for its size limits; the legacy
43
+ * `--dsh-anchored-*` names are mirrored while open, so sheets that still read
44
+ * the earlier contract keep their caps.
45
+ * @param options - Open state, element refs, placement, and optional host rect or content change key.
46
+ * @returns Fixed left/top coordinates, or null while closed or awaiting an anchor; the first measurement completes before paint.
34
47
  */
35
48
  export declare function useAnchoredPosition(options: AnchoredPositionOptions): CSSProperties | null;
36
49
  //# sourceMappingURL=useAnchoredPosition.d.ts.map
@@ -0,0 +1,20 @@
1
+ import type { DragEvent } from 'react';
2
+ /**
3
+ * Own a row's inert drag preview until drag end, replacement, or unmount.
4
+ * The offscreen copy keeps WebKit snapshots separate from overlapping panels.
5
+ * The wrapper copies only explicitly requested computed custom properties.
6
+ * Its cloned slot DOM remains in document.body for that lifetime; query live rows
7
+ * within their list to exclude duplicate text and element IDs.
8
+ * @param className - preview background matching the row's surface; WebKit fills
9
+ * transparent snapshot pixels white, including rounded corners.
10
+ * @param inheritedProperties - custom-property names whose computed values the preview needs.
11
+ * Omit to copy none. Keep the list reference stable; replacing it clears the active preview.
12
+ * @returns preview callbacks; cleanup reports whether this row started a drag,
13
+ * even when its preview failed or the row became non-draggable before drag end.
14
+ * Start accepts the source row when a child handle owns the drag event.
15
+ */
16
+ export declare function useRowDragPreview(className: string, inheritedProperties?: readonly `--${string}`[]): {
17
+ start: (event: DragEvent<HTMLElement>, row?: HTMLElement) => void;
18
+ clear: () => boolean;
19
+ };
20
+ //# sourceMappingURL=useRowDragPreview.d.ts.map
@@ -16,14 +16,35 @@
16
16
  * always draws masked, ahead of every reference decoration, and only the display
17
17
  * changes: the logged text, the copied text, and the model input keep the original
18
18
  * characters.
19
+ *
20
+ * Blockquote spans carry one more decoration: with quote presentation enabled, a
21
+ * consecutive `> ` run renders as one quote surface, and its own inline
22
+ * references and credentials stay literal excerpt text. Disabled, the same
23
+ * characters stay plain, so a caller that offers no quotes loses nothing.
19
24
  */
20
- import type { ReactNode } from 'react';
25
+ import type { HTMLAttributes, ReactNode } from 'react';
26
+ import type { QuotePreviewLabels } from './QuotePreview.tsx';
27
+ /** Quote presentation a caller offers for blockquoted text. */
28
+ export interface UserTextQuotes {
29
+ /** Localized copy and jump names for each excerpt surface. */
30
+ readonly labels: QuotePreviewLabels;
31
+ /** Reveal one quoted source by its Chat node key; omission hides jump controls. */
32
+ readonly onJump?: ((source: string) => void) | undefined;
33
+ }
21
34
  /** Optional navigation supplied by consumers that can preview references. */
22
35
  export interface UserTextReferences {
23
36
  /** Open a file path decoded from an `@` mention. */
24
37
  openFile: (path: string) => void;
25
38
  /** Open the source of a skill loaded for this message. */
26
39
  openSkill: (name: string) => void;
40
+ /**
41
+ * Gesture bindings for one reference chip's object menu; absent leaves the
42
+ * chip's native behavior and adds no menu trigger.
43
+ */
44
+ contextMenuProps?: (reference: {
45
+ readonly kind: 'file' | 'folder' | 'session' | 'skill' | 'command' | undefined;
46
+ readonly label: string;
47
+ }) => Pick<HTMLAttributes<HTMLElement>, 'onContextMenu' | 'onKeyDown' | 'tabIndex'> | undefined;
27
48
  }
28
49
  /** Accessible names of the control that reveals one masked credential. */
29
50
  export interface UserTextSecretLabels {
@@ -42,7 +63,8 @@ export interface UserTextSecretLabels {
42
63
  * @param slashKind - the chip kind those tokens render as.
43
64
  * @param references - optional file and skill preview actions; session and command tokens stay labels.
44
65
  * @param secretLabels - reveal-control copy for credentials; absent in text that stays masked.
66
+ * @param quotes - quote presentation for blockquoted spans; absent renders those characters as plain text.
45
67
  * @returns inline nodes covering the whole text.
46
68
  */
47
- export declare function projectUserText(text: string, sessionLabels: readonly string[], slashNames?: readonly string[], slashKind?: 'skill' | 'command', references?: UserTextReferences, secretLabels?: UserTextSecretLabels): ReactNode;
69
+ export declare function projectUserText(text: string, sessionLabels: readonly string[], slashNames?: readonly string[], slashKind?: 'skill' | 'command', references?: UserTextReferences, secretLabels?: UserTextSecretLabels, quotes?: UserTextQuotes): ReactNode;
48
70
  //# sourceMappingURL=user-text.d.ts.map
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@qilin-agent/client-ui-primitives",
3
3
  "description": "Pure React atoms for the qilin web UI: controls, icons, markdown, and JSON inspectors (zero cordis)",
4
- "version": "3.1.7",
4
+ "version": "3.1.9",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -26,6 +26,10 @@
26
26
  },
27
27
  "license": "MIT",
28
28
  "devDependencies": {
29
+ "@qilin-agent/client-store": "3.1.9",
30
+ "@qilin-agent/kylin": "~4.0.7",
31
+ "@qilin-agent/util-code-language": "3.1.9",
32
+ "@qilin-agent/util-workspace-path": "3.1.9",
29
33
  "@shikijs/langs": "^4.3.1",
30
34
  "@types/mdast": "^4.0.4",
31
35
  "@types/react": "~18.3.1",
@@ -44,16 +48,14 @@
44
48
  "micromark-factory-space": "^2.0.1",
45
49
  "micromark-util-character": "^2.1.1",
46
50
  "micromark-util-classify-character": "^2.0.1",
51
+ "micromark-util-decode-string": "^2.0.1",
47
52
  "micromark-util-sanitize-uri": "^2.0.1",
48
53
  "micromark-util-symbol": "^2.0.1",
49
54
  "micromark-util-types": "^2.0.2",
50
55
  "react": "^18.2.0",
51
56
  "react-dom": "^18.2.0",
52
57
  "shiki": "^4.3.1",
53
- "simple-icons": "16.31.0",
54
- "@qilin-agent/kylin": "~4.0.5",
55
- "@qilin-agent/util-workspace-path": "3.1.7",
56
- "@qilin-agent/client-store": "3.1.7"
58
+ "simple-icons": "16.31.0"
57
59
  },
58
60
  "files": [
59
61
  "lib/index.js",
@@ -61,6 +63,6 @@
61
63
  "lib/types/**/*.d.ts"
62
64
  ],
63
65
  "peerDependencies": {
64
- "@qilin-agent/kylin": "~4.0.5"
66
+ "@qilin-agent/kylin": "~4.0.7"
65
67
  }
66
68
  }
@@ -1,14 +0,0 @@
1
- /** The copy-feedback hook's return: the transient flag and the copy handler. */
2
- export interface CopyFeedback {
3
- /** True for {@link COPIED_FEEDBACK_MS} after a successful write; render the success label off it. */
4
- copied: boolean;
5
- /** Copy the hook's text; no-op while `copied` is still true, silent on a refused write. */
6
- onCopy: () => void;
7
- }
8
- /**
9
- * Copy `text` to the clipboard with one-second success feedback.
10
- * @param text - the text to write on copy.
11
- * @returns the `copied` flag and the `onCopy` handler.
12
- */
13
- export declare function useCopyFeedback(text: string): CopyFeedback;
14
- //# sourceMappingURL=use-copy-feedback.d.ts.map