@agents24/chat-react 0.5.36 → 0.5.37
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/dist/{chunk-INO5DSSJ.js → chunk-DWDHK4FZ.js} +3 -4
- package/dist/chunk-DWDHK4FZ.js.map +1 -0
- package/dist/generated/delegation-types.d.ts +3 -2
- package/dist/generated/run-usage.d.ts +11 -0
- package/dist/registry/agents24-chat.json +13 -13
- package/dist/runtime/index.cjs +25 -9
- package/dist/runtime/index.cjs.map +1 -1
- package/dist/runtime/index.js +24 -7
- package/dist/runtime/index.js.map +1 -1
- package/dist/runtime/input-contract.d.ts +1 -1
- package/dist/runtime/use-agent-chat-hitl.d.ts +2 -2
- package/dist/runtime/use-agent-chat-runtime.d.ts +4 -6
- package/dist/scaffold/app.d.ts +2 -2
- package/dist/scaffold/components/chat/chat-composer.d.ts +2 -1
- package/dist/scaffold/components/chat/chat-message.d.ts +2 -2
- package/dist/scaffold/components/chat/message-parts.d.ts +2 -2
- package/dist/scaffold/index.cjs +17 -13
- package/dist/scaffold/index.cjs.map +1 -1
- package/dist/scaffold/index.js +11 -7
- package/dist/scaffold/index.js.map +1 -1
- package/dist/styles.css +7 -0
- package/dist/ui/agent-chat-composer.d.ts +2 -1
- package/dist/ui/agent-chat-message.d.ts +6 -2
- package/dist/ui/agent-response-timeline.d.ts +11 -3
- package/dist/ui/ask-user-interaction.d.ts +5 -1
- package/dist/ui/hitl-submission-status.d.ts +22 -0
- package/dist/ui/index.cjs +514 -337
- package/dist/ui/index.cjs.map +1 -1
- package/dist/ui/index.d.ts +1 -0
- package/dist/ui/index.js +510 -335
- package/dist/ui/index.js.map +1 -1
- package/dist/ui/use-agent-chat-composer.d.ts +17 -2
- package/dist/ui/use-audio-recorder.d.ts +15 -2
- package/package.json +3 -3
- package/scaffold/src/app.tsx +2 -1
- package/scaffold/src/components/chat/chat-composer.tsx +5 -3
- package/scaffold/src/components/chat/chat-message.tsx +3 -3
- package/scaffold/src/components/chat/chat-shell.tsx +2 -1
- package/scaffold/src/components/chat/message-parts.tsx +6 -6
- package/dist/chunk-INO5DSSJ.js.map +0 -1
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import type { AgentChatInputContract } from "../runtime/input-contract";
|
|
3
3
|
import { type AgentChatComposerFile } from "./composer-attachments";
|
|
4
|
+
import { type RecordingCompletionContext } from "./use-audio-recorder";
|
|
5
|
+
export type AgentChatComposerSubmission = {
|
|
6
|
+
text: string;
|
|
7
|
+
files: AgentChatComposerFile[];
|
|
8
|
+
};
|
|
4
9
|
export type AgentChatComposerOptions = {
|
|
5
10
|
inputContract?: AgentChatInputContract | null;
|
|
6
11
|
recordingEnabled?: boolean;
|
|
@@ -10,6 +15,9 @@ export type AgentChatComposerOptions = {
|
|
|
10
15
|
isCancelling?: boolean;
|
|
11
16
|
forceExpanded?: boolean;
|
|
12
17
|
focusKey?: string | null;
|
|
18
|
+
scopeKey?: string | null;
|
|
19
|
+
/** Replaces recorded-audio attachment creation; transport and policy remain host-owned. */
|
|
20
|
+
onRecordingComplete?: (file: File, context: RecordingCompletionContext) => void | Promise<void>;
|
|
13
21
|
restoredDraft?: {
|
|
14
22
|
revision: number;
|
|
15
23
|
text: string;
|
|
@@ -26,7 +34,7 @@ export declare function useAgentChatComposer(options: AgentChatComposerOptions):
|
|
|
26
34
|
addFiles: (selected: readonly File[]) => void;
|
|
27
35
|
removeFile: (id: string) => void;
|
|
28
36
|
textareaRef: React.RefObject<HTMLTextAreaElement | null>;
|
|
29
|
-
submit: () => Promise<
|
|
37
|
+
submit: (submission?: AgentChatComposerSubmission) => Promise<boolean>;
|
|
30
38
|
canSubmit: boolean;
|
|
31
39
|
isSubmitting: boolean;
|
|
32
40
|
attachmentError: string | null;
|
|
@@ -35,9 +43,16 @@ export declare function useAgentChatComposer(options: AgentChatComposerOptions):
|
|
|
35
43
|
recorder: {
|
|
36
44
|
error: string | null;
|
|
37
45
|
isRecording: boolean;
|
|
38
|
-
|
|
46
|
+
isStarting: boolean;
|
|
47
|
+
isProcessing: boolean;
|
|
48
|
+
isCapturing: () => boolean;
|
|
49
|
+
start: (startOptions?: {
|
|
50
|
+
operationId?: string;
|
|
51
|
+
}) => Promise<void>;
|
|
39
52
|
stop: () => void;
|
|
53
|
+
cancel: () => void;
|
|
40
54
|
};
|
|
55
|
+
isProcessing: boolean;
|
|
41
56
|
recordingAllowed: boolean;
|
|
42
57
|
fileAccept: string;
|
|
43
58
|
onKeyDown: (event: React.KeyboardEvent<HTMLTextAreaElement>) => void;
|
|
@@ -1,10 +1,23 @@
|
|
|
1
|
+
export type RecordingCompletionContext = {
|
|
2
|
+
operationId: string;
|
|
3
|
+
signal: AbortSignal;
|
|
4
|
+
};
|
|
1
5
|
export type AudioRecorderOptions = {
|
|
2
6
|
/** Subscribe to the recorder-owned stream; consumers must not stop its tracks. */
|
|
3
7
|
onStreamChange?: (stream: MediaStream | null) => void;
|
|
8
|
+
/** Invalidates capture and completion work independently of focus. */
|
|
9
|
+
scopeKey?: string | null;
|
|
10
|
+
acceptedMimeTypes?: readonly string[];
|
|
4
11
|
};
|
|
5
|
-
export declare function useAudioRecorder(onRecorded: (file: File) => void
|
|
12
|
+
export declare function useAudioRecorder(onRecorded: (file: File, context: RecordingCompletionContext) => void | Promise<void>, options?: AudioRecorderOptions): {
|
|
6
13
|
error: string | null;
|
|
7
14
|
isRecording: boolean;
|
|
8
|
-
|
|
15
|
+
isStarting: boolean;
|
|
16
|
+
isProcessing: boolean;
|
|
17
|
+
isCapturing: () => boolean;
|
|
18
|
+
start: (startOptions?: {
|
|
19
|
+
operationId?: string;
|
|
20
|
+
}) => Promise<void>;
|
|
9
21
|
stop: () => void;
|
|
22
|
+
cancel: () => void;
|
|
10
23
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agents24/chat-react",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.37",
|
|
4
4
|
"description": "Accessible web UI and complete default layout for Agents24 chat applications.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -64,8 +64,8 @@
|
|
|
64
64
|
},
|
|
65
65
|
"dependencies": {
|
|
66
66
|
"@untitledui/file-icons": "0.0.9",
|
|
67
|
-
"@agents24/client": "0.2.
|
|
68
|
-
"@agents24/react": "0.2.
|
|
67
|
+
"@agents24/client": "0.2.19",
|
|
68
|
+
"@agents24/react": "0.2.24",
|
|
69
69
|
"@fontsource-variable/inter": "^5.3.0",
|
|
70
70
|
"@fontsource-variable/jetbrains-mono": "^5.3.0",
|
|
71
71
|
"@fontsource-variable/merriweather": "^5.3.0",
|
package/scaffold/src/app.tsx
CHANGED
|
@@ -10,6 +10,7 @@ import type { HitlActionState } from "@agents24/chat-react/ui"
|
|
|
10
10
|
import {
|
|
11
11
|
Agents24Provider,
|
|
12
12
|
type ChatHitlPart,
|
|
13
|
+
type HitlSubmissionOptions,
|
|
13
14
|
type ChatMessage,
|
|
14
15
|
type ChatPart,
|
|
15
16
|
} from "@agents24/react"
|
|
@@ -51,7 +52,7 @@ export type Agents24ChatTimelineSlotProps = {
|
|
|
51
52
|
parts: readonly ChatPart[]
|
|
52
53
|
isLoading: boolean
|
|
53
54
|
getHitlActionState: (part: ChatHitlPart) => HitlActionState | undefined
|
|
54
|
-
onHitlAction: (part: ChatHitlPart, response: ChatHitlResponse) => void
|
|
55
|
+
onHitlAction: (part: ChatHitlPart, response: ChatHitlResponse, options?: HitlSubmissionOptions) => void
|
|
55
56
|
}
|
|
56
57
|
|
|
57
58
|
function RuntimeChat({
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
useAgentChatComposer,
|
|
7
7
|
type AgentChatComposerFile,
|
|
8
8
|
} from "@agents24/chat-react/ui"
|
|
9
|
-
import { type AgentChatInputContract } from "@agents24/chat-react/runtime"
|
|
9
|
+
import { attachmentFileAccept, type AgentChatInputContract } from "@agents24/chat-react/runtime"
|
|
10
10
|
import type { ContextWindow } from "@agents24/client/protocol"
|
|
11
11
|
|
|
12
12
|
import { Button } from "../ui/button"
|
|
@@ -25,6 +25,7 @@ export type Agents24ChatComposerProps = {
|
|
|
25
25
|
inputContract: AgentChatInputContract
|
|
26
26
|
disabled?: boolean
|
|
27
27
|
contextWindow?: ContextWindow | null
|
|
28
|
+
scopeKey?: string | null
|
|
28
29
|
focusKey?: string | null
|
|
29
30
|
forceExpanded?: boolean
|
|
30
31
|
isCancelling?: boolean
|
|
@@ -48,6 +49,7 @@ export function ChatComposer({
|
|
|
48
49
|
disabled,
|
|
49
50
|
contextWindow,
|
|
50
51
|
focusKey,
|
|
52
|
+
scopeKey,
|
|
51
53
|
forceExpanded = false,
|
|
52
54
|
isCancelling = false,
|
|
53
55
|
isRunning,
|
|
@@ -66,7 +68,7 @@ export function ChatComposer({
|
|
|
66
68
|
const { text, setText, files, removeFile, addFiles, textareaRef, submit,
|
|
67
69
|
attachmentError, attachmentMimeTypes, allowAttachments, recordingAllowed,
|
|
68
70
|
recorder, isExpanded, validationError, action, canSubmit } = useAgentChatComposer({
|
|
69
|
-
inputContract, disabled, focusKey, forceExpanded, isCancelling, isRunning,
|
|
71
|
+
inputContract, disabled, scopeKey, focusKey, forceExpanded, isCancelling, isRunning,
|
|
70
72
|
onSubmit, restoredDraft, onRecordingStreamChange,
|
|
71
73
|
})
|
|
72
74
|
|
|
@@ -162,7 +164,7 @@ export function ChatComposer({
|
|
|
162
164
|
className="sr-only"
|
|
163
165
|
type="file"
|
|
164
166
|
multiple
|
|
165
|
-
accept={attachmentMimeTypes
|
|
167
|
+
accept={attachmentFileAccept(attachmentMimeTypes)}
|
|
166
168
|
onChange={(event) => {
|
|
167
169
|
addFiles(Array.from(event.target.files ?? []))
|
|
168
170
|
event.currentTarget.value = ""
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ChatHitlPart, ChatMessage as ChatMessageModel } from "@agents24/react"
|
|
1
|
+
import type { ChatHitlPart, HitlSubmissionOptions, ChatMessage as ChatMessageModel } from "@agents24/react"
|
|
2
2
|
import type { FeedbackReason, HitlResponse } from "@agents24/client"
|
|
3
3
|
import type { AgentChatRuntimeHitlActionState } from "@agents24/chat-react/runtime"
|
|
4
4
|
import { AgentChatDefaultActions, AgentChatUserMessageActionRow, ChatAttachmentRows, useCompletionActionEntrance, type ChatAttachmentContentResolver } from "@agents24/chat-react/ui"
|
|
@@ -14,7 +14,7 @@ type Props = {
|
|
|
14
14
|
message: ChatMessageModel
|
|
15
15
|
getHitlActionState: (part: ChatHitlPart) => AgentChatRuntimeHitlActionState | undefined
|
|
16
16
|
isLoading: boolean
|
|
17
|
-
onHitlAction: (part: ChatHitlPart, response: HitlResponse) => Promise<void>
|
|
17
|
+
onHitlAction: (part: ChatHitlPart, response: HitlResponse, options?: HitlSubmissionOptions) => Promise<void>
|
|
18
18
|
onRegenerate: () => Promise<void>
|
|
19
19
|
allowFeedback: boolean
|
|
20
20
|
onSetFeedback: (
|
|
@@ -71,7 +71,7 @@ export function ChatMessage({
|
|
|
71
71
|
</AgentChatUserMessageActionRow>
|
|
72
72
|
)}
|
|
73
73
|
{showCompletionActions && (
|
|
74
|
-
<MessageFooter className="
|
|
74
|
+
<MessageFooter className="a24-chat-message-action-row--assistant gap-1">
|
|
75
75
|
<AgentChatDefaultActions
|
|
76
76
|
animateCompletion={animateCompletionActions}
|
|
77
77
|
handlers={{
|
|
@@ -24,7 +24,7 @@ import {
|
|
|
24
24
|
import { SidebarInset, SidebarProvider, SidebarTrigger, useSidebar } from "../ui/sidebar"
|
|
25
25
|
|
|
26
26
|
const LOADING_INPUT_CONTRACT = {
|
|
27
|
-
version: "agents24.runtime-inputs.
|
|
27
|
+
version: "agents24.runtime-inputs.v3" as const,
|
|
28
28
|
modalities: {
|
|
29
29
|
text: { enabled: false, required: false },
|
|
30
30
|
files: { enabled: false, required: false, allowed_mime_types: [] },
|
|
@@ -250,6 +250,7 @@ export function ChatShell({
|
|
|
250
250
|
inputContract={runtime.inputContract || LOADING_INPUT_CONTRACT}
|
|
251
251
|
contextWindow={runtime.contextWindow}
|
|
252
252
|
disabled={runtime.isBootstrapping || runtime.isChangingAgent || isUploading || hasBlockingError}
|
|
253
|
+
scopeKey={controller.activeThreadId}
|
|
253
254
|
focusKey={controller.activeThreadId}
|
|
254
255
|
forceExpanded={isEmptyState}
|
|
255
256
|
isCancelling={isCancelling}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ChatHitlPart, ChatMessage } from "@agents24/react"
|
|
1
|
+
import type { ChatHitlPart, HitlSubmissionOptions, ChatMessage } from "@agents24/react"
|
|
2
2
|
import type { HitlResponse } from "@agents24/client"
|
|
3
3
|
import {
|
|
4
4
|
AgentChatPart,
|
|
@@ -39,7 +39,7 @@ export function MessageParts({
|
|
|
39
39
|
message: ChatMessage
|
|
40
40
|
getHitlActionState: (part: ChatHitlPart) => AgentChatRuntimeHitlActionState | undefined
|
|
41
41
|
isLoading: boolean
|
|
42
|
-
onHitlAction: (part: ChatHitlPart, response: HitlResponse) => Promise<void>
|
|
42
|
+
onHitlAction: (part: ChatHitlPart, response: HitlResponse, options?: HitlSubmissionOptions) => Promise<void>
|
|
43
43
|
Timeline?: React.ComponentType<Agents24ChatTimelineSlotProps>
|
|
44
44
|
}) {
|
|
45
45
|
if (message.role === "assistant") {
|
|
@@ -48,8 +48,8 @@ export function MessageParts({
|
|
|
48
48
|
getHitlActionState={getHitlActionState}
|
|
49
49
|
isLoading={isLoading}
|
|
50
50
|
message={message}
|
|
51
|
-
onHitlAction={(part, response) => {
|
|
52
|
-
void onHitlAction(part, response).catch(() => undefined)
|
|
51
|
+
onHitlAction={(part, response, options) => {
|
|
52
|
+
void onHitlAction(part, response, options).catch(() => undefined)
|
|
53
53
|
}}
|
|
54
54
|
parts={message.parts}
|
|
55
55
|
/>
|
|
@@ -71,8 +71,8 @@ export function MessageParts({
|
|
|
71
71
|
getHitlActionState={getHitlActionState}
|
|
72
72
|
key={part.id}
|
|
73
73
|
message={message}
|
|
74
|
-
onHitlAction={(hitlPart, response) => {
|
|
75
|
-
void onHitlAction(hitlPart, response).catch(() => undefined)
|
|
74
|
+
onHitlAction={(hitlPart, response, options) => {
|
|
75
|
+
void onHitlAction(hitlPart, response, options).catch(() => undefined)
|
|
76
76
|
}}
|
|
77
77
|
part={part}
|
|
78
78
|
/>
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/runtime/input-contract.ts"],"sourcesContent":["import { normalizeAttachmentMimeType, SUPPORTED_ATTACHMENT_MIME_TYPES, type ClientInputFeature, type PortableUpload } from \"@agents24/client\";\nexport { attachmentFileAccept, normalizeAttachmentMimeType } from \"@agents24/client\";\n\nexport type AgentChatInputContract = ClientInputFeature;\n\nexport const SUPPORTED_INPUT_MIME_TYPES = SUPPORTED_ATTACHMENT_MIME_TYPES;\n\nexport type WorkflowInputRule = {\n key: string;\n enabled?: boolean;\n required?: boolean;\n};\n\nexport function workflowInputRule(\n inputs: readonly WorkflowInputRule[] | null | undefined,\n key: string,\n): { enabled: boolean; required: boolean } {\n const input = inputs?.find((candidate) => candidate.key === key);\n return {\n enabled: input?.enabled !== false,\n required: input?.required === true,\n };\n}\n\nexport function validateWorkflowInputSubmission(\n inputs: readonly WorkflowInputRule[] | null | undefined,\n text: string,\n attachmentMimeTypes: readonly string[],\n): string | null {\n const present = { files: false, images: false, audio: false };\n if (text.trim() && !workflowInputRule(inputs, \"text\").enabled) {\n return \"This workflow does not accept text input.\";\n }\n for (const rawMimeType of attachmentMimeTypes) {\n const mimeType = canonicalInputMimeType(rawMimeType);\n const modality = mimeType.startsWith(\"image/\") ? \"images\" : mimeType.startsWith(\"audio/\") ? \"audio\" : \"files\";\n const supported = (SUPPORTED_INPUT_MIME_TYPES[modality] as readonly string[]).includes(mimeType);\n if (!supported || !workflowInputRule(inputs, modality).enabled) {\n return `The attachment type ${mimeType || \"unknown\"} is not accepted.`;\n }\n present[modality] = true;\n }\n if (workflowInputRule(inputs, \"text\").required && !text.trim()) return \"Text input is required.\";\n for (const modality of [\"files\", \"images\", \"audio\"] as const) {\n if (workflowInputRule(inputs, modality).required && !present[modality]) {\n return `${modality[0].toUpperCase()}${modality.slice(1)} input is required.`;\n }\n }\n return null;\n}\n\nexport const canonicalInputMimeType = normalizeAttachmentMimeType;\n\nfunction uploadByteSize(data: PortableUpload[\"data\"]): number {\n if (data instanceof Uint8Array || data instanceof ArrayBuffer) return data.byteLength;\n return data.size;\n}\n\nexport function inputMimeTypes(contract: AgentChatInputContract | null | undefined): string[] {\n if (!contract) return [];\n return [\n ...contract.modalities.files.allowed_mime_types,\n ...contract.modalities.images.allowed_mime_types,\n ...contract.modalities.audio.allowed_mime_types,\n ];\n}\n\nexport function validateAgentChatSubmission(\n contract: AgentChatInputContract,\n text: string,\n files: readonly (Pick<PortableUpload, \"mediaType\" | \"data\"> & Partial<Pick<PortableUpload, \"name\">> & { byteSize?: number })[],\n): string | null {\n const { modalities, limits } = contract;\n const normalizedText = text.trim();\n if (normalizedText && !modalities.text.enabled) return \"This agent does not accept text input.\";\n if (modalities.text.required && !normalizedText) return \"Text input is required.\";\n if (files.length > limits.max_attachments_per_turn) {\n return `You can attach up to ${limits.max_attachments_per_turn} items per message.`;\n }\n let totalBytes = 0;\n const present = { files: false, images: false, audio: false };\n for (const file of files) {\n const mediaType = normalizeAttachmentMimeType(file.mediaType, file.name);\n const modality = mediaType.startsWith(\"image/\") ? \"images\" : mediaType.startsWith(\"audio/\") ? \"audio\" : \"files\";\n const rule = modalities[modality];\n if (!rule.enabled || !rule.allowed_mime_types.includes(mediaType)) {\n return `The attachment type ${mediaType || \"unknown\"} is not accepted.`;\n }\n const byteSize = file.byteSize ?? uploadByteSize(file.data);\n if (byteSize > limits.max_file_bytes) return \"An attachment exceeds the per-file size limit.\";\n totalBytes += byteSize;\n present[modality] = true;\n }\n if (totalBytes > limits.max_total_bytes) return \"Attachments exceed the total size limit.\";\n for (const modality of [\"files\", \"images\", \"audio\"] as const) {\n if (modalities[modality].required && !present[modality]) return `${modality[0].toUpperCase()}${modality.slice(1)} input is required.`;\n }\n return null;\n}\n"],"mappings":";AAAA,SAAS,6BAA6B,uCAAqF;AAC3H,SAAS,sBAAsB,+BAAAA,oCAAmC;AAI3D,IAAM,6BAA6B;AAQnC,SAAS,kBACd,QACA,KACyC;AACzC,QAAM,QAAQ,QAAQ,KAAK,CAAC,cAAc,UAAU,QAAQ,GAAG;AAC/D,SAAO;AAAA,IACL,SAAS,OAAO,YAAY;AAAA,IAC5B,UAAU,OAAO,aAAa;AAAA,EAChC;AACF;AAEO,SAAS,gCACd,QACA,MACA,qBACe;AACf,QAAM,UAAU,EAAE,OAAO,OAAO,QAAQ,OAAO,OAAO,MAAM;AAC5D,MAAI,KAAK,KAAK,KAAK,CAAC,kBAAkB,QAAQ,MAAM,EAAE,SAAS;AAC7D,WAAO;AAAA,EACT;AACA,aAAW,eAAe,qBAAqB;AAC7C,UAAM,WAAW,uBAAuB,WAAW;AACnD,UAAM,WAAW,SAAS,WAAW,QAAQ,IAAI,WAAW,SAAS,WAAW,QAAQ,IAAI,UAAU;AACtG,UAAM,YAAa,2BAA2B,QAAQ,EAAwB,SAAS,QAAQ;AAC/F,QAAI,CAAC,aAAa,CAAC,kBAAkB,QAAQ,QAAQ,EAAE,SAAS;AAC9D,aAAO,uBAAuB,YAAY,SAAS;AAAA,IACrD;AACA,YAAQ,QAAQ,IAAI;AAAA,EACtB;AACA,MAAI,kBAAkB,QAAQ,MAAM,EAAE,YAAY,CAAC,KAAK,KAAK,EAAG,QAAO;AACvE,aAAW,YAAY,CAAC,SAAS,UAAU,OAAO,GAAY;AAC5D,QAAI,kBAAkB,QAAQ,QAAQ,EAAE,YAAY,CAAC,QAAQ,QAAQ,GAAG;AACtE,aAAO,GAAG,SAAS,CAAC,EAAE,YAAY,CAAC,GAAG,SAAS,MAAM,CAAC,CAAC;AAAA,IACzD;AAAA,EACF;AACA,SAAO;AACT;AAEO,IAAM,yBAAyB;AAEtC,SAAS,eAAe,MAAsC;AAC5D,MAAI,gBAAgB,cAAc,gBAAgB,YAAa,QAAO,KAAK;AAC3E,SAAO,KAAK;AACd;AAEO,SAAS,eAAe,UAA+D;AAC5F,MAAI,CAAC,SAAU,QAAO,CAAC;AACvB,SAAO;AAAA,IACL,GAAG,SAAS,WAAW,MAAM;AAAA,IAC7B,GAAG,SAAS,WAAW,OAAO;AAAA,IAC9B,GAAG,SAAS,WAAW,MAAM;AAAA,EAC/B;AACF;AAEO,SAAS,4BACd,UACA,MACA,OACe;AACf,QAAM,EAAE,YAAY,OAAO,IAAI;AAC/B,QAAM,iBAAiB,KAAK,KAAK;AACjC,MAAI,kBAAkB,CAAC,WAAW,KAAK,QAAS,QAAO;AACvD,MAAI,WAAW,KAAK,YAAY,CAAC,eAAgB,QAAO;AACxD,MAAI,MAAM,SAAS,OAAO,0BAA0B;AAClD,WAAO,wBAAwB,OAAO,wBAAwB;AAAA,EAChE;AACA,MAAI,aAAa;AACjB,QAAM,UAAU,EAAE,OAAO,OAAO,QAAQ,OAAO,OAAO,MAAM;AAC5D,aAAW,QAAQ,OAAO;AACxB,UAAM,YAAY,4BAA4B,KAAK,WAAW,KAAK,IAAI;AACvE,UAAM,WAAW,UAAU,WAAW,QAAQ,IAAI,WAAW,UAAU,WAAW,QAAQ,IAAI,UAAU;AACxG,UAAM,OAAO,WAAW,QAAQ;AAChC,QAAI,CAAC,KAAK,WAAW,CAAC,KAAK,mBAAmB,SAAS,SAAS,GAAG;AACjE,aAAO,uBAAuB,aAAa,SAAS;AAAA,IACtD;AACA,UAAM,WAAW,KAAK,YAAY,eAAe,KAAK,IAAI;AAC1D,QAAI,WAAW,OAAO,eAAgB,QAAO;AAC7C,kBAAc;AACd,YAAQ,QAAQ,IAAI;AAAA,EACtB;AACA,MAAI,aAAa,OAAO,gBAAiB,QAAO;AAChD,aAAW,YAAY,CAAC,SAAS,UAAU,OAAO,GAAY;AAC5D,QAAI,WAAW,QAAQ,EAAE,YAAY,CAAC,QAAQ,QAAQ,EAAG,QAAO,GAAG,SAAS,CAAC,EAAE,YAAY,CAAC,GAAG,SAAS,MAAM,CAAC,CAAC;AAAA,EAClH;AACA,SAAO;AACT;","names":["normalizeAttachmentMimeType"]}
|