@hachej/boring-ui-kit 0.4.26 → 0.4.27
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.
- package/README.md +15 -2
- package/dist/chat-dictation.d.ts +27 -0
- package/dist/chat-dictation.js +71 -0
- package/dist/chat-react-session.d.ts +170 -0
- package/dist/chat-react-session.js +367 -0
- package/dist/chat-react.d.ts +2 -0
- package/dist/chat-react.js +1 -0
- package/dist/chat-text.d.ts +9 -0
- package/dist/chat-text.js +4 -0
- package/dist/chat-voice-message.d.ts +65 -0
- package/dist/chat-voice-message.js +235 -0
- package/dist/chat-voice.d.ts +7 -0
- package/dist/chat-voice.js +4 -0
- package/dist/experience-catalog.d.ts +2 -2
- package/dist/experience-region-tree.d.ts +1 -1
- package/dist/markdown-editor-labels.d.ts +83 -0
- package/dist/markdown-editor-labels.js +75 -0
- package/dist/markdown-editor.d.ts +7 -1
- package/dist/markdown-editor.js +28 -18
- package/dist/markdown-extensions.d.ts +4 -2
- package/dist/markdown-extensions.js +2 -2
- package/dist/markdown-preserved.d.ts +5 -1
- package/dist/markdown-preserved.js +15 -4
- package/dist/markdown-proposal-diff.d.ts +3 -1
- package/dist/markdown-proposal-diff.js +6 -5
- package/dist/text-draft-controls.d.ts +5 -1
- package/dist/text-draft-controls.js +6 -5
- package/dist/text-draft-labels.d.ts +26 -0
- package/dist/text-draft-labels.js +47 -0
- package/dist/voice-attempt.d.ts +33 -0
- package/dist/voice-attempt.js +122 -0
- package/package.json +6 -2
- package/r/agent-target.json +4 -4
- package/r/app-shell.json +14 -14
- package/r/board-viewer.json +7 -6
- package/r/button.json +1 -1
- package/r/dock.json +2 -2
- package/r/documents.json +12 -12
- package/r/feedback-preview.json +1 -1
- package/r/feedback.json +7 -7
- package/r/file-tree.json +4 -4
- package/r/highlights.json +3 -3
- package/r/html-viewer.json +3 -3
- package/r/looks-viewer.json +3 -2
- package/r/maquette-viewer.json +1 -1
- package/r/markdown-editor.json +3 -3
- package/r/mockup-viewer.json +1 -1
- package/r/pi-ambient.json +9 -9
- package/r/pi-app-kinds.json +10 -10
- package/r/pi-app.json +13 -13
- package/r/pi-artifacts.json +2 -2
- package/r/pi-chat-approval.json +2 -2
- package/r/pi-chat-attachments.json +1 -1
- package/r/pi-chat-markdown.json +2 -2
- package/r/pi-chat-mentions.json +1 -1
- package/r/pi-chat-pickers.json +1 -1
- package/r/pi-chat-plugins.json +11 -11
- package/r/pi-chat-question.json +1 -1
- package/r/pi-chat-slash.json +2 -2
- package/r/pi-chat-subagent.json +1 -1
- package/r/pi-chat-voice.json +5 -5
- package/r/pi-chat.json +19 -19
- package/r/pi-workspace.json +4 -4
- package/r/placed-agent.json +4 -4
- package/r/provider-setup.json +1 -1
- package/r/registry.json +180 -168
- package/r/requests-board.json +1 -1
- package/r/shared-chat.json +9 -9
- package/r/task-list-viewer.json +2 -2
- package/r/utils.json +14 -2
- package/r/version-history.json +2 -2
- package/r/viewer-code.json +2 -2
- package/r/viewer-frame.json +7 -7
- package/r/viewer-html.json +4 -4
- package/r/viewer-image.json +2 -2
- package/r/viewer-markdown.json +5 -5
- package/r/viewer-pdf.json +3 -3
- package/r/viewers.json +6 -6
- package/r/windows.json +3 -3
package/README.md
CHANGED
|
@@ -38,7 +38,7 @@ import { MarkdownEditor } from '@hachej/boring-ui-kit/markdown-editor';
|
|
|
38
38
|
|
|
39
39
|
Install the pinned React, React DOM, Marked and Tiptap peers listed in this package's manifest when selecting this entry. They remain optional for headless consumers. The renderer borrows its controller; unmounting does not dispose it or close its resource client. The host owns selection, identity and teardown.
|
|
40
40
|
|
|
41
|
-
Formatting defaults to the native Tiptap selection bubble on desktop and phone. The header’s **Formatting** icon toggles the full bar; unpressed means selection-only formatting. It remembers the choice for the viewer session, stays visible on phones with a 48px target and hides in Markdown mode. The **…** menu keeps **Formatted** / **Markdown**. Pass `formattingToolbar="pinned"` for the full desktop row (on phones it docks above the keyboard while editing).
|
|
41
|
+
Formatting defaults to the native Tiptap selection bubble on desktop and phone. The header’s **Formatting** icon toggles the full bar; unpressed means selection-only formatting. It remembers the choice for the viewer session, stays visible on phones with a 48px target and hides in Markdown mode. The **…** menu keeps **Formatted** / **Markdown**. Pass `formattingToolbar="pinned"` for the full desktop row (on phones it docks above the keyboard while editing). `locale="fr"` translates everything the editor draws itself (status, Save, save and conflict alerts, toolbar and source-box accessible names, proposals, draft recovery); `labels={{ saved: 'Enregistré', sourceName: title => title }}` overrides single words of either set (`markdownEditorLabels.en` lists every key). Text the host supplies (`title`, `placeholder`, a save result's `reason`) is shown as given. Installed recipes supply real shadcn Base UI menu items through the optional `components.markdownMenu` binding. Keyboard shortcuts and Markdown input rules remain available. Rich formatting and Markdown source controls share controller state. Mounting, switching modes and projecting remote text never serialize the original buffer. Genuine rich edits serialize the changed block range through Tiptap; untouched source spans are retained verbatim.
|
|
42
42
|
|
|
43
43
|
Formatted opens documents with YAML front matter, raw HTML/comments, footnotes, reference links, CRLF and style variants (`*` bullets, four-space nesting, underscore emphasis and setext headings). Front matter is an editable literal code block. HTML and definitions are quiet raw blocks; inline HTML and references are inert source atoms, never executable HTML. A save without editing preserves the original bytes. Paragraph/heading/list-item source metadata also keeps unchanged spelling through edits and native undo. Edited content follows its existing heading style and document markers where valid; intraword emphasis uses stars when underscores would change its meaning. CRLF is edited as LF and restored on write. The existing legacy ` `-only paragraph cleanup remains.
|
|
44
44
|
|
|
@@ -84,6 +84,10 @@ Where the page has Web Audio, the recording is a 16 kHz mono 16-bit WAV (`audio/
|
|
|
84
84
|
|
|
85
85
|
`maxMs` caps captured audio at five minutes by default. Reaching the cap stops the microphone; callers still choose when to consume the recording through `stop()`. Transcription and persistence remain host capabilities. The existing `@hachej/boring-feedback/page` capture API re-exports this implementation unchanged. The headless root imports neither entry.
|
|
86
86
|
|
|
87
|
+
`@hachej/boring-ui-kit/chat-react/voice` adds the optional React voice orchestration without adding microphone capture to core `chat-react` imports: `useVoiceAttempt<Data, Phase>()` owns attempt identities, abort signals, microphone cleanup and sample-based elapsed time, input levels and pause/resume. `start(data, maxMs?)` begins from the gesture; `stop(attempt)` retains the recording; `remove(attempt)` cancels only that attempt. `useDictation({ transcribe, onText, labels, maxMs?, target? })` trims transcription and delivers it only while the attempt still belongs to the selected target. Cancel, finish during permission, target replacement and unmount fence late results. Supply the eight voice labels (`DictationLabels`); the registry adapter supplies its English/French table. `voiceRefusalLabel` in `chat-text` is the one refusal-to-label-key map.
|
|
88
|
+
|
|
89
|
+
`useVoiceMessage({ controller, config, attachments?, labels, admitted?, activeController? })` uses that same capture lifecycle, uploads and transcribes in parallel on release, and submits one native `followUp` with the audio mention and transcript. It preserves typed drafts, successful upload/transcription halves and exact native draft/request identity for uncertain admission; native reconciliation, not a new voice retry, resolves unknown sends. `useChatVoice({ controller, voice, attachments?, labels, onDone })` selects text-only draft/auto or retained message mode for a shell, including native send, draft restore and completion callbacks. Both lower-level hooks accept the capture's `maxMs` semantics: five minutes by default, nonpositive/nonfinite disables the cap, and the cap stops the microphone without automatically delivering text or sending. These are browser preparation hooks over the existing native controller, not Pi execution extensions. Evidence: `test/packages/ui-chat-react-voice.test.mjs`.
|
|
90
|
+
|
|
87
91
|
The [registry voice-message guide](../../registry/README.md#voice-messages-in-pi-chat) describes PiChat's optional tap-to-record composition. Public capture behavior runs in `test/packages/ui.test.mjs`, compatibility in `test/packages/feedback-voice.test.mjs`, and dependency-free installation in `test/scaffold/isolated-ui.test.mjs`.
|
|
88
92
|
|
|
89
93
|
## Document marks
|
|
@@ -158,6 +162,15 @@ A submit the host refused before admission (status 402 `submission-refused`, for
|
|
|
158
162
|
|
|
159
163
|
`@hachej/boring-ui-kit/chat-react` is opt-in React presentation over the existing `ChatController.subscribe/getSnapshot` seam, never a Pi executor or a second store. `useArtifactFollow` takes the controller, conversation ID, active descriptor/follow selection and host opening/guard callbacks. It returns versions, `open`, `isOpen`, `select`, `recordClosed` and viewer `onShown`/`onFailed` callbacks. Initial artifacts do not auto-open; new ones may when `autoOpen` is enabled (default false), unless the host reports a narrow screen, unsaved work or a close in this turn. Call `recordClosed` only after the host's leave guard accepts. A present is acknowledged with only the revision the viewer reports after it loads and mounts; unmount reports use `hidden: true`, failures refuse, and absent reports stay unconfirmed. Controller/conversation changes fence old reports and retries. The host retains its controller and execution lifetime. Evidence: `test/packages/ui-chat-react.test.mjs`, `test/packages/ui-present-ack.test.mjs`.
|
|
160
164
|
|
|
165
|
+
`useChatSession<Content>({ controller, activeController, mode, labels, ...features })` is the headless session. `labels` is the small `ChatSessionLabels` contract (no built-in locale or UI); renderers use generic `Content`, with optional tool-name renderers before the catch-all. The hook returns `state`, `derived`, active/archived `rows`, `files`, `queued`, `queueActions`, connection/loading/working/waiting/send readiness, errors and actions. Optional `artifacts.recognise` enables artifact derivation; the registry enables it only when its card is installed. Files are observed conversation references opened by host callbacks, not a filesystem or a new file store.
|
|
166
|
+
|
|
167
|
+
- `send()` uses native `followUp`; `putBack(text, images)` preserves later typing. Optional `feedback` attaches notes with exact native restore content. The existing `mentions.kinds` rules are preserved: native sends keep app tokens, refused drafts restore words/images without app chips, and app cards are excluded from observed files. Unknown admission stays blocked for native reconcile/retry; the hook never creates a retry identity.
|
|
168
|
+
- `queueActions.edit/steer` withdraw first and refuse already-placed messages; steer preserves the current draft. `upload(files)` uses host `attachments.upload` (paths become mentions; images native attachments), legacy `onFiles` or host-owned `onPickFiles`. `pending`, `picked`, `pickMention`, `dismissUpload` drive presentation. `pasteText({ text, start, end })` uploads Markdown/text and supplies a pending cancel action that restores the captured paste without deleting intervening edits. Paste coordinates address editable text, retaining hidden app tokens and the existing leading `slash.skills` order on cancel; the composer decides paste thresholds through `chat-text`.
|
|
169
|
+
- `currentModel`, `currentEffort`, `changing`, `changeError`, `changeModel`, `changeEffort` and `dismissChangeError` drive optional host pickers; accepted choices stay optimistic until the native watch agrees. `activateMention` checks the current command catalog and connection; host authorization is still separate.
|
|
170
|
+
- **Ownership:** mount one session per controller (key/remount on replacement, as the registry does), keep `activeController.current` bound to the host's current controller, and supply stable host configs where possible. The hook connects on mount, detaches its subscription and aborts only its own uploads on unmount. It never disposes the borrowed controller, stops Pi work or owns a Harness. `afterSend` and `onFocus` are event callbacks; DOM refs, JSX, pickers and composer slots stay in the registry. Nothing is exported by the files/Pi/agent-free root.
|
|
171
|
+
|
|
172
|
+
Public-output/StrictMode evidence: `test/packages/ui-chat-session.test.mjs`. `pi-chat/session.useChatSession` retains its presentation adapter API and old contract type imports for one release. `ChatConnectionNotice` and `ChatStatusLine` in `pi-chat/notice` are reusable presentation shared by PiChat and Ambient; Ambient's host connection-error renderer now survives both full and compact notices. `test/ui/ambient-position.test.mjs` drives the public Ambient component and status matrix.
|
|
173
|
+
|
|
161
174
|
The same entry exports the shared chat hooks (not from the headless root):
|
|
162
175
|
|
|
163
176
|
- `useChatView(controller, select)` selects the native `view` (possibly undefined) through the existing `subscribe/getSnapshot`; selection is memoized by view and selector identity. It borrows the controller and introduces no store. Keep a selector stable to avoid repeated derivation.
|
|
@@ -167,7 +180,7 @@ The same entry exports the shared chat hooks (not from the headless root):
|
|
|
167
180
|
- `useControllerPerConversation({ key, create, connect?, dispose?, retryMs? })` is shared selected-adapter ownership, not a Pi lifecycle. Use a stable key including source/owner/options; undefined acquires nothing. It returns connecting/offline/ready and preserves the factory's concrete controller type. It disposes only what the factory creates (including late results and StrictMode mounts), awaits async teardown on retry, and releases retry timers on selection change. A custom `dispose` releases any additional owned transport; teardown errors are reported to the console. It never aborts native work or closes a borrowed source/Harness. `useSharedChat` uses it without changing its existing connect-on-session-mount behavior.
|
|
168
181
|
- `useConversationsClient({ endpoint, fetch, activeId, onSelect, onChange?, pollMs? })` (alias `useConversations`) returns the headless list and reload/search/create/rename/archive/delete/fork callbacks once the first list arrives. Polling defaults to four seconds; fetch and selection callbacks remain host-owned. Registry `ConversationsConfig` adds presentation-only row actions.
|
|
169
182
|
|
|
170
|
-
Public hook/StrictMode evidence: `test/packages/ui-chat-react-hooks.test.mjs`. Existing `test/contracts/chat-placement*.test.mjs` and `test/ui/pi-chat-draft.test.mjs` also drive the compatibility entry points. Browser scroll and installed consumers are separate gates
|
|
183
|
+
Public hook/StrictMode evidence: `test/packages/ui-chat-react-hooks.test.mjs`. Existing `test/contracts/chat-placement*.test.mjs` and `test/ui/pi-chat-draft.test.mjs` also drive the compatibility entry points. Browser scroll and installed consumers are separate gates.
|
|
171
184
|
|
|
172
185
|
|
|
173
186
|
### Native conversation history
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { voiceRefusalLabel } from './chat-text.js';
|
|
2
|
+
export type DictationPhase = 'idle' | 'starting' | 'recording' | 'transcribing';
|
|
3
|
+
export interface Dictation {
|
|
4
|
+
readonly phase: DictationPhase;
|
|
5
|
+
readonly seconds: number;
|
|
6
|
+
readonly levels: readonly number[];
|
|
7
|
+
readonly paused: boolean;
|
|
8
|
+
readonly pause: (() => void) | undefined;
|
|
9
|
+
readonly resume: (() => void) | undefined;
|
|
10
|
+
readonly problem: string | undefined;
|
|
11
|
+
readonly start: () => void;
|
|
12
|
+
readonly finish: () => void;
|
|
13
|
+
readonly cancel: () => void;
|
|
14
|
+
}
|
|
15
|
+
export type DictationLabels = Readonly<Record<typeof voiceRefusalLabel[keyof typeof voiceRefusalLabel] | 'voiceNothingHeard' | 'voiceTranscribeFailed', string>>;
|
|
16
|
+
export interface DictationOptions {
|
|
17
|
+
readonly transcribe: ((audio: Blob, signal: AbortSignal) => Promise<string>) | undefined;
|
|
18
|
+
readonly onText: (text: string, signal: AbortSignal) => void | Promise<void>;
|
|
19
|
+
readonly labels: DictationLabels;
|
|
20
|
+
/** Capture stops at this limit; transcription still waits for finish (default five minutes). */
|
|
21
|
+
readonly maxMs?: number | undefined;
|
|
22
|
+
/** Changing the target drops pending permission/transcription instead of inserting into the new subject. */
|
|
23
|
+
readonly target?: unknown;
|
|
24
|
+
}
|
|
25
|
+
/** Gesture-started dictation over the shared attempt lifecycle; host callbacks and labels are captured per attempt. */
|
|
26
|
+
export declare function useDictation(options: DictationOptions): Dictation;
|
|
27
|
+
//# sourceMappingURL=chat-dictation.d.ts.map
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { useCallback, useLayoutEffect, useRef, useState } from 'react';
|
|
3
|
+
import { voiceRefusalLabel } from './chat-text.js';
|
|
4
|
+
import { useVoiceAttempt } from './voice-attempt.js';
|
|
5
|
+
/** Gesture-started dictation over the shared attempt lifecycle; host callbacks and labels are captured per attempt. */
|
|
6
|
+
export function useDictation(options) {
|
|
7
|
+
const latest = useRef(options);
|
|
8
|
+
useLayoutEffect(() => { latest.current = options; });
|
|
9
|
+
const [problem, setProblem] = useState();
|
|
10
|
+
const core = useVoiceAttempt({
|
|
11
|
+
valid: item => latest.current.target === item.options.target,
|
|
12
|
+
refused: result => setProblem(latest.current.labels[voiceRefusalLabel[result.code ?? 'failed']]),
|
|
13
|
+
});
|
|
14
|
+
const actions = useRef(core);
|
|
15
|
+
useLayoutEffect(() => { actions.current = core; });
|
|
16
|
+
const cancel = useCallback(() => { actions.current.clear(); setProblem(undefined); }, []);
|
|
17
|
+
// A committed subject change releases pending microphone permission and aborts transcription.
|
|
18
|
+
useLayoutEffect(() => cancel(), [options.target, cancel]);
|
|
19
|
+
const start = useCallback(() => {
|
|
20
|
+
const selected = latest.current;
|
|
21
|
+
if (!selected.transcribe || actions.current.current().length)
|
|
22
|
+
return;
|
|
23
|
+
setProblem(undefined);
|
|
24
|
+
actions.current.start({ options: selected }, selected.maxMs);
|
|
25
|
+
}, []);
|
|
26
|
+
const finish = useCallback(() => {
|
|
27
|
+
const api = actions.current, item = api.current()[0];
|
|
28
|
+
if (!item || !api.valid(item))
|
|
29
|
+
return;
|
|
30
|
+
if (item.phase === 'starting') {
|
|
31
|
+
cancel();
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
if (item.phase !== 'recording')
|
|
35
|
+
return;
|
|
36
|
+
const selected = item.options;
|
|
37
|
+
void (async () => {
|
|
38
|
+
try {
|
|
39
|
+
const recording = await api.stop(item);
|
|
40
|
+
if (!api.valid(item))
|
|
41
|
+
return;
|
|
42
|
+
if (!recording) {
|
|
43
|
+
setProblem(selected.labels.voiceNothingHeard);
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
const text = (await selected.transcribe(recording.audio, item.abort.signal)).trim();
|
|
47
|
+
if (!api.valid(item))
|
|
48
|
+
return;
|
|
49
|
+
if (!text) {
|
|
50
|
+
setProblem(selected.labels.voiceNothingHeard);
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
await selected.onText(text, item.abort.signal);
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
if (api.valid(item))
|
|
57
|
+
setProblem(selected.labels.voiceTranscribeFailed);
|
|
58
|
+
}
|
|
59
|
+
finally {
|
|
60
|
+
if (api.valid(item))
|
|
61
|
+
api.remove(item);
|
|
62
|
+
}
|
|
63
|
+
})();
|
|
64
|
+
}, [cancel]);
|
|
65
|
+
const item = core.attempts[0];
|
|
66
|
+
return { phase: item?.phase === 'processing' ? 'transcribing' : item?.phase ?? 'idle', seconds: item?.seconds ?? 0,
|
|
67
|
+
levels: item?.levels ?? [], paused: item?.paused ?? false,
|
|
68
|
+
pause: item?.phase === 'recording' && item.canPause ? () => core.pause(item) : undefined,
|
|
69
|
+
resume: item?.phase === 'recording' && item.canPause ? () => core.resume(item) : undefined,
|
|
70
|
+
problem, start, finish, cancel };
|
|
71
|
+
}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import type { ToolCall, ToolResultMessage } from '@earendil-works/pi-ai';
|
|
2
|
+
import type { ChatAttachment, ChatController } from './native-chat.js';
|
|
3
|
+
import type { ChatCard, DeriveOptions, Mode, QueuedMessage, Row } from './chat-view.js';
|
|
4
|
+
/** A conversation's observed file; opening remains a host operation, not workspace ownership. */
|
|
5
|
+
export interface ConversationFile {
|
|
6
|
+
readonly id: string;
|
|
7
|
+
readonly name: string;
|
|
8
|
+
readonly open: (() => void) | undefined;
|
|
9
|
+
}
|
|
10
|
+
export interface ModelRef {
|
|
11
|
+
readonly provider: string;
|
|
12
|
+
readonly modelId: string;
|
|
13
|
+
}
|
|
14
|
+
export interface PastedText {
|
|
15
|
+
readonly text: string;
|
|
16
|
+
readonly start: number;
|
|
17
|
+
readonly end: number;
|
|
18
|
+
}
|
|
19
|
+
export interface UploadResult {
|
|
20
|
+
readonly path?: string;
|
|
21
|
+
readonly image?: {
|
|
22
|
+
readonly data: string;
|
|
23
|
+
readonly mimeType: string;
|
|
24
|
+
};
|
|
25
|
+
readonly name: string;
|
|
26
|
+
}
|
|
27
|
+
export interface PendingUpload {
|
|
28
|
+
readonly id: string;
|
|
29
|
+
readonly name: string;
|
|
30
|
+
readonly state: 'uploading' | 'failed';
|
|
31
|
+
readonly error?: string;
|
|
32
|
+
readonly cancel?: (() => void) | undefined;
|
|
33
|
+
readonly restoreWhile?: string | undefined;
|
|
34
|
+
}
|
|
35
|
+
export type FilesHandler = (files: readonly File[], target: Pick<ReturnType<ChatController['getSnapshot']>, 'identity' | 'conversationId'> & {
|
|
36
|
+
readonly signal: AbortSignal;
|
|
37
|
+
}) => Promise<readonly ChatAttachment[]>;
|
|
38
|
+
export interface QueueActions {
|
|
39
|
+
readonly edit?: ((item: QueuedMessage) => Promise<void>) | undefined;
|
|
40
|
+
readonly steer?: ((item: QueuedMessage) => Promise<void>) | undefined;
|
|
41
|
+
}
|
|
42
|
+
/** Send-time feedback only; the annotate button and its state remain host presentation. */
|
|
43
|
+
export interface ChatSessionFeedback {
|
|
44
|
+
readonly count: number;
|
|
45
|
+
readonly attach: (text: string) => Promise<{
|
|
46
|
+
readonly kind: 'ok';
|
|
47
|
+
readonly text: string;
|
|
48
|
+
} | {
|
|
49
|
+
readonly kind: 'refused';
|
|
50
|
+
readonly reason: string;
|
|
51
|
+
}>;
|
|
52
|
+
readonly sent: () => void;
|
|
53
|
+
}
|
|
54
|
+
export interface ChatSessionLabels {
|
|
55
|
+
readonly connectionFailed: string;
|
|
56
|
+
readonly actionFailedShort: string;
|
|
57
|
+
readonly imageAttachment: string;
|
|
58
|
+
readonly queueLocked: string;
|
|
59
|
+
readonly tooLate: string;
|
|
60
|
+
readonly waitToSteer: string;
|
|
61
|
+
readonly steerRefused: string;
|
|
62
|
+
readonly steerFailed: string;
|
|
63
|
+
readonly uploadFailedShort: string;
|
|
64
|
+
readonly pastedFileName: (stamp: string) => string;
|
|
65
|
+
readonly changeRefused: string;
|
|
66
|
+
}
|
|
67
|
+
export interface ChatSessionOptions<Content = unknown> extends Partial<DeriveOptions<Content>> {
|
|
68
|
+
readonly controller: ChatController;
|
|
69
|
+
/** Surfaces remount on controller replacement. Late effects/actions also check this live binding. */
|
|
70
|
+
readonly activeController: {
|
|
71
|
+
readonly current: ChatController;
|
|
72
|
+
};
|
|
73
|
+
readonly mode: Mode;
|
|
74
|
+
readonly labels: ChatSessionLabels;
|
|
75
|
+
readonly actions?: {
|
|
76
|
+
readonly withdraw?: ((id: QueuedMessage['id']) => Promise<unknown>) | undefined;
|
|
77
|
+
} | undefined;
|
|
78
|
+
readonly toolRenderers?: Readonly<Record<string, (call: ToolCall, result: ToolResultMessage | undefined) => ChatCard<Content> | undefined>> | undefined;
|
|
79
|
+
readonly artifacts?: (NonNullable<DeriveOptions<Content>['artifacts']> & {
|
|
80
|
+
readonly open?: ((artifact: never) => void) | undefined;
|
|
81
|
+
}) | undefined;
|
|
82
|
+
readonly mentions?: {
|
|
83
|
+
readonly open?: ((path: string) => void) | undefined;
|
|
84
|
+
readonly kinds?: Readonly<Record<string, unknown>> | undefined;
|
|
85
|
+
} | undefined;
|
|
86
|
+
readonly slash?: {
|
|
87
|
+
readonly skills?: readonly {
|
|
88
|
+
readonly name: string;
|
|
89
|
+
}[] | undefined;
|
|
90
|
+
} | undefined;
|
|
91
|
+
readonly onOpenFile?: ((path: string) => void) | undefined;
|
|
92
|
+
readonly onOpenImage?: ((image: ChatAttachment['content']) => void) | undefined;
|
|
93
|
+
readonly onFiles?: FilesHandler | undefined;
|
|
94
|
+
readonly onPickFiles?: ((files: readonly File[]) => void) | undefined;
|
|
95
|
+
readonly attachments?: {
|
|
96
|
+
readonly upload: (files: File[], signal: AbortSignal) => Promise<readonly UploadResult[]>;
|
|
97
|
+
} | undefined;
|
|
98
|
+
readonly model?: {
|
|
99
|
+
readonly change: (model: ModelRef) => Promise<void>;
|
|
100
|
+
} | undefined;
|
|
101
|
+
readonly effort?: {
|
|
102
|
+
readonly change: (level: string) => Promise<void>;
|
|
103
|
+
} | undefined;
|
|
104
|
+
readonly commandMentions?: {
|
|
105
|
+
readonly commands: readonly {
|
|
106
|
+
readonly name: string;
|
|
107
|
+
readonly behavior: 'execute' | 'insert';
|
|
108
|
+
}[];
|
|
109
|
+
readonly onActivate: (command: {
|
|
110
|
+
readonly name: string;
|
|
111
|
+
readonly behavior: 'execute' | 'insert';
|
|
112
|
+
}) => void;
|
|
113
|
+
} | undefined;
|
|
114
|
+
readonly feedback?: ChatSessionFeedback | undefined;
|
|
115
|
+
readonly afterSend?: (() => void) | undefined;
|
|
116
|
+
/** Focus is presentation; called only by actions, never during render. */
|
|
117
|
+
readonly onFocus?: (() => void) | undefined;
|
|
118
|
+
}
|
|
119
|
+
/** Headless session over the public native controller snapshot/commands. Borrows it; cleanup only aborts this hook's uploads. */
|
|
120
|
+
export declare function useChatSession<Content = unknown>(options: ChatSessionOptions<Content>): {
|
|
121
|
+
state: import("./native-chat.js").ChatSnapshot;
|
|
122
|
+
derived: import("./chat-view.js").Derived<Content>;
|
|
123
|
+
rows: Row<Content>[];
|
|
124
|
+
queued: {
|
|
125
|
+
id: import("@earendil-works/pi-durable").SubmissionId;
|
|
126
|
+
mode: "steer" | "followUp";
|
|
127
|
+
content: import("@earendil-works/chord").JsonRepresentation<import("@earendil-works/pi-durable").UserInput>;
|
|
128
|
+
entry?: import("@earendil-works/chord").JsonRepresentation<import("@earendil-works/pi-durable").InputEntry>;
|
|
129
|
+
}[];
|
|
130
|
+
queueActions: QueueActions | undefined;
|
|
131
|
+
working: boolean;
|
|
132
|
+
waitingForAnswer: boolean;
|
|
133
|
+
connected: boolean;
|
|
134
|
+
sendBlocked: boolean;
|
|
135
|
+
error: string | null;
|
|
136
|
+
setError: import("react").Dispatch<import("react").SetStateAction<string | null>>;
|
|
137
|
+
report: (cause: unknown) => void;
|
|
138
|
+
act: (action: () => Promise<unknown>) => void;
|
|
139
|
+
current: () => boolean;
|
|
140
|
+
files: ConversationFile[];
|
|
141
|
+
empty: boolean;
|
|
142
|
+
loading: boolean;
|
|
143
|
+
developer: boolean;
|
|
144
|
+
natural: boolean;
|
|
145
|
+
send: () => void;
|
|
146
|
+
putBack: (text: string, images: readonly ChatAttachment[]) => void;
|
|
147
|
+
upload: (files: readonly File[], paste?: {
|
|
148
|
+
readonly restore: () => void;
|
|
149
|
+
readonly after: string;
|
|
150
|
+
}) => Promise<void>;
|
|
151
|
+
pasteText: (paste: PastedText) => void;
|
|
152
|
+
picked: readonly string[];
|
|
153
|
+
pending: readonly PendingUpload[];
|
|
154
|
+
uploading: number;
|
|
155
|
+
activateMention: (command: {
|
|
156
|
+
readonly name: string;
|
|
157
|
+
readonly behavior: "execute" | "insert";
|
|
158
|
+
}) => void;
|
|
159
|
+
pickMention: (path: string) => void;
|
|
160
|
+
dismissUpload: (id: string) => void;
|
|
161
|
+
currentModel: ModelRef | undefined;
|
|
162
|
+
currentEffort: string | undefined;
|
|
163
|
+
changing: "model" | "effort" | null;
|
|
164
|
+
changeError: string | null;
|
|
165
|
+
dismissChangeError: () => void;
|
|
166
|
+
changeModel: (next: ModelRef) => Promise<void>;
|
|
167
|
+
changeEffort: (next: string) => Promise<void>;
|
|
168
|
+
};
|
|
169
|
+
export type ChatSession<Content = unknown> = ReturnType<typeof useChatSession<Content>>;
|
|
170
|
+
//# sourceMappingURL=chat-react-session.d.ts.map
|