@joai/react 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,9 @@
1
+ # @joai/react
2
+
3
+ Reusable React building blocks for JoAi applications. This package owns browser and React concerns; `@joai/core` remains framework-neutral and `@joai/sdk` remains the API client.
4
+
5
+ ```tsx
6
+ import { EditableContent, EditableImage, ManagedContentEditor } from '@joai/react'
7
+ ```
8
+
9
+ React is a peer dependency. Core and SDK are regular package dependencies, so consumers only need to install `@joai/react` and a compatible React version.
@@ -0,0 +1,430 @@
1
+ 'use client';
2
+ import * as _$react from "react";
3
+ import { ReactNode } from "react";
4
+ import { AppEnv, Content, ContentValue, ContentVersion, IHttpService, ImageCompressionOptions, ImageCompressionOptions as ImageCompressionOptions$1, ImageCropOptions, ImageCropOptions as ImageCropOptions$1, ImageCropRectangle, ImageCropState, ImageCropState as ImageCropState$1, ImageDimensionRules, ImageDimensionRules as ImageDimensionRules$1, ImageDimensions, ImageDimensions as ImageDimensions$1, Media, PatchContentInput, containedImageDimensions, cropOutputDimensions, imageCropRectangle, validateImageDimensions } from "@joai/core";
5
+
6
+ //#region src/types.d.ts
7
+ type EmbedChatItem = {
8
+ content: string;
9
+ byUser: boolean;
10
+ streaming?: boolean;
11
+ error?: boolean;
12
+ };
13
+ //#endregion
14
+ //#region src/guest.d.ts
15
+ declare function getOrCreateGuestId(storageKey?: string): string;
16
+ declare function loadRoom(storageKey: string): string | null;
17
+ declare function saveRoom(storageKey: string, roomId: string): void;
18
+ declare function clearRoom(storageKey: string): void;
19
+ //#endregion
20
+ //#region src/stream-connection.d.ts
21
+ type StreamDataHandler = (data: unknown) => void;
22
+ declare function openEventSourceStream(url: string, onData: StreamDataHandler, options?: {
23
+ withCredentials?: boolean;
24
+ onError?: (e: unknown) => void;
25
+ onConnectionError?: (e: Event | null) => void;
26
+ }): () => void;
27
+ //#endregion
28
+ //#region src/components.d.ts
29
+ type ListClassNames = {
30
+ container?: string;
31
+ userBubble?: string;
32
+ agentBubble?: string;
33
+ errorBubble?: string;
34
+ thinkingWrapper?: string;
35
+ dot?: string;
36
+ };
37
+ type EmbedChatListProps = {
38
+ items: EmbedChatItem[];
39
+ agentName: string;
40
+ awaitingResponse: boolean;
41
+ classNames?: ListClassNames;
42
+ renderEmptyState?: () => ReactNode;
43
+ renderAgentContent?: (item: EmbedChatItem) => ReactNode;
44
+ };
45
+ declare function EmbedChatList({
46
+ items,
47
+ agentName,
48
+ awaitingResponse,
49
+ classNames,
50
+ renderEmptyState,
51
+ renderAgentContent
52
+ }: EmbedChatListProps): string | number | bigint | boolean | Iterable<ReactNode> | Promise<string | number | bigint | boolean | _$react.ReactPortal | _$react.ReactElement<unknown, string | _$react.JSXElementConstructor<any>> | Iterable<ReactNode> | null | undefined> | _$react.JSX.Element | null | undefined;
53
+ type EmbedChatInputProps = {
54
+ onSend: (message: string) => void;
55
+ disabled: boolean;
56
+ placeholder?: string;
57
+ maxLength?: number;
58
+ classNames?: {
59
+ container?: string;
60
+ textarea?: string;
61
+ };
62
+ renderSendButton: (props: {
63
+ onClick: () => void;
64
+ disabled: boolean;
65
+ }) => ReactNode;
66
+ };
67
+ declare function EmbedChatInput({
68
+ onSend,
69
+ disabled,
70
+ placeholder,
71
+ maxLength,
72
+ classNames,
73
+ renderSendButton
74
+ }: EmbedChatInputProps): _$react.JSX.Element;
75
+ //#endregion
76
+ //#region src/use-embed-chat.d.ts
77
+ type Options = {
78
+ agentUuid: string | null;
79
+ http: IHttpService;
80
+ cortexBaseUrl: string;
81
+ guestId: string;
82
+ roomStorageKey: string;
83
+ enabled?: boolean;
84
+ extraPayload?: Record<string, unknown>;
85
+ };
86
+ declare function useEmbedChat({
87
+ agentUuid,
88
+ http,
89
+ cortexBaseUrl,
90
+ guestId,
91
+ roomStorageKey,
92
+ enabled,
93
+ extraPayload
94
+ }: Options): {
95
+ items: EmbedChatItem[];
96
+ setItems: _$react.Dispatch<_$react.SetStateAction<EmbedChatItem[]>>;
97
+ sending: boolean;
98
+ awaitingResponse: boolean;
99
+ roomId: string | null;
100
+ setRoomId: _$react.Dispatch<_$react.SetStateAction<string | null>>;
101
+ send: (message: string) => Promise<void>;
102
+ connectionError: boolean;
103
+ };
104
+ //#endregion
105
+ //#region src/content/types.d.ts
106
+ type ContentResource = {
107
+ get: (contentId: string) => Promise<ContentResponse<Content>>;
108
+ getPublic: (slug: string) => Promise<ContentResponse<Content>>;
109
+ patch: (contentId: string, input: PatchContentInput) => Promise<ContentResponse<ContentVersion>>;
110
+ publish: (contentId: string, expectedVersion: number) => Promise<ContentResponse<ContentVersion>>;
111
+ };
112
+ type ContentResponse<T> = {
113
+ ok: boolean;
114
+ data?: T;
115
+ errors?: Record<string, unknown> | null;
116
+ original?: {
117
+ status?: number;
118
+ } | null;
119
+ };
120
+ type ContentClient = {
121
+ content: ContentResource;
122
+ media?: {
123
+ list: (team: string, params?: {
124
+ page?: number;
125
+ mime?: string;
126
+ }) => Promise<{
127
+ ok: boolean;
128
+ data?: Media[];
129
+ }>;
130
+ upload: (data: {
131
+ team: string;
132
+ file: File;
133
+ type: 'output';
134
+ label?: string;
135
+ }) => Promise<{
136
+ ok: boolean;
137
+ data?: Media;
138
+ }>;
139
+ };
140
+ };
141
+ type ContentContextValue = {
142
+ content: Content | null;
143
+ values: Record<string, ContentValue>;
144
+ isLoading: boolean;
145
+ isSaving: boolean;
146
+ operation: 'save' | 'publish' | null;
147
+ isEditing: boolean;
148
+ isDirty: boolean;
149
+ canEdit: boolean;
150
+ canPublish: boolean;
151
+ teamSlug?: string;
152
+ media?: ContentClient['media'];
153
+ error: string | null;
154
+ message: string | null;
155
+ imagePickerLabels: ContentImagePickerLabels;
156
+ cancel: () => void;
157
+ setEditing: (editing: boolean) => void;
158
+ setValue: (key: string, value: ContentValue) => void;
159
+ save: () => Promise<boolean>;
160
+ publish: () => Promise<boolean>;
161
+ };
162
+ type ContentProviderProp = {
163
+ children: ReactNode;
164
+ client: ContentClient;
165
+ contentId?: string;
166
+ publicSlug?: string;
167
+ initialContent?: Content;
168
+ canEdit?: boolean;
169
+ canPublish?: boolean;
170
+ teamSlug?: string;
171
+ confirmLeave?: () => boolean;
172
+ draftStore?: ContentDraftStore;
173
+ imagePickerLabels?: Partial<ContentImagePickerLabels>;
174
+ messages?: Partial<ContentEditorMessages>;
175
+ onUnauthorized?: () => void;
176
+ };
177
+ type ContentEditorMessages = {
178
+ conflict: string;
179
+ loadError: string;
180
+ publishError: string;
181
+ published: string;
182
+ saveError: string;
183
+ saved: string;
184
+ sessionExpired: string;
185
+ unsavedNavigation: string;
186
+ };
187
+ type ContentDraft = {
188
+ contentId?: string;
189
+ values: Record<string, ContentValue>;
190
+ };
191
+ type ContentDraftStore = {
192
+ clear: () => boolean | void;
193
+ get: () => ContentDraft | null;
194
+ set: (draft: ContentDraft) => boolean | void;
195
+ };
196
+ type ContentImagePickerLabels = {
197
+ applyCrop: string;
198
+ back: string;
199
+ choose: string;
200
+ close: string;
201
+ cropDescription: string;
202
+ cropTitle: string;
203
+ dimensionsTooLarge: string;
204
+ dimensionsTooSmall: string;
205
+ drop: string;
206
+ empty: string;
207
+ field: string;
208
+ invalidType: string;
209
+ libraryDescription: string;
210
+ libraryTitle: string;
211
+ loadError: string;
212
+ loading: string;
213
+ positionHorizontal: string;
214
+ positionVertical: string;
215
+ processing: string;
216
+ retry: string;
217
+ tooLarge: string;
218
+ upload: string;
219
+ uploadError: string;
220
+ uploading: string;
221
+ zoom: string;
222
+ };
223
+ type ImageProcessingOptions = {
224
+ compression?: ImageCompressionOptions$1 | false;
225
+ crop?: ImageCropOptions$1;
226
+ dimensions?: ImageDimensionRules$1 | false;
227
+ };
228
+ type EditableContentProp = {
229
+ field: string;
230
+ as?: 'span' | 'p' | 'div' | 'h1' | 'h2' | 'h3';
231
+ className?: string;
232
+ placeholder?: string;
233
+ multiline?: boolean;
234
+ editorClassName?: string;
235
+ label?: string;
236
+ };
237
+ type EditableImageProp = {
238
+ field: string;
239
+ alt?: string;
240
+ className?: string;
241
+ onSelect?: () => void;
242
+ inputClassName?: string;
243
+ placeholder?: string;
244
+ label?: string;
245
+ labels?: Partial<ContentImagePickerLabels>;
246
+ maxUploadBytes?: number;
247
+ maxSourceBytes?: number;
248
+ processing?: ImageProcessingOptions | false;
249
+ };
250
+ type ContentToolbarProp = {
251
+ className?: string;
252
+ confirmCancel?: () => boolean | Promise<boolean>;
253
+ confirmPublish?: () => boolean | Promise<boolean>;
254
+ labels?: {
255
+ edit?: string;
256
+ save?: string;
257
+ saving?: string;
258
+ publish?: string;
259
+ publishing?: string;
260
+ cancel?: string;
261
+ toolbar?: string;
262
+ unsaved?: string;
263
+ };
264
+ };
265
+ //#endregion
266
+ //#region src/content/provider.d.ts
267
+ declare function ContentProvider({
268
+ children,
269
+ client,
270
+ contentId,
271
+ publicSlug,
272
+ initialContent,
273
+ canEdit,
274
+ canPublish,
275
+ teamSlug,
276
+ confirmLeave,
277
+ draftStore,
278
+ imagePickerLabels: imagePickerLabelOverrides,
279
+ messages: messageOverrides,
280
+ onUnauthorized
281
+ }: ContentProviderProp): _$react.JSX.Element;
282
+ //#endregion
283
+ //#region src/content/editable-content.d.ts
284
+ declare function EditableContent({
285
+ field,
286
+ as: Tag,
287
+ className,
288
+ placeholder,
289
+ multiline,
290
+ editorClassName,
291
+ label
292
+ }: EditableContentProp): _$react.JSX.Element;
293
+ declare function useContentValue<T extends ContentValue = ContentValue>(key: string): T | undefined;
294
+ //#endregion
295
+ //#region src/content/editable-image.d.ts
296
+ declare const validateImageFile: (file: Pick<File, "size" | "type">, maxUploadBytes: number, labels: Pick<ContentImagePickerLabels, "invalidType" | "tooLarge">) => string | null;
297
+ declare function EditableImage({
298
+ field,
299
+ alt,
300
+ className,
301
+ inputClassName,
302
+ placeholder,
303
+ onSelect,
304
+ label,
305
+ labels: labelOverrides,
306
+ maxUploadBytes,
307
+ maxSourceBytes,
308
+ processing
309
+ }: EditableImageProp): _$react.JSX.Element | null;
310
+ //#endregion
311
+ //#region src/content/toolbar.d.ts
312
+ declare function ContentToolbar({
313
+ className,
314
+ labels,
315
+ confirmCancel,
316
+ confirmPublish
317
+ }: ContentToolbarProp): _$react.JSX.Element | null;
318
+ //#endregion
319
+ //#region src/content/content-editor.d.ts
320
+ type ContentEditorProp = ContentProviderProp & {
321
+ children: ReactNode;
322
+ confirmCancel?: ContentToolbarProp['confirmCancel'];
323
+ confirmPublish?: ContentToolbarProp['confirmPublish'];
324
+ labels?: ContentToolbarProp['labels'];
325
+ title?: string;
326
+ };
327
+ declare function ContentEditor({
328
+ children,
329
+ confirmCancel,
330
+ confirmPublish,
331
+ labels,
332
+ title,
333
+ ...provider
334
+ }: ContentEditorProp): _$react.JSX.Element;
335
+ //#endregion
336
+ //#region src/content/use-unsaved-navigation.d.ts
337
+ declare const useUnsavedNavigation: (shouldBlock: boolean, confirmLeave: () => boolean) => void;
338
+ //#endregion
339
+ //#region src/content/image-processing.d.ts
340
+ declare const readImageDimensions: (file: File) => Promise<ImageDimensions$1>;
341
+ declare const processImageFile: (file: File, compression: ImageCompressionOptions$1, cropOptions?: ImageCropOptions$1, cropState?: ImageCropState$1) => Promise<File>;
342
+ //#endregion
343
+ //#region src/content/session-store.d.ts
344
+ type Listener = () => void;
345
+ type BrowserStorage$1 = Pick<Storage, 'getItem' | 'setItem' | 'removeItem'>;
346
+ type SessionTokenStore = {
347
+ getSnapshot: () => string | null;
348
+ getServerSnapshot: () => null;
349
+ subscribe: (listener: Listener) => () => void;
350
+ set: (token: string) => boolean;
351
+ clear: () => boolean;
352
+ };
353
+ declare const createSessionTokenStore: (storageKey: string, storage?: BrowserStorage$1) => SessionTokenStore;
354
+ //#endregion
355
+ //#region src/content/managed-content-editor.d.ts
356
+ type ContentEditorConfig = {
357
+ contentId: string;
358
+ env?: AppEnv;
359
+ publicSlug: string;
360
+ sessionStorageKey: string;
361
+ team: string;
362
+ };
363
+ type ManagedContentEditorProps = {
364
+ authErrorClassName?: string;
365
+ authErrorMessage?: string;
366
+ children: ReactNode;
367
+ config: ContentEditorConfig;
368
+ confirmLeave?: () => boolean;
369
+ confirmCancel?: ContentToolbarProp['confirmCancel'];
370
+ confirmPublish?: ContentToolbarProp['confirmPublish'];
371
+ draftStore?: ContentDraftStore;
372
+ imagePickerLabels?: Partial<ContentImagePickerLabels>;
373
+ initialContent?: Content;
374
+ labels?: ContentToolbarProp['labels'];
375
+ messages?: Partial<ContentEditorMessages>;
376
+ reauthenticateLabel?: string;
377
+ reauthenticateUrl?: string | ((returnTo: string) => string);
378
+ sessionStore?: SessionTokenStore;
379
+ sessionExpiredMessage?: string;
380
+ title?: string;
381
+ };
382
+ declare function ManagedContentEditor({
383
+ authErrorClassName,
384
+ authErrorMessage,
385
+ children,
386
+ config,
387
+ confirmLeave,
388
+ confirmCancel,
389
+ confirmPublish,
390
+ draftStore,
391
+ imagePickerLabels,
392
+ initialContent,
393
+ labels,
394
+ messages,
395
+ reauthenticateLabel,
396
+ reauthenticateUrl,
397
+ sessionStore,
398
+ sessionExpiredMessage,
399
+ title
400
+ }: ManagedContentEditorProps): _$react.JSX.Element;
401
+ //#endregion
402
+ //#region src/content/return-url.d.ts
403
+ declare const getContentSessionReturnUrl: (currentUrl: string) => string;
404
+ //#endregion
405
+ //#region src/content/draft-store.d.ts
406
+ type BrowserStorage = Pick<Storage, 'getItem' | 'setItem' | 'removeItem'>;
407
+ declare const createContentDraftStore: (storageKey: string, storage?: BrowserStorage) => ContentDraftStore;
408
+ //#endregion
409
+ //#region src/content/use-content-session.d.ts
410
+ type ContentSessionOptions = {
411
+ env?: AppEnv;
412
+ exchange?: (handoffToken: string) => Promise<string>;
413
+ sessionStore?: SessionTokenStore;
414
+ sessionStorageKey: string;
415
+ team: string;
416
+ };
417
+ type ContentSession = {
418
+ clear: () => void;
419
+ error: string | null;
420
+ token: string | null;
421
+ };
422
+ declare const useContentSession: ({
423
+ env,
424
+ exchange,
425
+ sessionStore,
426
+ sessionStorageKey,
427
+ team
428
+ }: ContentSessionOptions) => ContentSession;
429
+ //#endregion
430
+ export { ContentClient, ContentContextValue, ContentDraft, ContentDraftStore, ContentEditor, ContentEditorConfig, ContentEditorMessages, ContentEditorProp, ContentImagePickerLabels, ContentProvider, ContentProviderProp, ContentResource, ContentResponse, ContentSession, ContentSessionOptions, ContentToolbar, ContentToolbarProp, EditableContent, EditableContentProp, EditableImage, EditableImageProp, EmbedChatInput, EmbedChatItem, EmbedChatList, type ImageCompressionOptions, type ImageCropOptions, type ImageCropRectangle, type ImageCropState, type ImageDimensionRules, type ImageDimensions, ImageProcessingOptions, ManagedContentEditor, ManagedContentEditorProps, SessionTokenStore, StreamDataHandler, clearRoom, containedImageDimensions, createContentDraftStore, createSessionTokenStore, cropOutputDimensions, getContentSessionReturnUrl, getOrCreateGuestId, imageCropRectangle, loadRoom, openEventSourceStream, processImageFile, readImageDimensions, saveRoom, useContentSession, useContentValue, useEmbedChat, useUnsavedNavigation, validateImageDimensions, validateImageFile };