@marktiderman/genesis-chat 0.1.1 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +379 -13
- package/dist/chunk-A4YJX7KT.js +544 -0
- package/dist/chunk-A4YJX7KT.js.map +1 -0
- package/dist/chunk-AZS3HNLM.js +30 -0
- package/dist/chunk-AZS3HNLM.js.map +1 -0
- package/dist/chunk-BJCE5O7V.js +70 -0
- package/dist/chunk-BJCE5O7V.js.map +1 -0
- package/dist/chunk-GBJ3YI4E.js +966 -0
- package/dist/chunk-GBJ3YI4E.js.map +1 -0
- package/dist/chunk-JU2FAPBI.js +609 -0
- package/dist/chunk-JU2FAPBI.js.map +1 -0
- package/dist/chunk-VEPZROQB.js +187 -0
- package/dist/chunk-VEPZROQB.js.map +1 -0
- package/dist/{client-BtdALFCx.d.ts → client-DUIG1hum.d.ts} +187 -11
- package/dist/index.d.ts +78 -163
- package/dist/index.js +3 -2
- package/dist/native.d.ts +1939 -1
- package/dist/native.js +5023 -0
- package/dist/native.js.map +1 -1
- package/dist/outbox-B9NAWQ58.d.ts +219 -0
- package/dist/push.d.ts +206 -0
- package/dist/push.js +3 -0
- package/dist/push.js.map +1 -0
- package/dist/react.d.ts +176 -7
- package/dist/react.js +3 -3
- package/dist/server.d.ts +551 -21
- package/dist/server.js +976 -34
- package/dist/server.js.map +1 -1
- package/dist/thread-oPra7pkW.d.ts +57 -0
- package/dist/web.d.ts +335 -12
- package/dist/web.js +1784 -64
- package/dist/web.js.map +1 -1
- package/migrations/0002_genesis_chat_coaching.sql +934 -0
- package/migrations/0003_genesis_chat_search_threads.sql +85 -0
- package/migrations/tests/0002_genesis_chat_coaching.test.sql +269 -0
- package/migrations/tests/0003_genesis_chat_search_threads.test.sql +50 -0
- package/package.json +78 -6
- package/src/__tests__/coaching-core.test.ts +334 -0
- package/src/__tests__/coaching-react.test.tsx +319 -0
- package/src/__tests__/coaching-web.test.tsx +521 -0
- package/src/__tests__/core.test.ts +91 -0
- package/src/__tests__/fake-xhr.ts +54 -0
- package/src/__tests__/outbox-hosted.test.ts +269 -0
- package/src/__tests__/outbox.test.ts +122 -0
- package/src/__tests__/realtime.test.ts +70 -0
- package/src/__tests__/review-279.test.ts +41 -0
- package/src/__tests__/review-fixes-core.test.ts +193 -0
- package/src/__tests__/review-fixes-web.test.tsx +405 -0
- package/src/__tests__/server-effects.test.ts +158 -0
- package/src/__tests__/server-fake-video.test.ts +81 -0
- package/src/__tests__/server-fakes.ts +96 -0
- package/src/__tests__/server-integrations.test.ts +247 -0
- package/src/__tests__/server-providers.test.ts +246 -0
- package/src/__tests__/server-runner.test.ts +277 -0
- package/src/__tests__/server-safety.test.ts +303 -0
- package/src/__tests__/web-markdown.test.tsx +88 -0
- package/src/__tests__/web.test.tsx +349 -0
- package/src/core/availability.ts +223 -0
- package/src/core/client.ts +783 -0
- package/src/core/extras.ts +106 -0
- package/src/core/media.ts +144 -0
- package/src/core/outbox.ts +635 -0
- package/src/core/thread.ts +171 -0
- package/src/core/transfer.ts +71 -0
- package/src/core/types.ts +328 -0
- package/src/index.ts +14 -0
- package/src/native/__tests__/auto-retry.test.tsx +128 -0
- package/src/native/__tests__/composer-mic-lock.test.tsx +115 -0
- package/src/native/__tests__/composer-state.test.ts +188 -0
- package/src/native/__tests__/composer.test.tsx +133 -0
- package/src/native/__tests__/emoji.test.tsx +254 -0
- package/src/native/__tests__/inbox-screen.test.tsx +48 -0
- package/src/native/__tests__/message-bubble.test.tsx +127 -0
- package/src/native/__tests__/organise-logic.test.ts +164 -0
- package/src/native/__tests__/organise-screens.test.tsx +424 -0
- package/src/native/__tests__/photo-viewer.test.tsx +226 -0
- package/src/native/__tests__/push-payload.test.ts +66 -0
- package/src/native/__tests__/push.test.ts +209 -0
- package/src/native/__tests__/recorder-machine.test.ts +96 -0
- package/src/native/__tests__/rows-actions.test.ts +76 -0
- package/src/native/__tests__/thread-layout.test.ts +41 -0
- package/src/native/__tests__/thread-screen.test.tsx +116 -0
- package/src/native/__tests__/tus.test.ts +154 -0
- package/src/native/__tests__/use-voice-recorder.test.tsx +316 -0
- package/src/native/__tests__/video-media.test.tsx +498 -0
- package/src/native/__tests__/video-recorder-state.test.ts +87 -0
- package/src/native/__tests__/video-upload.test.ts +227 -0
- package/src/native/access-error.tsx +31 -0
- package/src/native/actions.ts +42 -0
- package/src/native/attachment-cards.tsx +132 -0
- package/src/native/auto-retry.ts +69 -0
- package/src/native/availability.tsx +376 -0
- package/src/native/broadcast-sheet.tsx +193 -0
- package/src/native/composer-state.ts +302 -0
- package/src/native/composer.tsx +453 -0
- package/src/native/context.tsx +70 -0
- package/src/native/copy.ts +296 -0
- package/src/native/emoji-data.ts +21 -0
- package/src/native/emoji-picker.tsx +229 -0
- package/src/native/emoji.ts +118 -0
- package/src/native/icon-button.tsx +40 -0
- package/src/native/inbox-screen.tsx +122 -0
- package/src/native/index.ts +184 -0
- package/src/native/link-preview.tsx +104 -0
- package/src/native/media-colors.ts +15 -0
- package/src/native/media.tsx +187 -0
- package/src/native/message-actions-sheet.tsx +126 -0
- package/src/native/message-bubble.tsx +366 -0
- package/src/native/moderation.tsx +42 -0
- package/src/native/modules.ts +277 -0
- package/src/native/organise-copy.ts +195 -0
- package/src/native/photo-viewer.tsx +531 -0
- package/src/native/push.ts +375 -0
- package/src/native/recorder-machine.ts +176 -0
- package/src/native/rows.ts +81 -0
- package/src/native/schedule-sheet.tsx +328 -0
- package/src/native/search-screen.tsx +189 -0
- package/src/native/stack-options.ts +36 -0
- package/src/native/start-sheet.tsx +87 -0
- package/src/native/thread-screen.tsx +647 -0
- package/src/native/topics.tsx +234 -0
- package/src/native/tus.ts +282 -0
- package/src/native/use-composer.ts +71 -0
- package/src/native/use-jump.ts +91 -0
- package/src/native/use-voice-recorder.ts +234 -0
- package/src/native/video-player.tsx +410 -0
- package/src/native/video-recorder-state.ts +140 -0
- package/src/native/video-recorder.tsx +274 -0
- package/src/native/video-upload-tray.tsx +77 -0
- package/src/native/video-upload.ts +432 -0
- package/src/native/voice-player.tsx +301 -0
- package/src/react/index.tsx +894 -0
- package/src/react/previews.ts +51 -0
- package/src/server/assistant.ts +235 -0
- package/src/server/crypto.ts +86 -0
- package/src/server/expo.ts +119 -0
- package/src/server/fake-video.ts +171 -0
- package/src/server/handler.ts +32 -0
- package/src/server/index.ts +58 -0
- package/src/server/moderation.ts +155 -0
- package/src/server/mux.ts +290 -0
- package/src/server/notifications.ts +130 -0
- package/src/server/runner.ts +468 -0
- package/src/server/transcribers.ts +111 -0
- package/src/server/types.ts +171 -0
- package/src/server/unfurl.ts +480 -0
- package/src/server/upload.ts +158 -0
- package/src/web/attachments.tsx +379 -0
- package/src/web/availability.tsx +299 -0
- package/src/web/broadcast.tsx +166 -0
- package/src/web/bubble.tsx +585 -0
- package/src/web/cards.tsx +136 -0
- package/src/web/composer.tsx +571 -0
- package/src/web/emoji-picker.tsx +84 -0
- package/src/web/error-boundary.tsx +33 -0
- package/src/web/format.ts +55 -0
- package/src/web/inbox.tsx +196 -0
- package/src/web/index.ts +75 -0
- package/src/web/markdown.tsx +245 -0
- package/src/web/memory-client.ts +714 -0
- package/src/web/panel.tsx +135 -0
- package/src/web/schedule.tsx +204 -0
- package/src/web/search.tsx +129 -0
- package/src/web/thread-context.ts +64 -0
- package/src/web/thread.tsx +830 -0
- package/src/web/topics.tsx +160 -0
- package/src/web/use-voice-recorder.ts +264 -0
- package/src/web/video.tsx +516 -0
- package/src/web/voice-note.tsx +197 -0
- package/dist/chunk-27UTR5FW.js +0 -322
- package/dist/chunk-27UTR5FW.js.map +0 -1
- package/dist/chunk-JQXZUEEL.js +0 -525
- package/dist/chunk-JQXZUEEL.js.map +0 -1
- package/dist/chunk-SAY24E6V.js +0 -309
- package/dist/chunk-SAY24E6V.js.map +0 -1
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { q as ChatMessage, B as PendingMessage, g as ChatActivity } from './client-DUIG1hum.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @feat:chat
|
|
5
|
+
* Pure thread logic, the same on every platform: merging the server's messages
|
|
6
|
+
* with ones still on their way, grouping a run of messages from one sender,
|
|
7
|
+
* day dividers, the unread line, reactions by emoji, and the typing line.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
type ThreadItem = {
|
|
11
|
+
type: "message";
|
|
12
|
+
message: ChatMessage;
|
|
13
|
+
pending?: undefined;
|
|
14
|
+
} | {
|
|
15
|
+
type: "pending";
|
|
16
|
+
pending: PendingMessage;
|
|
17
|
+
message?: undefined;
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* The server's messages (by seq), then what is still sending, oldest first. A
|
|
21
|
+
* pending message the server already has (same clientId) is dropped, so a send
|
|
22
|
+
* that landed never shows twice.
|
|
23
|
+
*/
|
|
24
|
+
declare function mergeThread(server: ChatMessage[], pending: PendingMessage[]): ThreadItem[];
|
|
25
|
+
/** Adds or replaces messages in a page, keeping it sorted and free of duplicates. */
|
|
26
|
+
declare function upsertMessages(existing: ChatMessage[], incoming: ChatMessage[]): ChatMessage[];
|
|
27
|
+
declare function itemSender(item: ThreadItem): string | null;
|
|
28
|
+
declare function itemTime(item: ThreadItem): string;
|
|
29
|
+
/** Runs of one sender within five minutes share a header (name, avatar). */
|
|
30
|
+
declare function startsGroup(items: ThreadItem[], index: number, gapMs?: number): boolean;
|
|
31
|
+
declare function sameDay(a: string, b: string): boolean;
|
|
32
|
+
/** A day divider goes before this item. */
|
|
33
|
+
declare function startsDay(items: ThreadItem[], index: number): boolean;
|
|
34
|
+
/** "Today", "Yesterday", "Monday", or "12 Mar 2026". */
|
|
35
|
+
declare function dayLabel(iso: string, now?: Date, locale?: string): string;
|
|
36
|
+
/** The first message after what you had read, from someone else: where "New" goes. */
|
|
37
|
+
declare function firstUnreadId(messages: ChatMessage[], lastReadSeq: number, me: string): string | null;
|
|
38
|
+
interface ReactionSummary {
|
|
39
|
+
emoji: string;
|
|
40
|
+
count: number;
|
|
41
|
+
mine: boolean;
|
|
42
|
+
userIds: string[];
|
|
43
|
+
}
|
|
44
|
+
/** Reactions by emoji, most first, then in first-used order. */
|
|
45
|
+
declare function summarizeReactions(message: ChatMessage, me: string): ReactionSummary[];
|
|
46
|
+
/** Who is doing what now: activity older than six seconds has stopped. */
|
|
47
|
+
declare function currentActivity(activity: ChatActivity[], now?: number, staleMs?: number): ChatActivity[];
|
|
48
|
+
/** "Sam is typing…", "Sam and Lee are typing…", "Sam is recording a voice message…". */
|
|
49
|
+
declare function activityLine(activity: ChatActivity[], nameOf: (userId: string) => string): string | null;
|
|
50
|
+
/** Sends activity at most once every `everyMs`. */
|
|
51
|
+
declare function throttle(fn: () => void, everyMs?: number): () => void;
|
|
52
|
+
/** The ids @mentioned in markdown written as `@[Name](user:<uuid>)`. */
|
|
53
|
+
declare function parseMentions(body: string): string[];
|
|
54
|
+
/** A one-line preview: text, or what was attached. */
|
|
55
|
+
declare function previewText(body: string, attachment: string | null | undefined, deleted?: boolean): string;
|
|
56
|
+
|
|
57
|
+
export { type ReactionSummary as R, type ThreadItem as T, activityLine as a, itemTime as b, currentActivity as c, dayLabel as d, previewText as e, firstUnreadId as f, startsDay as g, startsGroup as h, itemSender as i, summarizeReactions as j, mergeThread as m, parseMentions as p, sameDay as s, throttle as t, upsertMessages as u };
|
package/dist/web.d.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
|
-
import { ComponentType, ReactNode, Ref } from 'react';
|
|
2
|
+
import { ComponentType, ReactNode, Ref, Component, ErrorInfo } from 'react';
|
|
3
3
|
import { SlotOverride } from '@marktiderman/genesis-core/slots';
|
|
4
|
-
import {
|
|
5
|
-
import { ConversationState } from './react.js';
|
|
6
|
-
import { ThreadItem
|
|
4
|
+
import { q as ChatMessage, m as ChatInboxItem, o as ChatLinkPreview$1, h as ChatAttachment, k as ChatConversation, p as ChatMember, t as ChatRole, i as ChatClient, g as ChatActivity, C as ChatAvailability, x as ChatTopic, T as TopicInput, u as ChatSearchHit, F as SendInput, A as AttachmentInput } from './client-DUIG1hum.js';
|
|
5
|
+
import { ChatAttachmentKind, ConversationState, ChatVideoUpload } from './react.js';
|
|
6
|
+
import { T as ThreadItem } from './thread-oPra7pkW.js';
|
|
7
|
+
import { L as LocalFile } from './outbox-B9NAWQ58.js';
|
|
7
8
|
import '@supabase/supabase-js';
|
|
8
9
|
import '@tanstack/query-core';
|
|
9
10
|
|
|
@@ -49,6 +50,46 @@ interface ChatInboxProps {
|
|
|
49
50
|
declare function inboxTitle(item: ChatInboxItem, resolveUser: ResolveUser, fallback?: (item: ChatInboxItem) => string): string;
|
|
50
51
|
declare function ChatInbox({ resolveUser, onSelect, selectedId, kind, archived, fallbackTitle, slots, className, ...rest }: ChatInboxProps): react.JSX.Element;
|
|
51
52
|
|
|
53
|
+
/**
|
|
54
|
+
* Whether a link preview shows its image. The image lives on the linked site, so loading it
|
|
55
|
+
* straight from there tells that site each reader's IP address and when they read the message
|
|
56
|
+
* (a tracking pixel the sender controls). So:
|
|
57
|
+
* - `"off"` (the default): no image;
|
|
58
|
+
* - `"proxy"`: through your own image proxy, `proxyUrl(imageUrl)`; no image without it;
|
|
59
|
+
* - `"direct"`: from the linked site (only if you accept that).
|
|
60
|
+
*/
|
|
61
|
+
type LinkPreviewImages = "off" | "proxy" | "direct";
|
|
62
|
+
/** The image a preview may show under this setting, or null. Only https image URLs are ever considered. */
|
|
63
|
+
declare function linkPreviewImageSrc(imageUrl: string | null | undefined, images?: LinkPreviewImages, proxyUrl?: (url: string) => string | null | undefined): string | null;
|
|
64
|
+
interface ChatLinkPreviewProps {
|
|
65
|
+
preview: ChatLinkPreview$1;
|
|
66
|
+
/** Whether to show its image, and how; `"off"` by default (see LinkPreviewImages). */
|
|
67
|
+
images?: LinkPreviewImages;
|
|
68
|
+
/** Your image proxy, for `images: "proxy"`: the URL to load instead of the linked site's. */
|
|
69
|
+
proxyUrl?: (url: string) => string | null | undefined;
|
|
70
|
+
tone?: "default" | "primary";
|
|
71
|
+
className?: string;
|
|
72
|
+
}
|
|
73
|
+
declare function ChatLinkPreview({ preview, images, proxyUrl, tone, className }: ChatLinkPreviewProps): react.JSX.Element | null;
|
|
74
|
+
interface ChatAttachmentCardProps {
|
|
75
|
+
title: string;
|
|
76
|
+
subtitle?: string | null;
|
|
77
|
+
/** An icon component (lucide); a clipboard by default. */
|
|
78
|
+
icon?: ComponentType<{
|
|
79
|
+
className?: string;
|
|
80
|
+
}>;
|
|
81
|
+
/** One button: a link (`href`, safe schemes only) or a handler. */
|
|
82
|
+
action?: {
|
|
83
|
+
label: string;
|
|
84
|
+
href?: string;
|
|
85
|
+
onPress?: () => void;
|
|
86
|
+
} | null;
|
|
87
|
+
tone?: "default" | "primary";
|
|
88
|
+
className?: string;
|
|
89
|
+
}
|
|
90
|
+
/** A card for an app's own attachment kind: an icon, a title, a subtitle and one action. */
|
|
91
|
+
declare function ChatAttachmentCard({ title, subtitle, icon: Icon, action, tone, className }: ChatAttachmentCardProps): react.JSX.Element;
|
|
92
|
+
|
|
52
93
|
/** One attachment as the renderers draw it, sent or still uploading. */
|
|
53
94
|
interface AttachmentView {
|
|
54
95
|
key: string;
|
|
@@ -95,6 +136,7 @@ interface LightboxProps {
|
|
|
95
136
|
}
|
|
96
137
|
/** A photo full size, with the previous and next ones on the arrow keys. */
|
|
97
138
|
declare function Lightbox({ images, index, onIndexChange }: LightboxProps): react.JSX.Element;
|
|
139
|
+
/** A video: a hosted one (Mux) streams once ready and says "Processing…" until then; a file plays from storage. */
|
|
98
140
|
declare function VideoAttachment({ view }: {
|
|
99
141
|
view: AttachmentView;
|
|
100
142
|
}): react.JSX.Element;
|
|
@@ -109,9 +151,11 @@ interface MessageAttachmentsProps {
|
|
|
109
151
|
tone: "default" | "primary";
|
|
110
152
|
/** For kinds the package doesn't draw; a generic card by default. */
|
|
111
153
|
slot?: ComponentType<ChatAttachmentSlotProps> | null;
|
|
154
|
+
/** Cards for registered app kinds (ChatProvider `attachmentKinds`), drawn before the slot. */
|
|
155
|
+
kinds?: Record<string, ChatAttachmentKind>;
|
|
112
156
|
}
|
|
113
157
|
/** Every attachment of one message, photos grouped into one grid. */
|
|
114
|
-
declare function MessageAttachments({ views, message, mine, tone, slot }: MessageAttachmentsProps): react.JSX.Element | null;
|
|
158
|
+
declare function MessageAttachments({ views, message, mine, tone, slot, kinds }: MessageAttachmentsProps): react.JSX.Element | null;
|
|
115
159
|
|
|
116
160
|
interface ChatThreadHeaderProps {
|
|
117
161
|
conversationId: string;
|
|
@@ -129,6 +173,8 @@ interface ChatThreadHeaderProps {
|
|
|
129
173
|
resolveUser: ResolveUser;
|
|
130
174
|
onBack?: () => void;
|
|
131
175
|
backClassName?: string;
|
|
176
|
+
/** Opens search in this conversation (absent when search is off). */
|
|
177
|
+
onSearch?: () => void;
|
|
132
178
|
}
|
|
133
179
|
interface ChatThreadEmptyProps {
|
|
134
180
|
conversationId: string;
|
|
@@ -163,15 +209,43 @@ interface ChatThreadProps {
|
|
|
163
209
|
threadRootId?: string;
|
|
164
210
|
/** One topic's messages. */
|
|
165
211
|
topic?: string;
|
|
166
|
-
/**
|
|
167
|
-
|
|
212
|
+
/**
|
|
213
|
+
* Shown on messages that have replies, to open their thread. Also called when a search result
|
|
214
|
+
* is a reply in a thread, with `jumpTo` (that reply) for the thread's own ChatThread.
|
|
215
|
+
*/
|
|
216
|
+
onOpenThread?: (message: ChatMessage, opts?: {
|
|
217
|
+
jumpTo?: {
|
|
218
|
+
messageId: string;
|
|
219
|
+
seq: number;
|
|
220
|
+
};
|
|
221
|
+
}) => void;
|
|
168
222
|
/** The quick reactions; 👍 ❤️ 😂 😮 😢 🙏 by default. */
|
|
169
223
|
quickReactions?: readonly string[];
|
|
170
224
|
placeholder?: string;
|
|
225
|
+
/** Show the topic bar even before the first topic exists (so an owner can add one). */
|
|
226
|
+
showTopics?: boolean;
|
|
227
|
+
/** Scroll to this message once it's loaded (a search result across conversations); `seq` lets earlier pages load to reach it. */
|
|
228
|
+
jumpTo?: {
|
|
229
|
+
messageId: string;
|
|
230
|
+
seq?: number;
|
|
231
|
+
threadRootId?: string | null;
|
|
232
|
+
} | null;
|
|
233
|
+
/** A search button in the header. Default true. */
|
|
234
|
+
search?: boolean;
|
|
235
|
+
/** Scheduled send in the composer. Default true. */
|
|
236
|
+
scheduling?: boolean;
|
|
237
|
+
/** The away banner. Default true. */
|
|
238
|
+
availability?: boolean;
|
|
239
|
+
/** Where the emoji picker loads its data from (self-hosted emojibase-data). Without it there is no full picker; the quick reactions stay. */
|
|
240
|
+
emojibaseUrl?: string;
|
|
241
|
+
/** Link preview images: `"off"` by default, `"proxy"` through `linkPreviewProxyUrl`, or `"direct"` from the linked site (which then sees readers' IPs). */
|
|
242
|
+
linkPreviewImages?: LinkPreviewImages;
|
|
243
|
+
/** Your image proxy for `linkPreviewImages: "proxy"`: the URL to load for an og:image. */
|
|
244
|
+
linkPreviewProxyUrl?: (url: string) => string | null | undefined;
|
|
171
245
|
slots?: ChatThreadSlots;
|
|
172
246
|
className?: string;
|
|
173
247
|
}
|
|
174
|
-
declare function ChatThread({ conversationId, resolveUser, title: titleProp, onBack, backClassName, threadRootId, topic, onOpenThread, quickReactions, placeholder, slots, className, }: ChatThreadProps): react.JSX.Element;
|
|
248
|
+
declare function ChatThread({ conversationId, resolveUser, title: titleProp, onBack, backClassName, threadRootId, topic: topicProp, onOpenThread, quickReactions, placeholder, showTopics, jumpTo: jumpToProp, search, scheduling, availability, emojibaseUrl, linkPreviewImages, linkPreviewProxyUrl, slots, className, }: ChatThreadProps): react.JSX.Element;
|
|
175
249
|
|
|
176
250
|
interface ChatPanelNoSelectionProps {
|
|
177
251
|
/** Words under the title; "Pick one on the left to read and reply." by default. */
|
|
@@ -193,9 +267,11 @@ interface ChatPanelProps {
|
|
|
193
267
|
slots?: {
|
|
194
268
|
noSelection?: SlotOverride<ChatPanelNoSelectionProps>;
|
|
195
269
|
};
|
|
270
|
+
/** Search across every conversation, above the inbox. Default true. */
|
|
271
|
+
search?: boolean;
|
|
196
272
|
className?: string;
|
|
197
273
|
}
|
|
198
|
-
declare function ChatPanel({ resolveUser, selectedId: controlled, defaultSelectedId, onSelectedChange, inboxHeader, inboxProps, threadProps, slots, className, }: ChatPanelProps): react.JSX.Element;
|
|
274
|
+
declare function ChatPanel({ resolveUser, selectedId: controlled, defaultSelectedId, onSelectedChange, inboxHeader, inboxProps, threadProps, slots, search, className, }: ChatPanelProps): react.JSX.Element;
|
|
199
275
|
|
|
200
276
|
interface ChatComposerHandle {
|
|
201
277
|
/** Adds files as if picked (a drop on the thread, say). Refused ones get a toast. */
|
|
@@ -206,7 +282,7 @@ interface ChatComposerHandle {
|
|
|
206
282
|
}
|
|
207
283
|
interface ChatComposerProps {
|
|
208
284
|
/** From `useConversation`: how to send, and whether you may. */
|
|
209
|
-
conversation: Pick<ConversationState, "send" | "signalTyping" | "signalRecording" | "role" | "canPost" | "accessLoading" | "conversation"
|
|
285
|
+
conversation: Pick<ConversationState, "send" | "signalTyping" | "signalRecording" | "role" | "canPost" | "accessLoading" | "conversation"> & Partial<Pick<ConversationState, "accessError" | "refetchAccess" | "signalRecordingVideo" | "conversationLoading">>;
|
|
210
286
|
/** The message being replied to; a banner shows above the box. */
|
|
211
287
|
replyTo?: ChatMessage | null;
|
|
212
288
|
onCancelReply?: () => void;
|
|
@@ -218,10 +294,22 @@ interface ChatComposerProps {
|
|
|
218
294
|
/** Called after a message is queued. */
|
|
219
295
|
onSent?: () => void;
|
|
220
296
|
autoFocus?: boolean;
|
|
297
|
+
/** Shown instead of the box, with a lock (a closed topic). */
|
|
298
|
+
disabledReason?: string | null;
|
|
299
|
+
/** The topic new messages go to (scheduled ones too). */
|
|
300
|
+
topic?: string | null;
|
|
301
|
+
/** Offer scheduled send (the arrow beside Send) and list what's scheduled. Default true. */
|
|
302
|
+
scheduling?: boolean;
|
|
303
|
+
/** Offer video recording. Default true where the browser can record. */
|
|
304
|
+
video?: boolean;
|
|
305
|
+
/** Offer the emoji picker (only when `emojibaseUrl` is given). Default true. */
|
|
306
|
+
emoji?: boolean;
|
|
307
|
+
/** Where the emoji picker loads its data from. */
|
|
308
|
+
emojibaseUrl?: string;
|
|
221
309
|
className?: string;
|
|
222
310
|
ref?: Ref<ChatComposerHandle>;
|
|
223
311
|
}
|
|
224
|
-
declare function ChatComposer({ conversation: state, replyTo, onCancelReply, resolveUser, placeholder, accessory, onSent, autoFocus, className, ref, }: ChatComposerProps): react.JSX.Element;
|
|
312
|
+
declare function ChatComposer({ conversation: state, replyTo, onCancelReply, resolveUser, placeholder, accessory, onSent, autoFocus, disabledReason, topic, scheduling, video, emoji, emojibaseUrl, className, ref, }: ChatComposerProps): react.JSX.Element;
|
|
225
313
|
|
|
226
314
|
interface MessageRowProps {
|
|
227
315
|
item: ThreadItem;
|
|
@@ -357,6 +445,12 @@ interface MemoryConversationSeed {
|
|
|
357
445
|
attachments?: Partial<ChatAttachment>[];
|
|
358
446
|
})[];
|
|
359
447
|
pins?: string[];
|
|
448
|
+
/** Its topics, in order. */
|
|
449
|
+
topics?: {
|
|
450
|
+
topic: string;
|
|
451
|
+
title?: string;
|
|
452
|
+
closed?: boolean;
|
|
453
|
+
}[];
|
|
360
454
|
}
|
|
361
455
|
interface MemoryChatOptions {
|
|
362
456
|
/** The signed-in user. */
|
|
@@ -366,6 +460,10 @@ interface MemoryChatOptions {
|
|
|
366
460
|
files?: Record<string, string>;
|
|
367
461
|
/** Milliseconds each call waits, to show loading states. Default 0. */
|
|
368
462
|
latencyMs?: number;
|
|
463
|
+
/** Away messages and working hours, by user id. */
|
|
464
|
+
availability?: Record<string, Partial<Omit<ChatAvailability, "userId">>>;
|
|
465
|
+
/** Link previews the "server" has fetched. */
|
|
466
|
+
linkPreviews?: Partial<ChatLinkPreview$1>[];
|
|
369
467
|
}
|
|
370
468
|
type MemoryChatClient = ChatClient & {
|
|
371
469
|
/** Someone else posts (a demo's other side). */
|
|
@@ -376,6 +474,10 @@ type MemoryChatClient = ChatClient & {
|
|
|
376
474
|
failNextSends(count?: number): void;
|
|
377
475
|
/** The next send waits until the returned function is called (a test watching the sending state). */
|
|
378
476
|
holdNextSend(): () => void;
|
|
477
|
+
/** Posts every scheduled message due by `now` (a demo's clock), as the server's runner does. */
|
|
478
|
+
sendDue(now?: Date): Promise<number>;
|
|
479
|
+
/** Moderation's verdict on a message (the server's side). */
|
|
480
|
+
moderate(messageId: string, result: "clean" | "flagged" | "hidden"): void;
|
|
379
481
|
/** Every call made, for tests. */
|
|
380
482
|
calls: {
|
|
381
483
|
method: string;
|
|
@@ -384,4 +486,225 @@ type MemoryChatClient = ChatClient & {
|
|
|
384
486
|
};
|
|
385
487
|
declare function createMemoryChatClient(options: MemoryChatOptions): MemoryChatClient;
|
|
386
488
|
|
|
387
|
-
|
|
489
|
+
interface ChatTopicBarProps {
|
|
490
|
+
topics: ChatTopic[];
|
|
491
|
+
/** The topic shown; null for "All". */
|
|
492
|
+
value: string | null;
|
|
493
|
+
onValueChange: (topic: string | null) => void;
|
|
494
|
+
/** Owners and admins: "New topic", and close or reopen. */
|
|
495
|
+
canManage?: boolean;
|
|
496
|
+
/** Saves a topic (from useTopics). */
|
|
497
|
+
onSetTopic?: (topic: string, input: TopicInput) => Promise<void>;
|
|
498
|
+
className?: string;
|
|
499
|
+
}
|
|
500
|
+
declare function ChatTopicBar({ topics, value, onValueChange, canManage, onSetTopic, className }: ChatTopicBarProps): react.JSX.Element;
|
|
501
|
+
|
|
502
|
+
interface ChatScheduleMenuProps {
|
|
503
|
+
/** Called with the chosen time. */
|
|
504
|
+
onSchedule: (at: Date) => void;
|
|
505
|
+
disabled?: boolean;
|
|
506
|
+
/** For tests and demos. */
|
|
507
|
+
now?: Date;
|
|
508
|
+
}
|
|
509
|
+
/** `YYYY-MM-DDTHH:mm` in local time, for a datetime-local input. */
|
|
510
|
+
declare function toLocalInput(d: Date): string;
|
|
511
|
+
/** The chevron beside Send: "Schedule message" with quick picks and a custom time. */
|
|
512
|
+
declare function ChatScheduleMenu({ onSchedule, disabled, now }: ChatScheduleMenuProps): react.JSX.Element;
|
|
513
|
+
interface ChatScheduledListProps {
|
|
514
|
+
/** One conversation's; all of yours without it. */
|
|
515
|
+
conversationId?: string | null;
|
|
516
|
+
/** Names a conversation in the all-conversations list. */
|
|
517
|
+
conversationTitle?: (conversationId: string) => string;
|
|
518
|
+
className?: string;
|
|
519
|
+
}
|
|
520
|
+
/** Your scheduled messages, soonest first, each with when it goes and Cancel (Remove for one that failed). */
|
|
521
|
+
declare function ChatScheduledList({ conversationId, conversationTitle, className }: ChatScheduledListProps): react.JSX.Element;
|
|
522
|
+
|
|
523
|
+
interface ChatAwayBannerProps {
|
|
524
|
+
/** One person (a direct conversation). */
|
|
525
|
+
availability?: ChatAvailability | null;
|
|
526
|
+
/** The person's name, as the app shows it. */
|
|
527
|
+
name?: string;
|
|
528
|
+
/** Several people (a group): everyone away or outside their hours is named, two at most. */
|
|
529
|
+
people?: {
|
|
530
|
+
availability: ChatAvailability | null | undefined;
|
|
531
|
+
name: string;
|
|
532
|
+
}[];
|
|
533
|
+
now?: Date;
|
|
534
|
+
className?: string;
|
|
535
|
+
}
|
|
536
|
+
/** "Mark replies Mon–Fri, 9–5 (Eastern)", "Away until Monday: Back Monday", "Sam, Lee and 2 others are away"; nothing when everyone is around. */
|
|
537
|
+
declare function ChatAwayBanner({ availability, name, people, now, className }: ChatAwayBannerProps): react.JSX.Element | null;
|
|
538
|
+
interface ChatAvailabilitySettingsProps {
|
|
539
|
+
/** Called after a save. */
|
|
540
|
+
onSaved?: () => void;
|
|
541
|
+
className?: string;
|
|
542
|
+
}
|
|
543
|
+
/**
|
|
544
|
+
* Your availability: an away message (and until when), working hours per weekday, and the
|
|
545
|
+
* time zone they're read in. One range per day here; extra ranges saved elsewhere are kept
|
|
546
|
+
* for the days you don't change.
|
|
547
|
+
*/
|
|
548
|
+
declare function ChatAvailabilitySettings({ onSaved, className }: ChatAvailabilitySettingsProps): react.JSX.Element;
|
|
549
|
+
|
|
550
|
+
interface ChatBroadcastProps {
|
|
551
|
+
resolveUser: ResolveUser;
|
|
552
|
+
/** Only this kind of conversation (`direct`, say). */
|
|
553
|
+
kind?: string;
|
|
554
|
+
/** Which inbox rows may be picked; every one by default. */
|
|
555
|
+
filter?: (item: ChatInboxItem) => boolean;
|
|
556
|
+
/** Called with how many it went to. */
|
|
557
|
+
onSent?: (sent: number, picked: number) => void;
|
|
558
|
+
className?: string;
|
|
559
|
+
}
|
|
560
|
+
declare function ChatBroadcast({ resolveUser, kind, filter, onSent, className }: ChatBroadcastProps): react.JSX.Element;
|
|
561
|
+
|
|
562
|
+
interface ChatSearchProps {
|
|
563
|
+
resolveUser: ResolveUser;
|
|
564
|
+
/** Search one conversation; all of yours without it. */
|
|
565
|
+
conversationId?: string | null;
|
|
566
|
+
/** Called with the chosen result. */
|
|
567
|
+
onOpen: (hit: ChatSearchHit) => void;
|
|
568
|
+
/** Names a conversation, on results across conversations. */
|
|
569
|
+
conversationTitle?: (conversationId: string) => string;
|
|
570
|
+
/** Start with the box focused. */
|
|
571
|
+
autoFocus?: boolean;
|
|
572
|
+
debounceMs?: number;
|
|
573
|
+
className?: string;
|
|
574
|
+
}
|
|
575
|
+
/** A snippet with its « » marks as <mark>. */
|
|
576
|
+
declare function SearchSnippet({ snippet, className }: {
|
|
577
|
+
snippet: string;
|
|
578
|
+
className?: string;
|
|
579
|
+
}): react.JSX.Element;
|
|
580
|
+
declare function ChatSearch({ resolveUser, conversationId, onOpen, conversationTitle, autoFocus, debounceMs, className }: ChatSearchProps): react.JSX.Element;
|
|
581
|
+
|
|
582
|
+
/**
|
|
583
|
+
* @feat:chat
|
|
584
|
+
* `ChatErrorBoundary`: what one message (or one app attachment card) draws can't take the whole
|
|
585
|
+
* thread down. Messages carry what other people sent, and app renderers get sender-controlled
|
|
586
|
+
* data, so a render that throws shows its fallback instead, and the error goes to `onError`
|
|
587
|
+
* (console.error by default) so it is never silent.
|
|
588
|
+
*/
|
|
589
|
+
|
|
590
|
+
interface ChatErrorBoundaryProps {
|
|
591
|
+
/** Drawn instead of the children once they throw. */
|
|
592
|
+
fallback: ReactNode;
|
|
593
|
+
/** Told about the error; console.error by default. */
|
|
594
|
+
onError?: (error: unknown, info: ErrorInfo) => void;
|
|
595
|
+
children: ReactNode;
|
|
596
|
+
}
|
|
597
|
+
declare class ChatErrorBoundary extends Component<ChatErrorBoundaryProps, {
|
|
598
|
+
failed: boolean;
|
|
599
|
+
}> {
|
|
600
|
+
state: {
|
|
601
|
+
failed: boolean;
|
|
602
|
+
};
|
|
603
|
+
static getDerivedStateFromError(): {
|
|
604
|
+
failed: boolean;
|
|
605
|
+
};
|
|
606
|
+
componentDidCatch(error: unknown, info: ErrorInfo): void;
|
|
607
|
+
render(): ReactNode;
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
/** The picker can load: an emojibase URL was given. */
|
|
611
|
+
declare const canPickEmoji: (emojibaseUrl: string | null | undefined) => emojibaseUrl is string;
|
|
612
|
+
interface ChatEmojiPickerProps {
|
|
613
|
+
onSelect: (emoji: string) => void;
|
|
614
|
+
/** Where the Emojibase data is fetched from (`${emojibaseUrl}/en/data.json`). Required to load anything. */
|
|
615
|
+
emojibaseUrl?: string;
|
|
616
|
+
locale?: "en";
|
|
617
|
+
columns?: number;
|
|
618
|
+
className?: string;
|
|
619
|
+
}
|
|
620
|
+
declare function ChatEmojiPicker({ onSelect, emojibaseUrl, locale, columns, className }: ChatEmojiPickerProps): react.JSX.Element;
|
|
621
|
+
|
|
622
|
+
type VideoRecorderState = "idle" | "requesting" | "previewing" | "recording" | "recorded" | "denied" | "unsupported" | "error";
|
|
623
|
+
interface VideoRecording {
|
|
624
|
+
blob: Blob;
|
|
625
|
+
/** Without codec parameters: `video/webm` or `video/mp4`. */
|
|
626
|
+
contentType: string;
|
|
627
|
+
name: string;
|
|
628
|
+
durationMs: number;
|
|
629
|
+
/** An object URL for playback before sending. */
|
|
630
|
+
url: string | null;
|
|
631
|
+
}
|
|
632
|
+
/** MP4 where the browser records it (Safari, recent Chrome), else WebM. */
|
|
633
|
+
declare function pickVideoMimeType(): string | null;
|
|
634
|
+
interface UseVideoRecorderOptions {
|
|
635
|
+
/** It stops itself here (`recordingLimitMs(role)`). */
|
|
636
|
+
maxMs: number;
|
|
637
|
+
/** About every second while recording (to tell the others). */
|
|
638
|
+
onTick?: () => void;
|
|
639
|
+
}
|
|
640
|
+
declare function useVideoRecorder({ maxMs, onTick }: UseVideoRecorderOptions): {
|
|
641
|
+
state: VideoRecorderState;
|
|
642
|
+
error: string | null;
|
|
643
|
+
elapsedMs: number;
|
|
644
|
+
maxMs: number;
|
|
645
|
+
stream: MediaStream | null;
|
|
646
|
+
recording: VideoRecording | null;
|
|
647
|
+
open: () => Promise<boolean>;
|
|
648
|
+
start: () => Promise<void>;
|
|
649
|
+
stop: () => void;
|
|
650
|
+
retake: () => Promise<void>;
|
|
651
|
+
reset: () => void;
|
|
652
|
+
handOff: () => void;
|
|
653
|
+
};
|
|
654
|
+
type VideoRecorder = ReturnType<typeof useVideoRecorder>;
|
|
655
|
+
/**
|
|
656
|
+
* A recording as a file for the outbox: `hosted` sends it to the hosted provider (the provider's
|
|
657
|
+
* `videoUpload`, the message going at once with the video pending); otherwise it goes to storage.
|
|
658
|
+
*/
|
|
659
|
+
declare function videoFile(rec: VideoRecording, opts?: {
|
|
660
|
+
hosted?: boolean;
|
|
661
|
+
}): LocalFile;
|
|
662
|
+
/**
|
|
663
|
+
* Uploads a recording through the app's `videoUpload` (a Mux direct upload: PUT the bytes to
|
|
664
|
+
* the URL it returns) and gives back the attachment to send, pending until Mux's webhook marks
|
|
665
|
+
* it ready. The composer no longer uses it: it sends `videoFile(rec, { hosted: true })` through
|
|
666
|
+
* the outbox, so the message keeps its place in the order while the bytes go up.
|
|
667
|
+
*/
|
|
668
|
+
declare function uploadVideo(videoUpload: ChatVideoUpload, conversationId: string, rec: Pick<VideoRecording, "blob" | "contentType" | "durationMs" | "name">, fetchImpl?: typeof fetch, signal?: AbortSignal): Promise<AttachmentInput>;
|
|
669
|
+
interface ChatVideoRecorderProps {
|
|
670
|
+
open: boolean;
|
|
671
|
+
onOpenChange: (open: boolean) => void;
|
|
672
|
+
conversationId: string;
|
|
673
|
+
maxMs: number;
|
|
674
|
+
/** From useConversation: sends through the outbox. */
|
|
675
|
+
send: (input: Omit<SendInput, "conversationId" | "clientId">, files?: LocalFile[]) => Promise<unknown>;
|
|
676
|
+
/** Merged into what's sent (a reply, a topic, an internal note). */
|
|
677
|
+
extra?: Omit<SendInput, "conversationId" | "clientId" | "attachments" | "body">;
|
|
678
|
+
/**
|
|
679
|
+
* Marks the take as hosted. Pass the same `videoUpload` the `ChatProvider` has (or give the
|
|
680
|
+
* provider a `hostedUpload`): the outbox does the upload, and without one it falls back to
|
|
681
|
+
* storage with no error.
|
|
682
|
+
*/
|
|
683
|
+
videoUpload?: ChatVideoUpload;
|
|
684
|
+
onTick?: () => void;
|
|
685
|
+
onSent?: () => void;
|
|
686
|
+
/** @deprecated Unused: the bytes go through the outbox's `hostedUpload` now. */
|
|
687
|
+
fetchImpl?: typeof fetch;
|
|
688
|
+
}
|
|
689
|
+
/** The composer's video dialog: preview, record (to the role's limit), review, retake, send. */
|
|
690
|
+
declare function ChatVideoRecorder({ open, onOpenChange, maxMs, send, extra, videoUpload, onTick, onSent }: ChatVideoRecorderProps): react.JSX.Element;
|
|
691
|
+
declare const muxStreamUrl: (playbackId: string) => string;
|
|
692
|
+
declare const muxPosterUrl: (playbackId: string) => string;
|
|
693
|
+
interface ChatVideoPlayerProps {
|
|
694
|
+
/** A file URL (storage), when there is no playback id. */
|
|
695
|
+
src?: string | null;
|
|
696
|
+
/** Mux's playback id: streamed as HLS. */
|
|
697
|
+
playbackId?: string | null;
|
|
698
|
+
poster?: string | null;
|
|
699
|
+
/** Mux is still working on it. */
|
|
700
|
+
processing?: boolean;
|
|
701
|
+
failed?: boolean;
|
|
702
|
+
/** Still uploading from this device. */
|
|
703
|
+
uploading?: boolean;
|
|
704
|
+
label?: string;
|
|
705
|
+
className?: string;
|
|
706
|
+
}
|
|
707
|
+
/** Plays a chat video: Mux HLS (native, else hls.js loaded on demand), or a file. */
|
|
708
|
+
declare function ChatVideoPlayer({ src, playbackId, poster, processing, failed, uploading, label, className }: ChatVideoPlayerProps): react.JSX.Element;
|
|
709
|
+
|
|
710
|
+
export { type AttachmentView, ChatAttachmentCard, type ChatAttachmentCardProps, type ChatAttachmentSlotProps, ChatAvailabilitySettings, type ChatAvailabilitySettingsProps, ChatAwayBanner, type ChatAwayBannerProps, ChatBroadcast, type ChatBroadcastProps, ChatComposer, type ChatComposerAccessoryProps, type ChatComposerHandle, type ChatComposerProps, ChatEmojiPicker, type ChatEmojiPickerProps, ChatErrorBoundary, type ChatErrorBoundaryProps, ChatInbox, type ChatInboxEmptyProps, type ChatInboxProps, ChatLinkPreview, type ChatLinkPreviewProps, ChatMarkdown, type ChatMarkdownProps, ChatPanel, type ChatPanelNoSelectionProps, type ChatPanelProps, ChatScheduleMenu, type ChatScheduleMenuProps, ChatScheduledList, type ChatScheduledListProps, ChatSearch, type ChatSearchProps, ChatThread, type ChatThreadEmptyProps, type ChatThreadHeaderProps, type ChatThreadProps, type ChatThreadSlots, ChatTopicBar, type ChatTopicBarProps, type ChatUser, ChatVideoPlayer, type ChatVideoPlayerProps, ChatVideoRecorder, type ChatVideoRecorderProps, FileChip, GenericAttachment, ImageGrid, type ImageGridProps, Lightbox, type LightboxProps, type LinkPreviewImages, type MarkdownNode, type MemoryChatClient, type MemoryChatOptions, type MemoryConversationSeed, MessageAttachments, type MessageAttachmentsProps, MessageRow, type MessageRowProps, QUICK_REACTIONS, type ResolveUser, SearchSnippet, type UseVideoRecorderOptions, type UseVoiceRecorderOptions, VideoAttachment, type VideoRecorder, type VideoRecorderState, type VideoRecording, VoiceNote, type VoiceNoteProps, type VoiceRecorder, type VoiceRecorderState, type VoiceRecording, canPickEmoji, clockTime, createMemoryChatClient, formatBytes, inboxTitle, isSafeHref, linkPreviewImageSrc, messageLabel, muxPosterUrl, muxStreamUrl, parseMarkdown, pickVideoMimeType, pickVoiceMimeType, plainText, relativeTime, toAttachmentView, toLocalInput, uploadVideo, useVideoRecorder, useVoiceRecorder, videoFile, voiceFile };
|