@cossistant/react 0.0.22 → 0.0.23
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/hooks/private/use-multimodal-input.d.ts.map +1 -1
- package/hooks/private/use-multimodal-input.js +16 -3
- package/hooks/private/use-multimodal-input.js.map +1 -1
- package/hooks/use-composer-refocus.js +1 -1
- package/hooks/use-composer-refocus.js.map +1 -1
- package/package.json +3 -3
- package/primitives/avatar/image.d.ts +1 -1
- package/realtime-events.d.ts +69 -0
- package/realtime-events.d.ts.map +1 -1
- package/support/components/multimodal-input.d.ts.map +1 -1
- package/support/components/multimodal-input.js +6 -2
- package/support/components/multimodal-input.js.map +1 -1
- package/support/components/timeline-message-item.js +1 -1
- package/support/components/timeline-message-item.js.map +1 -1
- package/support/components/typing-indicator.d.ts.map +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-multimodal-input.d.ts","names":[],"sources":["../../../src/hooks/private/use-multimodal-input.ts"],"sourcesContent":[],"mappings":";KAEY,yBAAA;EAAA,QAAA,CAAA,EAAA,CAAA,IAAA,EAAA;IACiC,OAAA,EAAA,MAAA;IAAoB,KAAA,EAApB,IAAoB,EAAA;EAC9C,CAAA,EAAA,GAAA,IAAA,GAD8C,OAC9C,CAAA,IAAA,CAAA;EAAK,OAAA,CAAA,EAAA,CAAA,KAAA,EAAL,KAAK,EAAA,GAAA,IAAA;EAMZ,WAAA,CAAA,EAAA,MAAA;EAGJ,QAAA,CAAA,EAAA,MAAA;EAEA,gBAAA,CAAA,EAAA,MAAA,EAAA;CAIW;AAGJ,KAZH,wBAAA,GAYG;EAAO,OAAA,EAAA,MAAA;EAaT,KAAA,EAtBL,IAsBK,EAAA;EAAsB,YAAA,EAAA,OAAA;EAAA,KAAA,EApB3B,KAoB2B,GAAA,IAAA;EAAA,UAAA,EAAA,CAAA,OAAA,EAAA,MAAA,EAAA,GAAA,IAAA;EAAA,QAAA,EAAA,CAAA,KAAA,EAhBhB,IAgBgB,EAAA,EAAA,GAAA,IAAA;EAAA,UAAA,EAAA,CAAA,KAAA,EAAA,MAAA,EAAA,GAAA,IAAA;EAMhC,UAAA,EAAA,GAAA,GAAA,IAAA;EAAiC,MAAA,EAAA,GAAA,GAnBrB,OAmBqB,CAAA,IAAA,CAAA;
|
|
1
|
+
{"version":3,"file":"use-multimodal-input.d.ts","names":[],"sources":["../../../src/hooks/private/use-multimodal-input.ts"],"sourcesContent":[],"mappings":";KAEY,yBAAA;EAAA,QAAA,CAAA,EAAA,CAAA,IAAA,EAAA;IACiC,OAAA,EAAA,MAAA;IAAoB,KAAA,EAApB,IAAoB,EAAA;EAC9C,CAAA,EAAA,GAAA,IAAA,GAD8C,OAC9C,CAAA,IAAA,CAAA;EAAK,OAAA,CAAA,EAAA,CAAA,KAAA,EAAL,KAAK,EAAA,GAAA,IAAA;EAMZ,WAAA,CAAA,EAAA,MAAA;EAGJ,QAAA,CAAA,EAAA,MAAA;EAEA,gBAAA,CAAA,EAAA,MAAA,EAAA;CAIW;AAGJ,KAZH,wBAAA,GAYG;EAAO,OAAA,EAAA,MAAA;EAaT,KAAA,EAtBL,IAsBK,EAAA;EAAsB,YAAA,EAAA,OAAA;EAAA,KAAA,EApB3B,KAoB2B,GAAA,IAAA;EAAA,UAAA,EAAA,CAAA,OAAA,EAAA,MAAA,EAAA,GAAA,IAAA;EAAA,QAAA,EAAA,CAAA,KAAA,EAhBhB,IAgBgB,EAAA,EAAA,GAAA,IAAA;EAAA,UAAA,EAAA,CAAA,KAAA,EAAA,MAAA,EAAA,GAAA,IAAA;EAMhC,UAAA,EAAA,GAAA,GAAA,IAAA;EAAiC,MAAA,EAAA,GAAA,GAnBrB,OAmBqB,CAAA,IAAA,CAAA;EAwKnC,KAAA,EAAA,GAAA,GAAA,IAAA;;;;;;;;;cA9KY;;;;;;IAMV,8BAAiC"}
|
|
@@ -79,21 +79,34 @@ const useMultimodalInput = ({ onSubmit, onError, maxFileSize = 10 * 1024 * 1024,
|
|
|
79
79
|
}, [clearFiles]);
|
|
80
80
|
const submit = useCallback(async () => {
|
|
81
81
|
if (!onSubmit) return;
|
|
82
|
-
|
|
82
|
+
const trimmedMessage = message.trim();
|
|
83
|
+
if (!trimmedMessage && files.length === 0) {
|
|
83
84
|
const err = /* @__PURE__ */ new Error("Please provide a message or attach files");
|
|
84
85
|
setError(err);
|
|
85
86
|
onError?.(err);
|
|
86
87
|
return;
|
|
87
88
|
}
|
|
89
|
+
const previousState = {
|
|
90
|
+
message,
|
|
91
|
+
files,
|
|
92
|
+
fileUrls: [...fileUrlsRef.current]
|
|
93
|
+
};
|
|
88
94
|
setIsSubmitting(true);
|
|
89
95
|
setError(null);
|
|
96
|
+
setMessage("");
|
|
97
|
+
setFiles([]);
|
|
98
|
+
fileUrlsRef.current = [];
|
|
90
99
|
try {
|
|
91
100
|
await onSubmit({
|
|
92
|
-
message:
|
|
93
|
-
files
|
|
101
|
+
message: trimmedMessage,
|
|
102
|
+
files: previousState.files
|
|
94
103
|
});
|
|
104
|
+
for (const url of previousState.fileUrls) URL.revokeObjectURL(url);
|
|
95
105
|
reset();
|
|
96
106
|
} catch (err) {
|
|
107
|
+
setMessage(previousState.message);
|
|
108
|
+
setFiles(previousState.files);
|
|
109
|
+
fileUrlsRef.current = previousState.fileUrls;
|
|
97
110
|
const _error = err instanceof Error ? err : /* @__PURE__ */ new Error("Failed to submit");
|
|
98
111
|
setError(_error);
|
|
99
112
|
onError?.(_error);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-multimodal-input.js","names":[],"sources":["../../../src/hooks/private/use-multimodal-input.ts"],"sourcesContent":["import { useCallback, useRef, useState } from \"react\";\n\nexport type UseMultimodalInputOptions = {\n\tonSubmit?: (data: { message: string; files: File[] }) => void | Promise<void>;\n\tonError?: (error: Error) => void;\n\tmaxFileSize?: number; // in bytes\n\tmaxFiles?: number;\n\tallowedFileTypes?: string[]; // MIME types\n};\n\nexport type UseMultimodalInputReturn = {\n\t// State\n\tmessage: string;\n\tfiles: File[];\n\tisSubmitting: boolean;\n\terror: Error | null;\n\n\t// Actions\n\tsetMessage: (message: string) => void;\n\taddFiles: (files: File[]) => void;\n\tremoveFile: (index: number) => void;\n\tclearFiles: () => void;\n\tsubmit: () => Promise<void>;\n\treset: () => void;\n\n\t// Validation\n\tisValid: boolean;\n\tcanSubmit: boolean;\n};\n\n/**\n * Manages message text, file attachments and validation for the multimodal\n * composer component. Provides ergonomic helpers for submit flows and error\n * reporting.\n */\nexport const useMultimodalInput = ({\n\tonSubmit,\n\tonError,\n\tmaxFileSize = 10 * 1024 * 1024, // 10MB default\n\tmaxFiles = 5,\n\tallowedFileTypes = [\"image/*\", \"application/pdf\", \"text/*\"],\n}: UseMultimodalInputOptions = {}): UseMultimodalInputReturn => {\n\tconst [message, setMessage] = useState(\"\");\n\tconst [files, setFiles] = useState<File[]>([]);\n\tconst [isSubmitting, setIsSubmitting] = useState(false);\n\tconst [error, setError] = useState<Error | null>(null);\n\n\t// Use ref to prevent re-renders when tracking file URLs\n\tconst fileUrlsRef = useRef<string[]>([]);\n\n\t// Validation helpers\n\tconst validateFile = useCallback(\n\t\t(file: File): string | null => {\n\t\t\tif (file.size > maxFileSize) {\n\t\t\t\treturn `File \"${file.name}\" exceeds maximum size of ${maxFileSize / 1024 / 1024}MB`;\n\t\t\t}\n\n\t\t\tif (allowedFileTypes.length > 0) {\n\t\t\t\tconst isAllowed = allowedFileTypes.some((type) => {\n\t\t\t\t\tif (type.endsWith(\"/*\")) {\n\t\t\t\t\t\tconst baseType = type.slice(0, -2);\n\t\t\t\t\t\treturn file.type.startsWith(baseType);\n\t\t\t\t\t}\n\t\t\t\t\treturn file.type === type;\n\t\t\t\t});\n\n\t\t\t\tif (!isAllowed) {\n\t\t\t\t\treturn `File type \"${file.type}\" is not allowed`;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn null;\n\t\t},\n\t\t[maxFileSize, allowedFileTypes]\n\t);\n\n\t// Actions\n\tconst addFiles = useCallback(\n\t\t(newFiles: File[]) => {\n\t\t\tsetError(null);\n\n\t\t\t// Check max files limit\n\t\t\tif (files.length + newFiles.length > maxFiles) {\n\t\t\t\tconst err = new Error(\n\t\t\t\t\t`Cannot add files: maximum ${maxFiles} files allowed`\n\t\t\t\t);\n\t\t\t\tsetError(err);\n\t\t\t\tonError?.(err);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Validate each file\n\t\t\tfor (const file of newFiles) {\n\t\t\t\tconst validationError = validateFile(file);\n\t\t\t\tif (validationError) {\n\t\t\t\t\tconst err = new Error(validationError);\n\t\t\t\t\tsetError(err);\n\t\t\t\t\tonError?.(err);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tsetFiles((prev) => [...prev, ...newFiles]);\n\t\t},\n\t\t[files.length, maxFiles, validateFile, onError]\n\t);\n\n\tconst removeFile = useCallback((index: number) => {\n\t\tsetFiles((prev) => {\n\t\t\tconst newFiles = [...prev];\n\t\t\tnewFiles.splice(index, 1);\n\n\t\t\t// Clean up object URL if it exists\n\t\t\tif (fileUrlsRef.current[index]) {\n\t\t\t\tURL.revokeObjectURL(fileUrlsRef.current[index]);\n\t\t\t\tfileUrlsRef.current.splice(index, 1);\n\t\t\t}\n\n\t\t\treturn newFiles;\n\t\t});\n\t\tsetError(null);\n\t}, []);\n\n\tconst clearFiles = useCallback(() => {\n\t\t// Clean up all object URLs\n\t\tfor (const url of fileUrlsRef.current) {\n\t\t\tURL.revokeObjectURL(url);\n\t\t}\n\t\tfileUrlsRef.current = [];\n\n\t\tsetFiles([]);\n\t\tsetError(null);\n\t}, []);\n\n\tconst reset = useCallback(() => {\n\t\tsetMessage(\"\");\n\t\tclearFiles();\n\t\tsetError(null);\n\t\tsetIsSubmitting(false);\n\t}, [clearFiles]);\n\n\tconst submit = useCallback(async () => {\n\t\tif (!onSubmit) {\n\t\t\treturn;\n\t\t}\n\n\t\
|
|
1
|
+
{"version":3,"file":"use-multimodal-input.js","names":[],"sources":["../../../src/hooks/private/use-multimodal-input.ts"],"sourcesContent":["import { useCallback, useRef, useState } from \"react\";\n\nexport type UseMultimodalInputOptions = {\n\tonSubmit?: (data: { message: string; files: File[] }) => void | Promise<void>;\n\tonError?: (error: Error) => void;\n\tmaxFileSize?: number; // in bytes\n\tmaxFiles?: number;\n\tallowedFileTypes?: string[]; // MIME types\n};\n\nexport type UseMultimodalInputReturn = {\n\t// State\n\tmessage: string;\n\tfiles: File[];\n\tisSubmitting: boolean;\n\terror: Error | null;\n\n\t// Actions\n\tsetMessage: (message: string) => void;\n\taddFiles: (files: File[]) => void;\n\tremoveFile: (index: number) => void;\n\tclearFiles: () => void;\n\tsubmit: () => Promise<void>;\n\treset: () => void;\n\n\t// Validation\n\tisValid: boolean;\n\tcanSubmit: boolean;\n};\n\n/**\n * Manages message text, file attachments and validation for the multimodal\n * composer component. Provides ergonomic helpers for submit flows and error\n * reporting.\n */\nexport const useMultimodalInput = ({\n\tonSubmit,\n\tonError,\n\tmaxFileSize = 10 * 1024 * 1024, // 10MB default\n\tmaxFiles = 5,\n\tallowedFileTypes = [\"image/*\", \"application/pdf\", \"text/*\"],\n}: UseMultimodalInputOptions = {}): UseMultimodalInputReturn => {\n\tconst [message, setMessage] = useState(\"\");\n\tconst [files, setFiles] = useState<File[]>([]);\n\tconst [isSubmitting, setIsSubmitting] = useState(false);\n\tconst [error, setError] = useState<Error | null>(null);\n\n\t// Use ref to prevent re-renders when tracking file URLs\n\tconst fileUrlsRef = useRef<string[]>([]);\n\n\t// Validation helpers\n\tconst validateFile = useCallback(\n\t\t(file: File): string | null => {\n\t\t\tif (file.size > maxFileSize) {\n\t\t\t\treturn `File \"${file.name}\" exceeds maximum size of ${maxFileSize / 1024 / 1024}MB`;\n\t\t\t}\n\n\t\t\tif (allowedFileTypes.length > 0) {\n\t\t\t\tconst isAllowed = allowedFileTypes.some((type) => {\n\t\t\t\t\tif (type.endsWith(\"/*\")) {\n\t\t\t\t\t\tconst baseType = type.slice(0, -2);\n\t\t\t\t\t\treturn file.type.startsWith(baseType);\n\t\t\t\t\t}\n\t\t\t\t\treturn file.type === type;\n\t\t\t\t});\n\n\t\t\t\tif (!isAllowed) {\n\t\t\t\t\treturn `File type \"${file.type}\" is not allowed`;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn null;\n\t\t},\n\t\t[maxFileSize, allowedFileTypes]\n\t);\n\n\t// Actions\n\tconst addFiles = useCallback(\n\t\t(newFiles: File[]) => {\n\t\t\tsetError(null);\n\n\t\t\t// Check max files limit\n\t\t\tif (files.length + newFiles.length > maxFiles) {\n\t\t\t\tconst err = new Error(\n\t\t\t\t\t`Cannot add files: maximum ${maxFiles} files allowed`\n\t\t\t\t);\n\t\t\t\tsetError(err);\n\t\t\t\tonError?.(err);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Validate each file\n\t\t\tfor (const file of newFiles) {\n\t\t\t\tconst validationError = validateFile(file);\n\t\t\t\tif (validationError) {\n\t\t\t\t\tconst err = new Error(validationError);\n\t\t\t\t\tsetError(err);\n\t\t\t\t\tonError?.(err);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tsetFiles((prev) => [...prev, ...newFiles]);\n\t\t},\n\t\t[files.length, maxFiles, validateFile, onError]\n\t);\n\n\tconst removeFile = useCallback((index: number) => {\n\t\tsetFiles((prev) => {\n\t\t\tconst newFiles = [...prev];\n\t\t\tnewFiles.splice(index, 1);\n\n\t\t\t// Clean up object URL if it exists\n\t\t\tif (fileUrlsRef.current[index]) {\n\t\t\t\tURL.revokeObjectURL(fileUrlsRef.current[index]);\n\t\t\t\tfileUrlsRef.current.splice(index, 1);\n\t\t\t}\n\n\t\t\treturn newFiles;\n\t\t});\n\t\tsetError(null);\n\t}, []);\n\n\tconst clearFiles = useCallback(() => {\n\t\t// Clean up all object URLs\n\t\tfor (const url of fileUrlsRef.current) {\n\t\t\tURL.revokeObjectURL(url);\n\t\t}\n\t\tfileUrlsRef.current = [];\n\n\t\tsetFiles([]);\n\t\tsetError(null);\n\t}, []);\n\n\tconst reset = useCallback(() => {\n\t\tsetMessage(\"\");\n\t\tclearFiles();\n\t\tsetError(null);\n\t\tsetIsSubmitting(false);\n\t}, [clearFiles]);\n\n\tconst submit = useCallback(async () => {\n\t\tif (!onSubmit) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst trimmedMessage = message.trim();\n\t\tif (!trimmedMessage && files.length === 0) {\n\t\t\tconst err = new Error(\"Please provide a message or attach files\");\n\t\t\tsetError(err);\n\t\t\tonError?.(err);\n\t\t\treturn;\n\t\t}\n\n\t\tconst previousState = {\n\t\t\tmessage,\n\t\t\tfiles,\n\t\t\tfileUrls: [...fileUrlsRef.current],\n\t\t};\n\n\t\tsetIsSubmitting(true);\n\t\tsetError(null);\n\t\tsetMessage(\"\");\n\t\tsetFiles([]);\n\t\tfileUrlsRef.current = [];\n\n\t\ttry {\n\t\t\tawait onSubmit({ message: trimmedMessage, files: previousState.files });\n\n\t\t\tfor (const url of previousState.fileUrls) {\n\t\t\t\tURL.revokeObjectURL(url);\n\t\t\t}\n\t\t\treset();\n\t\t} catch (err) {\n\t\t\tsetMessage(previousState.message);\n\t\t\tsetFiles(previousState.files);\n\t\t\tfileUrlsRef.current = previousState.fileUrls;\n\n\t\t\tconst _error = err instanceof Error ? err : new Error(\"Failed to submit\");\n\t\t\tsetError(_error);\n\t\t\tonError?.(_error);\n\t\t} finally {\n\t\t\tsetIsSubmitting(false);\n\t\t}\n\t}, [message, files, onSubmit, onError, reset]);\n\n\t// Computed values\n\tconst isValid = message.trim().length > 0 || files.length > 0;\n\tconst canSubmit = isValid && !isSubmitting && !error;\n\n\treturn {\n\t\t// State\n\t\tmessage,\n\t\tfiles,\n\t\tisSubmitting,\n\t\terror,\n\n\t\t// Actions\n\t\tsetMessage,\n\t\taddFiles,\n\t\tremoveFile,\n\t\tclearFiles,\n\t\tsubmit,\n\t\treset,\n\n\t\t// Validation\n\t\tisValid,\n\t\tcanSubmit,\n\t};\n};\n"],"mappings":"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"}
|
|
@@ -9,7 +9,7 @@ const useComposerRefocus = ({ disabled, hasContent, isSubmitting }) => {
|
|
|
9
9
|
});
|
|
10
10
|
useEffect(() => {
|
|
11
11
|
const previous = previousStateRef.current;
|
|
12
|
-
if (!(disabled ||
|
|
12
|
+
if (!(disabled || hasContent) && (previous.isSubmitting || previous.hadContent)) inputRef.current?.focus();
|
|
13
13
|
previousStateRef.current = {
|
|
14
14
|
isSubmitting,
|
|
15
15
|
hadContent: hasContent
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-composer-refocus.js","names":[],"sources":["../../src/hooks/use-composer-refocus.ts"],"sourcesContent":["import type { MutableRefObject } from \"react\";\nimport { useCallback, useEffect, useRef } from \"react\";\n\nexport type UseComposerRefocusOptions = {\n\tdisabled: boolean;\n\thasContent: boolean;\n\tisSubmitting: boolean;\n};\n\nexport type UseComposerRefocusReturn = {\n\tfocusComposer: () => void;\n\tinputRef: MutableRefObject<HTMLTextAreaElement | null>;\n};\n\nexport const useComposerRefocus = ({\n\tdisabled,\n\thasContent,\n\tisSubmitting,\n}: UseComposerRefocusOptions): UseComposerRefocusReturn => {\n\tconst inputRef = useRef<HTMLTextAreaElement | null>(null);\n\tconst previousStateRef = useRef({\n\t\tisSubmitting,\n\t\thadContent: hasContent,\n\t});\n\n\tuseEffect(() => {\n\t\tconst previous = previousStateRef.current;\n\n\t\tif (\n\t\t\t!(disabled ||
|
|
1
|
+
{"version":3,"file":"use-composer-refocus.js","names":[],"sources":["../../src/hooks/use-composer-refocus.ts"],"sourcesContent":["import type { MutableRefObject } from \"react\";\nimport { useCallback, useEffect, useRef } from \"react\";\n\nexport type UseComposerRefocusOptions = {\n\tdisabled: boolean;\n\thasContent: boolean;\n\tisSubmitting: boolean;\n};\n\nexport type UseComposerRefocusReturn = {\n\tfocusComposer: () => void;\n\tinputRef: MutableRefObject<HTMLTextAreaElement | null>;\n};\n\nexport const useComposerRefocus = ({\n\tdisabled,\n\thasContent,\n\tisSubmitting,\n}: UseComposerRefocusOptions): UseComposerRefocusReturn => {\n\tconst inputRef = useRef<HTMLTextAreaElement | null>(null);\n\tconst previousStateRef = useRef({\n\t\tisSubmitting,\n\t\thadContent: hasContent,\n\t});\n\n\tuseEffect(() => {\n\t\tconst previous = previousStateRef.current;\n\n\t\tif (\n\t\t\t!(disabled || hasContent) &&\n\t\t\t(previous.isSubmitting || previous.hadContent)\n\t\t) {\n\t\t\tinputRef.current?.focus();\n\t\t}\n\n\t\tpreviousStateRef.current = {\n\t\t\tisSubmitting,\n\t\t\thadContent: hasContent,\n\t\t};\n\t}, [disabled, hasContent, isSubmitting]);\n\n\tconst focusComposer = useCallback(() => {\n\t\tinputRef.current?.focus();\n\t}, []);\n\n\treturn {\n\t\tfocusComposer,\n\t\tinputRef,\n\t};\n};\n"],"mappings":";;;AAcA,MAAa,sBAAsB,EAClC,UACA,YACA,mBAC0D;CAC1D,MAAM,WAAW,OAAmC,KAAK;CACzD,MAAM,mBAAmB,OAAO;EAC/B;EACA,YAAY;EACZ,CAAC;AAEF,iBAAgB;EACf,MAAM,WAAW,iBAAiB;AAElC,MACC,EAAE,YAAY,gBACb,SAAS,gBAAgB,SAAS,YAEnC,UAAS,SAAS,OAAO;AAG1B,mBAAiB,UAAU;GAC1B;GACA,YAAY;GACZ;IACC;EAAC;EAAU;EAAY;EAAa,CAAC;AAMxC,QAAO;EACN,eALqB,kBAAkB;AACvC,YAAS,SAAS,OAAO;KACvB,EAAE,CAAC;EAIL;EACA"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cossistant/react",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.23",
|
|
5
5
|
"private": false,
|
|
6
6
|
"author": "Cossistant team",
|
|
7
7
|
"description": "Headless React SDK for building AI-powered support/chat widgets. Hooks + primitives, WS-driven, TypeScript-first. Next.js-ready, Tailwind optional.",
|
|
@@ -88,8 +88,8 @@
|
|
|
88
88
|
"*.css"
|
|
89
89
|
],
|
|
90
90
|
"dependencies": {
|
|
91
|
-
"@cossistant/core": "0.0.
|
|
92
|
-
"@cossistant/types": "0.0.
|
|
91
|
+
"@cossistant/core": "0.0.23",
|
|
92
|
+
"@cossistant/types": "0.0.23",
|
|
93
93
|
"class-variance-authority": "^0.7.1",
|
|
94
94
|
"clsx": "^2.1.1",
|
|
95
95
|
"nanoid": "^5.1.5",
|
|
@@ -15,7 +15,7 @@ type AvatarImageProps = Omit<React$1.ImgHTMLAttributes<HTMLImageElement>, "src"
|
|
|
15
15
|
* Controlled `<img>` that syncs its loading status back to the avatar context
|
|
16
16
|
* so fallbacks know when to display.
|
|
17
17
|
*/
|
|
18
|
-
declare const AvatarImage: React$1.ForwardRefExoticComponent<Omit<React$1.ImgHTMLAttributes<HTMLImageElement>, "
|
|
18
|
+
declare const AvatarImage: React$1.ForwardRefExoticComponent<Omit<React$1.ImgHTMLAttributes<HTMLImageElement>, "alt" | "src"> & {
|
|
19
19
|
src: string;
|
|
20
20
|
alt?: string;
|
|
21
21
|
asChild?: boolean;
|
package/realtime-events.d.ts
CHANGED
|
@@ -229,6 +229,75 @@ declare const realtimeSchema: {
|
|
|
229
229
|
deletedAt: ZodNullable<ZodString>;
|
|
230
230
|
lastMessageAt: ZodNullable<ZodString>;
|
|
231
231
|
lastSeenAt: ZodNullable<ZodString>;
|
|
232
|
+
lastMessageTimelineItem: ZodNullable<ZodObject<{
|
|
233
|
+
id: ZodOptional<ZodString>;
|
|
234
|
+
conversationId: ZodString;
|
|
235
|
+
organizationId: ZodString;
|
|
236
|
+
visibility: ZodEnum<{
|
|
237
|
+
public: "public";
|
|
238
|
+
private: "private";
|
|
239
|
+
}>;
|
|
240
|
+
type: ZodEnum<{
|
|
241
|
+
message: "message";
|
|
242
|
+
event: "event";
|
|
243
|
+
identification: "identification";
|
|
244
|
+
}>;
|
|
245
|
+
text: ZodNullable<ZodString>;
|
|
246
|
+
tool: ZodOptional<ZodNullable<ZodString>>;
|
|
247
|
+
parts: ZodArray<ZodUnion<readonly [ZodObject<{
|
|
248
|
+
type: ZodLiteral<"text">;
|
|
249
|
+
text: ZodString;
|
|
250
|
+
}, $strip>, ZodObject<{
|
|
251
|
+
type: ZodLiteral<"event">;
|
|
252
|
+
eventType: ZodEnum<{
|
|
253
|
+
assigned: "assigned";
|
|
254
|
+
unassigned: "unassigned";
|
|
255
|
+
participant_requested: "participant_requested";
|
|
256
|
+
participant_joined: "participant_joined";
|
|
257
|
+
participant_left: "participant_left";
|
|
258
|
+
status_changed: "status_changed";
|
|
259
|
+
priority_changed: "priority_changed";
|
|
260
|
+
tag_added: "tag_added";
|
|
261
|
+
tag_removed: "tag_removed";
|
|
262
|
+
resolved: "resolved";
|
|
263
|
+
reopened: "reopened";
|
|
264
|
+
visitor_blocked: "visitor_blocked";
|
|
265
|
+
visitor_unblocked: "visitor_unblocked";
|
|
266
|
+
visitor_identified: "visitor_identified";
|
|
267
|
+
}>;
|
|
268
|
+
actorUserId: ZodNullable<ZodString>;
|
|
269
|
+
actorAiAgentId: ZodNullable<ZodString>;
|
|
270
|
+
targetUserId: ZodNullable<ZodString>;
|
|
271
|
+
targetAiAgentId: ZodNullable<ZodString>;
|
|
272
|
+
message: ZodOptional<ZodNullable<ZodString>>;
|
|
273
|
+
}, $strip>, ZodObject<{
|
|
274
|
+
type: ZodLiteral<"image">;
|
|
275
|
+
url: ZodString;
|
|
276
|
+
mediaType: ZodString;
|
|
277
|
+
fileName: ZodOptional<ZodString>;
|
|
278
|
+
size: ZodOptional<ZodNumber>;
|
|
279
|
+
width: ZodOptional<ZodNumber>;
|
|
280
|
+
height: ZodOptional<ZodNumber>;
|
|
281
|
+
}, $strip>, ZodObject<{
|
|
282
|
+
type: ZodLiteral<"file">;
|
|
283
|
+
url: ZodString;
|
|
284
|
+
mediaType: ZodString;
|
|
285
|
+
fileName: ZodOptional<ZodString>;
|
|
286
|
+
size: ZodOptional<ZodNumber>;
|
|
287
|
+
}, $strip>, ZodObject<{
|
|
288
|
+
type: ZodLiteral<"metadata">;
|
|
289
|
+
source: ZodEnum<{
|
|
290
|
+
email: "email";
|
|
291
|
+
widget: "widget";
|
|
292
|
+
api: "api";
|
|
293
|
+
}>;
|
|
294
|
+
}, $strip>]>>;
|
|
295
|
+
userId: ZodNullable<ZodString>;
|
|
296
|
+
aiAgentId: ZodNullable<ZodString>;
|
|
297
|
+
visitorId: ZodNullable<ZodString>;
|
|
298
|
+
createdAt: ZodString;
|
|
299
|
+
deletedAt: ZodOptional<ZodNullable<ZodString>>;
|
|
300
|
+
}, $strip>>;
|
|
232
301
|
lastTimelineItem: ZodNullable<ZodObject<{
|
|
233
302
|
id: ZodOptional<ZodString>;
|
|
234
303
|
conversationId: ZodString;
|
package/realtime-events.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"realtime-events.d.ts","names":[],"sources":["../../types/src/realtime-events.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;cAiBa
|
|
1
|
+
{"version":3,"file":"realtime-events.d.ts","names":[],"sources":["../../types/src/realtime-events.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;cAiBa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAmED,iBAAA,gBAAiC;KAEjC,+BAA+B,qBAAqB,eACvD,gBAAgB;KAGb,wBAAwB;QAC7B;WACG,qBAAqB;;KAGnB,gBAAA,WACL,oBAAoB,cAAc,KACvC;KAEU,4BAA4B,qBACvC,qBAAqB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"multimodal-input.d.ts","names":[],"sources":["../../../src/support/components/multimodal-input.tsx"],"sourcesContent":[],"mappings":";;;KAWY,oBAAA;;EAAA,KAAA,EAAA,MAAA;EAKY,QAAA,EAAA,CAAA,KAAA,EAAA,MAAA,EAAA,GAAA,IAAA;EAIf,QAAA,EAAA,GAAA,GAAA,IAAA;EACA,YAAA,CAAA,EAAA,CAAA,KAAA,EALe,IAKf,EAAA,EAAA,GAAA,IAAA;EAAI,WAAA,CAAA,EAAA,MAAA;EAOA,QAAA,CAAA,EAAA,
|
|
1
|
+
{"version":3,"file":"multimodal-input.d.ts","names":[],"sources":["../../../src/support/components/multimodal-input.tsx"],"sourcesContent":[],"mappings":";;;KAWY,oBAAA;;EAAA,KAAA,EAAA,MAAA;EAKY,QAAA,EAAA,CAAA,KAAA,EAAA,MAAA,EAAA,GAAA,IAAA;EAIf,QAAA,EAAA,GAAA,GAAA,IAAA;EACA,YAAA,CAAA,EAAA,CAAA,KAAA,EALe,IAKf,EAAA,EAAA,GAAA,IAAA;EAAI,WAAA,CAAA,EAAA,MAAA;EAOA,QAAA,CAAA,EAAA,OAoKZ;EAEW,YAAA,CAAA,EAAA,OAAe;EAKd,KAAA,CAAA,EAnLJ,KAiMR,GAAA,IAAA;UAhMQ;;;;;;cAOI,iBAAiB,KAAA,CAAM,GAAG;KAsK3B,eAAA;;;;cAKC,YAAY,KAAA,CAAM,GAAG"}
|
|
@@ -24,13 +24,16 @@ const MultimodalInput = ({ className, value, onChange, onSubmit, onFileSelect, p
|
|
|
24
24
|
hasContent,
|
|
25
25
|
isSubmitting
|
|
26
26
|
});
|
|
27
|
-
const canSubmit = !
|
|
27
|
+
const canSubmit = !disabled && hasContent;
|
|
28
28
|
const text = useSupportText();
|
|
29
29
|
const resolvedPlaceholder = placeholder ?? text("component.multimodalInput.placeholder");
|
|
30
30
|
const handleSubmit = () => {
|
|
31
31
|
if (!canSubmit) return;
|
|
32
32
|
onSubmit();
|
|
33
33
|
focusComposer();
|
|
34
|
+
requestAnimationFrame(() => {
|
|
35
|
+
focusComposer();
|
|
36
|
+
});
|
|
34
37
|
};
|
|
35
38
|
const handleFormSubmit = (e) => {
|
|
36
39
|
e.preventDefault();
|
|
@@ -83,8 +86,9 @@ const MultimodalInput = ({ className, value, onChange, onSubmit, onFileSelect, p
|
|
|
83
86
|
/* @__PURE__ */ jsxs("div", {
|
|
84
87
|
className: "group/multimodal-input flex flex-col rounded border border-co-border bg-co-background ring-offset-2 focus-within:ring-1 focus-within:ring-co-primary/10 dark:bg-co-background-200",
|
|
85
88
|
children: [/* @__PURE__ */ jsx(MultimodalInput$1, {
|
|
89
|
+
autoFocus: true,
|
|
86
90
|
className: cn("flex-1 resize-none overflow-hidden p-3 text-co-foreground text-sm placeholder:text-co-primary/50 focus-visible:outline-none", className),
|
|
87
|
-
disabled
|
|
91
|
+
disabled,
|
|
88
92
|
error,
|
|
89
93
|
onChange,
|
|
90
94
|
onFileSelect,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"multimodal-input.js","names":["MultimodalInput: React.FC<MultimodalInputProps>","Icon","Primitive.MultimodalInput","SendButton: React.FC<SendButtonProps>","Primitive.Button"],"sources":["../../../src/support/components/multimodal-input.tsx"],"sourcesContent":["\"use client\";\n\nimport type React from \"react\";\nimport { useRef } from \"react\";\nimport { useComposerRefocus } from \"../../hooks/use-composer-refocus\";\nimport * as Primitive from \"../../primitives\";\nimport { useSupportText } from \"../text\";\nimport { cn } from \"../utils\";\nimport Icon from \"./icons\";\nimport { Watermark } from \"./watermark\";\n\nexport type MultimodalInputProps = {\n\tclassName?: string;\n\tvalue: string;\n\tonChange: (value: string) => void;\n\tonSubmit: () => void;\n\tonFileSelect?: (files: File[]) => void;\n\tplaceholder?: string;\n\tdisabled?: boolean;\n\tisSubmitting?: boolean;\n\terror?: Error | null;\n\tfiles?: File[];\n\tonRemoveFile?: (index: number) => void;\n\tmaxFiles?: number;\n\tmaxFileSize?: number;\n\tallowedFileTypes?: string[];\n};\n\nexport const MultimodalInput: React.FC<MultimodalInputProps> = ({\n\tclassName,\n\tvalue,\n\tonChange,\n\tonSubmit,\n\tonFileSelect,\n\tplaceholder,\n\tdisabled = false,\n\tisSubmitting = false,\n\terror,\n\tfiles = [],\n\tonRemoveFile,\n\tmaxFiles = 5,\n\tmaxFileSize = 10 * 1024 * 1024, // 10MB\n\tallowedFileTypes = [\"image/*\", \"application/pdf\", \"text/*\"],\n}) => {\n\tconst fileInputRef = useRef<HTMLInputElement>(null);\n\tconst hasContent = value.trim().length > 0 || files.length > 0;\n\tconst { focusComposer, inputRef } = useComposerRefocus({\n\t\tdisabled,\n\t\thasContent,\n\t\tisSubmitting,\n\t});\n\tconst canSubmit = !
|
|
1
|
+
{"version":3,"file":"multimodal-input.js","names":["MultimodalInput: React.FC<MultimodalInputProps>","Icon","Primitive.MultimodalInput","SendButton: React.FC<SendButtonProps>","Primitive.Button"],"sources":["../../../src/support/components/multimodal-input.tsx"],"sourcesContent":["\"use client\";\n\nimport type React from \"react\";\nimport { useRef } from \"react\";\nimport { useComposerRefocus } from \"../../hooks/use-composer-refocus\";\nimport * as Primitive from \"../../primitives\";\nimport { useSupportText } from \"../text\";\nimport { cn } from \"../utils\";\nimport Icon from \"./icons\";\nimport { Watermark } from \"./watermark\";\n\nexport type MultimodalInputProps = {\n\tclassName?: string;\n\tvalue: string;\n\tonChange: (value: string) => void;\n\tonSubmit: () => void;\n\tonFileSelect?: (files: File[]) => void;\n\tplaceholder?: string;\n\tdisabled?: boolean;\n\tisSubmitting?: boolean;\n\terror?: Error | null;\n\tfiles?: File[];\n\tonRemoveFile?: (index: number) => void;\n\tmaxFiles?: number;\n\tmaxFileSize?: number;\n\tallowedFileTypes?: string[];\n};\n\nexport const MultimodalInput: React.FC<MultimodalInputProps> = ({\n\tclassName,\n\tvalue,\n\tonChange,\n\tonSubmit,\n\tonFileSelect,\n\tplaceholder,\n\tdisabled = false,\n\tisSubmitting = false,\n\terror,\n\tfiles = [],\n\tonRemoveFile,\n\tmaxFiles = 5,\n\tmaxFileSize = 10 * 1024 * 1024, // 10MB\n\tallowedFileTypes = [\"image/*\", \"application/pdf\", \"text/*\"],\n}) => {\n\tconst fileInputRef = useRef<HTMLInputElement>(null);\n\tconst hasContent = value.trim().length > 0 || files.length > 0;\n\tconst { focusComposer, inputRef } = useComposerRefocus({\n\t\tdisabled,\n\t\thasContent,\n\t\tisSubmitting,\n\t});\n\tconst canSubmit = !disabled && hasContent;\n\tconst text = useSupportText();\n\tconst resolvedPlaceholder =\n\t\tplaceholder ?? text(\"component.multimodalInput.placeholder\");\n\n\tconst handleSubmit = () => {\n\t\tif (!canSubmit) {\n\t\t\treturn;\n\t\t}\n\n\t\tonSubmit();\n\t\t// Try focusing immediately for optimistic submission UX, then ensure focus\n\t\t// sticks after the submit button handles the click.\n\t\tfocusComposer();\n\t\trequestAnimationFrame(() => {\n\t\t\tfocusComposer();\n\t\t});\n\t};\n\n\tconst handleFormSubmit = (e: React.FormEvent) => {\n\t\te.preventDefault();\n\t\thandleSubmit();\n\t};\n\n\tconst handleAttachClick = () => {\n\t\tif (files.length < maxFiles) {\n\t\t\tfileInputRef.current?.click();\n\t\t}\n\t};\n\n\tconst formatFileSize = (bytes: number) => {\n\t\tif (bytes < 1024) {\n\t\t\treturn `${bytes} B`;\n\t\t}\n\t\tif (bytes < 1024 * 1024) {\n\t\t\treturn `${(bytes / 1024).toFixed(1)} KB`;\n\t\t}\n\t\treturn `${(bytes / (1024 * 1024)).toFixed(1)} MB`;\n\t};\n\n\treturn (\n\t\t<form className=\"flex flex-col gap-2\" onSubmit={handleFormSubmit}>\n\t\t\t{/* Error message */}\n\t\t\t{error && (\n\t\t\t\t<div\n\t\t\t\t\tclassName=\"rounded-md bg-co-destructive-muted p-2 text-co-destructive text-xs\"\n\t\t\t\t\tid=\"multimodal-input-error\"\n\t\t\t\t>\n\t\t\t\t\t{error.message}\n\t\t\t\t</div>\n\t\t\t)}\n\n\t\t\t{/* File attachments */}\n\t\t\t{files.length > 0 && (\n\t\t\t\t<div className=\"flex flex-wrap gap-2 p-2\">\n\t\t\t\t\t{files.map((file, index) => (\n\t\t\t\t\t\t<div\n\t\t\t\t\t\t\tclassName=\"flex items-center gap-2 rounded-md bg-co-muted px-2 py-1 text-xs\"\n\t\t\t\t\t\t\tkey={`${file.name}-${index}`}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<Icon className=\"h-3 w-3\" name=\"attachment\" />\n\t\t\t\t\t\t\t<span className=\"max-w-[150px] truncate\">{file.name}</span>\n\t\t\t\t\t\t\t<span className=\"text-co-muted-foreground\">\n\t\t\t\t\t\t\t\t{formatFileSize(file.size)}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t{onRemoveFile && (\n\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\taria-label={text(\"common.actions.removeFile\", {\n\t\t\t\t\t\t\t\t\t\tfileName: file.name,\n\t\t\t\t\t\t\t\t\t})}\n\t\t\t\t\t\t\t\t\tclassName=\"ml-1 hover:text-co-destructive\"\n\t\t\t\t\t\t\t\t\tonClick={() => onRemoveFile(index)}\n\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t<Icon className=\"h-3 w-3\" name=\"close\" />\n\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t))}\n\t\t\t\t</div>\n\t\t\t)}\n\n\t\t\t{/* Input area */}\n\t\t\t<div className=\"group/multimodal-input flex flex-col rounded border border-co-border bg-co-background ring-offset-2 focus-within:ring-1 focus-within:ring-co-primary/10 dark:bg-co-background-200\">\n\t\t\t\t<Primitive.MultimodalInput\n\t\t\t\t\tautoFocus\n\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\"flex-1 resize-none overflow-hidden p-3 text-co-foreground text-sm placeholder:text-co-primary/50 focus-visible:outline-none\",\n\t\t\t\t\t\tclassName\n\t\t\t\t\t)}\n\t\t\t\t\tdisabled={disabled}\n\t\t\t\t\terror={error}\n\t\t\t\t\tonChange={onChange}\n\t\t\t\t\tonFileSelect={onFileSelect}\n\t\t\t\t\tonSubmit={handleSubmit}\n\t\t\t\t\tplaceholder={resolvedPlaceholder}\n\t\t\t\t\tref={inputRef}\n\t\t\t\t\tvalue={value}\n\t\t\t\t/>\n\n\t\t\t\t<div className=\"flex items-center justify-between py-1 pr-1 pl-3\">\n\t\t\t\t\t<Watermark />\n\n\t\t\t\t\t<div className=\"flex items-center gap-0.5\">\n\t\t\t\t\t\t{/* File attachment button */}\n\t\t\t\t\t\t{/* {onFileSelect && (\n\t\t\t\t\t\t\t<>\n\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\taria-label={text(\"common.actions.attachFiles\")}\n\t\t\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\t\t\"group flex h-8 w-8 items-center justify-center rounded-md text-co-muted-foreground hover:bg-co-muted hover:text-co-foreground disabled:cursor-not-allowed disabled:opacity-50\",\n\t\t\t\t\t\t\t\t\t\tfiles.length >= maxFiles && \"opacity-50\"\n\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\tdisabled={\n\t\t\t\t\t\t\t\t\t\tdisabled || isSubmitting || files.length >= maxFiles\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tonClick={handleAttachClick}\n\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t<Icon className=\"h-4 w-4\" name=\"attachment\" />\n\t\t\t\t\t\t\t\t</button>\n\n\t\t\t\t\t\t\t\t<Primitive.FileInput\n\t\t\t\t\t\t\t\t\taccept={allowedFileTypes.join(\",\")}\n\t\t\t\t\t\t\t\t\tclassName=\"hidden\"\n\t\t\t\t\t\t\t\t\tdisabled={\n\t\t\t\t\t\t\t\t\t\tdisabled || isSubmitting || files.length >= maxFiles\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tonFileSelect={onFileSelect}\n\t\t\t\t\t\t\t\t\tref={fileInputRef}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t</>\n\t\t\t\t\t\t)} */}\n\n\t\t\t\t\t\t{/* Send button */}\n\t\t\t\t\t\t<SendButton disabled={!canSubmit} />\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</form>\n\t);\n};\n\nexport type SendButtonProps = {\n\tclassName?: string;\n\tdisabled?: boolean;\n};\n\nexport const SendButton: React.FC<SendButtonProps> = ({\n\tclassName,\n\tdisabled = false,\n}) => (\n\t<Primitive.Button\n\t\tclassName={cn(\n\t\t\t\"group flex h-8 w-8 items-center justify-center rounded-md text-co-primary hover:bg-co-muted disabled:cursor-not-allowed disabled:opacity-50\",\n\t\t\tclassName\n\t\t)}\n\t\tdisabled={disabled}\n\t\ttype=\"submit\"\n\t>\n\t\t<Icon className=\"h-4 w-4\" filledOnHover name=\"send\" />\n\t</Primitive.Button>\n);\n"],"mappings":";;;;;;;;;;;;;;AA4BA,MAAaA,mBAAmD,EAC/D,WACA,OACA,UACA,UACA,cACA,aACA,WAAW,OACX,eAAe,OACf,OACA,QAAQ,EAAE,EACV,cACA,WAAW,GACX,cAAc,KAAK,OAAO,MAC1B,mBAAmB;CAAC;CAAW;CAAmB;CAAS,OACtD;AACgB,QAAyB,KAAK;CACnD,MAAM,aAAa,MAAM,MAAM,CAAC,SAAS,KAAK,MAAM,SAAS;CAC7D,MAAM,EAAE,eAAe,aAAa,mBAAmB;EACtD;EACA;EACA;EACA,CAAC;CACF,MAAM,YAAY,CAAC,YAAY;CAC/B,MAAM,OAAO,gBAAgB;CAC7B,MAAM,sBACL,eAAe,KAAK,wCAAwC;CAE7D,MAAM,qBAAqB;AAC1B,MAAI,CAAC,UACJ;AAGD,YAAU;AAGV,iBAAe;AACf,8BAA4B;AAC3B,kBAAe;IACd;;CAGH,MAAM,oBAAoB,MAAuB;AAChD,IAAE,gBAAgB;AAClB,gBAAc;;CASf,MAAM,kBAAkB,UAAkB;AACzC,MAAI,QAAQ,KACX,QAAO,GAAG,MAAM;AAEjB,MAAI,QAAQ,OAAO,KAClB,QAAO,IAAI,QAAQ,MAAM,QAAQ,EAAE,CAAC;AAErC,SAAO,IAAI,SAAS,OAAO,OAAO,QAAQ,EAAE,CAAC;;AAG9C,QACC,qBAAC;EAAK,WAAU;EAAsB,UAAU;;GAE9C,SACA,oBAAC;IACA,WAAU;IACV,IAAG;cAEF,MAAM;KACF;GAIN,MAAM,SAAS,KACf,oBAAC;IAAI,WAAU;cACb,MAAM,KAAK,MAAM,UACjB,qBAAC;KACA,WAAU;;MAGV,oBAACC;OAAK,WAAU;OAAU,MAAK;QAAe;MAC9C,oBAAC;OAAK,WAAU;iBAA0B,KAAK;QAAY;MAC3D,oBAAC;OAAK,WAAU;iBACd,eAAe,KAAK,KAAK;QACpB;MACN,gBACA,oBAAC;OACA,cAAY,KAAK,6BAA6B,EAC7C,UAAU,KAAK,MACf,CAAC;OACF,WAAU;OACV,eAAe,aAAa,MAAM;OAClC,MAAK;iBAEL,oBAACA;QAAK,WAAU;QAAU,MAAK;SAAU;QACjC;;OAjBL,GAAG,KAAK,KAAK,GAAG,QAmBhB,CACL;KACG;GAIP,qBAAC;IAAI,WAAU;eACd,oBAACC;KACA;KACA,WAAW,GACV,+HACA,UACA;KACS;KACH;KACG;KACI;KACd,UAAU;KACV,aAAa;KACb,KAAK;KACE;MACN,EAEF,qBAAC;KAAI,WAAU;gBACd,oBAAC,cAAY,EAEb,oBAAC;MAAI,WAAU;gBAgCd,oBAAC,cAAW,UAAU,CAAC,YAAa;OAC/B;MACD;KACD;;GACA;;AAST,MAAaC,cAAyC,EACrD,WACA,WAAW,YAEX,oBAACC;CACA,WAAW,GACV,+IACA,UACA;CACS;CACV,MAAK;WAEL,oBAACH;EAAK,WAAU;EAAU;EAAc,MAAK;GAAS;EACpC"}
|
|
@@ -19,7 +19,7 @@ function TimelineMessageItem({ item, isLast = false, isSentByViewer = false }) {
|
|
|
19
19
|
children: /* @__PURE__ */ jsxs("div", {
|
|
20
20
|
className: cn("flex w-full min-w-0 flex-1 flex-col gap-1", isSentByViewerFinal && "items-end"),
|
|
21
21
|
children: [/* @__PURE__ */ jsx(TimelineItemContent, {
|
|
22
|
-
className: cn("block min-w-0 max-w-[300px] break-
|
|
22
|
+
className: cn("block min-w-0 max-w-[300px] whitespace-pre-wrap break-words rounded-lg px-3.5 py-2.5 text-sm", {
|
|
23
23
|
"bg-co-background-300 text-co-foreground dark:bg-co-background-600": !isSentByViewerFinal,
|
|
24
24
|
"bg-co-primary text-co-primary-foreground": isSentByViewerFinal,
|
|
25
25
|
"rounded-br-sm": isLast && isSentByViewerFinal,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"timeline-message-item.js","names":["PrimitiveTimelineItem"],"sources":["../../../src/support/components/timeline-message-item.tsx"],"sourcesContent":["import type { TimelineItem } from \"@cossistant/types/api/timeline-item\";\nimport type React from \"react\";\nimport {\n\tTimelineItem as PrimitiveTimelineItem,\n\tTimelineItemContent,\n\tTimelineItemTimestamp,\n} from \"../../primitives/timeline-item\";\nimport { useSupportText } from \"../text\";\nimport { cn } from \"../utils\";\n\nexport type TimelineMessageItemProps = {\n\titem: TimelineItem;\n\tisLast?: boolean;\n\tisSentByViewer?: boolean;\n};\n\n/**\n * Message bubble renderer that adapts layout depending on whether the visitor\n * or an agent sent the message.\n */\nexport function TimelineMessageItem({\n\titem,\n\tisLast = false,\n\tisSentByViewer = false,\n}: TimelineMessageItemProps): React.ReactElement {\n\tconst text = useSupportText();\n\treturn (\n\t\t<PrimitiveTimelineItem item={item}>\n\t\t\t{({ isAI, timestamp }) => {\n\t\t\t\t// isSentByViewer defaults to false, meaning messages are treated as received\n\t\t\t\t// (left side with background) unless explicitly marked as sent by viewer\n\t\t\t\tconst isSentByViewerFinal = isSentByViewer;\n\n\t\t\t\treturn (\n\t\t\t\t\t<div\n\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\"flex w-full gap-2\",\n\t\t\t\t\t\t\tisSentByViewerFinal && \"flex-row-reverse\",\n\t\t\t\t\t\t\t!isSentByViewerFinal && \"flex-row\"\n\t\t\t\t\t\t)}\n\t\t\t\t\t>\n\t\t\t\t\t\t<div\n\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\"flex w-full min-w-0 flex-1 flex-col gap-1\",\n\t\t\t\t\t\t\t\tisSentByViewerFinal && \"items-end\"\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<TimelineItemContent\n\t\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\t\"block min-w-0 max-w-[300px] break-
|
|
1
|
+
{"version":3,"file":"timeline-message-item.js","names":["PrimitiveTimelineItem"],"sources":["../../../src/support/components/timeline-message-item.tsx"],"sourcesContent":["import type { TimelineItem } from \"@cossistant/types/api/timeline-item\";\nimport type React from \"react\";\nimport {\n\tTimelineItem as PrimitiveTimelineItem,\n\tTimelineItemContent,\n\tTimelineItemTimestamp,\n} from \"../../primitives/timeline-item\";\nimport { useSupportText } from \"../text\";\nimport { cn } from \"../utils\";\n\nexport type TimelineMessageItemProps = {\n\titem: TimelineItem;\n\tisLast?: boolean;\n\tisSentByViewer?: boolean;\n};\n\n/**\n * Message bubble renderer that adapts layout depending on whether the visitor\n * or an agent sent the message.\n */\nexport function TimelineMessageItem({\n\titem,\n\tisLast = false,\n\tisSentByViewer = false,\n}: TimelineMessageItemProps): React.ReactElement {\n\tconst text = useSupportText();\n\treturn (\n\t\t<PrimitiveTimelineItem item={item}>\n\t\t\t{({ isAI, timestamp }) => {\n\t\t\t\t// isSentByViewer defaults to false, meaning messages are treated as received\n\t\t\t\t// (left side with background) unless explicitly marked as sent by viewer\n\t\t\t\tconst isSentByViewerFinal = isSentByViewer;\n\n\t\t\t\treturn (\n\t\t\t\t\t<div\n\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\"flex w-full gap-2\",\n\t\t\t\t\t\t\tisSentByViewerFinal && \"flex-row-reverse\",\n\t\t\t\t\t\t\t!isSentByViewerFinal && \"flex-row\"\n\t\t\t\t\t\t)}\n\t\t\t\t\t>\n\t\t\t\t\t\t<div\n\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\"flex w-full min-w-0 flex-1 flex-col gap-1\",\n\t\t\t\t\t\t\t\tisSentByViewerFinal && \"items-end\"\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<TimelineItemContent\n\t\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\t\"block min-w-0 max-w-[300px] whitespace-pre-wrap break-words rounded-lg px-3.5 py-2.5 text-sm\",\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\"bg-co-background-300 text-co-foreground dark:bg-co-background-600\":\n\t\t\t\t\t\t\t\t\t\t\t!isSentByViewerFinal,\n\t\t\t\t\t\t\t\t\t\t\"bg-co-primary text-co-primary-foreground\":\n\t\t\t\t\t\t\t\t\t\t\tisSentByViewerFinal,\n\t\t\t\t\t\t\t\t\t\t\"rounded-br-sm\": isLast && isSentByViewerFinal,\n\t\t\t\t\t\t\t\t\t\t\"rounded-bl-sm\": isLast && !isSentByViewerFinal,\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\trenderMarkdown\n\t\t\t\t\t\t\t\ttext={item.text}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t{isLast && (\n\t\t\t\t\t\t\t\t<TimelineItemTimestamp\n\t\t\t\t\t\t\t\t\tclassName=\"px-1 text-co-muted-foreground text-xs\"\n\t\t\t\t\t\t\t\t\ttimestamp={timestamp}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t{() => (\n\t\t\t\t\t\t\t\t\t\t<>\n\t\t\t\t\t\t\t\t\t\t\t{timestamp.toLocaleTimeString([], {\n\t\t\t\t\t\t\t\t\t\t\t\thour: \"2-digit\",\n\t\t\t\t\t\t\t\t\t\t\t\tminute: \"2-digit\",\n\t\t\t\t\t\t\t\t\t\t\t})}\n\t\t\t\t\t\t\t\t\t\t\t{isAI &&\n\t\t\t\t\t\t\t\t\t\t\t\t` ${text(\"component.message.timestamp.aiIndicator\")}`}\n\t\t\t\t\t\t\t\t\t\t</>\n\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t</TimelineItemTimestamp>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t);\n\t\t\t}}\n\t\t</PrimitiveTimelineItem>\n\t);\n}\n"],"mappings":";;;;;;;;;;AAoBA,SAAgB,oBAAoB,EACnC,MACA,SAAS,OACT,iBAAiB,SAC+B;CAChD,MAAM,OAAO,gBAAgB;AAC7B,QACC,oBAACA;EAA4B;aAC1B,EAAE,MAAM,gBAAgB;GAGzB,MAAM,sBAAsB;AAE5B,UACC,oBAAC;IACA,WAAW,GACV,qBACA,uBAAuB,oBACvB,CAAC,uBAAuB,WACxB;cAED,qBAAC;KACA,WAAW,GACV,6CACA,uBAAuB,YACvB;gBAED,oBAAC;MACA,WAAW,GACV,gGACA;OACC,qEACC,CAAC;OACF,4CACC;OACD,iBAAiB,UAAU;OAC3B,iBAAiB,UAAU,CAAC;OAC5B,CACD;MACD;MACA,MAAM,KAAK;OACV,EACD,UACA,oBAAC;MACA,WAAU;MACC;sBAGV,4CACE,UAAU,mBAAmB,EAAE,EAAE;OACjC,MAAM;OACN,QAAQ;OACR,CAAC,EACD,QACA,IAAI,KAAK,0CAA0C,MAClD;OAEmB;MAEpB;KACD;;GAGe"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"typing-indicator.d.ts","names":[],"sources":["../../../src/support/components/typing-indicator.tsx"],"sourcesContent":[],"mappings":";;;;KAKY,qBAAA;KAEA,iBAAA;EAFA,EAAA,EAAA,MAAA;EAEA,IAAA,EAEL,qBAFsB;AAK7B,CAAA;AAAwD,KAA5C,oBAAA,GAAuB,OAAA,CAAM,cAAe,CAAA,cAAA,CAAA,GAAA;EAArB,YAAM,EAC1B,iBAD0B,EAAA;EAC1B,iBAAA,CAAA,EACM,gBADN,EAAA;EACM,oBAAA,CAAA,EACG,mBADH,EAAA;EACG,WAAA,CAAA,EAAA,OAAA;CAAmB;AAI9B,cAAA,YAAgB,EAAA,CAAA;EAAA;
|
|
1
|
+
{"version":3,"file":"typing-indicator.d.ts","names":[],"sources":["../../../src/support/components/typing-indicator.tsx"],"sourcesContent":[],"mappings":";;;;KAKY,qBAAA;KAEA,iBAAA;EAFA,EAAA,EAAA,MAAA;EAEA,IAAA,EAEL,qBAFsB;AAK7B,CAAA;AAAwD,KAA5C,oBAAA,GAAuB,OAAA,CAAM,cAAe,CAAA,cAAA,CAAA,GAAA;EAArB,YAAM,EAC1B,iBAD0B,EAAA;EAC1B,iBAAA,CAAA,EACM,gBADN,EAAA;EACM,oBAAA,CAAA,EACG,mBADH,EAAA;EACG,WAAA,CAAA,EAAA,OAAA;CAAmB;AAI9B,cAAA,YAAgB,EAAA,CAAA;EAAA;CAIzB,EAAM;EAuBG,SAAA,CAAA,EAAA,MAuDZ;CAvD2B,EAAA,GAvBxB,OAAA,CAAM,YAuBkB;AAAA,cAAf,eAAe,EAAA,OAAA,CAAA,yBAAA,CAAA,OAAA,CAAA,cAAA,CAAA,cAAA,CAAA,GAAA;EAjCb,YAAA,EAAA,iBAAA,EAAA;EACM,iBAAA,CAAA,EAAA,gBAAA,EAAA;EACG,oBAAA,CAAA,EAAA,mBAAA,EAAA"}
|