@assistant-ui/react-devtools 1.2.11 → 1.2.12
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/views/attachments/formatBytes.js +1 -1
- package/dist/views/attachments/formatBytes.js.map +1 -1
- package/dist/views/attachments/parseAttachment.js +1 -1
- package/dist/views/attachments/parseAttachment.js.map +1 -1
- package/dist/views/message/parse.d.ts.map +1 -1
- package/dist/views/message/parse.js +1 -1
- package/dist/views/message/parse.js.map +1 -1
- package/package.json +3 -3
- package/src/views/attachments/formatBytes.ts +1 -3
- package/src/views/attachments/parseAttachment.test.ts +25 -0
- package/src/views/attachments/parseAttachment.ts +1 -1
- package/src/views/message/parse.test.ts +15 -0
- package/src/views/message/parse.ts +4 -3
|
@@ -7,7 +7,7 @@ const formatBytes = (bytes) => {
|
|
|
7
7
|
};
|
|
8
8
|
/** Rough size from base64 payload (no decoding). */
|
|
9
9
|
const estimateBase64Bytes = (data) => Math.max(0, Math.floor(data.length * 3 / 4));
|
|
10
|
-
const isSafeImagePreviewUrl = (url) =>
|
|
10
|
+
const isSafeImagePreviewUrl = (url) => /^data:image\//i.test(url) || /^https?:\/\//i.test(url);
|
|
11
11
|
//#endregion
|
|
12
12
|
export { estimateBase64Bytes, formatBytes, isSafeImagePreviewUrl };
|
|
13
13
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"formatBytes.js","names":["formatBytes","bytes","undefined","toFixed","estimateBase64Bytes","data","Math","max","floor","length","isSafeImagePreviewUrl","url","
|
|
1
|
+
{"version":3,"file":"formatBytes.js","names":["formatBytes","bytes","undefined","toFixed","estimateBase64Bytes","data","Math","max","floor","length","isSafeImagePreviewUrl","url","test"],"sources":["../../../src/views/attachments/formatBytes.ts"],"sourcesContent":["export const formatBytes = (bytes: number | undefined): string | undefined => {\n if (bytes === undefined || bytes <= 0) return undefined;\n if (bytes < 1024) return `${bytes} B`;\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;\n return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;\n};\n\n/** Rough size from base64 payload (no decoding). */\nexport const estimateBase64Bytes = (data: string): number =>\n Math.max(0, Math.floor((data.length * 3) / 4));\n\nexport const isSafeImagePreviewUrl = (url: string): boolean =>\n /^data:image\\//i.test(url) || /^https?:\\/\\//i.test(url);\n"],"mappings":";AAAA,MAAaA,eAAeC,UAAkD;CAC5E,IAAIA,UAAUC,KAAAA,KAAaD,SAAS,GAAG,OAAOC,KAAAA;CAC9C,IAAID,QAAQ,MAAM,OAAO,GAAGA,MAAK;CACjC,IAAIA,QAAQ,OAAO,MAAM,OAAO,IAAIA,QAAQ,KAAA,CAAME,QAAQ,CAAC,EAAC;CAC5D,OAAO,IAAIF,SAAS,OAAO,MAAA,CAAOE,QAAQ,CAAC,EAAC;AAC9C;;AAGA,MAAaC,uBAAuBC,SAClCC,KAAKC,IAAI,GAAGD,KAAKE,MAAOH,KAAKI,SAAS,IAAK,CAAC,CAAC;AAE/C,MAAaC,yBAAyBC,QACpC,iBAAiBC,KAAKD,GAAG,KAAK,gBAAgBC,KAAKD,GAAG"}
|
|
@@ -18,7 +18,7 @@ const mediaFromContent = (content) => {
|
|
|
18
18
|
if (type === "image") {
|
|
19
19
|
const image = asString(part.image);
|
|
20
20
|
if (image && isSafeImagePreviewUrl(image)) {
|
|
21
|
-
const sizeBytes =
|
|
21
|
+
const sizeBytes = /^data:/i.test(image) ? estimateBase64Bytes(image.split(",", 2)[1] ?? image) : void 0;
|
|
22
22
|
return {
|
|
23
23
|
previewUrl: image,
|
|
24
24
|
...sizeBytes !== void 0 ? { sizeBytes } : {}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parseAttachment.js","names":["isRecord","estimateBase64Bytes","isSafeImagePreviewUrl","AttachmentPreview","AttachmentStatusPreview","asString","value","length","undefined","parseAttachmentStatus","type","reason","progress","mediaFromContent","content","previewUrl","sizeBytes","Array","isArray","part","image","
|
|
1
|
+
{"version":3,"file":"parseAttachment.js","names":["isRecord","estimateBase64Bytes","isSafeImagePreviewUrl","AttachmentPreview","AttachmentStatusPreview","asString","value","length","undefined","parseAttachmentStatus","type","reason","progress","mediaFromContent","content","previewUrl","sizeBytes","Array","isArray","part","image","test","split","data","parseAttachment","name","filename","id","status","fromContent","kind","contentType","parseAttachments","map","item","filter","Boolean"],"sources":["../../../src/views/attachments/parseAttachment.ts"],"sourcesContent":["import { isRecord } from \"../../utils/common\";\nimport { estimateBase64Bytes, isSafeImagePreviewUrl } from \"./formatBytes\";\nimport type { AttachmentPreview, AttachmentStatusPreview } from \"./types\";\n\nconst asString = (value: unknown): string | undefined =>\n typeof value === \"string\" && value.length > 0 ? value : undefined;\n\nconst parseAttachmentStatus = (\n value: unknown,\n): AttachmentStatusPreview | undefined => {\n if (!isRecord(value) || typeof value.type !== \"string\") return undefined;\n return {\n type: value.type,\n ...(typeof value.reason === \"string\" ? { reason: value.reason } : {}),\n ...(typeof value.progress === \"number\" ? { progress: value.progress } : {}),\n };\n};\n\nconst mediaFromContent = (\n content: unknown,\n): { previewUrl?: string; sizeBytes?: number } => {\n if (!Array.isArray(content)) return {};\n for (const part of content) {\n if (!isRecord(part)) continue;\n const type = asString(part.type);\n if (type === \"image\") {\n const image = asString(part.image);\n if (image && isSafeImagePreviewUrl(image)) {\n const sizeBytes = /^data:/i.test(image)\n ? estimateBase64Bytes(image.split(\",\", 2)[1] ?? image)\n : undefined;\n return {\n previewUrl: image,\n ...(sizeBytes !== undefined ? { sizeBytes } : {}),\n };\n }\n }\n if (type === \"file\") {\n const data = asString(part.data);\n if (data) return { sizeBytes: estimateBase64Bytes(data) };\n }\n }\n return {};\n};\n\nexport const parseAttachment = (value: unknown): AttachmentPreview | null => {\n if (!isRecord(value)) return null;\n\n const name =\n asString(value.name) ??\n asString(value.filename) ??\n asString(value.id) ??\n \"(attachment)\";\n\n const status = parseAttachmentStatus(value.status);\n const fromContent = mediaFromContent(value.content);\n\n const image = asString(value.image);\n const previewUrl =\n fromContent.previewUrl ??\n (image && isSafeImagePreviewUrl(image) ? image : undefined);\n\n const data = asString(value.data);\n const sizeBytes =\n fromContent.sizeBytes ?? (data ? estimateBase64Bytes(data) : undefined);\n\n return {\n name,\n ...(asString(value.id) ? { id: asString(value.id)! } : {}),\n ...(asString(value.type) ? { kind: asString(value.type)! } : {}),\n ...(asString(value.contentType)\n ? { contentType: asString(value.contentType)! }\n : {}),\n ...(status ? { status } : {}),\n ...(previewUrl ? { previewUrl } : {}),\n ...(sizeBytes !== undefined ? { sizeBytes } : {}),\n };\n};\n\nexport const parseAttachments = (value: unknown): AttachmentPreview[] =>\n Array.isArray(value)\n ? value\n .map((item) => parseAttachment(item))\n .filter((item): item is AttachmentPreview => Boolean(item))\n : [];\n"],"mappings":";;;AAIA,MAAMK,YAAYC,UAChB,OAAOA,UAAU,YAAYA,MAAMC,SAAS,IAAID,QAAQE,KAAAA;AAE1D,MAAMC,yBACJH,UACwC;CACxC,IAAI,CAACN,SAASM,KAAK,KAAK,OAAOA,MAAMI,SAAS,UAAU,OAAOF,KAAAA;CAC/D,OAAO;EACLE,MAAMJ,MAAMI;EACZ,GAAI,OAAOJ,MAAMK,WAAW,WAAW,EAAEA,QAAQL,MAAMK,OAAO,IAAI,CAAC;EACnE,GAAI,OAAOL,MAAMM,aAAa,WAAW,EAAEA,UAAUN,MAAMM,SAAS,IAAI,CAAC;CAC3E;AACF;AAEA,MAAMC,oBACJC,YACgD;CAChD,IAAI,CAACG,MAAMC,QAAQJ,OAAO,GAAG,OAAO,CAAC;CACrC,KAAK,MAAMK,QAAQL,SAAS;EAC1B,IAAI,CAACd,SAASmB,IAAI,GAAG;EACrB,MAAMT,OAAOL,SAASc,KAAKT,IAAI;EAC/B,IAAIA,SAAS,SAAS;GACpB,MAAMU,QAAQf,SAASc,KAAKC,KAAK;GACjC,IAAIA,SAASlB,sBAAsBkB,KAAK,GAAG;IACzC,MAAMJ,YAAY,UAAUK,KAAKD,KAAK,IAClCnB,oBAAoBmB,MAAME,MAAM,KAAK,CAAC,CAAC,CAAC,MAAMF,KAAK,IACnDZ,KAAAA;IACJ,OAAO;KACLO,YAAYK;KACZ,GAAIJ,cAAcR,KAAAA,IAAY,EAAEQ,UAAU,IAAI,CAAC;IACjD;GACF;EACF;EACA,IAAIN,SAAS,QAAQ;GACnB,MAAMa,OAAOlB,SAASc,KAAKI,IAAI;GAC/B,IAAIA,MAAM,OAAO,EAAEP,WAAWf,oBAAoBsB,IAAI,EAAE;EAC1D;CACF;CACA,OAAO,CAAC;AACV;AAEA,MAAaC,mBAAmBlB,UAA6C;CAC3E,IAAI,CAACN,SAASM,KAAK,GAAG,OAAO;CAE7B,MAAMmB,OACJpB,SAASC,MAAMmB,IAAI,KACnBpB,SAASC,MAAMoB,QAAQ,KACvBrB,SAASC,MAAMqB,EAAE,KACjB;CAEF,MAAMC,SAASnB,sBAAsBH,MAAMsB,MAAM;CACjD,MAAMC,cAAchB,iBAAiBP,MAAMQ,OAAO;CAElD,MAAMM,QAAQf,SAASC,MAAMc,KAAK;CAClC,MAAML,aACJc,YAAYd,eACXK,SAASlB,sBAAsBkB,KAAK,IAAIA,QAAQZ,KAAAA;CAEnD,MAAMe,OAAOlB,SAASC,MAAMiB,IAAI;CAChC,MAAMP,YACJa,YAAYb,cAAcO,OAAOtB,oBAAoBsB,IAAI,IAAIf,KAAAA;CAE/D,OAAO;EACLiB;EACA,GAAIpB,SAASC,MAAMqB,EAAE,IAAI,EAAEA,IAAItB,SAASC,MAAMqB,EAAE,EAAG,IAAI,CAAC;EACxD,GAAItB,SAASC,MAAMI,IAAI,IAAI,EAAEoB,MAAMzB,SAASC,MAAMI,IAAI,EAAG,IAAI,CAAC;EAC9D,GAAIL,SAASC,MAAMyB,WAAW,IAC1B,EAAEA,aAAa1B,SAASC,MAAMyB,WAAW,EAAG,IAC5C,CAAC;EACL,GAAIH,SAAS,EAAEA,OAAO,IAAI,CAAC;EAC3B,GAAIb,aAAa,EAAEA,WAAW,IAAI,CAAC;EACnC,GAAIC,cAAcR,KAAAA,IAAY,EAAEQ,UAAU,IAAI,CAAC;CACjD;AACF;AAEA,MAAagB,oBAAoB1B,UAC/BW,MAAMC,QAAQZ,KAAK,IACfA,MACG2B,KAAKC,SAASV,gBAAgBU,IAAI,CAAC,CAAC,CACpCC,QAAQD,SAAoCE,QAAQF,IAAI,CAAC,IAC5D,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parse.d.ts","names":[],"sources":["../../../src/views/message/parse.ts"],"mappings":";;cAiBa,UAAW,MAAM,aAAa;cAK9B,kBACX,mBACC;cA2EU,YAAa,mBAAiB;
|
|
1
|
+
{"version":3,"file":"parse.d.ts","names":[],"sources":["../../../src/views/message/parse.ts"],"mappings":";;cAiBa,UAAW,MAAM,aAAa;cAK9B,kBACX,mBACC;cA2EU,YAAa,mBAAiB;cA2I9B,eACX,gBACA,kBACC"}
|
|
@@ -96,7 +96,7 @@ const parsePart = (value) => {
|
|
|
96
96
|
const filename = asString(value.filename);
|
|
97
97
|
const image = asString(value.image);
|
|
98
98
|
const previewUrl = image && isSafeImagePreviewUrl(image) ? image : void 0;
|
|
99
|
-
const sizeBytes = previewUrl
|
|
99
|
+
const sizeBytes = previewUrl && /^data:/i.test(previewUrl) ? estimateBase64Bytes(previewUrl.split(",", 2)[1] ?? previewUrl) : void 0;
|
|
100
100
|
return {
|
|
101
101
|
type: "image",
|
|
102
102
|
...filename ? { filename } : {},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parse.js","names":["parseAttachments","estimateBase64Bytes","isSafeImagePreviewUrl","asBool","asNumber","asString","isRecord","MessagePreview","MessageTimingPreview","MessageUsagePreview","PartPreview","PartStatusPreview","ToolCallApprovalPreview","ToolCallInterruptPreview","ToolCallPartPreview","partKey","part","index","type","toolCallId","parsePartStatus","value","undefined","reason","error","parseInterrupt","result","payload","Object","keys","length","parseApproval","id","approved","isAutomatic","parseToolCall","Record","base","status","argsText","isError","interrupt","approval","subMessageCount","Array","isArray","messages","toolName","args","artifact","mcp","modelContent","parsePart","rawType","raw","text","sourceType","title","url","filename","image","previewUrl","sizeBytes","startsWith","split","mimeType","data","audio","format","audioData","name","spec","parseTiming","streamStartTime","firstTokenTime","totalStreamTime","tokenCount","tokensPerSecond","totalChunks","toolCallCount","parseUsage","inputTokens","outputTokens","sawUsage","step","usage","input","output","stepCount","parseMessage","role","rawParts","parts","content","map","metadata","timing","steps","submittedFeedback","isOptimistic","createdAt","branchNumber","branchCount","messageIndex","attachments"],"sources":["../../../src/views/message/parse.ts"],"sourcesContent":["import { parseAttachments } from \"../attachments/parseAttachment\";\nimport {\n estimateBase64Bytes,\n isSafeImagePreviewUrl,\n} from \"../attachments/formatBytes\";\nimport { asBool, asNumber, asString, isRecord } from \"../../utils/common\";\nimport type {\n MessagePreview,\n MessageTimingPreview,\n MessageUsagePreview,\n PartPreview,\n PartStatusPreview,\n ToolCallApprovalPreview,\n ToolCallInterruptPreview,\n ToolCallPartPreview,\n} from \"./types\";\n\nexport const partKey = (part: PartPreview, index: number) =>\n part.type === \"tool-call\" && part.toolCallId\n ? `tc:${part.toolCallId}`\n : `p:${index}`;\n\nexport const parsePartStatus = (\n value: unknown,\n): PartStatusPreview | undefined => {\n if (!isRecord(value)) return undefined;\n const type = asString(value.type);\n if (!type) return undefined;\n const reason = asString(value.reason);\n return {\n type,\n ...(reason ? { reason } : {}),\n ...(\"error\" in value && value.error !== undefined\n ? { error: value.error }\n : {}),\n };\n};\n\nconst parseInterrupt = (\n value: unknown,\n): ToolCallInterruptPreview | undefined => {\n if (!isRecord(value)) return undefined;\n const type = asString(value.type);\n const result: ToolCallInterruptPreview = {\n ...(type ? { type } : {}),\n ...(\"payload\" in value ? { payload: value.payload } : {}),\n };\n return Object.keys(result).length > 0 ? result : undefined;\n};\n\nconst parseApproval = (value: unknown): ToolCallApprovalPreview | undefined => {\n if (!isRecord(value)) return undefined;\n const id = asString(value.id);\n const approved = asBool(value.approved);\n const reason = asString(value.reason);\n const isAutomatic = asBool(value.isAutomatic);\n const result: ToolCallApprovalPreview = {\n ...(id ? { id } : {}),\n ...(approved !== undefined ? { approved } : {}),\n ...(reason ? { reason } : {}),\n ...(isAutomatic !== undefined ? { isAutomatic } : {}),\n };\n return Object.keys(result).length > 0 ? result : undefined;\n};\n\nconst parseToolCall = (\n value: Record<string, unknown>,\n base: { status?: PartStatusPreview },\n): ToolCallPartPreview => {\n const argsText = asString(value.argsText);\n const isError = asBool(value.isError);\n const interrupt = parseInterrupt(value.interrupt);\n const approval = parseApproval(value.approval);\n const subMessageCount = Array.isArray(value.messages)\n ? value.messages.length\n : 0;\n\n return {\n type: \"tool-call\",\n toolCallId: asString(value.toolCallId) ?? \"\",\n toolName: asString(value.toolName) ?? \"(unknown)\",\n args: value.args,\n subMessageCount,\n ...(argsText !== undefined ? { argsText } : {}),\n ...(\"result\" in value && value.result !== undefined\n ? { result: value.result }\n : {}),\n ...(isError !== undefined ? { isError } : {}),\n ...(value.artifact !== undefined ? { artifact: value.artifact } : {}),\n ...(value.mcp !== undefined ? { mcp: value.mcp } : {}),\n ...(value.modelContent !== undefined\n ? { modelContent: value.modelContent }\n : {}),\n ...(interrupt ? { interrupt } : {}),\n ...(approval ? { approval } : {}),\n ...base,\n };\n};\n\nexport const parsePart = (value: unknown): PartPreview => {\n if (!isRecord(value)) {\n return { type: \"unknown\", rawType: typeof value, raw: value };\n }\n\n const status = parsePartStatus(value.status);\n const base = status ? { status } : {};\n const type = asString(value.type);\n\n switch (type) {\n case \"text\":\n return { type: \"text\", text: asString(value.text) ?? \"\", ...base };\n case \"reasoning\":\n return { type: \"reasoning\", text: asString(value.text) ?? \"\", ...base };\n case \"source\": {\n const sourceType = asString(value.sourceType);\n const title = asString(value.title);\n const url = asString(value.url);\n return {\n type: \"source\",\n ...(sourceType ? { sourceType } : {}),\n ...(title ? { title } : {}),\n ...(url ? { url } : {}),\n ...base,\n };\n }\n case \"image\": {\n const filename = asString(value.filename);\n const image = asString(value.image);\n const previewUrl =\n image && isSafeImagePreviewUrl(image) ? image : undefined;\n const sizeBytes = previewUrl?.startsWith(\"data:\")\n ? estimateBase64Bytes(previewUrl.split(\",\", 2)[1] ?? previewUrl)\n : undefined;\n return {\n type: \"image\",\n ...(filename ? { filename } : {}),\n ...(previewUrl ? { previewUrl } : {}),\n ...(sizeBytes !== undefined ? { sizeBytes } : {}),\n ...base,\n };\n }\n case \"file\": {\n const filename = asString(value.filename);\n const mimeType = asString(value.mimeType);\n const data = asString(value.data);\n const sizeBytes = data ? estimateBase64Bytes(data) : undefined;\n return {\n type: \"file\",\n ...(filename ? { filename } : {}),\n ...(mimeType ? { mimeType } : {}),\n ...(sizeBytes !== undefined ? { sizeBytes } : {}),\n ...base,\n };\n }\n case \"audio\": {\n const audio = isRecord(value.audio) ? value.audio : undefined;\n const format = asString(audio?.format);\n const audioData = asString(audio?.data);\n const sizeBytes = audioData ? estimateBase64Bytes(audioData) : undefined;\n return {\n type: \"audio\",\n ...(format ? { format } : {}),\n ...(sizeBytes !== undefined ? { sizeBytes } : {}),\n ...base,\n };\n }\n case \"data\": {\n const name = asString(value.name);\n return {\n type: \"data\",\n data: value.data,\n ...(name ? { name } : {}),\n ...base,\n };\n }\n case \"generative-ui\":\n return { type: \"generative-ui\", spec: value.spec, ...base };\n case \"tool-call\":\n return parseToolCall(value, base);\n default:\n return {\n type: \"unknown\",\n rawType: type ?? typeof value,\n raw: value,\n ...base,\n };\n }\n};\n\nconst parseTiming = (value: unknown): MessageTimingPreview | undefined => {\n if (!isRecord(value)) return undefined;\n\n const streamStartTime = asNumber(value.streamStartTime);\n const firstTokenTime = asNumber(value.firstTokenTime);\n const totalStreamTime = asNumber(value.totalStreamTime);\n const tokenCount = asNumber(value.tokenCount);\n const tokensPerSecond = asNumber(value.tokensPerSecond);\n const totalChunks = asNumber(value.totalChunks);\n const toolCallCount = asNumber(value.toolCallCount);\n\n const result: MessageTimingPreview = {\n ...(streamStartTime !== undefined ? { streamStartTime } : {}),\n ...(firstTokenTime !== undefined ? { firstTokenTime } : {}),\n ...(totalStreamTime !== undefined ? { totalStreamTime } : {}),\n ...(tokenCount !== undefined ? { tokenCount } : {}),\n ...(tokensPerSecond !== undefined ? { tokensPerSecond } : {}),\n ...(totalChunks !== undefined ? { totalChunks } : {}),\n ...(toolCallCount !== undefined ? { toolCallCount } : {}),\n };\n return Object.keys(result).length > 0 ? result : undefined;\n};\n\nconst parseUsage = (value: unknown): MessageUsagePreview | undefined => {\n if (!Array.isArray(value) || value.length === 0) return undefined;\n\n let inputTokens = 0;\n let outputTokens = 0;\n let sawUsage = false;\n\n for (const step of value) {\n if (!isRecord(step) || !isRecord(step.usage)) continue;\n const input = asNumber(step.usage.inputTokens);\n const output = asNumber(step.usage.outputTokens);\n if (input !== undefined) {\n inputTokens += input;\n sawUsage = true;\n }\n if (output !== undefined) {\n outputTokens += output;\n sawUsage = true;\n }\n }\n\n if (!sawUsage) return undefined;\n return { inputTokens, outputTokens, stepCount: value.length };\n};\n\nexport const parseMessage = (\n value: unknown,\n index: number,\n): MessagePreview | null => {\n if (!isRecord(value)) return null;\n\n const id = asString(value.id) ?? `message-${index}`;\n const role = asString(value.role) ?? \"unknown\";\n\n const rawParts = Array.isArray(value.parts)\n ? value.parts\n : Array.isArray(value.content)\n ? value.content\n : [];\n const parts = rawParts.map((part) => parsePart(part));\n\n const status = parsePartStatus(value.status);\n const metadata = isRecord(value.metadata) ? value.metadata : undefined;\n const timing = parseTiming(metadata?.timing);\n const usage = parseUsage(metadata?.steps);\n const submittedFeedback = isRecord(metadata?.submittedFeedback)\n ? asString(metadata.submittedFeedback.type)\n : undefined;\n const isOptimistic = asBool(metadata?.isOptimistic);\n const createdAt = asString(value.createdAt);\n const branchNumber = asNumber(value.branchNumber);\n const branchCount = asNumber(value.branchCount);\n const messageIndex = asNumber(value.index);\n\n return {\n id,\n role,\n parts,\n attachments: parseAttachments(value.attachments),\n ...(createdAt ? { createdAt } : {}),\n ...(status ? { status } : {}),\n ...(timing ? { timing } : {}),\n ...(usage ? { usage } : {}),\n ...(branchNumber !== undefined ? { branchNumber } : {}),\n ...(branchCount !== undefined ? { branchCount } : {}),\n ...(messageIndex !== undefined ? { index: messageIndex } : {}),\n ...(isOptimistic !== undefined ? { isOptimistic } : {}),\n ...(submittedFeedback ? { submittedFeedback } : {}),\n };\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"parse.js","names":["parseAttachments","estimateBase64Bytes","isSafeImagePreviewUrl","asBool","asNumber","asString","isRecord","MessagePreview","MessageTimingPreview","MessageUsagePreview","PartPreview","PartStatusPreview","ToolCallApprovalPreview","ToolCallInterruptPreview","ToolCallPartPreview","partKey","part","index","type","toolCallId","parsePartStatus","value","undefined","reason","error","parseInterrupt","result","payload","Object","keys","length","parseApproval","id","approved","isAutomatic","parseToolCall","Record","base","status","argsText","isError","interrupt","approval","subMessageCount","Array","isArray","messages","toolName","args","artifact","mcp","modelContent","parsePart","rawType","raw","text","sourceType","title","url","filename","image","previewUrl","sizeBytes","test","split","mimeType","data","audio","format","audioData","name","spec","parseTiming","streamStartTime","firstTokenTime","totalStreamTime","tokenCount","tokensPerSecond","totalChunks","toolCallCount","parseUsage","inputTokens","outputTokens","sawUsage","step","usage","input","output","stepCount","parseMessage","role","rawParts","parts","content","map","metadata","timing","steps","submittedFeedback","isOptimistic","createdAt","branchNumber","branchCount","messageIndex","attachments"],"sources":["../../../src/views/message/parse.ts"],"sourcesContent":["import { parseAttachments } from \"../attachments/parseAttachment\";\nimport {\n estimateBase64Bytes,\n isSafeImagePreviewUrl,\n} from \"../attachments/formatBytes\";\nimport { asBool, asNumber, asString, isRecord } from \"../../utils/common\";\nimport type {\n MessagePreview,\n MessageTimingPreview,\n MessageUsagePreview,\n PartPreview,\n PartStatusPreview,\n ToolCallApprovalPreview,\n ToolCallInterruptPreview,\n ToolCallPartPreview,\n} from \"./types\";\n\nexport const partKey = (part: PartPreview, index: number) =>\n part.type === \"tool-call\" && part.toolCallId\n ? `tc:${part.toolCallId}`\n : `p:${index}`;\n\nexport const parsePartStatus = (\n value: unknown,\n): PartStatusPreview | undefined => {\n if (!isRecord(value)) return undefined;\n const type = asString(value.type);\n if (!type) return undefined;\n const reason = asString(value.reason);\n return {\n type,\n ...(reason ? { reason } : {}),\n ...(\"error\" in value && value.error !== undefined\n ? { error: value.error }\n : {}),\n };\n};\n\nconst parseInterrupt = (\n value: unknown,\n): ToolCallInterruptPreview | undefined => {\n if (!isRecord(value)) return undefined;\n const type = asString(value.type);\n const result: ToolCallInterruptPreview = {\n ...(type ? { type } : {}),\n ...(\"payload\" in value ? { payload: value.payload } : {}),\n };\n return Object.keys(result).length > 0 ? result : undefined;\n};\n\nconst parseApproval = (value: unknown): ToolCallApprovalPreview | undefined => {\n if (!isRecord(value)) return undefined;\n const id = asString(value.id);\n const approved = asBool(value.approved);\n const reason = asString(value.reason);\n const isAutomatic = asBool(value.isAutomatic);\n const result: ToolCallApprovalPreview = {\n ...(id ? { id } : {}),\n ...(approved !== undefined ? { approved } : {}),\n ...(reason ? { reason } : {}),\n ...(isAutomatic !== undefined ? { isAutomatic } : {}),\n };\n return Object.keys(result).length > 0 ? result : undefined;\n};\n\nconst parseToolCall = (\n value: Record<string, unknown>,\n base: { status?: PartStatusPreview },\n): ToolCallPartPreview => {\n const argsText = asString(value.argsText);\n const isError = asBool(value.isError);\n const interrupt = parseInterrupt(value.interrupt);\n const approval = parseApproval(value.approval);\n const subMessageCount = Array.isArray(value.messages)\n ? value.messages.length\n : 0;\n\n return {\n type: \"tool-call\",\n toolCallId: asString(value.toolCallId) ?? \"\",\n toolName: asString(value.toolName) ?? \"(unknown)\",\n args: value.args,\n subMessageCount,\n ...(argsText !== undefined ? { argsText } : {}),\n ...(\"result\" in value && value.result !== undefined\n ? { result: value.result }\n : {}),\n ...(isError !== undefined ? { isError } : {}),\n ...(value.artifact !== undefined ? { artifact: value.artifact } : {}),\n ...(value.mcp !== undefined ? { mcp: value.mcp } : {}),\n ...(value.modelContent !== undefined\n ? { modelContent: value.modelContent }\n : {}),\n ...(interrupt ? { interrupt } : {}),\n ...(approval ? { approval } : {}),\n ...base,\n };\n};\n\nexport const parsePart = (value: unknown): PartPreview => {\n if (!isRecord(value)) {\n return { type: \"unknown\", rawType: typeof value, raw: value };\n }\n\n const status = parsePartStatus(value.status);\n const base = status ? { status } : {};\n const type = asString(value.type);\n\n switch (type) {\n case \"text\":\n return { type: \"text\", text: asString(value.text) ?? \"\", ...base };\n case \"reasoning\":\n return { type: \"reasoning\", text: asString(value.text) ?? \"\", ...base };\n case \"source\": {\n const sourceType = asString(value.sourceType);\n const title = asString(value.title);\n const url = asString(value.url);\n return {\n type: \"source\",\n ...(sourceType ? { sourceType } : {}),\n ...(title ? { title } : {}),\n ...(url ? { url } : {}),\n ...base,\n };\n }\n case \"image\": {\n const filename = asString(value.filename);\n const image = asString(value.image);\n const previewUrl =\n image && isSafeImagePreviewUrl(image) ? image : undefined;\n const sizeBytes =\n previewUrl && /^data:/i.test(previewUrl)\n ? estimateBase64Bytes(previewUrl.split(\",\", 2)[1] ?? previewUrl)\n : undefined;\n return {\n type: \"image\",\n ...(filename ? { filename } : {}),\n ...(previewUrl ? { previewUrl } : {}),\n ...(sizeBytes !== undefined ? { sizeBytes } : {}),\n ...base,\n };\n }\n case \"file\": {\n const filename = asString(value.filename);\n const mimeType = asString(value.mimeType);\n const data = asString(value.data);\n const sizeBytes = data ? estimateBase64Bytes(data) : undefined;\n return {\n type: \"file\",\n ...(filename ? { filename } : {}),\n ...(mimeType ? { mimeType } : {}),\n ...(sizeBytes !== undefined ? { sizeBytes } : {}),\n ...base,\n };\n }\n case \"audio\": {\n const audio = isRecord(value.audio) ? value.audio : undefined;\n const format = asString(audio?.format);\n const audioData = asString(audio?.data);\n const sizeBytes = audioData ? estimateBase64Bytes(audioData) : undefined;\n return {\n type: \"audio\",\n ...(format ? { format } : {}),\n ...(sizeBytes !== undefined ? { sizeBytes } : {}),\n ...base,\n };\n }\n case \"data\": {\n const name = asString(value.name);\n return {\n type: \"data\",\n data: value.data,\n ...(name ? { name } : {}),\n ...base,\n };\n }\n case \"generative-ui\":\n return { type: \"generative-ui\", spec: value.spec, ...base };\n case \"tool-call\":\n return parseToolCall(value, base);\n default:\n return {\n type: \"unknown\",\n rawType: type ?? typeof value,\n raw: value,\n ...base,\n };\n }\n};\n\nconst parseTiming = (value: unknown): MessageTimingPreview | undefined => {\n if (!isRecord(value)) return undefined;\n\n const streamStartTime = asNumber(value.streamStartTime);\n const firstTokenTime = asNumber(value.firstTokenTime);\n const totalStreamTime = asNumber(value.totalStreamTime);\n const tokenCount = asNumber(value.tokenCount);\n const tokensPerSecond = asNumber(value.tokensPerSecond);\n const totalChunks = asNumber(value.totalChunks);\n const toolCallCount = asNumber(value.toolCallCount);\n\n const result: MessageTimingPreview = {\n ...(streamStartTime !== undefined ? { streamStartTime } : {}),\n ...(firstTokenTime !== undefined ? { firstTokenTime } : {}),\n ...(totalStreamTime !== undefined ? { totalStreamTime } : {}),\n ...(tokenCount !== undefined ? { tokenCount } : {}),\n ...(tokensPerSecond !== undefined ? { tokensPerSecond } : {}),\n ...(totalChunks !== undefined ? { totalChunks } : {}),\n ...(toolCallCount !== undefined ? { toolCallCount } : {}),\n };\n return Object.keys(result).length > 0 ? result : undefined;\n};\n\nconst parseUsage = (value: unknown): MessageUsagePreview | undefined => {\n if (!Array.isArray(value) || value.length === 0) return undefined;\n\n let inputTokens = 0;\n let outputTokens = 0;\n let sawUsage = false;\n\n for (const step of value) {\n if (!isRecord(step) || !isRecord(step.usage)) continue;\n const input = asNumber(step.usage.inputTokens);\n const output = asNumber(step.usage.outputTokens);\n if (input !== undefined) {\n inputTokens += input;\n sawUsage = true;\n }\n if (output !== undefined) {\n outputTokens += output;\n sawUsage = true;\n }\n }\n\n if (!sawUsage) return undefined;\n return { inputTokens, outputTokens, stepCount: value.length };\n};\n\nexport const parseMessage = (\n value: unknown,\n index: number,\n): MessagePreview | null => {\n if (!isRecord(value)) return null;\n\n const id = asString(value.id) ?? `message-${index}`;\n const role = asString(value.role) ?? \"unknown\";\n\n const rawParts = Array.isArray(value.parts)\n ? value.parts\n : Array.isArray(value.content)\n ? value.content\n : [];\n const parts = rawParts.map((part) => parsePart(part));\n\n const status = parsePartStatus(value.status);\n const metadata = isRecord(value.metadata) ? value.metadata : undefined;\n const timing = parseTiming(metadata?.timing);\n const usage = parseUsage(metadata?.steps);\n const submittedFeedback = isRecord(metadata?.submittedFeedback)\n ? asString(metadata.submittedFeedback.type)\n : undefined;\n const isOptimistic = asBool(metadata?.isOptimistic);\n const createdAt = asString(value.createdAt);\n const branchNumber = asNumber(value.branchNumber);\n const branchCount = asNumber(value.branchCount);\n const messageIndex = asNumber(value.index);\n\n return {\n id,\n role,\n parts,\n attachments: parseAttachments(value.attachments),\n ...(createdAt ? { createdAt } : {}),\n ...(status ? { status } : {}),\n ...(timing ? { timing } : {}),\n ...(usage ? { usage } : {}),\n ...(branchNumber !== undefined ? { branchNumber } : {}),\n ...(branchCount !== undefined ? { branchCount } : {}),\n ...(messageIndex !== undefined ? { index: messageIndex } : {}),\n ...(isOptimistic !== undefined ? { isOptimistic } : {}),\n ...(submittedFeedback ? { submittedFeedback } : {}),\n };\n};\n"],"mappings":";;;;AAiBA,MAAae,WAAWC,MAAmBC,UACzCD,KAAKE,SAAS,eAAeF,KAAKG,aAC9B,MAAMH,KAAKG,eACX,KAAKF;AAEX,MAAaG,mBACXC,UACkC;CAClC,IAAI,CAACf,SAASe,KAAK,GAAG,OAAOC,KAAAA;CAC7B,MAAMJ,OAAOb,SAASgB,MAAMH,IAAI;CAChC,IAAI,CAACA,MAAM,OAAOI,KAAAA;CAClB,MAAMC,SAASlB,SAASgB,MAAME,MAAM;CACpC,OAAO;EACLL;EACA,GAAIK,SAAS,EAAEA,OAAO,IAAI,CAAC;EAC3B,GAAI,WAAWF,SAASA,MAAMG,UAAUF,KAAAA,IACpC,EAAEE,OAAOH,MAAMG,MAAM,IACrB,CAAC;CACP;AACF;AAEA,MAAMC,kBACJJ,UACyC;CACzC,IAAI,CAACf,SAASe,KAAK,GAAG,OAAOC,KAAAA;CAC7B,MAAMJ,OAAOb,SAASgB,MAAMH,IAAI;CAChC,MAAMQ,SAAmC;EACvC,GAAIR,OAAO,EAAEA,KAAK,IAAI,CAAC;EACvB,GAAI,aAAaG,QAAQ,EAAEM,SAASN,MAAMM,QAAQ,IAAI,CAAC;CACzD;CACA,OAAOC,OAAOC,KAAKH,MAAM,CAAC,CAACI,SAAS,IAAIJ,SAASJ,KAAAA;AACnD;AAEA,MAAMS,iBAAiBV,UAAwD;CAC7E,IAAI,CAACf,SAASe,KAAK,GAAG,OAAOC,KAAAA;CAC7B,MAAMU,KAAK3B,SAASgB,MAAMW,EAAE;CAC5B,MAAMC,WAAW9B,OAAOkB,MAAMY,QAAQ;CACtC,MAAMV,SAASlB,SAASgB,MAAME,MAAM;CACpC,MAAMW,cAAc/B,OAAOkB,MAAMa,WAAW;CAC5C,MAAMR,SAAkC;EACtC,GAAIM,KAAK,EAAEA,GAAG,IAAI,CAAC;EACnB,GAAIC,aAAaX,KAAAA,IAAY,EAAEW,SAAS,IAAI,CAAC;EAC7C,GAAIV,SAAS,EAAEA,OAAO,IAAI,CAAC;EAC3B,GAAIW,gBAAgBZ,KAAAA,IAAY,EAAEY,YAAY,IAAI,CAAC;CACrD;CACA,OAAON,OAAOC,KAAKH,MAAM,CAAC,CAACI,SAAS,IAAIJ,SAASJ,KAAAA;AACnD;AAEA,MAAMa,iBACJd,OACAgB,SACwB;CACxB,MAAME,WAAWlC,SAASgB,MAAMkB,QAAQ;CACxC,MAAMC,UAAUrC,OAAOkB,MAAMmB,OAAO;CACpC,MAAMC,YAAYhB,eAAeJ,MAAMoB,SAAS;CAChD,MAAMC,WAAWX,cAAcV,MAAMqB,QAAQ;CAC7C,MAAMC,kBAAkBC,MAAMC,QAAQxB,MAAMyB,QAAQ,IAChDzB,MAAMyB,SAAShB,SACf;CAEJ,OAAO;EACLZ,MAAM;EACNC,YAAYd,SAASgB,MAAMF,UAAU,KAAK;EAC1C4B,UAAU1C,SAASgB,MAAM0B,QAAQ,KAAK;EACtCC,MAAM3B,MAAM2B;EACZL;EACA,GAAIJ,aAAajB,KAAAA,IAAY,EAAEiB,SAAS,IAAI,CAAC;EAC7C,GAAI,YAAYlB,SAASA,MAAMK,WAAWJ,KAAAA,IACtC,EAAEI,QAAQL,MAAMK,OAAO,IACvB,CAAC;EACL,GAAIc,YAAYlB,KAAAA,IAAY,EAAEkB,QAAQ,IAAI,CAAC;EAC3C,GAAInB,MAAM4B,aAAa3B,KAAAA,IAAY,EAAE2B,UAAU5B,MAAM4B,SAAS,IAAI,CAAC;EACnE,GAAI5B,MAAM6B,QAAQ5B,KAAAA,IAAY,EAAE4B,KAAK7B,MAAM6B,IAAI,IAAI,CAAC;EACpD,GAAI7B,MAAM8B,iBAAiB7B,KAAAA,IACvB,EAAE6B,cAAc9B,MAAM8B,aAAa,IACnC,CAAC;EACL,GAAIV,YAAY,EAAEA,UAAU,IAAI,CAAC;EACjC,GAAIC,WAAW,EAAEA,SAAS,IAAI,CAAC;EAC/B,GAAGL;CACL;AACF;AAEA,MAAae,aAAa/B,UAAgC;CACxD,IAAI,CAACf,SAASe,KAAK,GACjB,OAAO;EAAEH,MAAM;EAAWmC,SAAS,OAAOhC;EAAOiC,KAAKjC;CAAM;CAG9D,MAAMiB,SAASlB,gBAAgBC,MAAMiB,MAAM;CAC3C,MAAMD,OAAOC,SAAS,EAAEA,OAAO,IAAI,CAAC;CACpC,MAAMpB,OAAOb,SAASgB,MAAMH,IAAI;CAEhC,QAAQA,MAAR;EACE,KAAK,QACH,OAAO;GAAEA,MAAM;GAAQqC,MAAMlD,SAASgB,MAAMkC,IAAI,KAAK;GAAI,GAAGlB;EAAK;EACnE,KAAK,aACH,OAAO;GAAEnB,MAAM;GAAaqC,MAAMlD,SAASgB,MAAMkC,IAAI,KAAK;GAAI,GAAGlB;EAAK;EACxE,KAAK,UAAU;GACb,MAAMmB,aAAanD,SAASgB,MAAMmC,UAAU;GAC5C,MAAMC,QAAQpD,SAASgB,MAAMoC,KAAK;GAClC,MAAMC,MAAMrD,SAASgB,MAAMqC,GAAG;GAC9B,OAAO;IACLxC,MAAM;IACN,GAAIsC,aAAa,EAAEA,WAAW,IAAI,CAAC;IACnC,GAAIC,QAAQ,EAAEA,MAAM,IAAI,CAAC;IACzB,GAAIC,MAAM,EAAEA,IAAI,IAAI,CAAC;IACrB,GAAGrB;GACL;EACF;EACA,KAAK,SAAS;GACZ,MAAMsB,WAAWtD,SAASgB,MAAMsC,QAAQ;GACxC,MAAMC,QAAQvD,SAASgB,MAAMuC,KAAK;GAClC,MAAMC,aACJD,SAAS1D,sBAAsB0D,KAAK,IAAIA,QAAQtC,KAAAA;GAClD,MAAMwC,YACJD,cAAc,UAAUE,KAAKF,UAAU,IACnC5D,oBAAoB4D,WAAWG,MAAM,KAAK,CAAC,CAAC,CAAC,MAAMH,UAAU,IAC7DvC,KAAAA;GACN,OAAO;IACLJ,MAAM;IACN,GAAIyC,WAAW,EAAEA,SAAS,IAAI,CAAC;IAC/B,GAAIE,aAAa,EAAEA,WAAW,IAAI,CAAC;IACnC,GAAIC,cAAcxC,KAAAA,IAAY,EAAEwC,UAAU,IAAI,CAAC;IAC/C,GAAGzB;GACL;EACF;EACA,KAAK,QAAQ;GACX,MAAMsB,WAAWtD,SAASgB,MAAMsC,QAAQ;GACxC,MAAMM,WAAW5D,SAASgB,MAAM4C,QAAQ;GACxC,MAAMC,OAAO7D,SAASgB,MAAM6C,IAAI;GAChC,MAAMJ,YAAYI,OAAOjE,oBAAoBiE,IAAI,IAAI5C,KAAAA;GACrD,OAAO;IACLJ,MAAM;IACN,GAAIyC,WAAW,EAAEA,SAAS,IAAI,CAAC;IAC/B,GAAIM,WAAW,EAAEA,SAAS,IAAI,CAAC;IAC/B,GAAIH,cAAcxC,KAAAA,IAAY,EAAEwC,UAAU,IAAI,CAAC;IAC/C,GAAGzB;GACL;EACF;EACA,KAAK,SAAS;GACZ,MAAM8B,QAAQ7D,SAASe,MAAM8C,KAAK,IAAI9C,MAAM8C,QAAQ7C,KAAAA;GACpD,MAAM8C,SAAS/D,SAAS8D,OAAOC,MAAM;GACrC,MAAMC,YAAYhE,SAAS8D,OAAOD,IAAI;GACtC,MAAMJ,YAAYO,YAAYpE,oBAAoBoE,SAAS,IAAI/C,KAAAA;GAC/D,OAAO;IACLJ,MAAM;IACN,GAAIkD,SAAS,EAAEA,OAAO,IAAI,CAAC;IAC3B,GAAIN,cAAcxC,KAAAA,IAAY,EAAEwC,UAAU,IAAI,CAAC;IAC/C,GAAGzB;GACL;EACF;EACA,KAAK,QAAQ;GACX,MAAMiC,OAAOjE,SAASgB,MAAMiD,IAAI;GAChC,OAAO;IACLpD,MAAM;IACNgD,MAAM7C,MAAM6C;IACZ,GAAII,OAAO,EAAEA,KAAK,IAAI,CAAC;IACvB,GAAGjC;GACL;EACF;EACA,KAAK,iBACH,OAAO;GAAEnB,MAAM;GAAiBqD,MAAMlD,MAAMkD;GAAM,GAAGlC;EAAK;EAC5D,KAAK,aACH,OAAOF,cAAcd,OAAOgB,IAAI;EAClC,SACE,OAAO;GACLnB,MAAM;GACNmC,SAASnC,QAAQ,OAAOG;GACxBiC,KAAKjC;GACL,GAAGgB;EACL;CACJ;AACF;AAEA,MAAMmC,eAAenD,UAAqD;CACxE,IAAI,CAACf,SAASe,KAAK,GAAG,OAAOC,KAAAA;CAE7B,MAAMmD,kBAAkBrE,SAASiB,MAAMoD,eAAe;CACtD,MAAMC,iBAAiBtE,SAASiB,MAAMqD,cAAc;CACpD,MAAMC,kBAAkBvE,SAASiB,MAAMsD,eAAe;CACtD,MAAMC,aAAaxE,SAASiB,MAAMuD,UAAU;CAC5C,MAAMC,kBAAkBzE,SAASiB,MAAMwD,eAAe;CACtD,MAAMC,cAAc1E,SAASiB,MAAMyD,WAAW;CAC9C,MAAMC,gBAAgB3E,SAASiB,MAAM0D,aAAa;CAElD,MAAMrD,SAA+B;EACnC,GAAI+C,oBAAoBnD,KAAAA,IAAY,EAAEmD,gBAAgB,IAAI,CAAC;EAC3D,GAAIC,mBAAmBpD,KAAAA,IAAY,EAAEoD,eAAe,IAAI,CAAC;EACzD,GAAIC,oBAAoBrD,KAAAA,IAAY,EAAEqD,gBAAgB,IAAI,CAAC;EAC3D,GAAIC,eAAetD,KAAAA,IAAY,EAAEsD,WAAW,IAAI,CAAC;EACjD,GAAIC,oBAAoBvD,KAAAA,IAAY,EAAEuD,gBAAgB,IAAI,CAAC;EAC3D,GAAIC,gBAAgBxD,KAAAA,IAAY,EAAEwD,YAAY,IAAI,CAAC;EACnD,GAAIC,kBAAkBzD,KAAAA,IAAY,EAAEyD,cAAc,IAAI,CAAC;CACzD;CACA,OAAOnD,OAAOC,KAAKH,MAAM,CAAC,CAACI,SAAS,IAAIJ,SAASJ,KAAAA;AACnD;AAEA,MAAM0D,cAAc3D,UAAoD;CACtE,IAAI,CAACuB,MAAMC,QAAQxB,KAAK,KAAKA,MAAMS,WAAW,GAAG,OAAOR,KAAAA;CAExD,IAAI2D,cAAc;CAClB,IAAIC,eAAe;CACnB,IAAIC,WAAW;CAEf,KAAK,MAAMC,QAAQ/D,OAAO;EACxB,IAAI,CAACf,SAAS8E,IAAI,KAAK,CAAC9E,SAAS8E,KAAKC,KAAK,GAAG;EAC9C,MAAMC,QAAQlF,SAASgF,KAAKC,MAAMJ,WAAW;EAC7C,MAAMM,SAASnF,SAASgF,KAAKC,MAAMH,YAAY;EAC/C,IAAII,UAAUhE,KAAAA,GAAW;GACvB2D,eAAeK;GACfH,WAAW;EACb;EACA,IAAII,WAAWjE,KAAAA,GAAW;GACxB4D,gBAAgBK;GAChBJ,WAAW;EACb;CACF;CAEA,IAAI,CAACA,UAAU,OAAO7D,KAAAA;CACtB,OAAO;EAAE2D;EAAaC;EAAcM,WAAWnE,MAAMS;CAAO;AAC9D;AAEA,MAAa2D,gBACXpE,OACAJ,UAC0B;CAC1B,IAAI,CAACX,SAASe,KAAK,GAAG,OAAO;CAE7B,MAAMW,KAAK3B,SAASgB,MAAMW,EAAE,KAAK,WAAWf;CAC5C,MAAMyE,OAAOrF,SAASgB,MAAMqE,IAAI,KAAK;CAOrC,MAAME,SALWhD,MAAMC,QAAQxB,MAAMuE,KAAK,IACtCvE,MAAMuE,QACNhD,MAAMC,QAAQxB,MAAMwE,OAAO,IACzBxE,MAAMwE,UACN,CAAA,EAAA,CACiBC,KAAK9E,SAASoC,UAAUpC,IAAI,CAAC;CAEpD,MAAMsB,SAASlB,gBAAgBC,MAAMiB,MAAM;CAC3C,MAAMyD,WAAWzF,SAASe,MAAM0E,QAAQ,IAAI1E,MAAM0E,WAAWzE,KAAAA;CAC7D,MAAM0E,SAASxB,YAAYuB,UAAUC,MAAM;CAC3C,MAAMX,QAAQL,WAAWe,UAAUE,KAAK;CACxC,MAAMC,oBAAoB5F,SAASyF,UAAUG,iBAAiB,IAC1D7F,SAAS0F,SAASG,kBAAkBhF,IAAI,IACxCI,KAAAA;CACJ,MAAM6E,eAAehG,OAAO4F,UAAUI,YAAY;CAClD,MAAMC,YAAY/F,SAASgB,MAAM+E,SAAS;CAC1C,MAAMC,eAAejG,SAASiB,MAAMgF,YAAY;CAChD,MAAMC,cAAclG,SAASiB,MAAMiF,WAAW;CAC9C,MAAMC,eAAenG,SAASiB,MAAMJ,KAAK;CAEzC,OAAO;EACLe;EACA0D;EACAE;EACAY,aAAaxG,iBAAiBqB,MAAMmF,WAAW;EAC/C,GAAIJ,YAAY,EAAEA,UAAU,IAAI,CAAC;EACjC,GAAI9D,SAAS,EAAEA,OAAO,IAAI,CAAC;EAC3B,GAAI0D,SAAS,EAAEA,OAAO,IAAI,CAAC;EAC3B,GAAIX,QAAQ,EAAEA,MAAM,IAAI,CAAC;EACzB,GAAIgB,iBAAiB/E,KAAAA,IAAY,EAAE+E,aAAa,IAAI,CAAC;EACrD,GAAIC,gBAAgBhF,KAAAA,IAAY,EAAEgF,YAAY,IAAI,CAAC;EACnD,GAAIC,iBAAiBjF,KAAAA,IAAY,EAAEL,OAAOsF,aAAa,IAAI,CAAC;EAC5D,GAAIJ,iBAAiB7E,KAAAA,IAAY,EAAE6E,aAAa,IAAI,CAAC;EACrD,GAAID,oBAAoB,EAAEA,kBAAkB,IAAI,CAAC;CACnD;AACF"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@assistant-ui/react-devtools",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.12",
|
|
4
4
|
"description": "React development tools for assistant-ui components",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"assistant-ui",
|
|
@@ -55,8 +55,8 @@
|
|
|
55
55
|
"react-dom": "^19.2.8",
|
|
56
56
|
"tailwindcss": "^4.3.3",
|
|
57
57
|
"vitest": "^4.1.10",
|
|
58
|
-
"@assistant-ui/
|
|
59
|
-
"@assistant-ui/
|
|
58
|
+
"@assistant-ui/react": "0.15.1",
|
|
59
|
+
"@assistant-ui/tap": "0.9.8",
|
|
60
60
|
"@assistant-ui/x-buildutils": "0.0.20"
|
|
61
61
|
},
|
|
62
62
|
"publishConfig": {
|
|
@@ -10,6 +10,4 @@ export const estimateBase64Bytes = (data: string): number =>
|
|
|
10
10
|
Math.max(0, Math.floor((data.length * 3) / 4));
|
|
11
11
|
|
|
12
12
|
export const isSafeImagePreviewUrl = (url: string): boolean =>
|
|
13
|
-
|
|
14
|
-
url.startsWith("https://") ||
|
|
15
|
-
url.startsWith("http://");
|
|
13
|
+
/^data:image\//i.test(url) || /^https?:\/\//i.test(url);
|
|
@@ -34,4 +34,29 @@ describe("parseAttachment", () => {
|
|
|
34
34
|
expect(result?.previewUrl).toBe("data:image/png;base64,abcd");
|
|
35
35
|
expect(result?.sizeBytes).toBeGreaterThan(0);
|
|
36
36
|
});
|
|
37
|
+
|
|
38
|
+
it("extracts preview and size for an uppercase data: scheme", () => {
|
|
39
|
+
const result = parseAttachment({
|
|
40
|
+
name: "shot.png",
|
|
41
|
+
type: "image",
|
|
42
|
+
content: [
|
|
43
|
+
{
|
|
44
|
+
type: "image",
|
|
45
|
+
image: "DATA:IMAGE/PNG;base64,abcd",
|
|
46
|
+
},
|
|
47
|
+
],
|
|
48
|
+
});
|
|
49
|
+
expect(result?.previewUrl).toBe("DATA:IMAGE/PNG;base64,abcd");
|
|
50
|
+
expect(result?.sizeBytes).toBeGreaterThan(0);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it("accepts an uppercase HTTPS preview URL without a size estimate", () => {
|
|
54
|
+
const result = parseAttachment({
|
|
55
|
+
name: "remote.png",
|
|
56
|
+
type: "image",
|
|
57
|
+
image: "HTTPS://example.com/pic.png",
|
|
58
|
+
});
|
|
59
|
+
expect(result?.previewUrl).toBe("HTTPS://example.com/pic.png");
|
|
60
|
+
expect(result?.sizeBytes).toBeUndefined();
|
|
61
|
+
});
|
|
37
62
|
});
|
|
@@ -26,7 +26,7 @@ const mediaFromContent = (
|
|
|
26
26
|
if (type === "image") {
|
|
27
27
|
const image = asString(part.image);
|
|
28
28
|
if (image && isSafeImagePreviewUrl(image)) {
|
|
29
|
-
const sizeBytes =
|
|
29
|
+
const sizeBytes = /^data:/i.test(image)
|
|
30
30
|
? estimateBase64Bytes(image.split(",", 2)[1] ?? image)
|
|
31
31
|
: undefined;
|
|
32
32
|
return {
|
|
@@ -99,6 +99,21 @@ describe("parsePart", () => {
|
|
|
99
99
|
expect(part.subMessageCount).toBe(2);
|
|
100
100
|
});
|
|
101
101
|
|
|
102
|
+
it("parses an image part with an uppercase data: scheme", () => {
|
|
103
|
+
const part = parsePart({
|
|
104
|
+
type: "image",
|
|
105
|
+
image: "DATA:IMAGE/PNG;base64,abcd",
|
|
106
|
+
filename: "shot.png",
|
|
107
|
+
});
|
|
108
|
+
expect(part).toMatchObject({
|
|
109
|
+
type: "image",
|
|
110
|
+
filename: "shot.png",
|
|
111
|
+
previewUrl: "DATA:IMAGE/PNG;base64,abcd",
|
|
112
|
+
});
|
|
113
|
+
if (part.type !== "image") return;
|
|
114
|
+
expect(part.sizeBytes).toBeGreaterThan(0);
|
|
115
|
+
});
|
|
116
|
+
|
|
102
117
|
it("falls back to unknown for unrecognized part types", () => {
|
|
103
118
|
const part = parsePart({ type: "mystery", foo: 1 });
|
|
104
119
|
expect(part.type).toBe("unknown");
|
|
@@ -128,9 +128,10 @@ export const parsePart = (value: unknown): PartPreview => {
|
|
|
128
128
|
const image = asString(value.image);
|
|
129
129
|
const previewUrl =
|
|
130
130
|
image && isSafeImagePreviewUrl(image) ? image : undefined;
|
|
131
|
-
const sizeBytes =
|
|
132
|
-
|
|
133
|
-
|
|
131
|
+
const sizeBytes =
|
|
132
|
+
previewUrl && /^data:/i.test(previewUrl)
|
|
133
|
+
? estimateBase64Bytes(previewUrl.split(",", 2)[1] ?? previewUrl)
|
|
134
|
+
: undefined;
|
|
134
135
|
return {
|
|
135
136
|
type: "image",
|
|
136
137
|
...(filename ? { filename } : {}),
|