@admin-layout/ai-chat-ext-browser 12.2.4-alpha.103 → 12.2.4-alpha.116
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/lib/components/MessageWidgets/MessageMedia.d.ts +7 -5
- package/lib/components/MessageWidgets/MessageMedia.d.ts.map +1 -1
- package/lib/components/MessageWidgets/MessageMedia.js +86 -54
- package/lib/components/MessageWidgets/MessageMedia.js.map +1 -1
- package/lib/components/MessageWidgets/mediaUtils.d.ts +21 -0
- package/lib/components/MessageWidgets/mediaUtils.d.ts.map +1 -0
- package/lib/components/MessageWidgets/mediaUtils.js +76 -0
- package/lib/components/MessageWidgets/mediaUtils.js.map +1 -0
- package/package.json +3 -3
|
@@ -7,11 +7,13 @@ export interface IMessageMediaProps {
|
|
|
7
7
|
onMessageClick?: (ref: IPost) => void;
|
|
8
8
|
}
|
|
9
9
|
/**
|
|
10
|
-
* Media card — renders the image / video / audio a cdecli generation
|
|
11
|
-
* (yantra-image-generation, yantra-video-generation, audio / TTS, …)
|
|
12
|
-
* inline in the chat. Shows the asset(s)
|
|
13
|
-
*
|
|
14
|
-
*
|
|
10
|
+
* Media result card — renders the image / video / audio a cdecli generation
|
|
11
|
+
* connector (yantra-image-generation, yantra-video-generation, audio / TTS, …)
|
|
12
|
+
* returned, inline in the chat. Shows the asset(s) in a real player, a technical
|
|
13
|
+
* metadata row (kind · dimensions · duration · format · model) derived from the
|
|
14
|
+
* loaded asset, the prompt as a caption, and open / download / copy-link actions.
|
|
15
|
+
* The asset URLs live on the message's propsConfiguration, so they persist with
|
|
16
|
+
* the chat.
|
|
15
17
|
*/
|
|
16
18
|
export declare const MessageMedia: (props: IMessageMediaProps) => React.JSX.Element;
|
|
17
19
|
//# sourceMappingURL=MessageMedia.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MessageMedia.d.ts","sourceRoot":"","sources":["../../../src/components/MessageWidgets/MessageMedia.tsx"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"MessageMedia.d.ts","sourceRoot":"","sources":["../../../src/components/MessageWidgets/MessageMedia.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAmB,MAAM,OAAO,CAAC;AAQxC,OAAO,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC;AAa/B,MAAM,WAAW,kBAAkB;IAC/B,OAAO,EAAE,KAAK,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,uFAAuF;IACvF,cAAc,CAAC,EAAE,CAAC,GAAG,EAAE,KAAK,KAAK,IAAI,CAAC;CACzC;AAaD;;;;;;;;GAQG;AACH,eAAO,MAAM,YAAY,GAAI,OAAO,kBAAkB,sBAyIrD,CAAC"}
|
|
@@ -1,29 +1,28 @@
|
|
|
1
|
-
import React
|
|
2
|
-
image: {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
},
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
}),
|
|
12
|
-
label: 'Video'
|
|
13
|
-
},
|
|
14
|
-
audio: {
|
|
15
|
-
icon: React.createElement(FiMusic, {
|
|
16
|
-
className: "w-4 h-4"
|
|
17
|
-
}),
|
|
18
|
-
label: 'Audio'
|
|
19
|
-
}
|
|
1
|
+
import React,{useState}from'react';import {FiImage}from'@react-icons/all-files/fi/FiImage.js';import {FiVideo}from'@react-icons/all-files/fi/FiVideo.js';import {FiMusic}from'@react-icons/all-files/fi/FiMusic.js';import {FiDownload}from'@react-icons/all-files/fi/FiDownload.js';import {FiExternalLink}from'@react-icons/all-files/fi/FiExternalLink.js';import {FiCopy}from'@react-icons/all-files/fi/FiCopy.js';import {FiCheck}from'@react-icons/all-files/fi/FiCheck.js';import {MediaAsset}from'@admin-layout/connector-widgets-browser';import {deriveMediaItems,MEDIA_KIND_LABEL,formatAttribution,formatDimensions,formatDuration,formatMediaFormat}from'./mediaUtils.js';const KIND_ICON = {
|
|
2
|
+
image: React.createElement(FiImage, {
|
|
3
|
+
className: "w-3.5 h-3.5"
|
|
4
|
+
}),
|
|
5
|
+
video: React.createElement(FiVideo, {
|
|
6
|
+
className: "w-3.5 h-3.5"
|
|
7
|
+
}),
|
|
8
|
+
audio: React.createElement(FiMusic, {
|
|
9
|
+
className: "w-3.5 h-3.5"
|
|
10
|
+
})
|
|
20
11
|
};
|
|
12
|
+
/** One compact `·`-separated metadata cell. */
|
|
13
|
+
const MetaCell = ({
|
|
14
|
+
children
|
|
15
|
+
}) => React.createElement("span", {
|
|
16
|
+
className: "inline-flex items-center whitespace-nowrap"
|
|
17
|
+
}, children);
|
|
21
18
|
/**
|
|
22
|
-
* Media card — renders the image / video / audio a cdecli generation
|
|
23
|
-
* (yantra-image-generation, yantra-video-generation, audio / TTS, …)
|
|
24
|
-
* inline in the chat. Shows the asset(s)
|
|
25
|
-
*
|
|
26
|
-
*
|
|
19
|
+
* Media result card — renders the image / video / audio a cdecli generation
|
|
20
|
+
* connector (yantra-image-generation, yantra-video-generation, audio / TTS, …)
|
|
21
|
+
* returned, inline in the chat. Shows the asset(s) in a real player, a technical
|
|
22
|
+
* metadata row (kind · dimensions · duration · format · model) derived from the
|
|
23
|
+
* loaded asset, the prompt as a caption, and open / download / copy-link actions.
|
|
24
|
+
* The asset URLs live on the message's propsConfiguration, so they persist with
|
|
25
|
+
* the chat.
|
|
27
26
|
*/
|
|
28
27
|
const MessageMedia = props => {
|
|
29
28
|
const {
|
|
@@ -34,58 +33,91 @@ const MessageMedia = props => {
|
|
|
34
33
|
const {
|
|
35
34
|
attachment
|
|
36
35
|
} = message.propsConfiguration?.contents ?? {};
|
|
36
|
+
const [assetMeta, setAssetMeta] = useState({});
|
|
37
|
+
const [copied, setCopied] = useState(false);
|
|
37
38
|
if (!attachment || !attachment.url) return null;
|
|
38
|
-
const items = attachment
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
const
|
|
39
|
+
const items = deriveMediaItems(attachment);
|
|
40
|
+
if (items.length === 0) return null;
|
|
41
|
+
const kind = attachment.kind;
|
|
42
|
+
const kindLabel = MEDIA_KIND_LABEL[kind] ?? MEDIA_KIND_LABEL.image;
|
|
43
|
+
const kindIcon = KIND_ICON[kind] ?? KIND_ICON.image;
|
|
44
|
+
const title = attachment.title || `${kindLabel} generated`;
|
|
45
|
+
const attribution = formatAttribution(attachment);
|
|
46
|
+
const isImageGrid = items.length > 1 && kind === 'image';
|
|
47
|
+
// Technical metadata — dimensions/duration come from the loaded asset (single
|
|
48
|
+
// asset only; a grid has no one set of dimensions), format from the mime type.
|
|
49
|
+
const dimensions = !isImageGrid ? formatDimensions(assetMeta.width, assetMeta.height) : undefined;
|
|
50
|
+
const duration = !isImageGrid ? formatDuration(assetMeta.duration) : undefined;
|
|
51
|
+
const format = formatMediaFormat(attachment.mimeType, attachment.fileName);
|
|
52
|
+
const metaCells = [isImageGrid ? `${items.length} images` : undefined, dimensions, duration, format, attribution].filter(Boolean);
|
|
49
53
|
const open = () => onMessageClick?.(message);
|
|
54
|
+
const copyLink = async () => {
|
|
55
|
+
try {
|
|
56
|
+
await navigator.clipboard?.writeText(attachment.url);
|
|
57
|
+
setCopied(true);
|
|
58
|
+
window.setTimeout(() => setCopied(false), 1600);
|
|
59
|
+
} catch {
|
|
60
|
+
/* clipboard unavailable — no-op */
|
|
61
|
+
}
|
|
62
|
+
};
|
|
50
63
|
return React.createElement("div", {
|
|
51
64
|
key: `${index}`,
|
|
52
|
-
className: "my-2 mx-2 rounded-xl border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800/60 overflow-hidden"
|
|
65
|
+
className: "my-2 mx-2 w-[calc(100%-1rem)] rounded-xl border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800/60 overflow-hidden"
|
|
53
66
|
}, React.createElement("div", {
|
|
54
|
-
className: "flex items-center gap-2 px-3 py-2 border-b border-gray-100 dark:border-gray-700"
|
|
67
|
+
className: "flex items-center gap-2 px-3 py-2.5 border-b border-gray-100 dark:border-gray-700/80"
|
|
55
68
|
}, React.createElement("span", {
|
|
56
|
-
className: "flex-shrink-0 text-blue-
|
|
57
|
-
},
|
|
58
|
-
className: "flex-1 min-w-0 text-sm font-semibold text-gray-900 dark:text-gray-100 truncate"
|
|
69
|
+
className: "flex-shrink-0 inline-flex items-center gap-1 rounded-md bg-blue-50 dark:bg-gray-700 px-1.5 py-0.5 text-[11px] font-medium text-blue-600 dark:text-blue-400"
|
|
70
|
+
}, kindIcon, kindLabel), React.createElement("p", {
|
|
71
|
+
className: "flex-1 min-w-0 text-sm font-semibold text-gray-900 dark:text-gray-100 truncate",
|
|
72
|
+
title: title
|
|
59
73
|
}, title), attribution && React.createElement("span", {
|
|
60
|
-
className: "flex-shrink-0 text-[
|
|
74
|
+
className: "flex-shrink-0 text-[11px] text-gray-400 dark:text-gray-500 truncate max-w-[55%]"
|
|
61
75
|
}, attribution)), React.createElement("div", {
|
|
62
|
-
className: `p-2 ${
|
|
76
|
+
className: `p-2 ${isImageGrid ? 'grid grid-cols-2 gap-2' : 'flex flex-col gap-2'}`,
|
|
63
77
|
onClick: open,
|
|
64
78
|
role: onMessageClick ? 'button' : undefined
|
|
65
79
|
}, items.map((item, i) => React.createElement(MediaAsset, {
|
|
66
80
|
key: (item.url ?? '') + i,
|
|
67
|
-
item: item
|
|
68
|
-
|
|
69
|
-
|
|
81
|
+
item: item,
|
|
82
|
+
onMeta: items.length === 1 ? setAssetMeta : undefined
|
|
83
|
+
}))), metaCells.length > 0 && React.createElement("div", {
|
|
84
|
+
className: "px-3 pb-1 flex flex-wrap items-center gap-2 text-[11px] text-gray-500 dark:text-gray-400"
|
|
85
|
+
}, metaCells.map((cell, i) => React.createElement(React.Fragment, {
|
|
86
|
+
key: cell + i
|
|
87
|
+
}, i > 0 && React.createElement("span", {
|
|
88
|
+
className: "text-gray-300 dark:text-gray-600"
|
|
89
|
+
}, "\u00B7"), React.createElement(MetaCell, null, cell)))), React.createElement("div", {
|
|
90
|
+
className: "px-3 pb-3 pt-2"
|
|
70
91
|
}, attachment.prompt && React.createElement("p", {
|
|
71
92
|
className: "text-xs text-gray-500 dark:text-gray-400 line-clamp-2",
|
|
72
93
|
title: attachment.prompt
|
|
73
94
|
}, attachment.prompt), React.createElement("div", {
|
|
74
|
-
className: "mt-2 flex items-center gap-
|
|
95
|
+
className: "mt-2 flex flex-wrap items-center gap-2"
|
|
75
96
|
}, React.createElement("a", {
|
|
76
97
|
href: attachment.url,
|
|
77
98
|
target: "_blank",
|
|
78
99
|
rel: "noopener noreferrer",
|
|
79
100
|
onClick: e => e.stopPropagation(),
|
|
80
|
-
className: "inline-flex items-center gap-1 text-xs font-medium text-
|
|
81
|
-
},
|
|
82
|
-
className: "w-3 h-3"
|
|
83
|
-
})), React.createElement("a", {
|
|
101
|
+
className: "inline-flex items-center gap-1.5 rounded-lg bg-blue-600 hover:bg-blue-700 px-2.5 py-1.5 text-xs font-medium text-white transition-colors"
|
|
102
|
+
}, React.createElement(FiExternalLink, {
|
|
103
|
+
className: "w-3.5 h-3.5"
|
|
104
|
+
}), " Open"), React.createElement("a", {
|
|
84
105
|
href: attachment.url,
|
|
85
106
|
download: attachment.fileName || true,
|
|
86
107
|
onClick: e => e.stopPropagation(),
|
|
87
|
-
className: "inline-flex items-center gap-1 text-xs font-medium text-gray-
|
|
88
|
-
},
|
|
89
|
-
className: "w-3 h-3"
|
|
90
|
-
}))
|
|
108
|
+
className: "inline-flex items-center gap-1.5 rounded-lg bg-gray-50 dark:bg-gray-700 hover:bg-gray-100 dark:hover:bg-gray-600 px-2.5 py-1.5 text-xs font-medium text-gray-700 dark:text-gray-200 transition-colors"
|
|
109
|
+
}, React.createElement(FiDownload, {
|
|
110
|
+
className: "w-3.5 h-3.5"
|
|
111
|
+
}), " Download"), React.createElement("button", {
|
|
112
|
+
type: "button",
|
|
113
|
+
onClick: e => {
|
|
114
|
+
e.stopPropagation();
|
|
115
|
+
void copyLink();
|
|
116
|
+
},
|
|
117
|
+
className: "inline-flex items-center gap-1.5 rounded-lg bg-gray-50 dark:bg-gray-700 hover:bg-gray-100 dark:hover:bg-gray-600 px-2.5 py-1.5 text-xs font-medium text-gray-700 dark:text-gray-200 transition-colors"
|
|
118
|
+
}, copied ? React.createElement(FiCheck, {
|
|
119
|
+
className: "w-3.5 h-3.5"
|
|
120
|
+
}) : React.createElement(FiCopy, {
|
|
121
|
+
className: "w-3.5 h-3.5"
|
|
122
|
+
}), copied ? 'Copied' : 'Copy link'))));
|
|
91
123
|
};export{MessageMedia};//# sourceMappingURL=MessageMedia.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MessageMedia.js","sources":["../../../src/components/MessageWidgets/MessageMedia.tsx"],"sourcesContent":[null],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"MessageMedia.js","sources":["../../../src/components/MessageWidgets/MessageMedia.tsx"],"sourcesContent":[null],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { MediaAttachmentsInterface, MediaItem } from '../../interfaces';
|
|
2
|
+
export type MediaKind = MediaItem['kind'];
|
|
3
|
+
/** Human label for each asset kind. */
|
|
4
|
+
export declare const MEDIA_KIND_LABEL: Record<MediaKind, string>;
|
|
5
|
+
/**
|
|
6
|
+
* Normalise an attachment into a flat list of assets. Agents emit two shapes:
|
|
7
|
+
* a single top-level `url`, or a nested `items[]` array — this tolerates both.
|
|
8
|
+
*/
|
|
9
|
+
export declare const deriveMediaItems: (attachment: MediaAttachmentsInterface) => MediaItem[];
|
|
10
|
+
/** Seconds → `m:ss` (hours fold into minutes). `65` → `"1:05"`, `8` → `"0:08"`. */
|
|
11
|
+
export declare const formatDuration: (seconds?: number) => string | undefined;
|
|
12
|
+
/** Intrinsic dimensions → `"1920×1080"`; undefined until both are known. */
|
|
13
|
+
export declare const formatDimensions: (width?: number, height?: number) => string | undefined;
|
|
14
|
+
/**
|
|
15
|
+
* Short uppercase format label from the mime subtype, falling back to the file
|
|
16
|
+
* extension. `"video/mp4"` → `"MP4"`, `"image/png"` → `"PNG"`, `foo.webp` → `"WEBP"`.
|
|
17
|
+
*/
|
|
18
|
+
export declare const formatMediaFormat: (mimeType?: string, fileName?: string) => string | undefined;
|
|
19
|
+
/** The attribution line: `connector · model`, omitting whichever is absent. */
|
|
20
|
+
export declare const formatAttribution: (attachment: MediaAttachmentsInterface) => string | undefined;
|
|
21
|
+
//# sourceMappingURL=mediaUtils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mediaUtils.d.ts","sourceRoot":"","sources":["../../../src/components/MessageWidgets/mediaUtils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,yBAAyB,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAExE,MAAM,MAAM,SAAS,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC;AAE1C,uCAAuC;AACvC,eAAO,MAAM,gBAAgB,EAAE,MAAM,CAAC,SAAS,EAAE,MAAM,CAItD,CAAC;AAuBF;;;GAGG;AACH,eAAO,MAAM,gBAAgB,GAAI,YAAY,yBAAyB,KAAG,SAAS,EAcjF,CAAC;AAEF,mFAAmF;AACnF,eAAO,MAAM,cAAc,GAAI,UAAU,MAAM,KAAG,MAAM,GAAG,SAM1D,CAAC;AAEF,4EAA4E;AAC5E,eAAO,MAAM,gBAAgB,GAAI,QAAQ,MAAM,EAAE,SAAS,MAAM,KAAG,MAAM,GAAG,SACtB,CAAC;AAUvD;;;GAGG;AACH,eAAO,MAAM,iBAAiB,GAAI,WAAW,MAAM,EAAE,WAAW,MAAM,KAAG,MAAM,GAAG,SAMjF,CAAC;AAEF,+EAA+E;AAC/E,eAAO,MAAM,iBAAiB,GAAI,YAAY,yBAAyB,KAAG,MAAM,GAAG,SAGlF,CAAC"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/** Human label for each asset kind. */
|
|
2
|
+
const MEDIA_KIND_LABEL = {
|
|
3
|
+
image: 'Image',
|
|
4
|
+
video: 'Video',
|
|
5
|
+
audio: 'Audio'
|
|
6
|
+
};
|
|
7
|
+
/** Extensions worth recognising when an item names no kind — the rest fall through to image. */
|
|
8
|
+
const VIDEO_EXTENSIONS = ['mp4', 'webm', 'mov', 'm4v', 'mkv'];
|
|
9
|
+
const AUDIO_EXTENSIONS = ['mp3', 'wav', 'ogg', 'm4a', 'aac', 'flac'];
|
|
10
|
+
/**
|
|
11
|
+
* The kind an item renders as. `MediaAsset` picks `<img>` for anything it does not
|
|
12
|
+
* recognise, so an item that names no kind renders a clip or a track as a broken
|
|
13
|
+
* image — hence the fallbacks: the attachment's own kind, then the mime type, then
|
|
14
|
+
* the URL's extension.
|
|
15
|
+
*/
|
|
16
|
+
const itemKind = (item, attachment) => {
|
|
17
|
+
if (item.kind) return item.kind;
|
|
18
|
+
if (attachment.kind) return attachment.kind;
|
|
19
|
+
const mime = item.mimeType?.split('/')[0]?.toLowerCase();
|
|
20
|
+
if (mime === 'video' || mime === 'audio' || mime === 'image') return mime;
|
|
21
|
+
const ext = item.url?.split(/[?#]/)[0]?.split('.').pop()?.toLowerCase() ?? '';
|
|
22
|
+
if (VIDEO_EXTENSIONS.includes(ext)) return 'video';
|
|
23
|
+
if (AUDIO_EXTENSIONS.includes(ext)) return 'audio';
|
|
24
|
+
return 'image';
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Normalise an attachment into a flat list of assets. Agents emit two shapes:
|
|
28
|
+
* a single top-level `url`, or a nested `items[]` array — this tolerates both.
|
|
29
|
+
*/
|
|
30
|
+
const deriveMediaItems = attachment => {
|
|
31
|
+
if (Array.isArray(attachment.items) && attachment.items.length > 0) {
|
|
32
|
+
return attachment.items.map(item => ({
|
|
33
|
+
...item,
|
|
34
|
+
kind: itemKind(item, attachment)
|
|
35
|
+
}));
|
|
36
|
+
}
|
|
37
|
+
if (!attachment.url) return [];
|
|
38
|
+
return [{
|
|
39
|
+
kind: itemKind(attachment, attachment),
|
|
40
|
+
url: attachment.url,
|
|
41
|
+
title: attachment.title,
|
|
42
|
+
mimeType: attachment.mimeType,
|
|
43
|
+
posterUrl: attachment.posterUrl
|
|
44
|
+
}];
|
|
45
|
+
};
|
|
46
|
+
/** Seconds → `m:ss` (hours fold into minutes). `65` → `"1:05"`, `8` → `"0:08"`. */
|
|
47
|
+
const formatDuration = seconds => {
|
|
48
|
+
if (seconds == null || !Number.isFinite(seconds) || seconds < 0) return undefined;
|
|
49
|
+
const total = Math.round(seconds);
|
|
50
|
+
const minutes = Math.floor(total / 60);
|
|
51
|
+
const secs = total % 60;
|
|
52
|
+
return `${minutes}:${secs.toString().padStart(2, '0')}`;
|
|
53
|
+
};
|
|
54
|
+
/** Intrinsic dimensions → `"1920×1080"`; undefined until both are known. */
|
|
55
|
+
const formatDimensions = (width, height) => width && height ? `${width}×${height}` : undefined;
|
|
56
|
+
/** Common mime subtypes that read better under a short alias than raw. */
|
|
57
|
+
const FORMAT_ALIASES = {
|
|
58
|
+
quicktime: 'MOV',
|
|
59
|
+
'x-matroska': 'MKV',
|
|
60
|
+
jpeg: 'JPG',
|
|
61
|
+
'svg+xml': 'SVG'
|
|
62
|
+
};
|
|
63
|
+
/**
|
|
64
|
+
* Short uppercase format label from the mime subtype, falling back to the file
|
|
65
|
+
* extension. `"video/mp4"` → `"MP4"`, `"image/png"` → `"PNG"`, `foo.webp` → `"WEBP"`.
|
|
66
|
+
*/
|
|
67
|
+
const formatMediaFormat = (mimeType, fileName) => {
|
|
68
|
+
const sub = mimeType?.split('/')[1]?.split(';')[0]?.trim().toLowerCase() || fileName?.split('.').pop()?.trim().toLowerCase();
|
|
69
|
+
if (!sub) return undefined;
|
|
70
|
+
return (FORMAT_ALIASES[sub] ?? sub).toUpperCase();
|
|
71
|
+
};
|
|
72
|
+
/** The attribution line: `connector · model`, omitting whichever is absent. */
|
|
73
|
+
const formatAttribution = attachment => {
|
|
74
|
+
const parts = [attachment.connector, attachment.model].filter(Boolean);
|
|
75
|
+
return parts.length ? parts.join(' · ') : undefined;
|
|
76
|
+
};export{MEDIA_KIND_LABEL,deriveMediaItems,formatAttribution,formatDimensions,formatDuration,formatMediaFormat};//# sourceMappingURL=mediaUtils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mediaUtils.js","sources":["../../../src/components/MessageWidgets/mediaUtils.ts"],"sourcesContent":[null],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@admin-layout/ai-chat-ext-browser",
|
|
3
|
-
"version": "12.2.4-alpha.
|
|
3
|
+
"version": "12.2.4-alpha.116",
|
|
4
4
|
"description": "AI Chat extension for Messenger - supports AI assistant, thinking, code blocks, tool use, and service messages",
|
|
5
5
|
"license": "ISC",
|
|
6
6
|
"author": "CDMBase LLC",
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"watch": "npm run build:lib:watch"
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@admin-layout/connector-widgets-browser": "12.2.4-alpha.
|
|
24
|
+
"@admin-layout/connector-widgets-browser": "12.2.4-alpha.104",
|
|
25
25
|
"html-react-parser": "^3.0.1"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"publishConfig": {
|
|
37
37
|
"access": "public"
|
|
38
38
|
},
|
|
39
|
-
"gitHead": "
|
|
39
|
+
"gitHead": "ebadf4007c67a8d0b0d6114c0fb0dde7f19ee5f7",
|
|
40
40
|
"typescript": {
|
|
41
41
|
"definition": "lib/index.d.ts"
|
|
42
42
|
}
|