quassel 0.3.0 → 0.4.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/ChatActionDock.d.ts +1 -1
- package/ChatInputToolbar.d.ts +9 -9
- package/ChatMessages.d.ts +10 -10
- package/ChatPanel.d.ts +3 -3
- package/DetailModeSwitch.d.ts +4 -6
- package/Markdown.d.ts +1 -1
- package/PendingActionCard.d.ts +2 -2
- package/README.md +2 -2
- package/TimestampSwitch.d.ts +1 -2
- package/WorkingScenes.d.ts +4 -4
- package/announce.d.ts +1 -1
- package/{chunk-IGCFQJZB.js → chunk-3VEL4HEO.js} +9 -9
- package/chunk-3VEL4HEO.js.map +7 -0
- package/events.d.ts +8 -8
- package/events.js +1 -1
- package/index.js +429 -437
- package/index.js.map +3 -3
- package/package.json +1 -1
- package/texts.d.ts +0 -1
- package/types.d.ts +4 -4
- package/ui/stop-button.d.ts +2 -2
- package/chunk-IGCFQJZB.js.map +0 -7
package/ChatActionDock.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
/**
|
|
1
|
+
/** Target of open actions: undefined = in the transcript, null = the dock above the input is not mounted yet. */
|
|
2
2
|
export declare const ChatActionDock: import("react").Context<HTMLElement | null | undefined>;
|
package/ChatInputToolbar.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { ReactNode, Ref } from "react";
|
|
|
2
2
|
import { ChatTexts } from "./texts";
|
|
3
3
|
import type { ChatAttachmentInput } from "./events";
|
|
4
4
|
import type { SendShortcut } from "./options";
|
|
5
|
-
/**
|
|
5
|
+
/** A toolbar button, declarative: icon, text or both - plus click handler. */
|
|
6
6
|
export interface ToolbarAction {
|
|
7
7
|
icon?: ReactNode;
|
|
8
8
|
label?: string;
|
|
@@ -11,9 +11,9 @@ export interface ToolbarAction {
|
|
|
11
11
|
active?: boolean;
|
|
12
12
|
onClick: () => void;
|
|
13
13
|
}
|
|
14
|
-
/** Hosts
|
|
14
|
+
/** Hosts control the draft from outside, e.g. to insert a template for editing. */
|
|
15
15
|
export interface ChatInputHandle {
|
|
16
|
-
/**
|
|
16
|
+
/** Replaces the draft, focuses the input and puts the caret at the end. */
|
|
17
17
|
insert: (text: string) => void;
|
|
18
18
|
focus: () => void;
|
|
19
19
|
reset: () => void;
|
|
@@ -22,11 +22,11 @@ export interface ChatInputHandle {
|
|
|
22
22
|
}) => Promise<void>;
|
|
23
23
|
}
|
|
24
24
|
/**
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
25
|
+
* The input card: borderless textarea on top, toolbar below a hairline. Custom buttons come
|
|
26
|
+
* declaratively through `actions` or freely through the left/right slots; while work is running
|
|
27
|
+
* the stop button stays visible, and typing shows the send button next to it, which then
|
|
28
|
+
* interjects. `rows` sets the initial height; with `maxRows` the textarea grows with its content
|
|
29
|
+
* up to that number of rows and scrolls after that.
|
|
30
30
|
*/
|
|
31
31
|
export declare function ChatInputToolbar({ onSend, onStop, running, disabled, sendDisabled, layout, detailsContainer, onErrorChange, inputAriaControls, rows, maxRows, actions, toolbarLeft, toolbarRight, texts, handleRef, attachments: attachmentsEnabled, attachmentCapabilities, attachmentCapabilitiesError, onAttachmentsChange, initialValue, onDraftChange, sendShortcut, autoFocus, onAutoFocusSettled, }: {
|
|
32
32
|
onSend: (text: string, attachments?: ChatAttachmentInput[]) => void | Promise<void>;
|
|
@@ -45,7 +45,7 @@ export declare function ChatInputToolbar({ onSend, onStop, running, disabled, se
|
|
|
45
45
|
toolbarRight?: ReactNode;
|
|
46
46
|
texts?: Partial<ChatTexts>;
|
|
47
47
|
handleRef?: Ref<ChatInputHandle>;
|
|
48
|
-
/** false =
|
|
48
|
+
/** false = no attaching: neither button nor dropping or pasting files. */
|
|
49
49
|
attachments?: boolean;
|
|
50
50
|
attachmentCapabilities?: {
|
|
51
51
|
input: readonly string[];
|
package/ChatMessages.d.ts
CHANGED
|
@@ -4,34 +4,34 @@ import { ChatTexts } from "./texts";
|
|
|
4
4
|
import { type LinkClickHandler } from "./Markdown";
|
|
5
5
|
import { type ChatAppearance, type TimestampOptions, type CodeBlockOptions, type BubbleOptions, type MessageActionsOptions } from "./options";
|
|
6
6
|
/**
|
|
7
|
-
*
|
|
8
|
-
*
|
|
7
|
+
* The transcript: its own scroll container with autoscroll while you are at the bottom, and a
|
|
8
|
+
* jump-to-end button as soon as you are reading. Usable read-only without a composer.
|
|
9
9
|
*/
|
|
10
10
|
export declare function ChatMessages({ messages, owner, detailMode, running, working, stepsExpandable, texts, toolArgumentsText, renderTool, renderAction, onDismissAction, onLinkClick, emptyState, className, scrollerClassName, showTimestamps, announce, scrollerRef, maxWidth, horizontalPadding, jumpToEndThreshold, appearance, timestampOptions, codeBlockOptions, bubbleOptions, messageActions, toolElapsedThreshold, }: {
|
|
11
11
|
messages: Message[];
|
|
12
|
-
/**
|
|
12
|
+
/** Contributions with this Message.sender appear without a speech bubble; null keeps the message defaults. */
|
|
13
13
|
owner?: string | null;
|
|
14
14
|
detailMode?: DetailMode;
|
|
15
15
|
running?: boolean;
|
|
16
16
|
working?: ReactNode;
|
|
17
|
-
/** false =
|
|
17
|
+
/** false = thinking and tool steps cannot be expanded (no popover). */
|
|
18
18
|
stepsExpandable?: boolean;
|
|
19
19
|
texts?: Partial<ChatTexts>;
|
|
20
20
|
toolArgumentsText?: (tool: ToolInfo) => string;
|
|
21
|
-
/**
|
|
21
|
+
/** Custom rendering for single tools; undefined = default rendering. */
|
|
22
22
|
renderTool?: (tool: ToolInfo) => ReactNode | undefined;
|
|
23
|
-
/**
|
|
23
|
+
/** Rendering of a pending action by its owner; undefined = generic card. */
|
|
24
24
|
renderAction?: (action: PendingAction, text: string) => ReactNode | undefined;
|
|
25
25
|
onDismissAction?: (actionId: string) => void;
|
|
26
|
-
/**
|
|
26
|
+
/** Intercepts clicks on Markdown links; true = handled, the browser does not follow. */
|
|
27
27
|
onLinkClick?: LinkClickHandler;
|
|
28
28
|
emptyState?: ReactNode;
|
|
29
29
|
className?: string;
|
|
30
|
-
/**
|
|
30
|
+
/** Host classes for the scroll container (data-quassel-transcript). */
|
|
31
31
|
scrollerClassName?: string;
|
|
32
|
-
/** true =
|
|
32
|
+
/** true = subtle HH:MM column to the left of each block (Message.at). */
|
|
33
33
|
showTimestamps?: boolean;
|
|
34
|
-
/**
|
|
34
|
+
/** Screen reader announcement of finished answers and new actions; false = the host announces itself. */
|
|
35
35
|
announce?: false | ((announcement: ChatAnnouncement) => string | undefined);
|
|
36
36
|
/** Exposes the scroll container so hosts can scroll without querying the DOM. */
|
|
37
37
|
scrollerRef?: (element: HTMLDivElement | null) => void;
|
package/ChatPanel.d.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { type CSSProperties, type ReactNode, type Ref } from "react";
|
|
2
2
|
import { type ChatAppearance } from "./options";
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* Transcript with an overlaid composer. The frame measures the composer itself and gives the
|
|
5
|
+
* transcript the footer space it needs, so no host has to set inner dimensions.
|
|
6
6
|
*/
|
|
7
7
|
export declare function ChatPanel({ children, composer, className, nodeRef, maxWidth, horizontalPadding, appearance, scrollOnSend, }: {
|
|
8
8
|
children: ReactNode;
|
|
9
|
-
/**
|
|
9
|
+
/** The composer; it lies over the transcript, not below it. */
|
|
10
10
|
composer?: ReactNode;
|
|
11
11
|
className?: string;
|
|
12
12
|
nodeRef?: Ref<HTMLDivElement>;
|
package/DetailModeSwitch.d.ts
CHANGED
|
@@ -3,16 +3,14 @@ import { ChatTexts } from "./texts";
|
|
|
3
3
|
export declare const DETAIL_MODES: readonly DetailMode[];
|
|
4
4
|
export declare const detailModeLabel: (mode: DetailMode, texts?: Partial<ChatTexts>) => string;
|
|
5
5
|
/**
|
|
6
|
-
*
|
|
7
|
-
*
|
|
6
|
+
* An icon button that cycles the detail level of the thinking and tool steps. The order comes
|
|
7
|
+
* from `modes`, the current value shows in the icon and in the tooltip.
|
|
8
8
|
*/
|
|
9
|
-
export declare function DetailModeSwitch({ mode, onChange, modes,
|
|
9
|
+
export declare function DetailModeSwitch({ mode, onChange, modes, texts, className, }: {
|
|
10
10
|
mode: DetailMode;
|
|
11
11
|
onChange: (mode: DetailMode) => void;
|
|
12
|
-
/**
|
|
12
|
+
/** Selection and order of cycling; the default is all modes. */
|
|
13
13
|
modes?: readonly DetailMode[];
|
|
14
|
-
/** false = die Beschriftung bleibt auch in einer schmalen Eingabe-Karte stehen. */
|
|
15
|
-
collapsible?: boolean;
|
|
16
14
|
texts?: Partial<ChatTexts>;
|
|
17
15
|
className?: string;
|
|
18
16
|
}): import("react").JSX.Element;
|
package/Markdown.d.ts
CHANGED
|
@@ -15,5 +15,5 @@ export declare function Markdown({ text, streaming }: {
|
|
|
15
15
|
text: string;
|
|
16
16
|
streaming?: boolean;
|
|
17
17
|
}): import("react").JSX.Element;
|
|
18
|
-
/**
|
|
18
|
+
/** The same syntax as the renderer, as plain text without markup, e.g. for screen reader announcements. */
|
|
19
19
|
export declare function markdownPlainText(text: string): string;
|
package/PendingActionCard.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { PendingAction } from "./types";
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
4
|
-
*
|
|
3
|
+
* The rendering of a pending action without a contribution from its owner: title, the hint
|
|
4
|
+
* that input is expected, and dismissing. Only the plugin knows the form.
|
|
5
5
|
*/
|
|
6
6
|
export declare function PendingActionCard({ text, action, waitingLabel, dismissLabel, dismissedLabel, onDismiss, }: {
|
|
7
7
|
text: string;
|
package/README.md
CHANGED
|
@@ -46,7 +46,7 @@ variables too.
|
|
|
46
46
|
- Timestamps, day separators, message actions, configurable bubbles and appearance.
|
|
47
47
|
- Screen reader friendly: the transcript is not a live region; finished replies and new
|
|
48
48
|
actions are announced once, with the host in control of the wording.
|
|
49
|
-
- All labels are replaceable through `texts` (defaults are
|
|
49
|
+
- All labels are replaceable through `texts` (defaults are English).
|
|
50
50
|
|
|
51
51
|
## Install
|
|
52
52
|
|
|
@@ -98,7 +98,7 @@ pnpm release --dry-run
|
|
|
98
98
|
```
|
|
99
99
|
|
|
100
100
|
The full guide - every prop, the event contract, theming, slots, dark mode and publishing -
|
|
101
|
-
is in [docs/guide.md](https://github.com/SchlenkR/quassel/blob/main/docs/guide.md)
|
|
101
|
+
is in [docs/guide.md](https://github.com/SchlenkR/quassel/blob/main/docs/guide.md). It is written so that an AI assistant can build
|
|
102
102
|
an application with quassel from it.
|
|
103
103
|
|
|
104
104
|
## License
|
package/TimestampSwitch.d.ts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { type ChatTexts } from "./texts";
|
|
2
|
-
export declare function TimestampSwitch({ showTimestamps, onChange,
|
|
2
|
+
export declare function TimestampSwitch({ showTimestamps, onChange, texts, className }: {
|
|
3
3
|
showTimestamps: boolean;
|
|
4
4
|
onChange: (showTimestamps: boolean) => void;
|
|
5
|
-
collapsible?: boolean;
|
|
6
5
|
texts?: Partial<ChatTexts>;
|
|
7
6
|
className?: string;
|
|
8
7
|
}): import("react").JSX.Element;
|
package/WorkingScenes.d.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
export interface WorkingScenesProps {
|
|
2
2
|
/** Limits scenes to one or two visible characters for small status fields. */
|
|
3
3
|
compact?: boolean;
|
|
4
|
-
/** Text
|
|
4
|
+
/** Text for screen readers, default "Working ...". */
|
|
5
5
|
label?: string;
|
|
6
6
|
}
|
|
7
7
|
/**
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
8
|
+
* The default working indicator: randomly rotating mini scenes. Random order and start phase,
|
|
9
|
+
* so the same scenes never feel like a loop. The working prop of ChatMessages replaces it
|
|
10
|
+
* completely.
|
|
11
11
|
*/
|
|
12
12
|
export declare function WorkingScenes({ label, compact }: WorkingScenesProps): import("react").JSX.Element;
|
package/announce.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
/**
|
|
1
|
+
/** Announces text through a shared, hidden live region: throttled, and paused while the tab is hidden. */
|
|
2
2
|
export declare function announce(text: string): void;
|
|
@@ -43,29 +43,29 @@ var chatAttachmentMediaType = (name, mediaType) => {
|
|
|
43
43
|
var parseChatAttachments = (value) => {
|
|
44
44
|
if (value === void 0) return [];
|
|
45
45
|
if (!Array.isArray(value) || value.length > MAX_CHAT_ATTACHMENTS) {
|
|
46
|
-
throw new Error(`
|
|
46
|
+
throw new Error(`Attachments must be a list of at most ${MAX_CHAT_ATTACHMENTS} files.`);
|
|
47
47
|
}
|
|
48
48
|
let total = 0;
|
|
49
49
|
return value.map((entry, index) => {
|
|
50
|
-
if (!entry || typeof entry !== "object" || Array.isArray(entry)) throw new Error(`
|
|
50
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry)) throw new Error(`Attachment ${index + 1} is invalid.`);
|
|
51
51
|
const { name, mediaType, data } = entry;
|
|
52
|
-
if (typeof name !== "string" || !name.trim() || name.length > 255 || /[/\\\u0000-\u001f\u007f]/.test(name) || name === "." || name === "..") throw new Error(`
|
|
52
|
+
if (typeof name !== "string" || !name.trim() || name.length > 255 || /[/\\\u0000-\u001f\u007f]/.test(name) || name === "." || name === "..") throw new Error(`Attachment ${index + 1} needs a valid file name without a path.`);
|
|
53
53
|
const normalizedType = typeof mediaType === "string" ? chatAttachmentMediaType(name, mediaType) : "";
|
|
54
54
|
if (!/^[a-z0-9!#$&^_.+-]+\/[a-z0-9!#$&^_.+-]+$/i.test(normalizedType)) {
|
|
55
|
-
throw new Error(`
|
|
55
|
+
throw new Error(`Attachment ${name} needs a valid MIME type.`);
|
|
56
56
|
}
|
|
57
57
|
if (typeof data === "string" && data.length > Math.ceil(MAX_CHAT_ATTACHMENT_BYTES / 3) * 4)
|
|
58
|
-
throw new Error("
|
|
58
|
+
throw new Error("Attachments may be at most 20 MiB in total.");
|
|
59
59
|
if (typeof data !== "string" || data.length % 4 !== 0 || !/^[A-Za-z0-9+/]*={0,2}$/.test(data)) {
|
|
60
|
-
throw new Error(`
|
|
60
|
+
throw new Error(`Attachment ${name} contains invalid Base64.`);
|
|
61
61
|
}
|
|
62
62
|
const padding = data.endsWith("==") ? 2 : data.endsWith("=") ? 1 : 0;
|
|
63
63
|
const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
64
64
|
if (padding && (alphabet.indexOf(data[data.length - padding - 1]) & (padding === 2 ? 15 : 3)) !== 0) {
|
|
65
|
-
throw new Error(`
|
|
65
|
+
throw new Error(`Attachment ${name} contains non-canonical Base64.`);
|
|
66
66
|
}
|
|
67
67
|
total += data.length / 4 * 3 - padding;
|
|
68
|
-
if (total > MAX_CHAT_ATTACHMENT_BYTES) throw new Error("
|
|
68
|
+
if (total > MAX_CHAT_ATTACHMENT_BYTES) throw new Error("Attachments may be at most 20 MiB in total.");
|
|
69
69
|
return { name, mediaType: normalizedType, data };
|
|
70
70
|
});
|
|
71
71
|
};
|
|
@@ -180,4 +180,4 @@ export {
|
|
|
180
180
|
prettyJson,
|
|
181
181
|
compactToolLine
|
|
182
182
|
};
|
|
183
|
-
//# sourceMappingURL=chunk-
|
|
183
|
+
//# sourceMappingURL=chunk-3VEL4HEO.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/chat-attachments.ts", "../src/events.ts"],
|
|
4
|
+
"sourcesContent": ["import type { ChatAttachmentInput } from \"./events\";\n\nexport const MAX_CHAT_ATTACHMENTS = 8;\nexport const MAX_CHAT_ATTACHMENT_BYTES = 20 * 1024 * 1024;\nexport const MAX_CHAT_REQUEST_BYTES = 29 * 1024 * 1024;\n\nexport const chatAttachmentMediaType = (name: string, mediaType: string): string => {\n const explicit = mediaType.trim().toLowerCase();\n if (explicit) return explicit === \"video/quicktime\" ? \"video/mov\" : explicit;\n const types: Record<string, string> = {\n png: \"image/png\", jpg: \"image/jpeg\", jpeg: \"image/jpeg\", gif: \"image/gif\", webp: \"image/webp\",\n avif: \"image/avif\", svg: \"image/svg+xml\", pdf: \"application/pdf\",\n mp4: \"video/mp4\", webm: \"video/webm\", mov: \"video/mov\", mpeg: \"video/mpeg\", mpg: \"video/mpeg\",\n txt: \"text/plain\", md: \"text/markdown\", csv: \"text/csv\", tsv: \"text/tab-separated-values\",\n json: \"application/json\", jsonl: \"application/x-ndjson\", xml: \"application/xml\",\n yaml: \"application/yaml\", yml: \"application/yaml\", ts: \"text/plain\", tsx: \"text/plain\",\n js: \"application/javascript\", py: \"text/plain\", cs: \"text/plain\", fs: \"text/plain\",\n html: \"text/html\", css: \"text/css\", log: \"text/plain\",\n };\n return types[name.split(\".\").at(-1)?.toLowerCase() ?? \"\"] ?? \"application/octet-stream\";\n};\n\nexport const parseChatAttachments = (value: unknown): ChatAttachmentInput[] => {\n if (value === undefined) return [];\n if (!Array.isArray(value) || value.length > MAX_CHAT_ATTACHMENTS) {\n throw new Error(`Attachments must be a list of at most ${MAX_CHAT_ATTACHMENTS} files.`);\n }\n let total = 0;\n return value.map((entry, index) => {\n if (!entry || typeof entry !== \"object\" || Array.isArray(entry)) throw new Error(`Attachment ${index + 1} is invalid.`);\n const { name, mediaType, data } = entry as Record<string, unknown>;\n if (typeof name !== \"string\" || !name.trim() || name.length > 255 || /[/\\\\\\u0000-\\u001f\\u007f]/.test(name)\n || name === \".\" || name === \"..\") throw new Error(`Attachment ${index + 1} needs a valid file name without a path.`);\n const normalizedType = typeof mediaType === \"string\" ? chatAttachmentMediaType(name, mediaType) : \"\";\n if (!/^[a-z0-9!#$&^_.+-]+\\/[a-z0-9!#$&^_.+-]+$/i.test(normalizedType)) {\n throw new Error(`Attachment ${name} needs a valid MIME type.`);\n }\n if (typeof data === \"string\" && data.length > Math.ceil(MAX_CHAT_ATTACHMENT_BYTES / 3) * 4)\n throw new Error(\"Attachments may be at most 20 MiB in total.\");\n if (typeof data !== \"string\" || data.length % 4 !== 0 || !/^[A-Za-z0-9+/]*={0,2}$/.test(data)) {\n throw new Error(`Attachment ${name} contains invalid Base64.`);\n }\n const padding = data.endsWith(\"==\") ? 2 : data.endsWith(\"=\") ? 1 : 0;\n const alphabet = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\";\n if (padding && (alphabet.indexOf(data[data.length - padding - 1]) & (padding === 2 ? 15 : 3)) !== 0) {\n throw new Error(`Attachment ${name} contains non-canonical Base64.`);\n }\n total += data.length / 4 * 3 - padding;\n if (total > MAX_CHAT_ATTACHMENT_BYTES) throw new Error(\"Attachments may be at most 20 MiB in total.\");\n return { name, mediaType: normalizedType, data };\n });\n};\n", "export type Role = \"user\" | \"assistant\" | \"thinking\" | \"tool\" | \"system\" | \"action\";\n\nexport interface ChatAttachmentInput {\n name: string;\n mediaType: string;\n data: string;\n}\n\nexport interface ChatAttachment {\n name: string;\n mediaType: string;\n size: number;\n url: string;\n}\n\nexport interface ChatAttachmentCapabilities {\n input: readonly string[];\n model: string;\n}\n\nexport interface ToolInfo {\n id: string;\n name: string;\n arguments: string;\n result?: string;\n isError?: boolean;\n}\n\nexport interface ChatTextCursor {\n conversationId: string;\n sequence: number;\n /** Accumulated non-whitespace UTF-16 units in the turn anchored by sequence. */\n offset: number;\n}\n\nexport interface ChatJournalCursor {\n conversationId: string;\n eventId: string;\n sequence: number;\n}\n\n/**\n * An action waiting for input from the user. Shape and content of `payload` and `result`\n * belong to the plugin in `owner`; a set `status` means: done, the card is a record.\n */\nexport interface PendingAction {\n actionId: string;\n owner: string | null;\n payload: unknown;\n status?: \"approved\" | \"dismissed\";\n result?: unknown;\n}\n\nexport interface Message {\n key: string;\n role: Role;\n /** Sender identifier for display; independent of role and bubble label. */\n sender?: string;\n text: string;\n textCursor?: ChatTextCursor;\n closed?: boolean;\n attachments?: ChatAttachment[];\n tool?: ToolInfo;\n action?: PendingAction;\n /** ISO timestamp of the message; display optional (ChatMessages showTimestamps). */\n at?: string;\n /** The ActorInput behind an incoming message; the steering mark hangs on it. */\n inputId?: string;\n /** The message was fed into an already running turn instead of starting its own. */\n steered?: boolean;\n /** Colored speech bubble instead of running text, e.g. for multi-party conversations. */\n bubble?: { color: string; side: \"start\" | \"end\"; label?: string };\n}\n\nexport interface ChatStartupStatus {\n status: \"preparing\" | \"failed\";\n message: string;\n}\n\nexport type ChatEvent =\n | { kind: \"reset\"; reason?: \"conversation-reset\"; conversationId: string | null }\n | { kind: \"replay-end\"; conversationId: string | null }\n | { kind: \"user\"; text: string; inputId?: string; at?: string; attachments?: ChatAttachment[] }\n | { kind: \"steered\"; inputId: string }\n | { kind: \"text\"; delta: string; at?: string; cursor: ChatTextCursor }\n | { kind: \"thinking\"; delta: string; at?: string }\n | { kind: \"tool\"; id: string; name: string; arguments: string; label?: string; at?: string }\n | { kind: \"tool-result\"; id: string; result: string; isError?: boolean }\n | { kind: \"action\"; actionId: string; owner: string | null; text: string; payload: unknown; at?: string }\n | { kind: \"action-resolved\"; actionId: string; status: \"approved\" | \"dismissed\"; result: unknown }\n | { kind: \"system\"; text: string; at?: string }\n | { kind: \"status\"; running: boolean; startup?: ChatStartupStatus }\n | { kind: \"turn-done\" }\n | { kind: \"plugin\"; pluginId: string; type: string; payload?: unknown; at?: string; journal?: ChatJournalCursor };\n\nlet keyCounter = 0;\n\n// crypto.randomUUID is missing outside secure contexts, e.g. for a host served over http.\nconst newKey = (): string => typeof crypto !== \"undefined\" && typeof crypto.randomUUID === \"function\"\n ? crypto.randomUUID()\n : `qsl-${Date.now().toString(36)}-${(keyCounter += 1).toString(36)}`;\n\nfunction openMessageIndex(messages: Message[]): number {\n for (let index = messages.length - 1; index >= 0; index--) {\n const message = messages[index];\n if (!message.closed && (message.role === \"assistant\" || message.role === \"thinking\")) return index;\n }\n return -1;\n}\n\nfunction closeOpen(messages: Message[]): Message[] {\n const index = openMessageIndex(messages);\n if (index === -1) {\n return messages;\n }\n return messages.map((message, position) => position === index ? { ...message, closed: true } : message);\n}\n\nfunction appendDelta(messages: Message[], role: Role, delta: string, at?: string, textCursor?: ChatTextCursor): Message[] {\n const index = openMessageIndex(messages);\n const current = messages[index];\n if (current && current.role === role\n && current.textCursor?.conversationId === textCursor?.conversationId\n && current.textCursor?.sequence === textCursor?.sequence) {\n return messages.map((message, position) => position === index\n ? { ...message, text: message.text + delta, ...(textCursor ? { textCursor } : {}) }\n : message);\n }\n return [...closeOpen(messages), { key: newKey(), role, text: delta, at, ...(textCursor ? { textCursor } : {}) }];\n}\n\n/** Incoming messages leave the running output block open. */\nexport function applyEvent(messages: Message[], event: ChatEvent): Message[] {\n switch (event.kind) {\n case \"reset\":\n return [];\n case \"user\":\n return [...messages, { key: newKey(), role: \"user\", text: event.text, closed: true, at: event.at,\n ...(event.inputId ? { inputId: event.inputId } : {}),\n ...(event.attachments?.length ? { attachments: event.attachments } : {}) }];\n case \"steered\":\n return messages.map((message) => message.inputId === event.inputId ? { ...message, steered: true } : message);\n case \"text\":\n return appendDelta(messages, \"assistant\", event.delta, event.at, event.cursor);\n case \"thinking\":\n return appendDelta(messages, \"thinking\", event.delta, event.at);\n case \"tool\":\n return [\n ...closeOpen(messages),\n {\n key: newKey(),\n role: \"tool\",\n text: event.label ?? event.name,\n closed: true,\n tool: { id: event.id, name: event.name, arguments: event.arguments },\n at: event.at,\n },\n ];\n case \"tool-result\": {\n const index = messages.map((message) => message.tool?.id).lastIndexOf(event.id);\n return messages.map((message, position) =>\n position === index && message.tool\n ? { ...message, tool: { ...message.tool, result: event.result, isError: event.isError } }\n : message,\n );\n }\n case \"action\":\n return [\n ...closeOpen(messages),\n {\n key: newKey(),\n role: \"action\",\n text: event.text,\n closed: true,\n action: { actionId: event.actionId, owner: event.owner, payload: event.payload },\n at: event.at,\n },\n ];\n case \"action-resolved\":\n return messages.map((message) =>\n message.action?.actionId === event.actionId\n ? { ...message, action: { ...message.action, status: event.status, result: event.result } }\n : message,\n );\n case \"system\":\n return [...closeOpen(messages), { key: newKey(), role: \"system\", text: event.text, closed: true, at: event.at }];\n case \"turn-done\":\n return closeOpen(messages);\n default:\n return messages;\n }\n}\n\nexport function prettyJson(value: string): string {\n try {\n return JSON.stringify(JSON.parse(value), null, 2);\n } catch {\n return value;\n }\n}\n\nexport function compactToolLine(tool: ToolInfo): string {\n const args = tool.arguments.replace(/\\s+/g, \" \").trim();\n const line = args && args !== \"{}\" ? `${tool.name} ${args}` : tool.name;\n return line.length > 160 ? `${line.slice(0, 160)} ...` : line;\n}\n\nexport { MAX_CHAT_ATTACHMENTS, MAX_CHAT_ATTACHMENT_BYTES, MAX_CHAT_REQUEST_BYTES, chatAttachmentMediaType, parseChatAttachments } from \"./chat-attachments\";\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/events.d.ts
CHANGED
|
@@ -33,8 +33,8 @@ export interface ChatJournalCursor {
|
|
|
33
33
|
sequence: number;
|
|
34
34
|
}
|
|
35
35
|
/**
|
|
36
|
-
*
|
|
37
|
-
*
|
|
36
|
+
* An action waiting for input from the user. Shape and content of `payload` and `result`
|
|
37
|
+
* belong to the plugin in `owner`; a set `status` means: done, the card is a record.
|
|
38
38
|
*/
|
|
39
39
|
export interface PendingAction {
|
|
40
40
|
actionId: string;
|
|
@@ -46,7 +46,7 @@ export interface PendingAction {
|
|
|
46
46
|
export interface Message {
|
|
47
47
|
key: string;
|
|
48
48
|
role: Role;
|
|
49
|
-
/**
|
|
49
|
+
/** Sender identifier for display; independent of role and bubble label. */
|
|
50
50
|
sender?: string;
|
|
51
51
|
text: string;
|
|
52
52
|
textCursor?: ChatTextCursor;
|
|
@@ -54,13 +54,13 @@ export interface Message {
|
|
|
54
54
|
attachments?: ChatAttachment[];
|
|
55
55
|
tool?: ToolInfo;
|
|
56
56
|
action?: PendingAction;
|
|
57
|
-
/** ISO
|
|
57
|
+
/** ISO timestamp of the message; display optional (ChatMessages showTimestamps). */
|
|
58
58
|
at?: string;
|
|
59
|
-
/**
|
|
59
|
+
/** The ActorInput behind an incoming message; the steering mark hangs on it. */
|
|
60
60
|
inputId?: string;
|
|
61
|
-
/**
|
|
61
|
+
/** The message was fed into an already running turn instead of starting its own. */
|
|
62
62
|
steered?: boolean;
|
|
63
|
-
/**
|
|
63
|
+
/** Colored speech bubble instead of running text, e.g. for multi-party conversations. */
|
|
64
64
|
bubble?: {
|
|
65
65
|
color: string;
|
|
66
66
|
side: "start" | "end";
|
|
@@ -138,7 +138,7 @@ export type ChatEvent = {
|
|
|
138
138
|
at?: string;
|
|
139
139
|
journal?: ChatJournalCursor;
|
|
140
140
|
};
|
|
141
|
-
/**
|
|
141
|
+
/** Incoming messages leave the running output block open. */
|
|
142
142
|
export declare function applyEvent(messages: Message[], event: ChatEvent): Message[];
|
|
143
143
|
export declare function prettyJson(value: string): string;
|
|
144
144
|
export declare function compactToolLine(tool: ToolInfo): string;
|