quassel 0.1.1 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/ChatInputToolbar.d.ts +35 -6
  2. package/ChatMessages.d.ts +19 -15
  3. package/ChatPanel.d.ts +4 -2
  4. package/ChatSendContext.d.ts +7 -12
  5. package/DetailModeSwitch.d.ts +1 -1
  6. package/Markdown.d.ts +4 -11
  7. package/MessageActions.d.ts +1 -10
  8. package/PendingActionCard.d.ts +13 -0
  9. package/QuasselProvider.d.ts +76 -0
  10. package/README.md +39 -38
  11. package/StepPopover.d.ts +1 -1
  12. package/TimestampSwitch.d.ts +2 -2
  13. package/WorkingScenes.d.ts +3 -1
  14. package/attachments.d.ts +24 -0
  15. package/chat-attachments.d.ts +6 -0
  16. package/chat-scroll.d.ts +12 -0
  17. package/chat.css +2051 -0
  18. package/chunk-IGCFQJZB.js +183 -0
  19. package/chunk-IGCFQJZB.js.map +7 -0
  20. package/events.d.ts +74 -17
  21. package/events.js +11 -1
  22. package/index.d.ts +17 -10
  23. package/index.js +1615 -988
  24. package/index.js.map +4 -4
  25. package/options.d.ts +1 -1
  26. package/package.json +12 -10
  27. package/texts.d.ts +38 -11
  28. package/timestamps.d.ts +6 -4
  29. package/types.d.ts +6 -6
  30. package/ui/button.d.ts +8 -0
  31. package/ui/card.d.ts +5 -0
  32. package/ui/cn.d.ts +2 -0
  33. package/ui/popover.d.ts +4 -0
  34. package/ui/spinner.d.ts +2 -0
  35. package/ui/stop-button.d.ts +10 -0
  36. package/ui/toggle.d.ts +8 -0
  37. package/ui/useFileInput.d.ts +20 -0
  38. package/useChatAction.d.ts +6 -0
  39. package/QuestionCard.d.ts +0 -12
  40. package/chunk-DOLTANTM.js +0 -84
  41. package/chunk-DOLTANTM.js.map +0 -7
  42. package/css/chat.css +0 -927
  43. package/css/foundation/base.css +0 -24
  44. package/css/foundation/index.css +0 -3
  45. package/css/foundation/panel.css +0 -111
  46. package/css/foundation/tokens.css +0 -68
  47. package/css/foundation/wash.css +0 -26
  48. package/css/question.css +0 -149
  49. package/icons.d.ts +0 -15
  50. package/server/http.d.ts +0 -17
  51. package/server/index.d.ts +0 -6
  52. package/server/index.js +0 -478
  53. package/server/index.js.map +0 -7
  54. package/server/manager.d.ts +0 -12
  55. package/server/openai.d.ts +0 -16
  56. package/server/session.d.ts +0 -27
  57. package/server/store.d.ts +0 -18
  58. package/server/types.d.ts +0 -62
  59. package/useChat.d.ts +0 -14
@@ -0,0 +1,183 @@
1
+ // packages/quassel/src/chat-attachments.ts
2
+ var MAX_CHAT_ATTACHMENTS = 8;
3
+ var MAX_CHAT_ATTACHMENT_BYTES = 20 * 1024 * 1024;
4
+ var MAX_CHAT_REQUEST_BYTES = 29 * 1024 * 1024;
5
+ var chatAttachmentMediaType = (name, mediaType) => {
6
+ const explicit = mediaType.trim().toLowerCase();
7
+ if (explicit) return explicit === "video/quicktime" ? "video/mov" : explicit;
8
+ const types = {
9
+ png: "image/png",
10
+ jpg: "image/jpeg",
11
+ jpeg: "image/jpeg",
12
+ gif: "image/gif",
13
+ webp: "image/webp",
14
+ avif: "image/avif",
15
+ svg: "image/svg+xml",
16
+ pdf: "application/pdf",
17
+ mp4: "video/mp4",
18
+ webm: "video/webm",
19
+ mov: "video/mov",
20
+ mpeg: "video/mpeg",
21
+ mpg: "video/mpeg",
22
+ txt: "text/plain",
23
+ md: "text/markdown",
24
+ csv: "text/csv",
25
+ tsv: "text/tab-separated-values",
26
+ json: "application/json",
27
+ jsonl: "application/x-ndjson",
28
+ xml: "application/xml",
29
+ yaml: "application/yaml",
30
+ yml: "application/yaml",
31
+ ts: "text/plain",
32
+ tsx: "text/plain",
33
+ js: "application/javascript",
34
+ py: "text/plain",
35
+ cs: "text/plain",
36
+ fs: "text/plain",
37
+ html: "text/html",
38
+ css: "text/css",
39
+ log: "text/plain"
40
+ };
41
+ return types[name.split(".").at(-1)?.toLowerCase() ?? ""] ?? "application/octet-stream";
42
+ };
43
+ var parseChatAttachments = (value) => {
44
+ if (value === void 0) return [];
45
+ if (!Array.isArray(value) || value.length > MAX_CHAT_ATTACHMENTS) {
46
+ throw new Error(`Anh\xE4nge m\xFCssen eine Liste mit h\xF6chstens ${MAX_CHAT_ATTACHMENTS} Dateien sein.`);
47
+ }
48
+ let total = 0;
49
+ return value.map((entry, index) => {
50
+ if (!entry || typeof entry !== "object" || Array.isArray(entry)) throw new Error(`Anhang ${index + 1} ist ung\xFCltig.`);
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(`Anhang ${index + 1} ben\xF6tigt einen g\xFCltigen Dateinamen ohne Pfad.`);
53
+ const normalizedType = typeof mediaType === "string" ? chatAttachmentMediaType(name, mediaType) : "";
54
+ if (!/^[a-z0-9!#$&^_.+-]+\/[a-z0-9!#$&^_.+-]+$/i.test(normalizedType)) {
55
+ throw new Error(`Anhang ${name} ben\xF6tigt einen g\xFCltigen MIME-Typ.`);
56
+ }
57
+ if (typeof data === "string" && data.length > Math.ceil(MAX_CHAT_ATTACHMENT_BYTES / 3) * 4)
58
+ throw new Error("Anh\xE4nge d\xFCrfen zusammen h\xF6chstens 20 MiB gro\xDF sein.");
59
+ if (typeof data !== "string" || data.length % 4 !== 0 || !/^[A-Za-z0-9+/]*={0,2}$/.test(data)) {
60
+ throw new Error(`Anhang ${name} enth\xE4lt ung\xFCltiges Base64.`);
61
+ }
62
+ const padding = data.endsWith("==") ? 2 : data.endsWith("=") ? 1 : 0;
63
+ const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
64
+ if (padding && (alphabet.indexOf(data[data.length - padding - 1]) & (padding === 2 ? 15 : 3)) !== 0) {
65
+ throw new Error(`Anhang ${name} enth\xE4lt nichtkanonisches Base64.`);
66
+ }
67
+ total += data.length / 4 * 3 - padding;
68
+ if (total > MAX_CHAT_ATTACHMENT_BYTES) throw new Error("Anh\xE4nge d\xFCrfen zusammen h\xF6chstens 20 MiB gro\xDF sein.");
69
+ return { name, mediaType: normalizedType, data };
70
+ });
71
+ };
72
+
73
+ // packages/quassel/src/events.ts
74
+ var keyCounter = 0;
75
+ var newKey = () => typeof crypto !== "undefined" && typeof crypto.randomUUID === "function" ? crypto.randomUUID() : `qsl-${Date.now().toString(36)}-${(keyCounter += 1).toString(36)}`;
76
+ function openMessageIndex(messages) {
77
+ for (let index = messages.length - 1; index >= 0; index--) {
78
+ const message = messages[index];
79
+ if (!message.closed && (message.role === "assistant" || message.role === "thinking")) return index;
80
+ }
81
+ return -1;
82
+ }
83
+ function closeOpen(messages) {
84
+ const index = openMessageIndex(messages);
85
+ if (index === -1) {
86
+ return messages;
87
+ }
88
+ return messages.map((message, position) => position === index ? { ...message, closed: true } : message);
89
+ }
90
+ function appendDelta(messages, role, delta, at, textCursor) {
91
+ const index = openMessageIndex(messages);
92
+ const current = messages[index];
93
+ if (current && current.role === role && current.textCursor?.conversationId === textCursor?.conversationId && current.textCursor?.sequence === textCursor?.sequence) {
94
+ return messages.map((message, position) => position === index ? { ...message, text: message.text + delta, ...textCursor ? { textCursor } : {} } : message);
95
+ }
96
+ return [...closeOpen(messages), { key: newKey(), role, text: delta, at, ...textCursor ? { textCursor } : {} }];
97
+ }
98
+ function applyEvent(messages, event) {
99
+ switch (event.kind) {
100
+ case "reset":
101
+ return [];
102
+ case "user":
103
+ return [...messages, {
104
+ key: newKey(),
105
+ role: "user",
106
+ text: event.text,
107
+ closed: true,
108
+ at: event.at,
109
+ ...event.inputId ? { inputId: event.inputId } : {},
110
+ ...event.attachments?.length ? { attachments: event.attachments } : {}
111
+ }];
112
+ case "steered":
113
+ return messages.map((message) => message.inputId === event.inputId ? { ...message, steered: true } : message);
114
+ case "text":
115
+ return appendDelta(messages, "assistant", event.delta, event.at, event.cursor);
116
+ case "thinking":
117
+ return appendDelta(messages, "thinking", event.delta, event.at);
118
+ case "tool":
119
+ return [
120
+ ...closeOpen(messages),
121
+ {
122
+ key: newKey(),
123
+ role: "tool",
124
+ text: event.label ?? event.name,
125
+ closed: true,
126
+ tool: { id: event.id, name: event.name, arguments: event.arguments },
127
+ at: event.at
128
+ }
129
+ ];
130
+ case "tool-result": {
131
+ const index = messages.map((message) => message.tool?.id).lastIndexOf(event.id);
132
+ return messages.map(
133
+ (message, position) => position === index && message.tool ? { ...message, tool: { ...message.tool, result: event.result, isError: event.isError } } : message
134
+ );
135
+ }
136
+ case "action":
137
+ return [
138
+ ...closeOpen(messages),
139
+ {
140
+ key: newKey(),
141
+ role: "action",
142
+ text: event.text,
143
+ closed: true,
144
+ action: { actionId: event.actionId, owner: event.owner, payload: event.payload },
145
+ at: event.at
146
+ }
147
+ ];
148
+ case "action-resolved":
149
+ return messages.map(
150
+ (message) => message.action?.actionId === event.actionId ? { ...message, action: { ...message.action, status: event.status, result: event.result } } : message
151
+ );
152
+ case "system":
153
+ return [...closeOpen(messages), { key: newKey(), role: "system", text: event.text, closed: true, at: event.at }];
154
+ case "turn-done":
155
+ return closeOpen(messages);
156
+ default:
157
+ return messages;
158
+ }
159
+ }
160
+ function prettyJson(value) {
161
+ try {
162
+ return JSON.stringify(JSON.parse(value), null, 2);
163
+ } catch {
164
+ return value;
165
+ }
166
+ }
167
+ function compactToolLine(tool) {
168
+ const args = tool.arguments.replace(/\s+/g, " ").trim();
169
+ const line = args && args !== "{}" ? `${tool.name} ${args}` : tool.name;
170
+ return line.length > 160 ? `${line.slice(0, 160)} ...` : line;
171
+ }
172
+
173
+ export {
174
+ MAX_CHAT_ATTACHMENTS,
175
+ MAX_CHAT_ATTACHMENT_BYTES,
176
+ MAX_CHAT_REQUEST_BYTES,
177
+ chatAttachmentMediaType,
178
+ parseChatAttachments,
179
+ applyEvent,
180
+ prettyJson,
181
+ compactToolLine
182
+ };
183
+ //# sourceMappingURL=chunk-IGCFQJZB.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(`Anh\u00E4nge m\u00FCssen eine Liste mit h\u00F6chstens ${MAX_CHAT_ATTACHMENTS} Dateien sein.`);\n }\n let total = 0;\n return value.map((entry, index) => {\n if (!entry || typeof entry !== \"object\" || Array.isArray(entry)) throw new Error(`Anhang ${index + 1} ist ung\u00FCltig.`);\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(`Anhang ${index + 1} ben\u00F6tigt einen g\u00FCltigen Dateinamen ohne Pfad.`);\n const normalizedType = typeof mediaType === \"string\" ? chatAttachmentMediaType(name, mediaType) : \"\";\n if (!/^[a-z0-9!#$&^_.+-]+\\/[a-z0-9!#$&^_.+-]+$/i.test(normalizedType)) {\n throw new Error(`Anhang ${name} ben\u00F6tigt einen g\u00FCltigen MIME-Typ.`);\n }\n if (typeof data === \"string\" && data.length > Math.ceil(MAX_CHAT_ATTACHMENT_BYTES / 3) * 4)\n throw new Error(\"Anh\u00E4nge d\u00FCrfen zusammen h\u00F6chstens 20 MiB gro\u00DF sein.\");\n if (typeof data !== \"string\" || data.length % 4 !== 0 || !/^[A-Za-z0-9+/]*={0,2}$/.test(data)) {\n throw new Error(`Anhang ${name} enth\u00E4lt ung\u00FCltiges 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(`Anhang ${name} enth\u00E4lt nichtkanonisches Base64.`);\n }\n total += data.length / 4 * 3 - padding;\n if (total > MAX_CHAT_ATTACHMENT_BYTES) throw new Error(\"Anh\u00E4nge d\u00FCrfen zusammen h\u00F6chstens 20 MiB gro\u00DF sein.\");\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 * Eine Aktion, die auf eine Eingabe des Benutzers wartet. Form und Inhalt von `payload` und\n * `result` geh\u00F6ren dem Plugin in `owner`; `status` gesetzt hei\u00DFt: erledigt, die Karte ist Beleg.\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 /** Absenderkennung f\u00FCr die Darstellung; unabh\u00E4ngig von Rolle und Sprechblasenlabel. */\n sender?: string;\n text: string;\n textCursor?: ChatTextCursor;\n closed?: boolean;\n attachments?: ChatAttachment[];\n tool?: ToolInfo;\n action?: PendingAction;\n /** ISO-Zeitpunkt der Nachricht; Anzeige optional (ChatMessages showTimestamps). */\n at?: string;\n /** Der ActorInput hinter einer eingehenden Nachricht; an ihm h\u00E4ngt die Steering-Markierung. */\n inputId?: string;\n /** Die Nachricht ist in einen schon laufenden Turn eingespeist worden, statt einen eigenen zu beginnen. */\n steered?: boolean;\n /** Farbige Sprechblase statt Fliesstext, z.B. fuer Mehrparteien-Gespraeche. */\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 fehlt ausserhalb sicherer Kontexte, etwa bei einem Host unter 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/** Eingehende Nachrichten lassen den laufenden Ausgabeblock offen. */\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
@@ -1,4 +1,19 @@
1
- export type Role = "user" | "assistant" | "thinking" | "tool" | "system" | "question";
1
+ export type Role = "user" | "assistant" | "thinking" | "tool" | "system" | "action";
2
+ export interface ChatAttachmentInput {
3
+ name: string;
4
+ mediaType: string;
5
+ data: string;
6
+ }
7
+ export interface ChatAttachment {
8
+ name: string;
9
+ mediaType: string;
10
+ size: number;
11
+ url: string;
12
+ }
13
+ export interface ChatAttachmentCapabilities {
14
+ input: readonly string[];
15
+ model: string;
16
+ }
2
17
  export interface ToolInfo {
3
18
  id: string;
4
19
  name: string;
@@ -6,22 +21,45 @@ export interface ToolInfo {
6
21
  result?: string;
7
22
  isError?: boolean;
8
23
  }
9
- /** Eine Rückfrage des Assistenten. `answer` gesetzt heißt: beantwortet, die Karte ist nur noch Beleg. */
10
- export interface Question {
11
- callId: string;
12
- options: string[];
13
- multi?: boolean;
14
- answer?: string;
24
+ export interface ChatTextCursor {
25
+ conversationId: string;
26
+ sequence: number;
27
+ /** Accumulated non-whitespace UTF-16 units in the turn anchored by sequence. */
28
+ offset: number;
29
+ }
30
+ export interface ChatJournalCursor {
31
+ conversationId: string;
32
+ eventId: string;
33
+ sequence: number;
34
+ }
35
+ /**
36
+ * Eine Aktion, die auf eine Eingabe des Benutzers wartet. Form und Inhalt von `payload` und
37
+ * `result` gehören dem Plugin in `owner`; `status` gesetzt heißt: erledigt, die Karte ist Beleg.
38
+ */
39
+ export interface PendingAction {
40
+ actionId: string;
41
+ owner: string | null;
42
+ payload: unknown;
43
+ status?: "approved" | "dismissed";
44
+ result?: unknown;
15
45
  }
16
46
  export interface Message {
17
47
  key: string;
18
48
  role: Role;
49
+ /** Absenderkennung für die Darstellung; unabhängig von Rolle und Sprechblasenlabel. */
50
+ sender?: string;
19
51
  text: string;
52
+ textCursor?: ChatTextCursor;
20
53
  closed?: boolean;
54
+ attachments?: ChatAttachment[];
21
55
  tool?: ToolInfo;
22
- question?: Question;
56
+ action?: PendingAction;
23
57
  /** ISO-Zeitpunkt der Nachricht; Anzeige optional (ChatMessages showTimestamps). */
24
58
  at?: string;
59
+ /** Der ActorInput hinter einer eingehenden Nachricht; an ihm hängt die Steering-Markierung. */
60
+ inputId?: string;
61
+ /** Die Nachricht ist in einen schon laufenden Turn eingespeist worden, statt einen eigenen zu beginnen. */
62
+ steered?: boolean;
25
63
  /** Farbige Sprechblase statt Fliesstext, z.B. fuer Mehrparteien-Gespraeche. */
26
64
  bubble?: {
27
65
  color: string;
@@ -29,16 +67,31 @@ export interface Message {
29
67
  label?: string;
30
68
  };
31
69
  }
70
+ export interface ChatStartupStatus {
71
+ status: "preparing" | "failed";
72
+ message: string;
73
+ }
32
74
  export type ChatEvent = {
33
75
  kind: "reset";
76
+ reason?: "conversation-reset";
77
+ conversationId: string | null;
78
+ } | {
79
+ kind: "replay-end";
80
+ conversationId: string | null;
34
81
  } | {
35
82
  kind: "user";
36
83
  text: string;
84
+ inputId?: string;
37
85
  at?: string;
86
+ attachments?: ChatAttachment[];
87
+ } | {
88
+ kind: "steered";
89
+ inputId: string;
38
90
  } | {
39
91
  kind: "text";
40
92
  delta: string;
41
93
  at?: string;
94
+ cursor: ChatTextCursor;
42
95
  } | {
43
96
  kind: "thinking";
44
97
  delta: string;
@@ -56,16 +109,17 @@ export type ChatEvent = {
56
109
  result: string;
57
110
  isError?: boolean;
58
111
  } | {
59
- kind: "question";
60
- callId: string;
112
+ kind: "action";
113
+ actionId: string;
114
+ owner: string | null;
61
115
  text: string;
62
- options: string[];
63
- multi?: boolean;
116
+ payload: unknown;
64
117
  at?: string;
65
118
  } | {
66
- kind: "question-answered";
67
- callId: string;
68
- answer: string;
119
+ kind: "action-resolved";
120
+ actionId: string;
121
+ status: "approved" | "dismissed";
122
+ result: unknown;
69
123
  } | {
70
124
  kind: "system";
71
125
  text: string;
@@ -73,16 +127,19 @@ export type ChatEvent = {
73
127
  } | {
74
128
  kind: "status";
75
129
  running: boolean;
130
+ startup?: ChatStartupStatus;
76
131
  } | {
77
132
  kind: "turn-done";
78
133
  } | {
79
- kind: "extension";
134
+ kind: "plugin";
80
135
  pluginId: string;
81
136
  type: string;
82
137
  payload?: unknown;
83
138
  at?: string;
139
+ journal?: ChatJournalCursor;
84
140
  };
85
- /** Der Streaming-Kern: Deltas verschmelzen mit dem letzten offenen Block, alles andere schließt ihn. */
141
+ /** Eingehende Nachrichten lassen den laufenden Ausgabeblock offen. */
86
142
  export declare function applyEvent(messages: Message[], event: ChatEvent): Message[];
87
143
  export declare function prettyJson(value: string): string;
88
144
  export declare function compactToolLine(tool: ToolInfo): string;
145
+ export { MAX_CHAT_ATTACHMENTS, MAX_CHAT_ATTACHMENT_BYTES, MAX_CHAT_REQUEST_BYTES, chatAttachmentMediaType, parseChatAttachments } from "./chat-attachments";
package/events.js CHANGED
@@ -1,11 +1,21 @@
1
1
  import {
2
+ MAX_CHAT_ATTACHMENTS,
3
+ MAX_CHAT_ATTACHMENT_BYTES,
4
+ MAX_CHAT_REQUEST_BYTES,
2
5
  applyEvent,
6
+ chatAttachmentMediaType,
3
7
  compactToolLine,
8
+ parseChatAttachments,
4
9
  prettyJson
5
- } from "./chunk-DOLTANTM.js";
10
+ } from "./chunk-IGCFQJZB.js";
6
11
  export {
12
+ MAX_CHAT_ATTACHMENTS,
13
+ MAX_CHAT_ATTACHMENT_BYTES,
14
+ MAX_CHAT_REQUEST_BYTES,
7
15
  applyEvent,
16
+ chatAttachmentMediaType,
8
17
  compactToolLine,
18
+ parseChatAttachments,
9
19
  prettyJson
10
20
  };
11
21
  //# sourceMappingURL=events.js.map
package/index.d.ts CHANGED
@@ -1,16 +1,23 @@
1
- export type { Role, ToolInfo, Question, Message, DetailMode, StepState, ChatEvent, ChatAnnouncement } from "./types";
1
+ export type { Role, ToolInfo, PendingAction, Message, ChatEvent, ChatTextCursor, ChatJournalCursor, ChatAttachment, ChatAttachmentInput, ChatAttachmentCapabilities, ChatStartupStatus, DetailMode, StepState, ChatAnnouncement, } from "./types";
2
2
  export { applyEvent, prettyJson, compactToolLine, stepState } from "./types";
3
3
  export type { ChatTexts } from "./texts";
4
- export { defaultTexts } from "./texts";
5
- export { useChat } from "./useChat";
4
+ export { defaultTexts, fillText } from "./texts";
6
5
  export { ChatMessages } from "./ChatMessages";
7
6
  export { ChatPanel } from "./ChatPanel";
7
+ export { ChatInputToolbar, type ChatInputHandle, type ToolbarAction } from "./ChatInputToolbar";
8
+ export { DetailModeSwitch, DETAIL_MODES, detailModeLabel } from "./DetailModeSwitch";
8
9
  export { TimestampSwitch } from "./TimestampSwitch";
9
- export { ChatInputToolbar } from "./ChatInputToolbar";
10
- export type { ToolbarAction } from "./ChatInputToolbar";
11
- export { Markdown, markdownPlainText } from "./Markdown";
10
+ export { Markdown, MarkdownCodeBlocks, MarkdownLinks, markdownPlainText, type LinkClickHandler } from "./Markdown";
11
+ export { MessageActions } from "./MessageActions";
12
+ export { PendingActionCard } from "./PendingActionCard";
13
+ export { StepPopover } from "./StepPopover";
14
+ export { WorkingScenes, type WorkingScenesProps } from "./WorkingScenes";
12
15
  export { announce } from "./announce";
13
- export type { ChatAppearance, TimestampOptions, CodeBlockOptions, BubbleOptions, MessageAction, MessageActionsOptions, SendShortcut } from "./options";
14
- export type { LinkClickHandler } from "./Markdown";
15
- export { DetailModeSwitch, DETAIL_MODES, detailModeLabel } from "./DetailModeSwitch";
16
- export { IconSend, IconStop, IconX, IconChevronDown, IconChevronRight, IconLayers, IconCheck, IconSpark, IconTool, IconClock, } from "./icons";
16
+ export { useChatAction, copyChatText } from "./useChatAction";
17
+ export { createChatScroll } from "./chat-scroll";
18
+ export { ChatSendContext, createChatSendScope, type ChatSendScope } from "./ChatSendContext";
19
+ export { messageDate, timestampDay, timestampLabel } from "./timestamps";
20
+ export { attachmentCapabilityError, attachmentDownloadUrl, attachmentMediaType, encodeAttachment, formatAttachmentSize, validateAttachmentSelection, MAX_CHAT_ATTACHMENTS, MAX_CHAT_ATTACHMENT_BYTES, } from "./attachments";
21
+ export type { ChatAppearance, TimestampOptions, CodeBlockOptions, BubbleOptions, MessageAction, MessageActionsOptions, SendShortcut, } from "./options";
22
+ export { appearanceStyle } from "./options";
23
+ export { QuasselProvider, defaultComponents, useQuasselComponents, type QuasselComponents, type QuasselButtonProps, type QuasselButtonVariant, type QuasselButtonSize, type QuasselToggleProps, type QuasselCardProps, type QuasselStopButtonProps, type QuasselPopoverProps, type QuasselPopoverContentProps, type QuasselPopoverAnchor, } from "./QuasselProvider";