@ykurilov/agent-chat-react 0.0.0-stage → 0.56.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.
- package/CHANGELOG.md +1688 -0
- package/LICENSE +21 -0
- package/README.md +132 -2
- package/dist/chunk-7DVTSVKQ.js +6967 -0
- package/dist/chunk-7DVTSVKQ.js.map +1 -0
- package/dist/index.js +501 -0
- package/dist/index.js.map +1 -0
- package/dist/panel.js +399 -0
- package/dist/panel.js.map +1 -0
- package/dist/types/agent-chat-extensions.d.ts +80 -0
- package/dist/types/agent-chat.d.ts +103 -0
- package/dist/types/agent-panel/agent-panel-hooks.d.ts +14 -0
- package/dist/types/agent-panel/agent-panel-provider.d.ts +24 -0
- package/dist/types/agent-panel/agent-panel-skin.d.ts +41 -0
- package/dist/types/agent-panel/agent-panel.d.ts +13 -0
- package/dist/types/agent-panel/index.d.ts +8 -0
- package/dist/types/agent-panel/panel-context.d.ts +46 -0
- package/dist/types/agent-panel/panel-state.d.ts +12 -0
- package/dist/types/answer-editing/answer-editing.d.ts +25 -0
- package/dist/types/app-bridge/app-bridge.d.ts +50 -0
- package/dist/types/app-bridge/browser-calls.d.ts +24 -0
- package/dist/types/app-bridge/create-app-bridge.d.ts +35 -0
- package/dist/types/app-bridge/remembered-ids.d.ts +6 -0
- package/dist/types/app-bridge/tab-id.d.ts +10 -0
- package/dist/types/app-events.d.ts +39 -0
- package/dist/types/app-refusal.d.ts +21 -0
- package/dist/types/attachments-contract.d.ts +82 -0
- package/dist/types/background-tasks/use-background-tasks.d.ts +53 -0
- package/dist/types/chat-access.d.ts +33 -0
- package/dist/types/chat-list/active-chat-memory.d.ts +5 -0
- package/dist/types/chat-list/chat-list-client.d.ts +35 -0
- package/dist/types/chat-list/chat-list-skin.d.ts +63 -0
- package/dist/types/chat-list/chat-workspace.d.ts +17 -0
- package/dist/types/chat-list/use-chat-list-view.d.ts +12 -0
- package/dist/types/chat-list/use-chat-list.d.ts +47 -0
- package/dist/types/chat-list/use-workspace-list-sync.d.ts +7 -0
- package/dist/types/chat-state/chat-state.d.ts +27 -0
- package/dist/types/chat-types.d.ts +85 -0
- package/dist/types/chat-watch/chat-watch.d.ts +57 -0
- package/dist/types/close-stopped-tool-calls.d.ts +8 -0
- package/dist/types/composer-actions.d.ts +29 -0
- package/dist/types/diagrams/diagram-renders.d.ts +16 -0
- package/dist/types/diagrams/mermaid-blocks.d.ts +18 -0
- package/dist/types/diagrams/unique-svg-ids.d.ts +6 -0
- package/dist/types/diagrams/use-diagram-blocks.d.ts +25 -0
- package/dist/types/diagrams/use-diagram-colors.d.ts +8 -0
- package/dist/types/dictation-field.d.ts +19 -0
- package/dist/types/expanded-parts.d.ts +11 -0
- package/dist/types/feed-announcement.d.ts +7 -0
- package/dist/types/helpers/helper-call.d.ts +57 -0
- package/dist/types/index.d.ts +53 -0
- package/dist/types/labels.d.ts +468 -0
- package/dist/types/live-demos/answer-fences.d.ts +25 -0
- package/dist/types/live-demos/live-demo-document.d.ts +42 -0
- package/dist/types/live-demos/live-demo-frame.d.ts +14 -0
- package/dist/types/live-demos/use-live-demo-blocks.d.ts +18 -0
- package/dist/types/liveness/connection-errors.d.ts +15 -0
- package/dist/types/liveness/reconnect-attempts.d.ts +14 -0
- package/dist/types/liveness/request-deadline.d.ts +16 -0
- package/dist/types/liveness/silence-watchdog.d.ts +21 -0
- package/dist/types/media-viewer/answer-file-images.d.ts +18 -0
- package/dist/types/media-viewer/collect-viewer-items.d.ts +16 -0
- package/dist/types/media-viewer/generated-images.d.ts +20 -0
- package/dist/types/media-viewer/media-viewer-types.d.ts +17 -0
- package/dist/types/media-viewer/open-attachments.d.ts +9 -0
- package/dist/types/media-viewer/use-media-viewer.d.ts +23 -0
- package/dist/types/memory/agent-memory.d.ts +13 -0
- package/dist/types/memory/memory-client.d.ts +19 -0
- package/dist/types/memory/memory-skin.d.ts +103 -0
- package/dist/types/memory/memory-tree.d.ts +17 -0
- package/dist/types/memory/memory-types.d.ts +77 -0
- package/dist/types/memory/use-memory-notes.d.ts +23 -0
- package/dist/types/message-actions.d.ts +45 -0
- package/dist/types/message-feed.d.ts +27 -0
- package/dist/types/notices/agent-notices.d.ts +40 -0
- package/dist/types/pause.d.ts +2 -0
- package/dist/types/server-refusal.d.ts +16 -0
- package/dist/types/skin-contract.d.ts +687 -0
- package/dist/types/spending-status.d.ts +76 -0
- package/dist/types/steering/corrections.d.ts +19 -0
- package/dist/types/step-reset.d.ts +15 -0
- package/dist/types/subject-chat/subject-chat-loading.d.ts +8 -0
- package/dist/types/subject-chat/use-subject-chat-id.d.ts +21 -0
- package/dist/types/thinking-status/thinking-status.d.ts +33 -0
- package/dist/types/turn-finished.d.ts +14 -0
- package/dist/types/use-agent-chat.d.ts +228 -0
- package/dist/types/use-model-choice.d.ts +31 -0
- package/dist/types/use-voice-dialog.d.ts +18 -0
- package/dist/types/use-voice-dictation.d.ts +17 -0
- package/dist/types/voice-dialog.d.ts +103 -0
- package/dist/types/voice-input.d.ts +47 -0
- package/dist/types/voice-tags.d.ts +18 -0
- package/dist/types/voice-turn.d.ts +11 -0
- package/dist/types/widgets/custom-widget-guard.d.ts +21 -0
- package/dist/types/widgets/form-check.d.ts +29 -0
- package/dist/types/widgets/suggested-replies.d.ts +6 -0
- package/dist/types/widgets/use-widget-answer.d.ts +54 -0
- package/dist/types/widgets/widget-answer-keys.d.ts +14 -0
- package/dist/types/widgets/widget-messages.d.ts +14 -0
- package/dist/types/widgets/widget-part.d.ts +17 -0
- package/dist/types/widgets/widget-view.d.ts +50 -0
- package/dist/types/without-markdown.d.ts +1 -0
- package/dist/types/work-view/step-phrases.d.ts +37 -0
- package/dist/types/work-view/work-layout.d.ts +57 -0
- package/dist/types/work-view/work-steps-part.d.ts +15 -0
- package/package.json +53 -3
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { LiveDemoColors } from "../skin-contract.js";
|
|
2
|
+
/** Предел кода в байтах UTF-8: больше — показывается код, а не запуск. */
|
|
3
|
+
export declare const MAX_LIVE_DEMO_BYTES: number;
|
|
4
|
+
/** Выше рамка не бывает: сообщение с большей высотой не принимается. */
|
|
5
|
+
export declare const MAX_LIVE_DEMO_HEIGHT = 10000;
|
|
6
|
+
/** Песочница рамки: скрипты, и только. Без своего источника, окон, форм, диалогов и перехода страницы. */
|
|
7
|
+
export declare const LIVE_DEMO_SANDBOX = "allow-scripts";
|
|
8
|
+
/** Вид сообщения высоты из рамки. */
|
|
9
|
+
export declare const LIVE_DEMO_HEIGHT_MESSAGE = "agent-chat-live-demo:height";
|
|
10
|
+
/** Вид сообщения «код ушёл со своей страницы»: его шлёт только промежуточный документ, демонстрация погашена. */
|
|
11
|
+
export declare const LIVE_DEMO_LEFT_MESSAGE = "agent-chat-live-demo:left";
|
|
12
|
+
/** Кусок потока сервера: в чат попал чужой текст, демонстрации в нём больше не запускаются. */
|
|
13
|
+
export declare const EXTERNAL_CONTENT_PART_TYPE = "data-external-content";
|
|
14
|
+
/** Ничего по сети: скрипты и стили только встроенные, картинки, шрифты и медиа — из самого кода. */
|
|
15
|
+
export declare const LIVE_DEMO_CSP: string;
|
|
16
|
+
/**
|
|
17
|
+
* CSP промежуточного документа. Вложенный документ наследует его вдобавок к своему, поэтому картинки, шрифты, медиа и
|
|
18
|
+
* стили тут те же. Главное — `frame-src about:`: вложенная рамка не уйдёт ни на какой адрес, кроме `about:`.
|
|
19
|
+
*/
|
|
20
|
+
export declare const LIVE_DEMO_SHELL_CSP: string;
|
|
21
|
+
/** Цвета, если скин своих не дал. */
|
|
22
|
+
export declare const FALLBACK_DEMO_COLORS: Record<"light" | "dark", LiveDemoColors>;
|
|
23
|
+
/**
|
|
24
|
+
* Документ внешней рамки для `srcdoc` — промежуточный: CSP с `frame-src about:`, свой скрипт и вложенная рамка, в
|
|
25
|
+
* которой `liveDemoInnerDocument`. Код модели этот документ не разбирает: он лежит строкой внутри скрипта.
|
|
26
|
+
*/
|
|
27
|
+
export declare function liveDemoDocument(source: string, colors: LiveDemoColors): string;
|
|
28
|
+
/**
|
|
29
|
+
* Документ вложенной рамки: запрет искать адреса заранее, CSP, цвета темы CSS-переменными и свои скрипты — в `<head>`,
|
|
30
|
+
* код модели — в `<body>` без вложенных рамок и заблаговременных соединений. Документ модели целиком (`<html>`, `<head>`) парсер вливает в `<body>`: `<meta>`
|
|
31
|
+
* с CSP там не действует, поэтому ослабить политику код не может.
|
|
32
|
+
*/
|
|
33
|
+
export declare function liveDemoInnerDocument(source: string, colors: LiveDemoColors): string;
|
|
34
|
+
/** Помещается ли код в предел `MAX_LIVE_DEMO_BYTES`. */
|
|
35
|
+
export declare function liveDemoFits(source: string): boolean;
|
|
36
|
+
/** Сообщение «код ушёл со своей страницы» — только от своей рамки. */
|
|
37
|
+
export declare function demoLeftPage(event: Pick<MessageEvent, "data" | "source">, frameWindow: Window | null | undefined): boolean;
|
|
38
|
+
/**
|
|
39
|
+
* Высота из сообщения рамки: только от её окна (`event.source`), только `{ type, height }` с конечным числом в пределах
|
|
40
|
+
* 1…`MAX_LIVE_DEMO_HEIGHT`, с округлением вверх. Иначе — `null`.
|
|
41
|
+
*/
|
|
42
|
+
export declare function demoHeightOf(event: Pick<MessageEvent, "data" | "source">, frameWindow: Window | null | undefined): number | null;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { LiveDemoColors } from "../skin-contract.js";
|
|
2
|
+
export interface LiveDemoFrameProps {
|
|
3
|
+
source: string;
|
|
4
|
+
colors: LiveDemoColors;
|
|
5
|
+
/** Подпись рамки для диктора. */
|
|
6
|
+
title: string;
|
|
7
|
+
/** Код ушёл со своей страницы (сообщение промежуточного документа или вторая загрузка рамки). */
|
|
8
|
+
onLeftPage: () => void;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Рамка живой демонстрации: изолированная песочница, документ через `srcdoc`, ширина во весь ответ, высота — из
|
|
12
|
+
* сообщения своей рамки. Новые цвета — новый документ: демонстрация перерисовывается.
|
|
13
|
+
*/
|
|
14
|
+
export declare const LiveDemoFrame: import("react").MemoExoticComponent<({ source, colors, title, onLeftPage }: LiveDemoFrameProps) => import("react").JSX.Element>;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { ChatSkin, LiveDemoColors, LiveDemoView } from "../skin-contract.js";
|
|
2
|
+
/**
|
|
3
|
+
* Цвета демонстраций для текущей темы страницы: `skin.demoColors(dark)`, без него — свои. Смена темы даёт новые цвета,
|
|
4
|
+
* и демонстрации перерисовываются. `enabled: false` (демонстрации не подключены) — за темой не следит, `undefined`.
|
|
5
|
+
*/
|
|
6
|
+
export declare function useDemoColors(skin: ChatSkin, enabled: boolean): LiveDemoColors | undefined;
|
|
7
|
+
/**
|
|
8
|
+
* Живые демонстрации одной текстовой части для скина (`AgentText.renderDemo`). Незакрытый блок, пока текст печатается,
|
|
9
|
+
* — `streaming`; закрытый запускается сразу, не дожидаясь конца ответа. Код не запускается, если он больше предела, не
|
|
10
|
+
* дописан к концу ответа, уже пытался уйти со своей страницы или в чате чужой текст (`blocked`: сервер сообщил, что в чат
|
|
11
|
+
* попали веб, файл или инструмент с пометкой). Нет цветов (демонстрации не подключены) — `undefined`.
|
|
12
|
+
*/
|
|
13
|
+
export declare function useLiveDemoBlocks({ markdown, streaming, colors, blocked }: {
|
|
14
|
+
markdown: string;
|
|
15
|
+
streaming: boolean;
|
|
16
|
+
colors: LiveDemoColors | undefined;
|
|
17
|
+
blocked: boolean;
|
|
18
|
+
}): ((source: string, blockIndex: number) => LiveDemoView) | undefined;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Свои ошибки связи. Имя у них не `TypeError` и не `AbortError`: SDK чата принимает сетевой
|
|
3
|
+
* `TypeError` за «обрыв, можно продолжить с места» и удвоит части ответа, а `AbortError` — за «стоп».
|
|
4
|
+
*/
|
|
5
|
+
/** Сеть отказала, соединение оборвалось или вышел срок запроса. */
|
|
6
|
+
export declare class ConnectionLostError extends Error {
|
|
7
|
+
constructor(message?: string, options?: {
|
|
8
|
+
cause?: unknown;
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
/** Сервер слишком долго не присылал ни байта. */
|
|
12
|
+
export declare class ConnectionSilentError extends Error {
|
|
13
|
+
constructor(silenceMs?: number);
|
|
14
|
+
}
|
|
15
|
+
export declare function isConnectionError(error: unknown): error is ConnectionLostError | ConnectionSilentError;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export declare const DEFAULT_RECONNECT_DELAYS_MS: readonly number[];
|
|
2
|
+
export type ReconnectOutcome = "connected" | "exhausted" | "cancelled";
|
|
3
|
+
export interface ReconnectAttemptsOptions {
|
|
4
|
+
/** Пауза перед каждой попыткой; сколько пауз — столько попыток. */
|
|
5
|
+
delaysMs: readonly number[];
|
|
6
|
+
/** Срок попытки целиком: история, заголовки потока и первый байт. */
|
|
7
|
+
attemptTimeoutMs: number;
|
|
8
|
+
/** Одна попытка: `true` — подключились. Её сигнал отменяется по сроку и при отмене серии. */
|
|
9
|
+
attempt: (signal: AbortSignal, index: number) => Promise<boolean>;
|
|
10
|
+
/** Отмена всей серии («стоп», уход со страницы). */
|
|
11
|
+
signal: AbortSignal;
|
|
12
|
+
}
|
|
13
|
+
/** Серия попыток переподключения с паузами и общим сроком на каждую попытку (research R10). */
|
|
14
|
+
export declare function runReconnectAttempts(options: ReconnectAttemptsOptions): Promise<ReconnectOutcome>;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { SilenceWatchOptions } from "./silence-watchdog.js";
|
|
2
|
+
export interface DeadlineOptions {
|
|
3
|
+
/** Срок до заголовков ответа. */
|
|
4
|
+
timeoutMs: number;
|
|
5
|
+
/** Своя отмена (серия попыток): для SDK это обрыв связи, а не стоп собеседника. */
|
|
6
|
+
signal?: AbortSignal;
|
|
7
|
+
/** Срок покрывает и тело ответа: для запросов без потока. */
|
|
8
|
+
wholeBody?: boolean;
|
|
9
|
+
/** Следить за тишиной в теле потока; параметры строятся по ответу (например, по заголовку сервера). */
|
|
10
|
+
silence?: (response: Response) => Omit<SilenceWatchOptions, "translateError" | "onEnd">;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* `fetch` со сроком. Сетевой `TypeError` и истёкший срок становятся `ConnectionLostError`.
|
|
14
|
+
* Отмена собеседником (`init.signal`) остаётся `AbortError`.
|
|
15
|
+
*/
|
|
16
|
+
export declare function fetchWithDeadline(fetchFn: (input: string, init?: RequestInit) => Promise<Response>, input: string, init: RequestInit | undefined, options: DeadlineOptions): Promise<Response>;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/** Действующий срок тишины: не меньше трёх признаков жизни сервера (FR-017). */
|
|
2
|
+
export declare function effectiveSilenceTimeoutMs(silenceTimeoutMs: number, heartbeatMs: number): number;
|
|
3
|
+
export interface SilenceWatchOptions {
|
|
4
|
+
silenceTimeoutMs: number;
|
|
5
|
+
/** Срок до самого первого байта, если он короче: сервер шлёт пинг сразу после заголовков. */
|
|
6
|
+
firstByteTimeoutMs?: number;
|
|
7
|
+
onFirstByte?: () => void;
|
|
8
|
+
/** Тишина затянулась: закрыть исходное соединение и сообщить наверх. */
|
|
9
|
+
onSilence?: () => void;
|
|
10
|
+
/** Во что превратить ошибку чтения тела перед тем, как отдать её читателю. */
|
|
11
|
+
translateError?: (error: unknown) => unknown;
|
|
12
|
+
/** Тело закончилось любым способом: конец, ошибка, тишина или отмена читателем. */
|
|
13
|
+
onEnd?: () => void;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Пропускает тело ответа как есть (ошибки чтения — через `translateError`), но следит за временем последнего байта (любого, и пинга тоже).
|
|
17
|
+
* Тишина дольше срока — поток обрывается `ConnectionSilentError`, исходное соединение отменяется.
|
|
18
|
+
* Сравнение времён, а не «таймер на 30 с»: фоновая вкладка и сон телефона не рвут живой поток,
|
|
19
|
+
* а проснувшись — замечают тишину сразу (`visibilitychange`, `online`).
|
|
20
|
+
*/
|
|
21
|
+
export declare function watchSilence(body: ReadableStream<Uint8Array>, options: SilenceWatchOptions): ReadableStream<Uint8Array>;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { MessageAttachmentsView } from "../attachments-contract.js";
|
|
2
|
+
import type { AgentChatMessage } from "../chat-types.js";
|
|
3
|
+
import type { GeneratedImagesView } from "../skin-contract.js";
|
|
4
|
+
/** Как показать часть `file` ответа агента: группой картинок или строкой со ссылкой на файл (текст ответа). */
|
|
5
|
+
export type AnswerFileView = {
|
|
6
|
+
kind: "images";
|
|
7
|
+
view: GeneratedImagesView;
|
|
8
|
+
} | {
|
|
9
|
+
kind: "file";
|
|
10
|
+
markdown: string;
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Файлы в ответе агента (импорт переписки). Часть узнаётся по адресу (`MessageAttachmentItem.partUrl`), не по порядку.
|
|
14
|
+
* С `withImages` подряд идущие картинки — одна группа `GeneratedImages` на месте первой; ключ — номер части, `null` —
|
|
15
|
+
* часть уже в группе выше. Остальное (не картинка, неизвестная плагину часть, скин без `GeneratedImages`) — строкой
|
|
16
|
+
* «📎 имя» со ссылкой, если адрес известен: файл не пропадает молча.
|
|
17
|
+
*/
|
|
18
|
+
export declare function answerFileViews(message: AgentChatMessage, describe: ((message: AgentChatMessage) => MessageAttachmentsView | undefined) | undefined, withImages: boolean): Map<number, AnswerFileView | null>;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { MessageAttachmentsView } from "../attachments-contract.js";
|
|
2
|
+
import type { AgentChatMessage } from "../chat-types.js";
|
|
3
|
+
import type { MediaViewerItem } from "./media-viewer-types.js";
|
|
4
|
+
export interface CollectViewerItemsInput {
|
|
5
|
+
/** Сообщения текущей ветки по порядку ленты. */
|
|
6
|
+
messages: readonly AgentChatMessage[];
|
|
7
|
+
/** `describe` плагина вложений; нет — у человека картинок нет. */
|
|
8
|
+
describeAttachments?: ((message: AgentChatMessage) => MessageAttachmentsView | undefined) | undefined;
|
|
9
|
+
/** Свои элементы сообщения (картинки агента): встают на место сообщения в ленте, после его вложений. */
|
|
10
|
+
extraItems?: ((message: AgentChatMessage) => readonly MediaViewerItem[]) | undefined;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Карусель окна просмотра: все картинки текущей ветки по порядку ленты. Одна картинка в нескольких сообщениях
|
|
14
|
+
* (то же вложение) — один элемент на первом месте: `key` по ней возвращает фокус и должен быть единственным.
|
|
15
|
+
*/
|
|
16
|
+
export declare function collectViewerItems({ messages, describeAttachments, extraItems }: CollectViewerItemsInput): MediaViewerItem[];
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { DynamicToolUIPart, ToolUIPart } from "ai";
|
|
2
|
+
import type { ImagesClient } from "../agent-chat-extensions.js";
|
|
3
|
+
import type { AgentChatMessage } from "../chat-types.js";
|
|
4
|
+
import type { GeneratedImagesView } from "../skin-contract.js";
|
|
5
|
+
import type { MediaViewerItem } from "./media-viewer-types.js";
|
|
6
|
+
/** Вид картинок агента для части ответа; `undefined` — это не вызов картинок, он остаётся строкой инструмента. */
|
|
7
|
+
export type DescribeGeneratedImages = (part: ToolUIPart | DynamicToolUIPart) => GeneratedImagesView | undefined;
|
|
8
|
+
/** `images` страницы — всегда списком: один клиент или несколько. */
|
|
9
|
+
export declare const imageClientsOf: (images: ImagesClient | readonly ImagesClient[] | undefined) => readonly ImagesClient[];
|
|
10
|
+
/**
|
|
11
|
+
* Картинки агента (`images` страницы): вызов инструмента одного из клиентов → вид для скина; клиент выбирается по имени
|
|
12
|
+
* инструмента. Нет `open` (у скина нет окна просмотра) — клик по картинке ничего не делает.
|
|
13
|
+
*/
|
|
14
|
+
export declare function describeGeneratedImages(images: ImagesClient | readonly ImagesClient[], call: {
|
|
15
|
+
api: string;
|
|
16
|
+
chatId: string;
|
|
17
|
+
open?: ((key: string, trigger: HTMLElement) => void) | undefined;
|
|
18
|
+
}): DescribeGeneratedImages;
|
|
19
|
+
/** Готовые картинки агента в сообщении — элементы карусели окна просмотра, по порядку частей. */
|
|
20
|
+
export declare function generatedViewerItems(describe: DescribeGeneratedImages): (message: AgentChatMessage) => MediaViewerItem[];
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Один элемент окна просмотра (договор скина, версия 13). `key` — id вложения или `<messageId>:<номер блока>` у схемы:
|
|
3
|
+
* по нему слой поведения возвращает фокус на элемент-источник.
|
|
4
|
+
*/
|
|
5
|
+
export type MediaViewerItem = {
|
|
6
|
+
kind: "image";
|
|
7
|
+
key: string;
|
|
8
|
+
src: string;
|
|
9
|
+
alt: string;
|
|
10
|
+
width?: number;
|
|
11
|
+
height?: number;
|
|
12
|
+
} | {
|
|
13
|
+
kind: "diagram";
|
|
14
|
+
key: string;
|
|
15
|
+
svg: string;
|
|
16
|
+
source: string;
|
|
17
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { MessageAttachmentsView } from "../attachments-contract.js";
|
|
2
|
+
import type { AgentChatMessage } from "../chat-types.js";
|
|
3
|
+
type DescribeAttachments = (message: AgentChatMessage) => MessageAttachmentsView | undefined;
|
|
4
|
+
/**
|
|
5
|
+
* `describe` плагина вложений, у картинок которого есть `onOpen` (договор скина, версия 13): открыть в окне просмотра
|
|
6
|
+
* по id вложения. Нет `open` (у скина нет окна) — `describe` как есть, миниатюры как в 0.15.
|
|
7
|
+
*/
|
|
8
|
+
export declare function describeWithViewer(describe: DescribeAttachments | undefined, open: ((key: string, trigger: HTMLElement) => void) | undefined): DescribeAttachments | undefined;
|
|
9
|
+
export {};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { MediaViewerProps } from "../skin-contract.js";
|
|
2
|
+
import type { MediaViewerItem } from "./media-viewer-types.js";
|
|
3
|
+
export interface UseMediaViewerOptions {
|
|
4
|
+
/** Карусель чата на сейчас; зовётся, только пока окно открыто. Новая функция — карусель пересобирается. */
|
|
5
|
+
getItems: () => readonly MediaViewerItem[];
|
|
6
|
+
/** Смена значения (другой чат, другая ветка) закрывает окно. */
|
|
7
|
+
resetKey: string;
|
|
8
|
+
}
|
|
9
|
+
export interface MediaViewerController {
|
|
10
|
+
/**
|
|
11
|
+
* Открыть элемент `key`. `items` — свой набор (схема открывается одна); без него — карусель чата.
|
|
12
|
+
* Функция стабильна: строки ленты, которые её держат, не перерисовываются.
|
|
13
|
+
*/
|
|
14
|
+
open: (key: string, trigger: HTMLElement, items?: readonly MediaViewerItem[]) => void;
|
|
15
|
+
/** Свойства для `skin.MediaViewer`; `null` — окно закрыто. */
|
|
16
|
+
viewer: MediaViewerProps | null;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Окно просмотра со стороны поведения: какой элемент открыт, запись в истории браузера (жест «назад» закрывает окно)
|
|
20
|
+
* и возврат фокуса на элемент, с которого открыли. Открытый элемент помнится по `key`: новые картинки в ленте
|
|
21
|
+
* окно не сдвигают, а пропавший элемент окно закрывает.
|
|
22
|
+
*/
|
|
23
|
+
export declare function useMediaViewer({ getItems, resetKey }: UseMediaViewerOptions): MediaViewerController;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { MemorySkin } from "./memory-skin.js";
|
|
2
|
+
import type { MemoryApi } from "./memory-types.js";
|
|
3
|
+
export interface AgentMemoryProps {
|
|
4
|
+
/** Обмен с сервером: `createMemoryApi({ api: "/api/chat" })`. Создайте один раз — при смене объекта список грузится заново. */
|
|
5
|
+
api: MemoryApi;
|
|
6
|
+
/** Скин экрана памяти (договор 11), например `tandemMemorySkin`. */
|
|
7
|
+
skin: MemorySkin;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Экран памяти агента: записи по разделам, добавление, правка, удаление, «Стереть всю память» и блок «Объединено» с кнопкой «Вернуть как было».
|
|
11
|
+
* Всё видимое рисует скин; здесь — состояние, черновики и проверка ввода.
|
|
12
|
+
*/
|
|
13
|
+
export declare function AgentMemory({ api, skin }: AgentMemoryProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { MemoryApi } from "./memory-types.js";
|
|
2
|
+
type Fetch = typeof globalThis.fetch;
|
|
3
|
+
/** Сервер ответил отказом; `status` — код ответа, `kind` — `error.kind` из тела, если оно было (404 без тела — памяти нет). */
|
|
4
|
+
export declare class MemoryRequestError extends Error {
|
|
5
|
+
readonly status: number;
|
|
6
|
+
readonly kind: string | undefined;
|
|
7
|
+
constructor(status: number, kind: string | undefined, action: string);
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Клиент адресов памяти сервера: `GET/POST {api}/memory`, `PATCH/DELETE {api}/memory/{id}`, `DELETE {api}/memory`,
|
|
11
|
+
* склейки повторов — `GET {api}/memory/merges` и `POST {api}/memory/merges/{runId}/revert`.
|
|
12
|
+
* `api` — тот же адрес обработчика чата, например `/api/chat`. Вызывайте один раз (например, в `useMemo`):
|
|
13
|
+
* экран загружает список заново при смене объекта.
|
|
14
|
+
*/
|
|
15
|
+
export declare function createMemoryApi({ api, fetch: customFetch }: {
|
|
16
|
+
api: string;
|
|
17
|
+
fetch?: Fetch;
|
|
18
|
+
}): MemoryApi;
|
|
19
|
+
export {};
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import type { ComponentType, ReactNode } from "react";
|
|
2
|
+
import type { MemoryCounts, MemoryStatus } from "./memory-types.js";
|
|
3
|
+
/** Ошибки полей формы записи готовыми надписями; `form` — отказ сервера или другое, что не про одно поле. */
|
|
4
|
+
export interface MemoryNoteEditorErrors {
|
|
5
|
+
path?: string;
|
|
6
|
+
content?: string;
|
|
7
|
+
form?: string;
|
|
8
|
+
}
|
|
9
|
+
/** Что подтверждает окно: удаление одной записи или стирание всей памяти. */
|
|
10
|
+
export type MemoryConfirmTarget = {
|
|
11
|
+
kind: "note";
|
|
12
|
+
content: string;
|
|
13
|
+
} | {
|
|
14
|
+
kind: "all";
|
|
15
|
+
total: number;
|
|
16
|
+
};
|
|
17
|
+
/** Одна склейка для экрана: тексты записей «было» (скрытые оригиналы) и «стало» (результат в списке). */
|
|
18
|
+
export interface MemoryMergeGroupView {
|
|
19
|
+
before: string[];
|
|
20
|
+
after: string;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Договор скина, версия 11: отдельный интерфейс для экрана памяти `AgentMemory` (договор чата `ChatSkin`
|
|
24
|
+
* и договор списка чатов `ChatListSkin` не менялись). Слой поведения решает, что показать, и хранит черновики; скин рисует
|
|
25
|
+
* готовые данные и зовёт колбэки. Надписи компоненты берут из `useChatLabels()`; ширина — любая, вплоть до телефона,
|
|
26
|
+
* без горизонтальной прокрутки.
|
|
27
|
+
*/
|
|
28
|
+
export interface MemorySkin {
|
|
29
|
+
/**
|
|
30
|
+
* Экран целиком: заголовок, счётчик, кнопки и состояния. `status`: `loading` и `error` (тогда `onRetry`) — записей нет,
|
|
31
|
+
* рисовать надпись состояния; `empty` — надпись «память пуста»; `unavailable` — памяти в приложении нет; `ready` — `children`.
|
|
32
|
+
* `counts` нет, пока список не загружен; если `shown < total` — показать подсказку `memoryShownHint`.
|
|
33
|
+
* Нет `onAdd` — форма новой записи уже открыта или памяти нет, кнопку не рисовать; нет `onClearAll` — стирать нечего.
|
|
34
|
+
* `error` — сообщение о том, что не вышло, готовой надписью (`role="alert"`). `newNoteEditor` — форма новой записи
|
|
35
|
+
* (над списком), `confirm` — окно подтверждения. `merges` — блок «Объединено» (карточки `MemoryMerge`); есть, только
|
|
36
|
+
* когда склейки есть; скин рисует его под списком записей со своим заголовком `memoryMergesHeading`.
|
|
37
|
+
*/
|
|
38
|
+
MemoryScreen: ComponentType<{
|
|
39
|
+
status: MemoryStatus;
|
|
40
|
+
counts?: MemoryCounts;
|
|
41
|
+
onAdd?: () => void;
|
|
42
|
+
onClearAll?: () => void;
|
|
43
|
+
onRetry?: () => void;
|
|
44
|
+
error?: string;
|
|
45
|
+
newNoteEditor?: ReactNode;
|
|
46
|
+
merges?: ReactNode;
|
|
47
|
+
confirm?: ReactNode;
|
|
48
|
+
children: ReactNode;
|
|
49
|
+
}>;
|
|
50
|
+
/**
|
|
51
|
+
* Раздел дерева. `depth` — 0 для верхнего; `count` — записей в разделе, вместе с вложенными. В `children` сначала записи
|
|
52
|
+
* раздела, потом вложенные разделы.
|
|
53
|
+
*/
|
|
54
|
+
MemoryGroup: ComponentType<{
|
|
55
|
+
name: string;
|
|
56
|
+
depth: number;
|
|
57
|
+
count: number;
|
|
58
|
+
children: ReactNode;
|
|
59
|
+
}>;
|
|
60
|
+
/** Запись. Нет обработчика — действие сейчас недоступно (идёт другое действие): кнопку показать неактивной. */
|
|
61
|
+
MemoryNoteRow: ComponentType<{
|
|
62
|
+
content: string;
|
|
63
|
+
onEdit?: () => void;
|
|
64
|
+
onDelete?: () => void;
|
|
65
|
+
}>;
|
|
66
|
+
/**
|
|
67
|
+
* Форма записи. Слой поведения хранит черновик: скин рисует поля с `path` (разделы через «/», например
|
|
68
|
+
* «О себе / Работа») и `content`. `canSave` — `false` для пустых полей; `pending` — запись уходит на сервер.
|
|
69
|
+
* Enter в поле раздела и кнопка «Сохранить» зовут `onSave`, Escape — `onCancel`.
|
|
70
|
+
*/
|
|
71
|
+
MemoryNoteEditor: ComponentType<{
|
|
72
|
+
mode: "create" | "edit";
|
|
73
|
+
path: string;
|
|
74
|
+
content: string;
|
|
75
|
+
onPathChange: (value: string) => void;
|
|
76
|
+
onContentChange: (value: string) => void;
|
|
77
|
+
onSave: () => void;
|
|
78
|
+
onCancel: () => void;
|
|
79
|
+
canSave: boolean;
|
|
80
|
+
pending: boolean;
|
|
81
|
+
errors: MemoryNoteEditorErrors;
|
|
82
|
+
}>;
|
|
83
|
+
/**
|
|
84
|
+
* Одно слияние в блоке «Объединено»: для каждой склейки — «было» (тексты оригиналов) и «стало» (результат).
|
|
85
|
+
* `finishedAt` — время слияния (ISO 8601); человеку его показывает `labels.memoryMergeDate(new Date(finishedAt))`.
|
|
86
|
+
* `revertible: false` — записи с тех пор менялись, вернуть нельзя: кнопки нет, вместо неё `memoryMergeNotRevertible`.
|
|
87
|
+
* `revertible: true` и нет `onRevert` — идёт другое действие: кнопку «Вернуть как было» показать неактивной.
|
|
88
|
+
*/
|
|
89
|
+
MemoryMerge: ComponentType<{
|
|
90
|
+
finishedAt: string;
|
|
91
|
+
groups: MemoryMergeGroupView[];
|
|
92
|
+
revertible: boolean;
|
|
93
|
+
onRevert?: () => void;
|
|
94
|
+
}>;
|
|
95
|
+
/** Подтверждение: удалить можно только через `onConfirm`. `error` — почему не вышло (окно остаётся, можно повторить). */
|
|
96
|
+
MemoryConfirm: ComponentType<{
|
|
97
|
+
target: MemoryConfirmTarget;
|
|
98
|
+
pending: boolean;
|
|
99
|
+
error?: string;
|
|
100
|
+
onConfirm: () => void;
|
|
101
|
+
onCancel: () => void;
|
|
102
|
+
}>;
|
|
103
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { MemoryNote } from "./memory-types.js";
|
|
2
|
+
/** Раздел дерева: записи, лежащие прямо в нём, и вложенные разделы. */
|
|
3
|
+
export interface MemoryTreeGroup {
|
|
4
|
+
name: string;
|
|
5
|
+
/** Путь от верхнего раздела, имена в нижнем регистре: годится в ключ. */
|
|
6
|
+
key: string;
|
|
7
|
+
notes: MemoryNote[];
|
|
8
|
+
groups: MemoryTreeGroup[];
|
|
9
|
+
/** Записей в разделе вместе с вложенными. */
|
|
10
|
+
count: number;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Раскладывает записи по разделам: запись с разделами `["О себе", "Работа"]` лежит в «Работа» внутри «О себе».
|
|
14
|
+
* Разделы, различающиеся только регистром, сливаются (имя берётся у первой записи); разделы идут по алфавиту,
|
|
15
|
+
* записи — в порядке списка.
|
|
16
|
+
*/
|
|
17
|
+
export declare function buildMemoryTree(notes: readonly MemoryNote[]): MemoryTreeGroup[];
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/** Запись памяти, как её отдаёт сервер (`GET {api}/memory`). Владельца в ответах нет. */
|
|
2
|
+
export interface MemoryNote {
|
|
3
|
+
id: string;
|
|
4
|
+
/** Раздел записи от общего к частному: от 1 до 3 уровней. */
|
|
5
|
+
categories: string[];
|
|
6
|
+
content: string;
|
|
7
|
+
/** Замок: правка и удаление называют версию, которую человек видел. */
|
|
8
|
+
version: number;
|
|
9
|
+
createdAt: string;
|
|
10
|
+
updatedAt: string;
|
|
11
|
+
}
|
|
12
|
+
/** То, что человек вводит: разделы и текст. */
|
|
13
|
+
export interface MemoryNoteInput {
|
|
14
|
+
categories: string[];
|
|
15
|
+
content: string;
|
|
16
|
+
}
|
|
17
|
+
export interface MemoryCounts {
|
|
18
|
+
/** Записей всего. */
|
|
19
|
+
total: number;
|
|
20
|
+
/** Предел записей. */
|
|
21
|
+
limit: number;
|
|
22
|
+
/** Сколько записей видит агент в каждом запросе (остальные он находит поиском). */
|
|
23
|
+
shown: number;
|
|
24
|
+
}
|
|
25
|
+
/** Одна склейка повторов: `originals` («было») заменены записью `result` («стало»). */
|
|
26
|
+
export interface MemoryMergeGroup {
|
|
27
|
+
result: MemoryNote;
|
|
28
|
+
originals: MemoryNote[];
|
|
29
|
+
}
|
|
30
|
+
/** Слияние одного запуска оптимизации (`GET {api}/memory/merges`), новейшие первыми. */
|
|
31
|
+
export interface MemoryMerge {
|
|
32
|
+
runId: string;
|
|
33
|
+
/** Время в формате ISO 8601. */
|
|
34
|
+
finishedAt: string;
|
|
35
|
+
/** Можно ли «Вернуть как было»: результаты склеек с тех пор не менялись. */
|
|
36
|
+
revertible: boolean;
|
|
37
|
+
groups: MemoryMergeGroup[];
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Обмен с сервером памяти. Каждый метод при отказе бросает `MemoryRequestError`
|
|
41
|
+
* (обрыв связи — обычная ошибка `fetch`). `createMemoryApi` строит его по адресам `{api}/memory`.
|
|
42
|
+
*/
|
|
43
|
+
export interface MemoryApi {
|
|
44
|
+
list(): Promise<{
|
|
45
|
+
notes: MemoryNote[];
|
|
46
|
+
counts: MemoryCounts;
|
|
47
|
+
}>;
|
|
48
|
+
create(input: MemoryNoteInput): Promise<MemoryNote>;
|
|
49
|
+
update(id: string, version: number, input: MemoryNoteInput): Promise<MemoryNote>;
|
|
50
|
+
remove(id: string, version: number): Promise<void>;
|
|
51
|
+
/** Стирает всю память человека; возвращает, сколько записей удалено. */
|
|
52
|
+
clear(): Promise<number>;
|
|
53
|
+
/** Склейки повторов, которые можно показать и вернуть. */
|
|
54
|
+
listMerges(): Promise<MemoryMerge[]>;
|
|
55
|
+
/** Возвращает слияние как было. Отказ 409: `conflict` — результат с тех пор менялся; `limit_reached` — памяти не хватит места; `duplicate` — запись, что вернулась бы, уже добавлена заново. */
|
|
56
|
+
revertMerge(runId: string): Promise<void>;
|
|
57
|
+
}
|
|
58
|
+
/** Что пошло не так с действием: коды сервера (`conflict` — слияние вернуть нельзя) плюс `unavailable` (памяти нет) и `other` (сбой, обрыв связи). */
|
|
59
|
+
export type MemoryFailure = "not_found" | "stale_version" | "limit_reached" | "duplicate" | "invalid" | "conflict" | "unavailable" | "other";
|
|
60
|
+
/** Итог действия. Отказ — не исключение: скину нужна надпись, а не стек. */
|
|
61
|
+
export type MemoryActionResult = {
|
|
62
|
+
ok: true;
|
|
63
|
+
} | {
|
|
64
|
+
ok: false;
|
|
65
|
+
failure: MemoryFailure;
|
|
66
|
+
};
|
|
67
|
+
/**
|
|
68
|
+
* Состояние экрана: `loading` — грузится; `ready` — записи есть; `empty` — записей нет; `unavailable` — сервер ответил
|
|
69
|
+
* 404 (память в приложении не включена); `error` — список не загрузился.
|
|
70
|
+
*/
|
|
71
|
+
export type MemoryStatus = "loading" | "ready" | "empty" | "unavailable" | "error";
|
|
72
|
+
/** Форма записи (как у пакета памяти): до 3 разделов по 60 знаков, текст до 500. Экран проверяет её до отправки. */
|
|
73
|
+
export declare const memoryNoteLimits: {
|
|
74
|
+
readonly maxCategories: 3;
|
|
75
|
+
readonly maxCategoryChars: 60;
|
|
76
|
+
readonly maxContentChars: 500;
|
|
77
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { MemoryActionResult, MemoryApi, MemoryCounts, MemoryFailure, MemoryMerge, MemoryNote, MemoryNoteInput, MemoryStatus } from "./memory-types.js";
|
|
2
|
+
export interface MemoryNotesState {
|
|
3
|
+
state: MemoryStatus;
|
|
4
|
+
/** Записи в порядке сервера: новейшие первыми. */
|
|
5
|
+
notes: MemoryNote[];
|
|
6
|
+
/** `null`, пока список не загружен. */
|
|
7
|
+
counts: MemoryCounts | null;
|
|
8
|
+
/** Склейки повторов, новейшие первыми; пусто, если их нет или сервер их не отдаёт. */
|
|
9
|
+
merges: MemoryMerge[];
|
|
10
|
+
/** Как закончилось последнее действие, если отказом; следующее действие сбрасывает. */
|
|
11
|
+
error: MemoryFailure | null;
|
|
12
|
+
add(input: MemoryNoteInput): Promise<MemoryActionResult>;
|
|
13
|
+
/** Уходит версия записи из списка. Конфликт версий: список перезагружен, вызывающий держит свои значения и может повторить. */
|
|
14
|
+
edit(id: string, input: MemoryNoteInput): Promise<MemoryActionResult>;
|
|
15
|
+
remove(id: string): Promise<MemoryActionResult>;
|
|
16
|
+
clear(): Promise<MemoryActionResult>;
|
|
17
|
+
/** «Вернуть как было»: оригиналы возвращаются вместо результата, список записей и склеек загружается заново. */
|
|
18
|
+
revertMerge(runId: string): Promise<MemoryActionResult>;
|
|
19
|
+
/** Загружает список заново: после отказа — с экраном загрузки, иначе тихо. */
|
|
20
|
+
reload(): void;
|
|
21
|
+
}
|
|
22
|
+
/** Состояние экрана памяти: загрузка, добавление, правка, удаление, стирание. Ничего не рисует. */
|
|
23
|
+
export declare function useMemoryNotes(api: MemoryApi): MemoryNotesState;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
import type { AgentChatMessage, AnswerStatus } from "./chat-types.js";
|
|
3
|
+
/** Ответ, под которым решается, показывать ли кнопку. */
|
|
4
|
+
export interface MessageActionTarget {
|
|
5
|
+
message: AgentChatMessage;
|
|
6
|
+
/** Итог ответа: пока ответ пишется, кнопок нет, и `visible` не зовётся. */
|
|
7
|
+
status: Exclude<AnswerStatus, "streaming">;
|
|
8
|
+
/** Последний ответ ветки. */
|
|
9
|
+
last: boolean;
|
|
10
|
+
}
|
|
11
|
+
/** Что получает обработчик кнопки. */
|
|
12
|
+
export interface MessageActionContext {
|
|
13
|
+
chatId: string;
|
|
14
|
+
/** Id ответа — тот же, что в истории на сервере и в `onTurnFinished`. */
|
|
15
|
+
messageId: string;
|
|
16
|
+
message: AgentChatMessage;
|
|
17
|
+
/** Текст ответа, как его копирует «Скопировать». */
|
|
18
|
+
text: string;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Своя кнопка приложения под ответом агента. Иконку даёт приложение готовым элементом (`<Heart />`): слой поведения и
|
|
22
|
+
* скин дизайн-систему приложения не знают. Массив кнопок держите постоянным (вне компонента или в `useMemo`):
|
|
23
|
+
* новый массив на каждой отрисовке перерисовывает все ответы ленты.
|
|
24
|
+
*/
|
|
25
|
+
export interface MessageAction {
|
|
26
|
+
/** Постоянный и уникальный среди кнопок. */
|
|
27
|
+
id: string;
|
|
28
|
+
/** Подпись: текст кнопки или подсказка и `aria-label` у кнопки с одной иконкой. */
|
|
29
|
+
label: string;
|
|
30
|
+
icon?: ReactNode;
|
|
31
|
+
/** Показывать ли кнопку у этого ответа. Нет — у каждого законченного ответа. */
|
|
32
|
+
visible?: (target: MessageActionTarget) => boolean;
|
|
33
|
+
/** Пока обещание не выполнено, кнопка «занята». Ошибка уходит в консоль, а не в чат. */
|
|
34
|
+
onRun: (context: MessageActionContext) => void | Promise<void>;
|
|
35
|
+
}
|
|
36
|
+
/** Id кнопок, чьи обработчики ещё идут, по id ответа. Список ответа меняется, только когда меняются его кнопки. */
|
|
37
|
+
export type RunningMessageActions = ReadonlyMap<string, readonly string[]>;
|
|
38
|
+
/**
|
|
39
|
+
* Запуск своих кнопок ответа: двойное нажатие не запускает обработчик второй раз, пока первый идёт; сбой обработчика
|
|
40
|
+
* уходит в консоль.
|
|
41
|
+
*/
|
|
42
|
+
export declare function useMessageActionRuns(chatId: string): {
|
|
43
|
+
running: RunningMessageActions;
|
|
44
|
+
run: (action: MessageAction, message: AgentChatMessage, text: string) => void;
|
|
45
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { ReactElement, Ref, RefObject } from "react";
|
|
2
|
+
import type { AgentChatMessage } from "./chat-types.js";
|
|
3
|
+
export interface MessageFeedHandle {
|
|
4
|
+
/** Ставит прилипание и ведёт ленту вниз: после отправки сообщения. */
|
|
5
|
+
scrollToBottom: () => void;
|
|
6
|
+
}
|
|
7
|
+
export interface MessageFeedProps {
|
|
8
|
+
/** Скроллер скина: элемент, который прокручивается. */
|
|
9
|
+
scrollRef: RefObject<HTMLElement | null>;
|
|
10
|
+
messages: AgentChatMessage[];
|
|
11
|
+
renderMessage: (message: AgentChatMessage, index: number) => ReactElement;
|
|
12
|
+
/** Пока идёт ответ, последнее сообщение остаётся в дереве, даже если окно ленты уехало вверх. */
|
|
13
|
+
keepLastMounted: boolean;
|
|
14
|
+
/** Что показать при первом показе: сообщение с этим id встаёт у верха; по умолчанию лента внизу. */
|
|
15
|
+
openAtMessageId?: string;
|
|
16
|
+
ref?: Ref<MessageFeedHandle>;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Лента сообщений окном: в дереве только сообщения у экрана и небольшой запас. Правило
|
|
20
|
+
* прокрутки живёт здесь, а не в скине: при первом показе истории лента внизу, у низа она едет
|
|
21
|
+
* за растущим ответом, а после шага читателя вверх (колесо, касание, клавиша) стоит на месте.
|
|
22
|
+
* Снова прилипает, когда читатель вернулся к низу ближе чем на 96 px.
|
|
23
|
+
*/
|
|
24
|
+
declare function MessageFeedView({ scrollRef, messages, renderMessage, keepLastMounted, openAtMessageId, ref }: MessageFeedProps): import("react").JSX.Element;
|
|
25
|
+
/** `memo`: ввод в поле и другие перерисовки чата, не меняющие ленту, её не трогают. */
|
|
26
|
+
export declare const MessageFeed: import("react").MemoExoticComponent<typeof MessageFeedView>;
|
|
27
|
+
export {};
|