inline-chat-kit 0.49.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 (62) hide show
  1. package/CHANGELOG.md +2111 -0
  2. package/LICENSE +21 -0
  3. package/README.md +1430 -0
  4. package/dist/AnswerActions/AnswerActions.d.ts +35 -0
  5. package/dist/Approval/Approval.d.ts +42 -0
  6. package/dist/Artifact/ArtifactCard.d.ts +45 -0
  7. package/dist/Artifact/ArtifactPane.d.ts +50 -0
  8. package/dist/Artifact/ChatLayout.d.ts +35 -0
  9. package/dist/Artifact/useArtifacts.d.ts +21 -0
  10. package/dist/Attachments/Attachments.d.ts +50 -0
  11. package/dist/Branch/Branch.d.ts +28 -0
  12. package/dist/Button/Button.d.ts +23 -0
  13. package/dist/ChainOfThought/ChainOfThought.d.ts +49 -0
  14. package/dist/ChatHeader/ChatHeader.d.ts +89 -0
  15. package/dist/ChatInput/AddCardsOverlay.d.ts +11 -0
  16. package/dist/ChatInput/ChatInput.d.ts +118 -0
  17. package/dist/ChatInput/HoverActionsRow.d.ts +13 -0
  18. package/dist/ChatInput/MorphGlyph.d.ts +15 -0
  19. package/dist/ChatTurnRow/ChatTurnRow.d.ts +115 -0
  20. package/dist/Chip/Chip.d.ts +7 -0
  21. package/dist/CodeBlock/CodeBlock.d.ts +24 -0
  22. package/dist/CodeBlock/grammars.d.ts +16 -0
  23. package/dist/CodeBlock/highlight.d.ts +38 -0
  24. package/dist/Context/Context.d.ts +36 -0
  25. package/dist/Conversation/Conversation.d.ts +64 -0
  26. package/dist/CustomCursor/CustomCursor.d.ts +1 -0
  27. package/dist/EmptyState/EmptyState.d.ts +24 -0
  28. package/dist/GlassButton/GlassButton.d.ts +19 -0
  29. package/dist/InlineCitation/InlineCitation.d.ts +30 -0
  30. package/dist/Loader/Loader.d.ts +23 -0
  31. package/dist/QuestionCard/QuestionCard.d.ts +32 -0
  32. package/dist/QuestionCard/parts.d.ts +84 -0
  33. package/dist/QuestionCard/types.d.ts +54 -0
  34. package/dist/QuestionGroup/QuestionGroup.d.ts +87 -0
  35. package/dist/Reasoning/Reasoning.d.ts +37 -0
  36. package/dist/ReplyThreadPopup/ReplyThreadPopup.d.ts +18 -0
  37. package/dist/Sources/Sources.d.ts +46 -0
  38. package/dist/SystemMessage/SystemMessage.d.ts +39 -0
  39. package/dist/TaskList/TaskList.d.ts +42 -0
  40. package/dist/TextHighlighter/TextHighlighter.d.ts +17 -0
  41. package/dist/Tool/Tool.d.ts +41 -0
  42. package/dist/announce/announce.d.ts +27 -0
  43. package/dist/disclosure/DisclosureBody.d.ts +22 -0
  44. package/dist/disclosure/DisclosureHeader.d.ts +42 -0
  45. package/dist/disclosure/useDisclosure.d.ts +30 -0
  46. package/dist/duration/formatDuration.d.ts +9 -0
  47. package/dist/grammars-B19jp7qm.js +3181 -0
  48. package/dist/grammars-B19jp7qm.js.map +1 -0
  49. package/dist/index.d.ts +80 -0
  50. package/dist/inline-chat-kit.css +2 -0
  51. package/dist/inline-chat-kit.js +5314 -0
  52. package/dist/inline-chat-kit.js.map +1 -0
  53. package/dist/markdown/parse.d.ts +105 -0
  54. package/dist/markdown/parseMarkdown.d.ts +47 -0
  55. package/dist/radiusCorrection/useCorrectedRadius.d.ts +24 -0
  56. package/dist/reducedMotion/reducedMotion.d.ts +3 -0
  57. package/dist/stateGlyph/StateGlyph.d.ts +23 -0
  58. package/dist/turnParts/turnParts.d.ts +156 -0
  59. package/dist/useChatTurns/useChatTurns.d.ts +127 -0
  60. package/dist/voice/useVoiceInput.d.ts +79 -0
  61. package/package.json +95 -0
  62. package/theming.md +234 -0
@@ -0,0 +1,87 @@
1
+ import { ReactNode } from 'react';
2
+ import { Answer, Question } from '../QuestionCard/types';
3
+ /** Below this, folding saves less room than the summary row costs. */
4
+ export declare const FOLDABLE_FROM = 3;
5
+ /**
6
+ * What the fold is made of, in numbers.
7
+ *
8
+ * Springs for anything that moves, tweens for opacity alone.
9
+ *
10
+ * That split is not a preference. A spring is a description of *travel* —
11
+ * where a thing is going and how it arrives — and opacity has nowhere to
12
+ * travel: it is bounded at 0 and 1, so a spring with any bounce in it
13
+ * overshoots into a clamp and spends the overshoot sitting still. Position and
14
+ * size have no such ceiling, which is exactly why they are worth springing.
15
+ *
16
+ * `visualDuration` rather than stiffness and damping, for the same reason
17
+ * throughout: the two of them describe one spring between them without either
18
+ * answering "how long is this", which is the only question anybody tuning it
19
+ * is asking. Motion solves the spring for the duration you name.
20
+ */
21
+ export interface FoldMotion {
22
+ /** How long the ground looks like it takes to resize, in seconds. */
23
+ visualDuration: number;
24
+ /** The ground's overshoot, 0–1. */
25
+ bounce: number;
26
+ /** How long a row takes to arrive, in seconds. */
27
+ rowDuration: number;
28
+ /** A row's overshoot, 0–1. Enough to read as arriving, not as bouncing. */
29
+ rowBounce: number;
30
+ /** How far above its place a row starts, in pixels. */
31
+ rowOffset: number;
32
+ /** Between one row and the next, in seconds. */
33
+ stagger: number;
34
+ /** A row's fade, in seconds. Tween: see the note above. */
35
+ fadeIn: number;
36
+ /** And the fade of one leaving. */
37
+ fadeOut: number;
38
+ /**
39
+ * How long the arriving body waits, in seconds.
40
+ *
41
+ * Not a stylistic pause — it is what stops the two bodies from being in the
42
+ * same pixels at once. They are anchored to the same top edge, so the folded
43
+ * row and the first card always want that same band; crossfading them put
44
+ * two different sentences on top of each other at half opacity, which is
45
+ * mush rather than motion. At `fadeOut` the one is gone before the other
46
+ * starts, and the box growing underneath is what carries the eye across.
47
+ */
48
+ fadeInDelay: number;
49
+ }
50
+ export declare const defaultFoldMotion: FoldMotion;
51
+ export interface QuestionGroupProps {
52
+ /** Distinguishes one group's layout animations from another's. */
53
+ id: string;
54
+ /**
55
+ * What this step is about, at the top of the group.
56
+ *
57
+ * Also the fold control when there is one — which is the point of putting it
58
+ * there. A control at the top does not move when the thing under it opens,
59
+ * so folding stops being two shapes swapping places and becomes a body
60
+ * changing under a header that stays put.
61
+ *
62
+ * Left out on a foldable group, the count stands in, so the control still
63
+ * has a name.
64
+ */
65
+ title?: ReactNode;
66
+ questions: Question[];
67
+ answers: Record<string, Answer | undefined>;
68
+ /** Index of the question being answered, or `null` when none is. */
69
+ activeIndex?: number | null;
70
+ /** Offer to fold the whole group into one row. */
71
+ collapsible?: boolean;
72
+ onCommit?: (questionId: string, answer: Answer) => void;
73
+ onEdit?: (index: number) => void;
74
+ readOnly?: boolean;
75
+ labels?: Partial<Record<"answers", string>>;
76
+ /** Overrides for the fold's timing. See `defaultFoldMotion`. */
77
+ foldMotion?: Partial<FoldMotion>;
78
+ className?: string;
79
+ }
80
+ /**
81
+ * One step of a questionnaire: the surface holding its questions.
82
+ *
83
+ * Once the conversation has moved past it, it folds down to a single row.
84
+ * Not a peek at the list — a peek costs more height than the answers it shows,
85
+ * and a conversation with four half-open steps above it is unreadable.
86
+ */
87
+ export declare function QuestionGroup({ id, title, questions, answers, activeIndex, collapsible, onCommit, onEdit, readOnly, labels, foldMotion, className, }: QuestionGroupProps): import("react").JSX.Element;
@@ -0,0 +1,37 @@
1
+ import { HTMLAttributes, ReactNode } from 'react';
2
+ /** Still working it out, or finished. */
3
+ export type ReasoningState = "thinking" | "done";
4
+ type Labels = Record<"thinking" | "thought" | "thoughtFor", string>;
5
+ export interface ReasoningProps extends Omit<HTMLAttributes<HTMLDivElement>, "title"> {
6
+ /** The thinking itself. Prose, not structure — see `Tool` for that. */
7
+ children: ReactNode;
8
+ state?: ReasoningState;
9
+ /**
10
+ * How long it took, in ms.
11
+ *
12
+ * Left out, the block times itself: it starts a clock when it begins
13
+ * thinking and reads it when it stops. Pass one when you already know —
14
+ * replaying a transcript, where the thinking did not happen just now.
15
+ */
16
+ duration?: number;
17
+ /** Controlled. Leave it out and the block looks after itself. */
18
+ open?: boolean;
19
+ /** Where it starts, overruling the block's own preference. */
20
+ defaultOpen?: boolean;
21
+ onOpenChange?: (open: boolean) => void;
22
+ labels?: Partial<Labels>;
23
+ }
24
+ /**
25
+ * What the model worked through before it answered.
26
+ *
27
+ * **Open while it thinks, folded away once the answer starts.** That is the
28
+ * one detail every kit that ships this has converged on, and it is right:
29
+ * thinking is worth watching while it is happening and worth almost nothing
30
+ * afterwards — but it has to stay reachable, because the times it matters are
31
+ * exactly the times the answer looks wrong.
32
+ *
33
+ * Folding is the block's *preference*, not something done to the reader. Open
34
+ * it and it stays open, however many times the state changes underneath.
35
+ */
36
+ export declare function Reasoning({ children, state, duration, open, defaultOpen, onOpenChange, labels, className, ...rest }: ReasoningProps): import("react").JSX.Element;
37
+ export {};
@@ -0,0 +1,18 @@
1
+ import { ChatTurn, SendContext } from '../useChatTurns/useChatTurns';
2
+ /** @deprecated Use `ChatTurn`. Kept so existing imports keep resolving. */
3
+ export type Turn = ChatTurn;
4
+ export interface ReplyThreadPopupProps {
5
+ activeReply: {
6
+ text: string;
7
+ rect: DOMRect;
8
+ };
9
+ onClose: () => void;
10
+ onSave?: () => void;
11
+ /**
12
+ * Produce the reply to a message sent inside the thread. Receives the
13
+ * message and the passage the thread hangs off. Required — the popup has no
14
+ * answers of its own to fall back on.
15
+ */
16
+ onSendMessage: (message: string, quotedText: string, context: SendContext) => AsyncIterable<string> | Promise<string> | string;
17
+ }
18
+ export declare function ReplyThreadPopup({ activeReply, onClose, onSave, onSendMessage }: ReplyThreadPopupProps): import("react").JSX.Element;
@@ -0,0 +1,46 @@
1
+ import { HTMLAttributes, ReactNode } from 'react';
2
+ /** Where a claim came from. */
3
+ export interface Source {
4
+ id: string;
5
+ title: string;
6
+ /** Opened when the entry is a link. Without one it is a row, not a link. */
7
+ url?: string;
8
+ /** A domain, a filename, a page — whatever names where this is. */
9
+ origin?: string;
10
+ /** The passage in the source that carries the claim. */
11
+ quote?: string;
12
+ }
13
+ type Labels = {
14
+ title: string;
15
+ one: string;
16
+ many: string;
17
+ };
18
+ export interface SourcesProps extends Omit<HTMLAttributes<HTMLElement>, "title" | "onSelect"> {
19
+ sources: Source[];
20
+ /** Heads the list. Defaults to `labels.title`. */
21
+ title?: ReactNode;
22
+ /** Offer to fold the list to its own row. */
23
+ collapsible?: boolean;
24
+ /**
25
+ * The one just arrived at, from a citation in the text.
26
+ *
27
+ * Marked rather than scrolled to: the list is directly under the answer, and
28
+ * moving the page under somebody who clicked a marker in a sentence they
29
+ * were reading loses them the sentence.
30
+ */
31
+ activeId?: string | null;
32
+ onSelect?: (source: Source, index: number) => void;
33
+ open?: boolean;
34
+ defaultOpen?: boolean;
35
+ onOpenChange?: (open: boolean) => void;
36
+ labels?: Partial<Labels>;
37
+ }
38
+ /**
39
+ * What the answer was drawn from, numbered to match the markers in it.
40
+ *
41
+ * Open by default. Sources are the difference between an answer somebody can
42
+ * check and one they have to trust, and folding that away by default says the
43
+ * opposite of what a citation is for.
44
+ */
45
+ export declare function Sources({ sources, title, collapsible, activeId, onSelect, open, defaultOpen, onOpenChange, labels, className, ...rest }: SourcesProps): import("react").JSX.Element;
46
+ export {};
@@ -0,0 +1,39 @@
1
+ import { HTMLAttributes, ReactNode } from 'react';
2
+ /**
3
+ * Two, not three.
4
+ *
5
+ * `Context` settled this already: an amber in the middle makes somebody learn
6
+ * a scale to read a state they can already read in words. Something either
7
+ * went wrong or it did not.
8
+ */
9
+ export type SystemTone = "notice" | "danger";
10
+ export interface SystemMessageProps extends Omit<HTMLAttributes<HTMLDivElement>, "children"> {
11
+ children: ReactNode;
12
+ tone?: SystemTone;
13
+ }
14
+ /**
15
+ * The conversation itself saying something — not the reader, not the agent.
16
+ *
17
+ * "The oldest messages are dropping out of the window." "The model changed
18
+ * partway through this answer." "You went offline." Things that happened *to*
19
+ * the conversation, which until now had nowhere to be said: `Context` warns
20
+ * that the window is nearly full and then nothing speaks when it fills.
21
+ *
22
+ * **No glyph**, deliberately, and for the reason the approval's shield came
23
+ * off an hour before this was written. Every picture in this kit carries a
24
+ * state the words beside it also carry — queued, running, failed, allowed — so
25
+ * that a reader the picture does not reach loses nothing. An icon here would
26
+ * carry "something is being announced", next to a sentence announcing it. The
27
+ * tint says which of the two tones it is and the sentence says the rest, which
28
+ * is the only part anybody can act on.
29
+ *
30
+ * **No dismiss, and no action.** It is a line of the transcript rather than a
31
+ * toast: dismissing one would be editing what happened. A host that needs a
32
+ * button under it composes one, which costs them a line and costs this
33
+ * component a whole API.
34
+ *
35
+ * **No live region either.** The kit has one, shared and written to on a later
36
+ * tick — see `announce`. A second one says everything twice, which is the
37
+ * fault that region exists to have fixed.
38
+ */
39
+ export declare function SystemMessage({ children, tone, className, ...rest }: SystemMessageProps): import("react").JSX.Element;
@@ -0,0 +1,42 @@
1
+ import { HTMLAttributes, ReactNode } from 'react';
2
+ import { WorkState } from '../stateGlyph/StateGlyph';
3
+ /** The same four as a tool call. See `StateGlyph`. */
4
+ export type TaskState = WorkState;
5
+ export interface Task {
6
+ id: string;
7
+ label: ReactNode;
8
+ state?: TaskState;
9
+ /** A line under the label — what it found, or why it failed. */
10
+ detail?: ReactNode;
11
+ }
12
+ type Labels = Record<TaskState, string> & {
13
+ /** `{done}` and `{total}` are filled in. */
14
+ progress: string;
15
+ };
16
+ export interface TaskListProps extends Omit<HTMLAttributes<HTMLElement>, "title"> {
17
+ tasks: Task[];
18
+ /** Names the list, and gives it a row to fold into. */
19
+ title?: ReactNode;
20
+ /**
21
+ * Let the whole list fold away.
22
+ *
23
+ * It folds itself once every task is done — a plan is worth watching while
24
+ * it runs and worth little afterwards — and anybody reading it can overrule
25
+ * that, in either direction, for good.
26
+ */
27
+ collapsible?: boolean;
28
+ open?: boolean;
29
+ defaultOpen?: boolean;
30
+ onOpenChange?: (open: boolean) => void;
31
+ labels?: Partial<Labels>;
32
+ }
33
+ /**
34
+ * What the agent means to do, what it is doing, and what it has finished.
35
+ *
36
+ * The order never changes. A list that sorted itself as work progressed would
37
+ * move the line somebody is reading out from under them, and the sequence is
38
+ * half of what the list is saying — these steps, in this order. Only the
39
+ * glyphs change.
40
+ */
41
+ export declare function TaskList({ tasks, title, collapsible, open, defaultOpen, onOpenChange, labels, className, ...rest }: TaskListProps): import("react").JSX.Element;
42
+ export {};
@@ -0,0 +1,17 @@
1
+ import { default as React } from 'react';
2
+ import { Source } from '../Sources/Sources';
3
+ export interface TextHighlighterProps {
4
+ text: string;
5
+ selectionMode?: "marker" | "precise";
6
+ onHighlightComplete?: (highlightedText: string) => void;
7
+ onReplyInThread?: (text: string, rect: DOMRect) => void;
8
+ /**
9
+ * What `[^1]` in the text points at, in the order the answer cites them.
10
+ *
11
+ * Leave it out and a marker still draws — as a number with nothing behind
12
+ * it, which is what it is until the list arrives.
13
+ */
14
+ sources?: Source[];
15
+ onSelectSource?: (index: number, source?: Source) => void;
16
+ }
17
+ export declare function TextHighlighter({ text, selectionMode, onHighlightComplete, onReplyInThread, sources, onSelectSource, }: TextHighlighterProps): React.JSX.Element;
@@ -0,0 +1,41 @@
1
+ import { HTMLAttributes, ReactNode } from 'react';
2
+ import { WorkState } from '../stateGlyph/StateGlyph';
3
+ /** Queued, working, finished, or failed — the kit's four, shared with `TaskList`. */
4
+ export type ToolState = WorkState;
5
+ type Labels = Record<"input" | "output" | "error" | ToolState, string>;
6
+ export interface ToolProps extends Omit<HTMLAttributes<HTMLDivElement>, "title" | "onToggle"> {
7
+ /** What was called. Set in mono: it is an identifier, not prose. */
8
+ name: string;
9
+ state?: ToolState;
10
+ /** A sentence for what it did — "Searched the web", "Read 3 files". */
11
+ summary?: ReactNode;
12
+ /**
13
+ * What it was called with.
14
+ *
15
+ * A string is shown as text and an object as JSON. Anything you want drawn
16
+ * some other way, pass as an element and it is rendered untouched.
17
+ */
18
+ input?: unknown;
19
+ /** What came back. Read the same way as `input`. */
20
+ output?: unknown;
21
+ /** What went wrong. Drawn instead of the output, and opens the row. */
22
+ error?: ReactNode;
23
+ /** How long it took, in ms. Shown once it has finished. */
24
+ duration?: number;
25
+ /** Open, controlled. Leave out and the row keeps its own. */
26
+ open?: boolean;
27
+ /** Where it starts. Defaults to open only if it failed. */
28
+ defaultOpen?: boolean;
29
+ onOpenChange?: (open: boolean) => void;
30
+ labels?: Partial<Labels>;
31
+ }
32
+ /**
33
+ * One tool call: what was run, what with, what came back.
34
+ *
35
+ * Collapsed, because most of the time nobody cares — and open when it failed,
36
+ * because an error nobody can see has not been reported. The glyph says which
37
+ * state it is in and the row says so in words too; a status carried only by
38
+ * colour is a status half the people reading it do not have.
39
+ */
40
+ export declare function Tool({ name, state, summary, input, output, error, duration, open, defaultOpen, onOpenChange, labels, className, ...rest }: ToolProps): import("react").JSX.Element;
41
+ export {};
@@ -0,0 +1,27 @@
1
+ /**
2
+ * A shared live region, for telling a screen reader what just happened.
3
+ *
4
+ * Three things about live regions decide the shape of this file:
5
+ *
6
+ * 1. The region has to be in the document *before* the text goes into it.
7
+ * Adding a region and its content in the same commit is, to most screen
8
+ * readers, an ordinary DOM insertion — nothing is spoken. So the node is
9
+ * created first and written to on a later tick.
10
+ * 2. Writing the same string twice is not a change, and is not announced.
11
+ * Clearing first makes a repeated answer speak again.
12
+ * 3. One region per politeness, shared by every component on the page. Several
13
+ * regions competing is how announcements get dropped or interleaved.
14
+ *
15
+ * This is deliberately not a React component: it must not be something a
16
+ * consumer can forget to render.
17
+ */
18
+ export type Politeness = "polite" | "assertive";
19
+ /**
20
+ * Speak a message. Empty strings are ignored — clearing is not an announcement.
21
+ */
22
+ export declare function announce(message: string, politeness?: Politeness): void;
23
+ /**
24
+ * Drop the regions and anything queued. For tests, so one case cannot hear
25
+ * what a previous one said.
26
+ */
27
+ export declare function resetAnnouncer(): void;
@@ -0,0 +1,22 @@
1
+ import { ReactNode } from 'react';
2
+ export interface DisclosureBodyProps {
3
+ /** What the header's `aria-controls` points at. */
4
+ id: string;
5
+ open: boolean;
6
+ /** On the animated box, so a caller can pad or lay out what is revealed. */
7
+ className?: string;
8
+ children?: ReactNode;
9
+ }
10
+ /**
11
+ * What a `DisclosureHeader` opens, and how it opens.
12
+ *
13
+ * The outer box is **always rendered**, empty and flat when shut, so the
14
+ * header's `aria-controls` always points at something — a control that names
15
+ * an id nothing has is a control that says nothing.
16
+ *
17
+ * The reveal was ten lines of identical `motion` props in five components. The
18
+ * height animation is what makes growing read as a reveal rather than as
19
+ * content arriving before its container has caught up, and `overflow: hidden`
20
+ * on the moving box is what keeps it from spilling on the way.
21
+ */
22
+ export declare function DisclosureBody({ id, open, className, children }: DisclosureBodyProps): import("react").JSX.Element;
@@ -0,0 +1,42 @@
1
+ import { ReactNode } from 'react';
2
+ export interface DisclosureHeaderProps {
3
+ /**
4
+ * `band` is a full-width row — it has a right edge, so the meta and the
5
+ * chevron are pushed to it. `inline` is a label that hugs its own words,
6
+ * for a header that sits in the flow of an answer as an aside.
7
+ *
8
+ * The two are not a style choice. An inline header's chevron pushed to a
9
+ * right edge 500px away floats alone in white space with nothing beside it.
10
+ */
11
+ fit?: "band" | "inline";
12
+ open: boolean;
13
+ /** Leave it out and this is a heading rather than a control — no chevron,
14
+ no `aria-expanded`, nothing to press. */
15
+ onToggle?: () => void;
16
+ /** The id of the box it opens. Always present, so this always points at
17
+ something even while the box is empty. */
18
+ controls: string;
19
+ /** A control with nothing to open yet. Still a row worth reading. */
20
+ disabled?: boolean;
21
+ glyph?: ReactNode;
22
+ /** The control's name. */
23
+ label: ReactNode;
24
+ /** Shimmers the label: the kit's way of saying "still happening". */
25
+ pending?: boolean;
26
+ /** A duration, a count — pushed to the right end of a band. */
27
+ meta?: ReactNode;
28
+ /** Anything between the label and the meta. */
29
+ children?: ReactNode;
30
+ className?: string;
31
+ }
32
+ /**
33
+ * The row you click to open something.
34
+ *
35
+ * Five components had one of these and the markup was written out five times,
36
+ * with two pairs byte-for-byte identical and the others differing in ways
37
+ * nobody had decided on. The shimmer under the label was in two of them, with
38
+ * the same twenty lines of gradient in each.
39
+ *
40
+ * What it does not do is force one shape on all five — see `fit`.
41
+ */
42
+ export declare function DisclosureHeader({ fit, open, onToggle, controls, disabled, glyph, label, pending, meta, children, className, }: DisclosureHeaderProps): import("react").JSX.Element;
@@ -0,0 +1,30 @@
1
+ export interface DisclosureOptions {
2
+ /** Controlled. Leave it out and the row keeps its own. */
3
+ open?: boolean;
4
+ /** Where it starts, when the row's own preference is not what you want. */
5
+ defaultOpen?: boolean;
6
+ onOpenChange?: (open: boolean) => void;
7
+ /**
8
+ * What the row would do if nobody had an opinion.
9
+ *
10
+ * Read every render, so it follows the state: a tool call that fails later
11
+ * opens itself, a reasoning block folds away when the answer starts.
12
+ */
13
+ preferOpen: boolean;
14
+ }
15
+ /**
16
+ * Open or shut, decided by three sources in order of who gets the last word:
17
+ * the host if it is controlling the row, then whoever clicked it, then the
18
+ * row's own preference.
19
+ *
20
+ * That order is the whole point, and it is why this is not an effect. An
21
+ * effect forcing the row open on a state change would also reopen one that
22
+ * somebody had deliberately shut — showing a reader again what they have
23
+ * already dismissed is not help. Derived, a reader's decision simply outlives
24
+ * every state change after it, and a row nobody has touched still follows
25
+ * along.
26
+ */
27
+ export declare function useDisclosure({ open, defaultOpen, onOpenChange, preferOpen }: DisclosureOptions): {
28
+ isOpen: boolean;
29
+ toggle: () => void;
30
+ };
@@ -0,0 +1,9 @@
1
+ /**
2
+ * How long something took, in words somebody reads rather than in the number
3
+ * the clock handed over.
4
+ *
5
+ * `840ms` under a second, `1.2s` over it, whole seconds past ten. Nobody reads
6
+ * `1173ms`, and the tenth stops mattering once there are two digits in front
7
+ * of it.
8
+ */
9
+ export declare function formatDuration(ms: number): string;