@polymorfa/browser 0.1.0-dev.20260922094209
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/LICENSE +21 -0
- package/README.md +248 -0
- package/dist/calls/api.d.ts +22 -0
- package/dist/calls/api.d.ts.map +1 -0
- package/dist/calls/api.js +104 -0
- package/dist/calls/api.js.map +1 -0
- package/dist/calls/backend.d.ts +85 -0
- package/dist/calls/backend.d.ts.map +1 -0
- package/dist/calls/backend.js +141 -0
- package/dist/calls/backend.js.map +1 -0
- package/dist/calls/client.d.ts +56 -0
- package/dist/calls/client.d.ts.map +1 -0
- package/dist/calls/client.js +260 -0
- package/dist/calls/client.js.map +1 -0
- package/dist/calls/controller.d.ts +329 -0
- package/dist/calls/controller.d.ts.map +1 -0
- package/dist/calls/controller.js +1328 -0
- package/dist/calls/controller.js.map +1 -0
- package/dist/calls/diagnostics.d.ts +52 -0
- package/dist/calls/diagnostics.d.ts.map +1 -0
- package/dist/calls/diagnostics.js +188 -0
- package/dist/calls/diagnostics.js.map +1 -0
- package/dist/calls/index.d.ts +3 -0
- package/dist/calls/index.d.ts.map +1 -0
- package/dist/calls/index.js +5 -0
- package/dist/calls/index.js.map +1 -0
- package/dist/calls/internal.d.ts +8 -0
- package/dist/calls/internal.d.ts.map +1 -0
- package/dist/calls/internal.js +8 -0
- package/dist/calls/internal.js.map +1 -0
- package/dist/calls/media.d.ts +144 -0
- package/dist/calls/media.d.ts.map +1 -0
- package/dist/calls/media.js +512 -0
- package/dist/calls/media.js.map +1 -0
- package/dist/calls/signaling.d.ts +74 -0
- package/dist/calls/signaling.d.ts.map +1 -0
- package/dist/calls/signaling.js +169 -0
- package/dist/calls/signaling.js.map +1 -0
- package/dist/calls/socket.d.ts +95 -0
- package/dist/calls/socket.d.ts.map +1 -0
- package/dist/calls/socket.js +267 -0
- package/dist/calls/socket.js.map +1 -0
- package/dist/chat/composer.d.ts +59 -0
- package/dist/chat/composer.d.ts.map +1 -0
- package/dist/chat/composer.js +217 -0
- package/dist/chat/composer.js.map +1 -0
- package/dist/chat/conversation.d.ts +71 -0
- package/dist/chat/conversation.d.ts.map +1 -0
- package/dist/chat/conversation.js +204 -0
- package/dist/chat/conversation.js.map +1 -0
- package/dist/chat/index.d.ts +4 -0
- package/dist/chat/index.d.ts.map +1 -0
- package/dist/chat/index.js +4 -0
- package/dist/chat/index.js.map +1 -0
- package/dist/chat/voice-recorder.d.ts +75 -0
- package/dist/chat/voice-recorder.d.ts.map +1 -0
- package/dist/chat/voice-recorder.js +346 -0
- package/dist/chat/voice-recorder.js.map +1 -0
- package/dist/controller.d.ts +16 -0
- package/dist/controller.d.ts.map +1 -0
- package/dist/controller.js +50 -0
- package/dist/controller.js.map +1 -0
- package/dist/diagnostics.d.ts +27 -0
- package/dist/diagnostics.d.ts.map +1 -0
- package/dist/diagnostics.js +2 -0
- package/dist/diagnostics.js.map +1 -0
- package/dist/dropin/client.d.ts +82 -0
- package/dist/dropin/client.d.ts.map +1 -0
- package/dist/dropin/client.js +314 -0
- package/dist/dropin/client.js.map +1 -0
- package/dist/dropin/connect.d.ts +23 -0
- package/dist/dropin/connect.d.ts.map +1 -0
- package/dist/dropin/connect.js +54 -0
- package/dist/dropin/connect.js.map +1 -0
- package/dist/dropin/inbox.d.ts +131 -0
- package/dist/dropin/inbox.d.ts.map +1 -0
- package/dist/dropin/inbox.js +406 -0
- package/dist/dropin/inbox.js.map +1 -0
- package/dist/dropin/index.d.ts +5 -0
- package/dist/dropin/index.d.ts.map +1 -0
- package/dist/dropin/index.js +5 -0
- package/dist/dropin/index.js.map +1 -0
- package/dist/dropin/permissions.d.ts +29 -0
- package/dist/dropin/permissions.d.ts.map +1 -0
- package/dist/dropin/permissions.js +53 -0
- package/dist/dropin/permissions.js.map +1 -0
- package/dist/errors.d.ts +39 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +41 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/internal.d.ts +12 -0
- package/dist/internal.d.ts.map +1 -0
- package/dist/internal.js +13 -0
- package/dist/internal.js.map +1 -0
- package/dist/messaging/client.d.ts +54 -0
- package/dist/messaging/client.d.ts.map +1 -0
- package/dist/messaging/client.js +153 -0
- package/dist/messaging/client.js.map +1 -0
- package/dist/messaging/composer.d.ts +12 -0
- package/dist/messaging/composer.d.ts.map +1 -0
- package/dist/messaging/composer.js +36 -0
- package/dist/messaging/composer.js.map +1 -0
- package/dist/messaging/index.d.ts +4 -0
- package/dist/messaging/index.d.ts.map +1 -0
- package/dist/messaging/index.js +3 -0
- package/dist/messaging/index.js.map +1 -0
- package/dist/messaging/types.d.ts +209 -0
- package/dist/messaging/types.d.ts.map +1 -0
- package/dist/messaging/types.js +2 -0
- package/dist/messaging/types.js.map +1 -0
- package/dist/templates/controller.d.ts +45 -0
- package/dist/templates/controller.d.ts.map +1 -0
- package/dist/templates/controller.js +298 -0
- package/dist/templates/controller.js.map +1 -0
- package/dist/templates/index.d.ts +4 -0
- package/dist/templates/index.d.ts.map +1 -0
- package/dist/templates/index.js +3 -0
- package/dist/templates/index.js.map +1 -0
- package/dist/templates/transport.d.ts +7 -0
- package/dist/templates/transport.d.ts.map +1 -0
- package/dist/templates/transport.js +124 -0
- package/dist/templates/transport.js.map +1 -0
- package/dist/templates/types.d.ts +141 -0
- package/dist/templates/types.d.ts.map +1 -0
- package/dist/templates/types.js +2 -0
- package/dist/templates/types.js.map +1 -0
- package/dist/token.d.ts +37 -0
- package/dist/token.d.ts.map +1 -0
- package/dist/token.js +152 -0
- package/dist/token.js.map +1 -0
- package/dist/transport.d.ts +53 -0
- package/dist/transport.d.ts.map +1 -0
- package/dist/transport.js +328 -0
- package/dist/transport.js.map +1 -0
- package/package.json +45 -0
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import { ObservableController, } from "../controller.js";
|
|
2
|
+
/** Describe a picked, pasted, or dropped file as a composer attachment. */
|
|
3
|
+
export function localAttachmentFromFile(file, createId = () => crypto.randomUUID()) {
|
|
4
|
+
return {
|
|
5
|
+
id: createId(),
|
|
6
|
+
name: file.name,
|
|
7
|
+
size: file.size,
|
|
8
|
+
contentType: file.type === "" ? "application/octet-stream" : file.type,
|
|
9
|
+
file,
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Composer actions that send through a conversation, so the composer and the
|
|
14
|
+
* message list share one optimistic history. `upload` stays yours: it turns
|
|
15
|
+
* the local bytes into a hosted `MessageAttachment`.
|
|
16
|
+
*/
|
|
17
|
+
export function createConversationComposerActions(conversation, upload) {
|
|
18
|
+
return {
|
|
19
|
+
upload,
|
|
20
|
+
send: (draft, signal) => conversation.send({
|
|
21
|
+
text: draft.text,
|
|
22
|
+
...(draft.replyTo === undefined ? {} : { replyTo: draft.replyTo }),
|
|
23
|
+
...(draft.attachments.length === 0
|
|
24
|
+
? {}
|
|
25
|
+
: { attachments: draft.attachments }),
|
|
26
|
+
}, signal),
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
export class MessageComposerController extends ObservableController {
|
|
30
|
+
#actions;
|
|
31
|
+
#maxTextLength;
|
|
32
|
+
#maxAttachmentSize;
|
|
33
|
+
#uploads = new Map();
|
|
34
|
+
#sendAbort;
|
|
35
|
+
constructor(actions, options = {}) {
|
|
36
|
+
super({ status: "ready", text: "", attachments: [], sending: false }, options.now);
|
|
37
|
+
this.#actions = actions;
|
|
38
|
+
this.#maxTextLength = options.maxTextLength ?? 4096;
|
|
39
|
+
this.#maxAttachmentSize = options.maxAttachmentSize ?? 25 * 1024 * 1024;
|
|
40
|
+
}
|
|
41
|
+
setText(text) {
|
|
42
|
+
this.#update({ text });
|
|
43
|
+
}
|
|
44
|
+
setReplyTo(replyTo) {
|
|
45
|
+
this.#update(replyTo === undefined ? { clearReply: true } : { replyTo });
|
|
46
|
+
}
|
|
47
|
+
async addAttachment(attachment) {
|
|
48
|
+
this.assertActive();
|
|
49
|
+
if (attachment.size > this.#maxAttachmentSize)
|
|
50
|
+
throw new Error(`Attachment exceeds ${this.#maxAttachmentSize} bytes.`);
|
|
51
|
+
if (this.getSnapshot().attachments.some(({ id }) => id === attachment.id))
|
|
52
|
+
return;
|
|
53
|
+
const abort = new AbortController();
|
|
54
|
+
this.#uploads.set(attachment.id, abort);
|
|
55
|
+
this.#replaceAttachment({
|
|
56
|
+
...attachment,
|
|
57
|
+
status: "uploading",
|
|
58
|
+
progress: 0,
|
|
59
|
+
});
|
|
60
|
+
try {
|
|
61
|
+
const uploaded = await this.#actions.upload(attachment, (progress) => this.#progress(attachment.id, progress), abort.signal);
|
|
62
|
+
if (!abort.signal.aborted)
|
|
63
|
+
this.#replaceAttachment({
|
|
64
|
+
...attachment,
|
|
65
|
+
status: "ready",
|
|
66
|
+
progress: 1,
|
|
67
|
+
uploaded,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
catch (cause) {
|
|
71
|
+
if (!abort.signal.aborted)
|
|
72
|
+
this.#replaceAttachment({
|
|
73
|
+
...attachment,
|
|
74
|
+
status: "failed",
|
|
75
|
+
progress: 0,
|
|
76
|
+
error: errorMessage(cause),
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
finally {
|
|
80
|
+
this.#uploads.delete(attachment.id);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
cancelAttachment(id) {
|
|
84
|
+
this.#uploads.get(id)?.abort();
|
|
85
|
+
this.#uploads.delete(id);
|
|
86
|
+
const current = this.getSnapshot();
|
|
87
|
+
this.transition({
|
|
88
|
+
...composerFields(current),
|
|
89
|
+
attachments: current.attachments.filter((item) => item.id !== id),
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
async submit() {
|
|
93
|
+
const current = this.getSnapshot();
|
|
94
|
+
if (current.sending)
|
|
95
|
+
throw new Error("Composer is already sending.");
|
|
96
|
+
if (current.text.length > this.#maxTextLength)
|
|
97
|
+
this.#rejectSubmit(current, `Message text cannot exceed ${this.#maxTextLength} characters.`);
|
|
98
|
+
if (current.attachments.some(({ status }) => status !== "ready"))
|
|
99
|
+
this.#rejectSubmit(current, "Attachments must finish uploading before send.");
|
|
100
|
+
if (current.text.trim() === "" && current.attachments.length === 0)
|
|
101
|
+
throw new Error("Message cannot be empty.");
|
|
102
|
+
const abort = new AbortController();
|
|
103
|
+
this.#sendAbort = abort;
|
|
104
|
+
this.transition({ ...composerFields(current), sending: true });
|
|
105
|
+
try {
|
|
106
|
+
await this.#actions.send({
|
|
107
|
+
text: current.text,
|
|
108
|
+
...(current.replyTo === undefined
|
|
109
|
+
? {}
|
|
110
|
+
: { replyTo: current.replyTo }),
|
|
111
|
+
attachments: current.attachments.flatMap(({ uploaded }) => uploaded === undefined ? [] : [uploaded]),
|
|
112
|
+
}, abort.signal);
|
|
113
|
+
if (!abort.signal.aborted)
|
|
114
|
+
this.reset();
|
|
115
|
+
}
|
|
116
|
+
catch (cause) {
|
|
117
|
+
if (!abort.signal.aborted)
|
|
118
|
+
this.transition({
|
|
119
|
+
...composerFields(current),
|
|
120
|
+
status: "error",
|
|
121
|
+
sending: false,
|
|
122
|
+
error: errorMessage(cause),
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
finally {
|
|
126
|
+
if (this.#sendAbort === abort)
|
|
127
|
+
this.#sendAbort = undefined;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
/** Publish a validation failure so bound UI can show it, then reject. */
|
|
131
|
+
#rejectSubmit(current, message) {
|
|
132
|
+
this.transition({
|
|
133
|
+
...composerFields(current),
|
|
134
|
+
status: "error",
|
|
135
|
+
error: message,
|
|
136
|
+
});
|
|
137
|
+
throw new Error(message);
|
|
138
|
+
}
|
|
139
|
+
cancelSend() {
|
|
140
|
+
this.#sendAbort?.abort();
|
|
141
|
+
this.#sendAbort = undefined;
|
|
142
|
+
const current = this.getSnapshot();
|
|
143
|
+
this.transition({ ...composerFields(current), sending: false });
|
|
144
|
+
}
|
|
145
|
+
reset() {
|
|
146
|
+
for (const upload of this.#uploads.values())
|
|
147
|
+
upload.abort();
|
|
148
|
+
this.#uploads.clear();
|
|
149
|
+
this.transition({
|
|
150
|
+
status: "ready",
|
|
151
|
+
text: "",
|
|
152
|
+
attachments: [],
|
|
153
|
+
sending: false,
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
onDispose() {
|
|
157
|
+
for (const upload of this.#uploads.values())
|
|
158
|
+
upload.abort();
|
|
159
|
+
this.#uploads.clear();
|
|
160
|
+
this.#sendAbort?.abort();
|
|
161
|
+
}
|
|
162
|
+
#update(change) {
|
|
163
|
+
const current = this.getSnapshot();
|
|
164
|
+
const base = {
|
|
165
|
+
status: "ready",
|
|
166
|
+
text: change.text ?? current.text,
|
|
167
|
+
attachments: current.attachments,
|
|
168
|
+
sending: current.sending,
|
|
169
|
+
...(current.error === undefined ? {} : { error: current.error }),
|
|
170
|
+
};
|
|
171
|
+
this.transition({
|
|
172
|
+
...base,
|
|
173
|
+
...(change.clearReply
|
|
174
|
+
? {}
|
|
175
|
+
: change.replyTo === undefined
|
|
176
|
+
? current.replyTo === undefined
|
|
177
|
+
? {}
|
|
178
|
+
: { replyTo: current.replyTo }
|
|
179
|
+
: { replyTo: change.replyTo }),
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
#progress(id, progress) {
|
|
183
|
+
const current = this.getSnapshot();
|
|
184
|
+
if (!this.#uploads.has(id))
|
|
185
|
+
return;
|
|
186
|
+
this.transition({
|
|
187
|
+
...composerFields(current),
|
|
188
|
+
attachments: current.attachments.map((item) => item.id === id
|
|
189
|
+
? { ...item, progress: Math.max(0, Math.min(progress, 1)) }
|
|
190
|
+
: item),
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
#replaceAttachment(attachment) {
|
|
194
|
+
const current = this.getSnapshot();
|
|
195
|
+
const existing = current.attachments.some(({ id }) => id === attachment.id);
|
|
196
|
+
this.transition({
|
|
197
|
+
...composerFields(current),
|
|
198
|
+
attachments: existing
|
|
199
|
+
? current.attachments.map((item) => item.id === attachment.id ? attachment : item)
|
|
200
|
+
: [...current.attachments, attachment],
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
function composerFields(snapshot) {
|
|
205
|
+
return {
|
|
206
|
+
status: snapshot.status,
|
|
207
|
+
text: snapshot.text,
|
|
208
|
+
attachments: snapshot.attachments,
|
|
209
|
+
sending: snapshot.sending,
|
|
210
|
+
...(snapshot.replyTo === undefined ? {} : { replyTo: snapshot.replyTo }),
|
|
211
|
+
...(snapshot.error === undefined ? {} : { error: snapshot.error }),
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
function errorMessage(cause) {
|
|
215
|
+
return cause instanceof Error ? cause.message : "Composer request failed.";
|
|
216
|
+
}
|
|
217
|
+
//# sourceMappingURL=composer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"composer.js","sourceRoot":"","sources":["../../src/chat/composer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { ObservableController, type ControllerSnapshot } from "../controller.js";
|
|
2
|
+
export type MessageDirection = "inbound" | "outbound";
|
|
3
|
+
export type MessageStatus = "pending" | "sent" | "failed";
|
|
4
|
+
export interface MessageAttachment {
|
|
5
|
+
readonly id: string;
|
|
6
|
+
readonly name: string;
|
|
7
|
+
readonly size: number;
|
|
8
|
+
readonly contentType: string;
|
|
9
|
+
/** Where the full file can be opened or downloaded. */
|
|
10
|
+
readonly url?: string;
|
|
11
|
+
/** A smaller image to show inline; falls back to `url` for images. */
|
|
12
|
+
readonly previewUrl?: string;
|
|
13
|
+
}
|
|
14
|
+
export interface ConversationMessage {
|
|
15
|
+
readonly id: string;
|
|
16
|
+
readonly clientId?: string;
|
|
17
|
+
readonly text: string;
|
|
18
|
+
readonly createdAt: number;
|
|
19
|
+
readonly direction: MessageDirection;
|
|
20
|
+
readonly status: MessageStatus;
|
|
21
|
+
readonly replyTo?: string;
|
|
22
|
+
readonly attachments?: readonly MessageAttachment[];
|
|
23
|
+
readonly error?: string;
|
|
24
|
+
}
|
|
25
|
+
export interface OutgoingMessage {
|
|
26
|
+
readonly clientId: string;
|
|
27
|
+
readonly text: string;
|
|
28
|
+
readonly replyTo?: string;
|
|
29
|
+
readonly attachments?: readonly MessageAttachment[];
|
|
30
|
+
}
|
|
31
|
+
export interface ConversationPage {
|
|
32
|
+
readonly messages: readonly ConversationMessage[];
|
|
33
|
+
readonly nextCursor?: string;
|
|
34
|
+
}
|
|
35
|
+
export type ConversationEvent = {
|
|
36
|
+
readonly type: "upsert";
|
|
37
|
+
readonly message: ConversationMessage;
|
|
38
|
+
} | {
|
|
39
|
+
readonly type: "delete";
|
|
40
|
+
readonly messageId: string;
|
|
41
|
+
};
|
|
42
|
+
export interface ConversationDataSource {
|
|
43
|
+
load(cursor?: string, signal?: AbortSignal): Promise<ConversationPage>;
|
|
44
|
+
subscribe(listener: (event: ConversationEvent) => void): () => void;
|
|
45
|
+
send(message: OutgoingMessage, signal?: AbortSignal): Promise<ConversationMessage>;
|
|
46
|
+
}
|
|
47
|
+
export interface ConversationSnapshot extends ControllerSnapshot {
|
|
48
|
+
readonly status: "idle" | "loading" | "ready" | "loading_more" | "error";
|
|
49
|
+
readonly messages: readonly ConversationMessage[];
|
|
50
|
+
readonly hasMore: boolean;
|
|
51
|
+
readonly cursor?: string;
|
|
52
|
+
readonly error?: string;
|
|
53
|
+
}
|
|
54
|
+
export interface ConversationControllerOptions {
|
|
55
|
+
readonly createClientId?: () => string;
|
|
56
|
+
readonly now?: () => number;
|
|
57
|
+
}
|
|
58
|
+
export declare class ConversationController extends ObservableController<ConversationSnapshot> {
|
|
59
|
+
#private;
|
|
60
|
+
constructor(source: ConversationDataSource, options?: ConversationControllerOptions);
|
|
61
|
+
load(): Promise<void>;
|
|
62
|
+
loadMore(): Promise<void>;
|
|
63
|
+
/**
|
|
64
|
+
* Send optimistically. Aborting `signal` cancels the data-source request;
|
|
65
|
+
* the message is then marked failed so it can be retried.
|
|
66
|
+
*/
|
|
67
|
+
send(input: Omit<OutgoingMessage, "clientId">, signal?: AbortSignal): Promise<void>;
|
|
68
|
+
retry(clientId: string): Promise<void>;
|
|
69
|
+
protected onDispose(): void;
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=conversation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"conversation.d.ts","sourceRoot":"","sources":["../../src/chat/conversation.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import { ObservableController, } from "../controller.js";
|
|
2
|
+
export class ConversationController extends ObservableController {
|
|
3
|
+
#source;
|
|
4
|
+
#createClientId;
|
|
5
|
+
#now;
|
|
6
|
+
#abort = new AbortController();
|
|
7
|
+
#unsubscribe;
|
|
8
|
+
constructor(source, options = {}) {
|
|
9
|
+
const now = options.now ?? Date.now;
|
|
10
|
+
super({ status: "idle", messages: [], hasMore: false }, now);
|
|
11
|
+
this.#source = source;
|
|
12
|
+
this.#now = now;
|
|
13
|
+
this.#createClientId =
|
|
14
|
+
options.createClientId ?? (() => crypto.randomUUID());
|
|
15
|
+
}
|
|
16
|
+
async load() {
|
|
17
|
+
this.assertActive();
|
|
18
|
+
this.#abort.abort();
|
|
19
|
+
this.#abort = new AbortController();
|
|
20
|
+
this.transition({ status: "loading", messages: [], hasMore: false });
|
|
21
|
+
try {
|
|
22
|
+
const page = await this.#source.load(undefined, this.#abort.signal);
|
|
23
|
+
if (this.#abort.signal.aborted)
|
|
24
|
+
return;
|
|
25
|
+
this.transition(pageSnapshot(page));
|
|
26
|
+
this.#unsubscribe?.();
|
|
27
|
+
this.#unsubscribe = this.#source.subscribe((event) => this.#receive(event));
|
|
28
|
+
}
|
|
29
|
+
catch (cause) {
|
|
30
|
+
if (!this.#abort.signal.aborted)
|
|
31
|
+
this.transition({
|
|
32
|
+
status: "error",
|
|
33
|
+
messages: [],
|
|
34
|
+
hasMore: false,
|
|
35
|
+
error: errorMessage(cause),
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
async loadMore() {
|
|
40
|
+
const current = this.getSnapshot();
|
|
41
|
+
if (current.status === "loading_more" || current.cursor === undefined)
|
|
42
|
+
return;
|
|
43
|
+
this.transition({ ...conversationFields(current), status: "loading_more" });
|
|
44
|
+
try {
|
|
45
|
+
const page = await this.#source.load(current.cursor, this.#abort.signal);
|
|
46
|
+
if (this.#abort.signal.aborted)
|
|
47
|
+
return;
|
|
48
|
+
this.transition({
|
|
49
|
+
status: "ready",
|
|
50
|
+
messages: mergeMessages(current.messages, page.messages),
|
|
51
|
+
hasMore: page.nextCursor !== undefined,
|
|
52
|
+
...(page.nextCursor === undefined ? {} : { cursor: page.nextCursor }),
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
catch (cause) {
|
|
56
|
+
this.transition({
|
|
57
|
+
...conversationFields(current),
|
|
58
|
+
status: "error",
|
|
59
|
+
error: errorMessage(cause),
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Send optimistically. Aborting `signal` cancels the data-source request;
|
|
65
|
+
* the message is then marked failed so it can be retried.
|
|
66
|
+
*/
|
|
67
|
+
async send(input, signal) {
|
|
68
|
+
const clientId = this.#createClientId();
|
|
69
|
+
await this.#send({ ...input, clientId }, false, signal);
|
|
70
|
+
}
|
|
71
|
+
async retry(clientId) {
|
|
72
|
+
const message = this.getSnapshot().messages.find((candidate) => candidate.clientId === clientId);
|
|
73
|
+
if (message === undefined || message.status !== "failed")
|
|
74
|
+
throw new Error(`Failed message ${clientId} was not found.`);
|
|
75
|
+
await this.#send({
|
|
76
|
+
clientId,
|
|
77
|
+
text: message.text,
|
|
78
|
+
...(message.replyTo === undefined ? {} : { replyTo: message.replyTo }),
|
|
79
|
+
...(message.attachments === undefined
|
|
80
|
+
? {}
|
|
81
|
+
: { attachments: message.attachments }),
|
|
82
|
+
}, true);
|
|
83
|
+
}
|
|
84
|
+
onDispose() {
|
|
85
|
+
this.#abort.abort();
|
|
86
|
+
this.#unsubscribe?.();
|
|
87
|
+
this.#unsubscribe = undefined;
|
|
88
|
+
}
|
|
89
|
+
async #send(outgoing, replacing, external) {
|
|
90
|
+
this.assertActive();
|
|
91
|
+
const current = this.getSnapshot();
|
|
92
|
+
const optimistic = {
|
|
93
|
+
id: outgoing.clientId,
|
|
94
|
+
clientId: outgoing.clientId,
|
|
95
|
+
text: outgoing.text,
|
|
96
|
+
createdAt: this.#now(),
|
|
97
|
+
direction: "outbound",
|
|
98
|
+
status: "pending",
|
|
99
|
+
...(outgoing.replyTo === undefined ? {} : { replyTo: outgoing.replyTo }),
|
|
100
|
+
...(outgoing.attachments === undefined
|
|
101
|
+
? {}
|
|
102
|
+
: { attachments: outgoing.attachments }),
|
|
103
|
+
};
|
|
104
|
+
const messages = replacing
|
|
105
|
+
? replaceByClientId(current.messages, optimistic)
|
|
106
|
+
: [optimistic, ...current.messages];
|
|
107
|
+
this.transition({
|
|
108
|
+
...conversationFields(current),
|
|
109
|
+
status: "ready",
|
|
110
|
+
messages,
|
|
111
|
+
});
|
|
112
|
+
const signal = eitherSignal(this.#abort.signal, external);
|
|
113
|
+
try {
|
|
114
|
+
const acknowledged = await this.#source.send(outgoing, signal.signal);
|
|
115
|
+
if (!this.#abort.signal.aborted)
|
|
116
|
+
this.#replaceMessage(outgoing.clientId, {
|
|
117
|
+
...acknowledged,
|
|
118
|
+
status: "sent",
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
catch (cause) {
|
|
122
|
+
if (!this.#abort.signal.aborted)
|
|
123
|
+
this.#replaceMessage(outgoing.clientId, {
|
|
124
|
+
...optimistic,
|
|
125
|
+
status: "failed",
|
|
126
|
+
error: external?.aborted === true
|
|
127
|
+
? "Message send was cancelled."
|
|
128
|
+
: errorMessage(cause),
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
finally {
|
|
132
|
+
signal.release();
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
#receive(event) {
|
|
136
|
+
const current = this.getSnapshot();
|
|
137
|
+
const messages = event.type === "delete"
|
|
138
|
+
? current.messages.filter(({ id }) => id !== event.messageId)
|
|
139
|
+
: mergeMessages(current.messages, [event.message]);
|
|
140
|
+
this.transition({
|
|
141
|
+
...conversationFields(current),
|
|
142
|
+
status: "ready",
|
|
143
|
+
messages,
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
#replaceMessage(clientId, message) {
|
|
147
|
+
const current = this.getSnapshot();
|
|
148
|
+
this.transition({
|
|
149
|
+
...conversationFields(current),
|
|
150
|
+
status: "ready",
|
|
151
|
+
messages: replaceByClientId(current.messages, message),
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
/** A signal that aborts when either input does, plus listener cleanup. */
|
|
156
|
+
function eitherSignal(own, external) {
|
|
157
|
+
if (external === undefined)
|
|
158
|
+
return { signal: own, release: () => undefined };
|
|
159
|
+
const combined = new AbortController();
|
|
160
|
+
const abort = () => combined.abort();
|
|
161
|
+
if (own.aborted || external.aborted)
|
|
162
|
+
abort();
|
|
163
|
+
own.addEventListener("abort", abort, { once: true });
|
|
164
|
+
external.addEventListener("abort", abort, { once: true });
|
|
165
|
+
return {
|
|
166
|
+
signal: combined.signal,
|
|
167
|
+
release: () => {
|
|
168
|
+
own.removeEventListener("abort", abort);
|
|
169
|
+
external.removeEventListener("abort", abort);
|
|
170
|
+
},
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
function pageSnapshot(page) {
|
|
174
|
+
return {
|
|
175
|
+
status: "ready",
|
|
176
|
+
messages: mergeMessages([], page.messages),
|
|
177
|
+
hasMore: page.nextCursor !== undefined,
|
|
178
|
+
...(page.nextCursor === undefined ? {} : { cursor: page.nextCursor }),
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
function conversationFields(snapshot) {
|
|
182
|
+
return {
|
|
183
|
+
messages: snapshot.messages,
|
|
184
|
+
hasMore: snapshot.hasMore,
|
|
185
|
+
...(snapshot.cursor === undefined ? {} : { cursor: snapshot.cursor }),
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
function replaceByClientId(messages, message) {
|
|
189
|
+
const index = messages.findIndex((candidate) => candidate.id === message.id ||
|
|
190
|
+
(message.clientId !== undefined &&
|
|
191
|
+
candidate.clientId === message.clientId));
|
|
192
|
+
if (index < 0)
|
|
193
|
+
return [message, ...messages];
|
|
194
|
+
return messages.map((candidate, candidateIndex) => candidateIndex === index ? message : candidate);
|
|
195
|
+
}
|
|
196
|
+
function mergeMessages(current, incoming) {
|
|
197
|
+
return incoming.reduce((messages, message) => replaceByClientId(messages, message), [...current]);
|
|
198
|
+
}
|
|
199
|
+
function errorMessage(cause) {
|
|
200
|
+
return cause instanceof Error
|
|
201
|
+
? cause.message
|
|
202
|
+
: "Conversation request failed.";
|
|
203
|
+
}
|
|
204
|
+
//# sourceMappingURL=conversation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"conversation.js","sourceRoot":"","sources":["../../src/chat/conversation.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { ConversationController, type ConversationControllerOptions, type ConversationDataSource, type ConversationEvent, type ConversationMessage, type ConversationPage, type ConversationSnapshot, type MessageAttachment, type MessageDirection, type MessageStatus, type OutgoingMessage, } from "./conversation.js";
|
|
2
|
+
export { MessageComposerController, createConversationComposerActions, localAttachmentFromFile, type ComposerActions, type ComposerAttachment, type ComposerDraft, type LocalAttachment, type MessageComposerOptions, type MessageComposerSnapshot, } from "./composer.js";
|
|
3
|
+
export { VoiceNoteRecorder, voiceNoteName, type MediaRecorderConstructor, type VoiceNoteError, type VoiceNoteRecorderOptions, type VoiceNoteRecorderSnapshot, type VoiceNoteStatus, } from "./voice-recorder.js";
|
|
4
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/chat/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,sBAAsB,EACtB,KAAK,6BAA6B,EAClC,KAAK,sBAAsB,EAC3B,KAAK,iBAAiB,EACtB,KAAK,mBAAmB,EACxB,KAAK,gBAAgB,EACrB,KAAK,oBAAoB,EACzB,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,EACrB,KAAK,aAAa,EAClB,KAAK,eAAe,GACrB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,yBAAyB,EACzB,iCAAiC,EACjC,uBAAuB,EACvB,KAAK,eAAe,EACpB,KAAK,kBAAkB,EACvB,KAAK,aAAa,EAClB,KAAK,eAAe,EACpB,KAAK,sBAAsB,EAC3B,KAAK,uBAAuB,GAC7B,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,iBAAiB,EACjB,aAAa,EACb,KAAK,wBAAwB,EAC7B,KAAK,cAAc,EACnB,KAAK,wBAAwB,EAC7B,KAAK,yBAAyB,EAC9B,KAAK,eAAe,GACrB,MAAM,qBAAqB,CAAC"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { ConversationController, } from "./conversation.js";
|
|
2
|
+
export { MessageComposerController, createConversationComposerActions, localAttachmentFromFile, } from "./composer.js";
|
|
3
|
+
export { VoiceNoteRecorder, voiceNoteName, } from "./voice-recorder.js";
|
|
4
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/chat/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,sBAAsB,GAWvB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,yBAAyB,EACzB,iCAAiC,EACjC,uBAAuB,GAOxB,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,iBAAiB,EACjB,aAAa,GAMd,MAAM,qBAAqB,CAAC"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { ObservableController, type ControllerSnapshot } from "../controller.js";
|
|
2
|
+
export type VoiceNoteStatus = "idle" | "requesting" | "recording" | "stopping" | "error";
|
|
3
|
+
/** Why the last recording attempt failed. */
|
|
4
|
+
export type VoiceNoteError = "permission-denied" | "unavailable" | "failed";
|
|
5
|
+
export interface VoiceNoteRecorderSnapshot extends ControllerSnapshot {
|
|
6
|
+
readonly status: VoiceNoteStatus;
|
|
7
|
+
/** Milliseconds recorded so far; `0` when idle. */
|
|
8
|
+
readonly elapsed: number;
|
|
9
|
+
/** Input level from `0` to `1`, updated about every animation frame. */
|
|
10
|
+
readonly level: number;
|
|
11
|
+
/** Container type of the recording: `audio/webm`, `audio/ogg`, or `audio/mp4`. */
|
|
12
|
+
readonly mimeType?: string;
|
|
13
|
+
readonly error?: VoiceNoteError;
|
|
14
|
+
}
|
|
15
|
+
type MediaRecorderLike = Pick<MediaRecorder, "start" | "stop" | "state" | "mimeType"> & {
|
|
16
|
+
ondataavailable: ((event: {
|
|
17
|
+
readonly data: Blob;
|
|
18
|
+
}) => void) | null;
|
|
19
|
+
onstop: (() => void) | null;
|
|
20
|
+
onerror: (() => void) | null;
|
|
21
|
+
};
|
|
22
|
+
export interface MediaRecorderConstructor {
|
|
23
|
+
new (stream: MediaStream, options?: {
|
|
24
|
+
mimeType?: string;
|
|
25
|
+
}): MediaRecorderLike;
|
|
26
|
+
isTypeSupported?(type: string): boolean;
|
|
27
|
+
}
|
|
28
|
+
type AudioContextLike = {
|
|
29
|
+
createMediaStreamSource(stream: MediaStream): {
|
|
30
|
+
connect(node: unknown): void;
|
|
31
|
+
};
|
|
32
|
+
createAnalyser(): Pick<AnalyserNode, "fftSize" | "frequencyBinCount" | "getByteTimeDomainData">;
|
|
33
|
+
close(): Promise<void>;
|
|
34
|
+
};
|
|
35
|
+
export interface VoiceNoteRecorderOptions {
|
|
36
|
+
readonly mediaDevices?: Pick<MediaDevices, "getUserMedia">;
|
|
37
|
+
readonly MediaRecorder?: MediaRecorderConstructor;
|
|
38
|
+
/** Pass `null` to skip the level meter. */
|
|
39
|
+
readonly AudioContext?: (new () => AudioContextLike) | null;
|
|
40
|
+
readonly requestAnimationFrame?: (callback: () => void) => number;
|
|
41
|
+
readonly cancelAnimationFrame?: (handle: number) => void;
|
|
42
|
+
readonly now?: () => number;
|
|
43
|
+
/** Shortest interval between published level updates. Defaults to 50 ms. */
|
|
44
|
+
readonly updateInterval?: number;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Records a voice note from the microphone with `getUserMedia` and
|
|
48
|
+
* `MediaRecorder`, publishing status, elapsed time, and input level.
|
|
49
|
+
* `stop()` resolves with the recording as a `File`; `cancel()` discards it.
|
|
50
|
+
* Microphone tracks stop whenever recording ends and on `dispose()`.
|
|
51
|
+
*/
|
|
52
|
+
export declare class VoiceNoteRecorder extends ObservableController<VoiceNoteRecorderSnapshot> {
|
|
53
|
+
#private;
|
|
54
|
+
constructor(options?: VoiceNoteRecorderOptions);
|
|
55
|
+
/** Whether this environment can record: `getUserMedia` and `MediaRecorder`. */
|
|
56
|
+
static isSupported(options?: VoiceNoteRecorderOptions): boolean;
|
|
57
|
+
/** Ask for the microphone and start recording. Resolves once recording. */
|
|
58
|
+
start(): Promise<void>;
|
|
59
|
+
/**
|
|
60
|
+
* Stop recording and resolve with the voice note, or `undefined` when
|
|
61
|
+
* nothing was recording or the recorder produced no audio.
|
|
62
|
+
*/
|
|
63
|
+
stop(): Promise<File | undefined>;
|
|
64
|
+
/** Discard the recording, or abandon a pending permission request. */
|
|
65
|
+
cancel(): void;
|
|
66
|
+
protected onDispose(): void;
|
|
67
|
+
dispose(): void;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* `voice-note-<ISO time>.webm` (`.ogg` or `.m4a` for those containers), with
|
|
71
|
+
* `:` replaced for portability.
|
|
72
|
+
*/
|
|
73
|
+
export declare function voiceNoteName(type: string, time?: number): string;
|
|
74
|
+
export {};
|
|
75
|
+
//# sourceMappingURL=voice-recorder.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"voice-recorder.d.ts","sourceRoot":"","sources":["../../src/chat/voice-recorder.ts"],"names":[],"mappings":"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"}
|