@blocklet/aigne-hub 0.4.56 → 0.4.58
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/cjs/components/conversation/conversation.js +30 -4
- package/lib/cjs/components/conversation/use-conversation.js +22 -9
- package/lib/cjs/components/index.js +3 -1
- package/lib/cjs/components/video-preview.js +91 -0
- package/lib/esm/components/conversation/conversation.js +30 -4
- package/lib/esm/components/conversation/use-conversation.js +22 -9
- package/lib/esm/components/index.js +1 -0
- package/lib/esm/components/video-preview.js +85 -0
- package/lib/types/components/conversation/conversation.d.ts +13 -1
- package/lib/types/components/conversation/use-conversation.d.ts +10 -3
- package/lib/types/components/index.d.ts +1 -0
- package/lib/types/components/video-preview.d.ts +16 -0
- package/package.json +1 -1
|
@@ -10,6 +10,7 @@ const isNil_1 = __importDefault(require("lodash/isNil"));
|
|
|
10
10
|
const react_1 = require("react");
|
|
11
11
|
const alert_1 = __importDefault(require("../credit/alert"));
|
|
12
12
|
const image_preview_1 = __importDefault(require("../image-preview"));
|
|
13
|
+
const video_preview_1 = __importDefault(require("../video-preview"));
|
|
13
14
|
const message_1 = __importDefault(require("./message"));
|
|
14
15
|
const prompt_1 = __importDefault(require("./prompt"));
|
|
15
16
|
function Conversation({ ref, messages, onSubmit, customActions = () => [], renderAvatar = undefined, maxWidth = 1000, scrollContainer = undefined, promptProps = {}, chatLayout = 'left-right', ...props }) {
|
|
@@ -32,9 +33,7 @@ function Conversation({ ref, messages, onSubmit, customActions = () => [], rende
|
|
|
32
33
|
typeof msg.response === 'object' &&
|
|
33
34
|
'images' in msg.response &&
|
|
34
35
|
Array.isArray(msg.response.images) &&
|
|
35
|
-
msg.response.images.length > 0 && ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [msg.response.images.some((img) => img.url && img.url
|
|
36
|
-
.filter((img) => img.url && img.url.startsWith('data:'))
|
|
37
|
-
.map(({ url }) => ({
|
|
36
|
+
msg.response.images.length > 0 && ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [msg.response.images.some((img) => img.url && img.url !== '[IMAGE_PLACEHOLDER]') && ((0, jsx_runtime_1.jsx)(image_preview_1.default, { itemWidth: 200, borderRadius: 12, dataSource: msg.response.images.map(({ url }) => ({
|
|
38
37
|
src: url,
|
|
39
38
|
onLoad: () => scrollToBottom(),
|
|
40
39
|
})) })), msg.response.images.some((img) => !img.url || img.url === '[IMAGE_PLACEHOLDER]') && ((0, jsx_runtime_1.jsxs)(material_1.Box, { sx: {
|
|
@@ -58,7 +57,34 @@ function Conversation({ ref, messages, onSubmit, customActions = () => [], rende
|
|
|
58
57
|
}, children: msg.response.images.filter((img) => !img.url || img.url === '[IMAGE_PLACEHOLDER]')
|
|
59
58
|
.length === 1
|
|
60
59
|
? 'Image (Not Cached)'
|
|
61
|
-
: `${msg.response.images.filter((img) => !img.url || img.url === '[IMAGE_PLACEHOLDER]').length} Images (Not Cached)` })] }))] })), msg.
|
|
60
|
+
: `${msg.response.images.filter((img) => !img.url || img.url === '[IMAGE_PLACEHOLDER]').length} Images (Not Cached)` })] }))] })), msg.response &&
|
|
61
|
+
typeof msg.response === 'object' &&
|
|
62
|
+
'videos' in msg.response &&
|
|
63
|
+
Array.isArray(msg.response.videos) &&
|
|
64
|
+
msg.response.videos.length > 0 && ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [msg.response.videos.some((video) => (video.data && video.data.startsWith('data:')) || video.url) && ((0, jsx_runtime_1.jsx)(video_preview_1.default, { itemWidth: 300, borderRadius: 12, dataSource: msg.response.videos.map(({ data, url, type }) => ({
|
|
65
|
+
src: type === 'file' && data ? data : url,
|
|
66
|
+
onLoad: () => scrollToBottom(),
|
|
67
|
+
})) })), msg.response.videos.some((video) => video.data === '[VIDEO_PLACEHOLDER]') && ((0, jsx_runtime_1.jsxs)(material_1.Box, { sx: {
|
|
68
|
+
margin: '8px 0',
|
|
69
|
+
minHeight: '200px',
|
|
70
|
+
background: '#f5f5f5',
|
|
71
|
+
borderRadius: '8px',
|
|
72
|
+
padding: '16px',
|
|
73
|
+
display: 'flex',
|
|
74
|
+
alignItems: 'center',
|
|
75
|
+
justifyContent: 'center',
|
|
76
|
+
flexDirection: 'column',
|
|
77
|
+
gap: '12px',
|
|
78
|
+
border: '2px dashed #ddd',
|
|
79
|
+
}, children: [(0, jsx_runtime_1.jsx)(material_1.Box, { sx: { fontSize: '48px', opacity: 0.4 }, children: "\uD83D\uDDBC\uFE0F" }), (0, jsx_runtime_1.jsx)(material_1.Box, { sx: {
|
|
80
|
+
fontSize: '14px',
|
|
81
|
+
color: '#666',
|
|
82
|
+
textAlign: 'center',
|
|
83
|
+
fontWeight: 500,
|
|
84
|
+
minWidth: 200,
|
|
85
|
+
}, children: msg.response.videos.filter((video) => !video.url || video.data === '[VIDEO_PLACEHOLDER]').length === 1
|
|
86
|
+
? 'Video (Not Cached)'
|
|
87
|
+
: `${msg.response.videos.filter((video) => !video.url || video.data === '[VIDEO_PLACEHOLDER]').length} Videos (Not Cached)` })] }))] })), msg.error ? (
|
|
62
88
|
// @ts-ignore
|
|
63
89
|
(0, jsx_runtime_1.jsx)(alert_1.default, { error: msg.error })) : (msg.loading &&
|
|
64
90
|
!msg.response && ((0, jsx_runtime_1.jsxs)(material_1.Box, { sx: {
|
|
@@ -39,17 +39,26 @@ const saveMessages = async (messages) => {
|
|
|
39
39
|
.filter((msg) => !msg.loading)
|
|
40
40
|
.slice(-MAX_CACHED_MESSAGES) // Keep only last N messages
|
|
41
41
|
.map((msg) => {
|
|
42
|
-
var _a;
|
|
43
42
|
// Replace image data URLs with placeholders to save storage space
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
43
|
+
let response = msg === null || msg === void 0 ? void 0 : msg.response;
|
|
44
|
+
if (response && typeof response === 'object' && 'images' in response && response.images) {
|
|
45
|
+
response = {
|
|
46
|
+
...response,
|
|
47
|
+
images: (response.images || []).map((img) => ({
|
|
48
48
|
...img,
|
|
49
|
-
url: img.url && img.url.startsWith('data:') ? '[IMAGE_PLACEHOLDER]' : img.url,
|
|
50
|
-
}))
|
|
51
|
-
}
|
|
52
|
-
|
|
49
|
+
url: (img === null || img === void 0 ? void 0 : img.url) && img.url.startsWith('data:') ? '[IMAGE_PLACEHOLDER]' : img.url,
|
|
50
|
+
})),
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
if (response && typeof response === 'object' && 'videos' in response && response.videos) {
|
|
54
|
+
response = {
|
|
55
|
+
...response,
|
|
56
|
+
videos: (response.videos || []).map((video) => ({
|
|
57
|
+
...video,
|
|
58
|
+
data: (video === null || video === void 0 ? void 0 : video.data) && video.data.startsWith('data:') ? '[VIDEO_PLACEHOLDER]' : video.data,
|
|
59
|
+
})),
|
|
60
|
+
};
|
|
61
|
+
}
|
|
53
62
|
return {
|
|
54
63
|
id: msg.id,
|
|
55
64
|
prompt: msg.prompt,
|
|
@@ -161,6 +170,7 @@ function useConversation({ scrollToBottom, textCompletions, imageGenerations, en
|
|
|
161
170
|
const result = await textCompletions(prompt, { meta });
|
|
162
171
|
const isText = (i) => i.type === 'text';
|
|
163
172
|
const isImages = (i) => i.type === 'images';
|
|
173
|
+
const isVideo = (i) => i.type === 'video';
|
|
164
174
|
const reader = result.getReader();
|
|
165
175
|
const decoder = new TextDecoder();
|
|
166
176
|
let response = '';
|
|
@@ -178,6 +188,9 @@ function useConversation({ scrollToBottom, textCompletions, imageGenerations, en
|
|
|
178
188
|
else if (isImages(value)) {
|
|
179
189
|
response = { images: value.images };
|
|
180
190
|
}
|
|
191
|
+
else if (isVideo(value)) {
|
|
192
|
+
response = { videos: value.videos };
|
|
193
|
+
}
|
|
181
194
|
else {
|
|
182
195
|
delta = decoder.decode(value);
|
|
183
196
|
}
|
|
@@ -18,11 +18,13 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
18
18
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
19
19
|
};
|
|
20
20
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
21
|
-
exports.FormLabel = exports.UserCreditCard = exports.Table = exports.Switch = exports.CreditErrorAlert = exports.CreditBalance = exports.CreditButton = exports.SubscribeErrorAlert = exports.SubscribeButton = exports.Conversation = exports.ImagePreview = exports.LoadingImage = void 0;
|
|
21
|
+
exports.FormLabel = exports.UserCreditCard = exports.Table = exports.Switch = exports.CreditErrorAlert = exports.CreditBalance = exports.CreditButton = exports.SubscribeErrorAlert = exports.SubscribeButton = exports.Conversation = exports.VideoPreview = exports.ImagePreview = exports.LoadingImage = void 0;
|
|
22
22
|
var loading_image_1 = require("./loading-image");
|
|
23
23
|
Object.defineProperty(exports, "LoadingImage", { enumerable: true, get: function () { return __importDefault(loading_image_1).default; } });
|
|
24
24
|
var image_preview_1 = require("./image-preview");
|
|
25
25
|
Object.defineProperty(exports, "ImagePreview", { enumerable: true, get: function () { return __importDefault(image_preview_1).default; } });
|
|
26
|
+
var video_preview_1 = require("./video-preview");
|
|
27
|
+
Object.defineProperty(exports, "VideoPreview", { enumerable: true, get: function () { return __importDefault(video_preview_1).default; } });
|
|
26
28
|
var conversation_1 = require("./conversation");
|
|
27
29
|
Object.defineProperty(exports, "Conversation", { enumerable: true, get: function () { return __importDefault(conversation_1).default; } });
|
|
28
30
|
__exportStar(require("./conversation"), exports);
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.default = VideoPreview;
|
|
7
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
8
|
+
const CloudDownloadOutlined_1 = __importDefault(require("@mui/icons-material/CloudDownloadOutlined"));
|
|
9
|
+
const material_1 = require("@mui/material");
|
|
10
|
+
const ahooks_1 = require("ahooks");
|
|
11
|
+
const renderIconButton = (children, onClick, { key, ...extraProps } = {}) => {
|
|
12
|
+
return ((0, jsx_runtime_1.jsx)(material_1.IconButton, { sx: {
|
|
13
|
+
transition: 'all 0.3s',
|
|
14
|
+
color: 'rgba(255,255,255,0.75)',
|
|
15
|
+
'&:hover': {
|
|
16
|
+
color: 'rgba(255,255,255,1)',
|
|
17
|
+
},
|
|
18
|
+
}, onClick: onClick, ...extraProps, children: children }, key));
|
|
19
|
+
};
|
|
20
|
+
function getExtFromBase64(base64) {
|
|
21
|
+
var _a;
|
|
22
|
+
// eslint-disable-next-line prefer-regex-literals
|
|
23
|
+
const re = new RegExp('data:video/([a-z]+);base64,.+');
|
|
24
|
+
const res = re.exec(base64);
|
|
25
|
+
if ((_a = res === null || res === void 0 ? void 0 : res.groups) === null || _a === void 0 ? void 0 : _a.ext) {
|
|
26
|
+
return res.groups.ext;
|
|
27
|
+
}
|
|
28
|
+
return '';
|
|
29
|
+
}
|
|
30
|
+
function VideoPreview({ dataSource = [], itemWidth = undefined, itemHeight = undefined, spacing = 1, transition = 'all 0.3s', borderRadius = 8, showDownloadButton = true, }) {
|
|
31
|
+
const state = (0, ahooks_1.useReactive)({
|
|
32
|
+
downloadingIndexMap: {},
|
|
33
|
+
});
|
|
34
|
+
const getDownloadButton = (currentIndex, extraProps = {}) => renderIconButton((0, jsx_runtime_1.jsx)(CloudDownloadOutlined_1.default, { fontSize: "inherit" }), async () => {
|
|
35
|
+
const { src } = (dataSource === null || dataSource === void 0 ? void 0 : dataSource[currentIndex]) || {};
|
|
36
|
+
state.downloadingIndexMap = {
|
|
37
|
+
...state.downloadingIndexMap,
|
|
38
|
+
[currentIndex]: true,
|
|
39
|
+
};
|
|
40
|
+
if (src) {
|
|
41
|
+
// download base64 video
|
|
42
|
+
if (src === null || src === void 0 ? void 0 : src.startsWith('data:video/')) {
|
|
43
|
+
const link = document.createElement('a');
|
|
44
|
+
link.href = src;
|
|
45
|
+
link.download = `video-${currentIndex}.${getExtFromBase64(src) || 'mp4'}`;
|
|
46
|
+
link.click();
|
|
47
|
+
}
|
|
48
|
+
state.downloadingIndexMap = {
|
|
49
|
+
...state.downloadingIndexMap,
|
|
50
|
+
[currentIndex]: false,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
}, {
|
|
54
|
+
key: 'download',
|
|
55
|
+
disabled: !!state.downloadingIndexMap[currentIndex],
|
|
56
|
+
...extraProps,
|
|
57
|
+
});
|
|
58
|
+
return ((0, jsx_runtime_1.jsx)(material_1.Grid, { spacing: spacing, container: true, className: "video-wrapper", children: dataSource === null || dataSource === void 0 ? void 0 : dataSource.map((item, index) => {
|
|
59
|
+
const { width, height } = item;
|
|
60
|
+
return ((0, jsx_runtime_1.jsx)(material_1.Grid
|
|
61
|
+
// eslint-disable-next-line react/no-array-index-key
|
|
62
|
+
, { className: "video-item", sx: {
|
|
63
|
+
transition,
|
|
64
|
+
'&:hover': {
|
|
65
|
+
cursor: 'pointer',
|
|
66
|
+
'& .video-toolbar': {
|
|
67
|
+
transition,
|
|
68
|
+
opacity: 1,
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
}, children: (0, jsx_runtime_1.jsxs)(material_1.Box, { sx: { position: 'relative' }, children: [(0, jsx_runtime_1.jsx)("video", { src: item.src, controls: true, style: {
|
|
72
|
+
transition,
|
|
73
|
+
borderRadius,
|
|
74
|
+
objectFit: 'cover',
|
|
75
|
+
width: width || itemWidth || '100%',
|
|
76
|
+
height: height || itemHeight || 'auto',
|
|
77
|
+
backgroundColor: '#000',
|
|
78
|
+
maxWidth: '400px',
|
|
79
|
+
}, preload: "metadata", children: (0, jsx_runtime_1.jsx)("track", { kind: "captions" }) }), (0, jsx_runtime_1.jsx)(material_1.Box, { className: "video-toolbar", sx: {
|
|
80
|
+
position: 'absolute',
|
|
81
|
+
right: 8,
|
|
82
|
+
top: 8,
|
|
83
|
+
opacity: 0,
|
|
84
|
+
background: 'rgba(0,0,0,0.7)',
|
|
85
|
+
borderRadius: '4px',
|
|
86
|
+
}, children: showDownloadButton &&
|
|
87
|
+
getDownloadButton(index, {
|
|
88
|
+
size: 'small',
|
|
89
|
+
}) })] }) }, index));
|
|
90
|
+
}) }));
|
|
91
|
+
}
|
|
@@ -4,6 +4,7 @@ import isNil from 'lodash/isNil';
|
|
|
4
4
|
import { useCallback, useEffect, useImperativeHandle, useRef } from 'react';
|
|
5
5
|
import CreditErrorAlert from '../credit/alert';
|
|
6
6
|
import ImagePreview from '../image-preview';
|
|
7
|
+
import VideoPreview from '../video-preview';
|
|
7
8
|
import Message from './message';
|
|
8
9
|
import Prompt from './prompt';
|
|
9
10
|
export default function Conversation({ ref, messages, onSubmit, customActions = () => [], renderAvatar = undefined, maxWidth = 1000, scrollContainer = undefined, promptProps = {}, chatLayout = 'left-right', ...props }) {
|
|
@@ -26,9 +27,7 @@ export default function Conversation({ ref, messages, onSubmit, customActions =
|
|
|
26
27
|
typeof msg.response === 'object' &&
|
|
27
28
|
'images' in msg.response &&
|
|
28
29
|
Array.isArray(msg.response.images) &&
|
|
29
|
-
msg.response.images.length > 0 && (_jsxs(_Fragment, { children: [msg.response.images.some((img) => img.url && img.url
|
|
30
|
-
.filter((img) => img.url && img.url.startsWith('data:'))
|
|
31
|
-
.map(({ url }) => ({
|
|
30
|
+
msg.response.images.length > 0 && (_jsxs(_Fragment, { children: [msg.response.images.some((img) => img.url && img.url !== '[IMAGE_PLACEHOLDER]') && (_jsx(ImagePreview, { itemWidth: 200, borderRadius: 12, dataSource: msg.response.images.map(({ url }) => ({
|
|
32
31
|
src: url,
|
|
33
32
|
onLoad: () => scrollToBottom(),
|
|
34
33
|
})) })), msg.response.images.some((img) => !img.url || img.url === '[IMAGE_PLACEHOLDER]') && (_jsxs(Box, { sx: {
|
|
@@ -52,7 +51,34 @@ export default function Conversation({ ref, messages, onSubmit, customActions =
|
|
|
52
51
|
}, children: msg.response.images.filter((img) => !img.url || img.url === '[IMAGE_PLACEHOLDER]')
|
|
53
52
|
.length === 1
|
|
54
53
|
? 'Image (Not Cached)'
|
|
55
|
-
: `${msg.response.images.filter((img) => !img.url || img.url === '[IMAGE_PLACEHOLDER]').length} Images (Not Cached)` })] }))] })), msg.
|
|
54
|
+
: `${msg.response.images.filter((img) => !img.url || img.url === '[IMAGE_PLACEHOLDER]').length} Images (Not Cached)` })] }))] })), msg.response &&
|
|
55
|
+
typeof msg.response === 'object' &&
|
|
56
|
+
'videos' in msg.response &&
|
|
57
|
+
Array.isArray(msg.response.videos) &&
|
|
58
|
+
msg.response.videos.length > 0 && (_jsxs(_Fragment, { children: [msg.response.videos.some((video) => (video.data && video.data.startsWith('data:')) || video.url) && (_jsx(VideoPreview, { itemWidth: 300, borderRadius: 12, dataSource: msg.response.videos.map(({ data, url, type }) => ({
|
|
59
|
+
src: type === 'file' && data ? data : url,
|
|
60
|
+
onLoad: () => scrollToBottom(),
|
|
61
|
+
})) })), msg.response.videos.some((video) => video.data === '[VIDEO_PLACEHOLDER]') && (_jsxs(Box, { sx: {
|
|
62
|
+
margin: '8px 0',
|
|
63
|
+
minHeight: '200px',
|
|
64
|
+
background: '#f5f5f5',
|
|
65
|
+
borderRadius: '8px',
|
|
66
|
+
padding: '16px',
|
|
67
|
+
display: 'flex',
|
|
68
|
+
alignItems: 'center',
|
|
69
|
+
justifyContent: 'center',
|
|
70
|
+
flexDirection: 'column',
|
|
71
|
+
gap: '12px',
|
|
72
|
+
border: '2px dashed #ddd',
|
|
73
|
+
}, children: [_jsx(Box, { sx: { fontSize: '48px', opacity: 0.4 }, children: "\uD83D\uDDBC\uFE0F" }), _jsx(Box, { sx: {
|
|
74
|
+
fontSize: '14px',
|
|
75
|
+
color: '#666',
|
|
76
|
+
textAlign: 'center',
|
|
77
|
+
fontWeight: 500,
|
|
78
|
+
minWidth: 200,
|
|
79
|
+
}, children: msg.response.videos.filter((video) => !video.url || video.data === '[VIDEO_PLACEHOLDER]').length === 1
|
|
80
|
+
? 'Video (Not Cached)'
|
|
81
|
+
: `${msg.response.videos.filter((video) => !video.url || video.data === '[VIDEO_PLACEHOLDER]').length} Videos (Not Cached)` })] }))] })), msg.error ? (
|
|
56
82
|
// @ts-ignore
|
|
57
83
|
_jsx(CreditErrorAlert, { error: msg.error })) : (msg.loading &&
|
|
58
84
|
!msg.response && (_jsxs(Box, { sx: {
|
|
@@ -36,17 +36,26 @@ const saveMessages = async (messages) => {
|
|
|
36
36
|
.filter((msg) => !msg.loading)
|
|
37
37
|
.slice(-MAX_CACHED_MESSAGES) // Keep only last N messages
|
|
38
38
|
.map((msg) => {
|
|
39
|
-
var _a;
|
|
40
39
|
// Replace image data URLs with placeholders to save storage space
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
40
|
+
let response = msg === null || msg === void 0 ? void 0 : msg.response;
|
|
41
|
+
if (response && typeof response === 'object' && 'images' in response && response.images) {
|
|
42
|
+
response = {
|
|
43
|
+
...response,
|
|
44
|
+
images: (response.images || []).map((img) => ({
|
|
45
45
|
...img,
|
|
46
|
-
url: img.url && img.url.startsWith('data:') ? '[IMAGE_PLACEHOLDER]' : img.url,
|
|
47
|
-
}))
|
|
48
|
-
}
|
|
49
|
-
|
|
46
|
+
url: (img === null || img === void 0 ? void 0 : img.url) && img.url.startsWith('data:') ? '[IMAGE_PLACEHOLDER]' : img.url,
|
|
47
|
+
})),
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
if (response && typeof response === 'object' && 'videos' in response && response.videos) {
|
|
51
|
+
response = {
|
|
52
|
+
...response,
|
|
53
|
+
videos: (response.videos || []).map((video) => ({
|
|
54
|
+
...video,
|
|
55
|
+
data: (video === null || video === void 0 ? void 0 : video.data) && video.data.startsWith('data:') ? '[VIDEO_PLACEHOLDER]' : video.data,
|
|
56
|
+
})),
|
|
57
|
+
};
|
|
58
|
+
}
|
|
50
59
|
return {
|
|
51
60
|
id: msg.id,
|
|
52
61
|
prompt: msg.prompt,
|
|
@@ -158,6 +167,7 @@ export default function useConversation({ scrollToBottom, textCompletions, image
|
|
|
158
167
|
const result = await textCompletions(prompt, { meta });
|
|
159
168
|
const isText = (i) => i.type === 'text';
|
|
160
169
|
const isImages = (i) => i.type === 'images';
|
|
170
|
+
const isVideo = (i) => i.type === 'video';
|
|
161
171
|
const reader = result.getReader();
|
|
162
172
|
const decoder = new TextDecoder();
|
|
163
173
|
let response = '';
|
|
@@ -175,6 +185,9 @@ export default function useConversation({ scrollToBottom, textCompletions, image
|
|
|
175
185
|
else if (isImages(value)) {
|
|
176
186
|
response = { images: value.images };
|
|
177
187
|
}
|
|
188
|
+
else if (isVideo(value)) {
|
|
189
|
+
response = { videos: value.videos };
|
|
190
|
+
}
|
|
178
191
|
else {
|
|
179
192
|
delta = decoder.decode(value);
|
|
180
193
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/// <reference path="../env.d.ts" />
|
|
2
2
|
export { default as LoadingImage } from './loading-image';
|
|
3
3
|
export { default as ImagePreview } from './image-preview';
|
|
4
|
+
export { default as VideoPreview } from './video-preview';
|
|
4
5
|
export { default as Conversation } from './conversation';
|
|
5
6
|
export * from './conversation';
|
|
6
7
|
export { default as SubscribeButton } from './subscribe/button';
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import CloudDownloadOutlinedIcon from '@mui/icons-material/CloudDownloadOutlined';
|
|
3
|
+
import { Box, Grid, IconButton } from '@mui/material';
|
|
4
|
+
import { useReactive } from 'ahooks';
|
|
5
|
+
const renderIconButton = (children, onClick, { key, ...extraProps } = {}) => {
|
|
6
|
+
return (_jsx(IconButton, { sx: {
|
|
7
|
+
transition: 'all 0.3s',
|
|
8
|
+
color: 'rgba(255,255,255,0.75)',
|
|
9
|
+
'&:hover': {
|
|
10
|
+
color: 'rgba(255,255,255,1)',
|
|
11
|
+
},
|
|
12
|
+
}, onClick: onClick, ...extraProps, children: children }, key));
|
|
13
|
+
};
|
|
14
|
+
function getExtFromBase64(base64) {
|
|
15
|
+
var _a;
|
|
16
|
+
// eslint-disable-next-line prefer-regex-literals
|
|
17
|
+
const re = new RegExp('data:video/([a-z]+);base64,.+');
|
|
18
|
+
const res = re.exec(base64);
|
|
19
|
+
if ((_a = res === null || res === void 0 ? void 0 : res.groups) === null || _a === void 0 ? void 0 : _a.ext) {
|
|
20
|
+
return res.groups.ext;
|
|
21
|
+
}
|
|
22
|
+
return '';
|
|
23
|
+
}
|
|
24
|
+
export default function VideoPreview({ dataSource = [], itemWidth = undefined, itemHeight = undefined, spacing = 1, transition = 'all 0.3s', borderRadius = 8, showDownloadButton = true, }) {
|
|
25
|
+
const state = useReactive({
|
|
26
|
+
downloadingIndexMap: {},
|
|
27
|
+
});
|
|
28
|
+
const getDownloadButton = (currentIndex, extraProps = {}) => renderIconButton(_jsx(CloudDownloadOutlinedIcon, { fontSize: "inherit" }), async () => {
|
|
29
|
+
const { src } = (dataSource === null || dataSource === void 0 ? void 0 : dataSource[currentIndex]) || {};
|
|
30
|
+
state.downloadingIndexMap = {
|
|
31
|
+
...state.downloadingIndexMap,
|
|
32
|
+
[currentIndex]: true,
|
|
33
|
+
};
|
|
34
|
+
if (src) {
|
|
35
|
+
// download base64 video
|
|
36
|
+
if (src === null || src === void 0 ? void 0 : src.startsWith('data:video/')) {
|
|
37
|
+
const link = document.createElement('a');
|
|
38
|
+
link.href = src;
|
|
39
|
+
link.download = `video-${currentIndex}.${getExtFromBase64(src) || 'mp4'}`;
|
|
40
|
+
link.click();
|
|
41
|
+
}
|
|
42
|
+
state.downloadingIndexMap = {
|
|
43
|
+
...state.downloadingIndexMap,
|
|
44
|
+
[currentIndex]: false,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
}, {
|
|
48
|
+
key: 'download',
|
|
49
|
+
disabled: !!state.downloadingIndexMap[currentIndex],
|
|
50
|
+
...extraProps,
|
|
51
|
+
});
|
|
52
|
+
return (_jsx(Grid, { spacing: spacing, container: true, className: "video-wrapper", children: dataSource === null || dataSource === void 0 ? void 0 : dataSource.map((item, index) => {
|
|
53
|
+
const { width, height } = item;
|
|
54
|
+
return (_jsx(Grid
|
|
55
|
+
// eslint-disable-next-line react/no-array-index-key
|
|
56
|
+
, { className: "video-item", sx: {
|
|
57
|
+
transition,
|
|
58
|
+
'&:hover': {
|
|
59
|
+
cursor: 'pointer',
|
|
60
|
+
'& .video-toolbar': {
|
|
61
|
+
transition,
|
|
62
|
+
opacity: 1,
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
}, children: _jsxs(Box, { sx: { position: 'relative' }, children: [_jsx("video", { src: item.src, controls: true, style: {
|
|
66
|
+
transition,
|
|
67
|
+
borderRadius,
|
|
68
|
+
objectFit: 'cover',
|
|
69
|
+
width: width || itemWidth || '100%',
|
|
70
|
+
height: height || itemHeight || 'auto',
|
|
71
|
+
backgroundColor: '#000',
|
|
72
|
+
maxWidth: '400px',
|
|
73
|
+
}, preload: "metadata", children: _jsx("track", { kind: "captions" }) }), _jsx(Box, { className: "video-toolbar", sx: {
|
|
74
|
+
position: 'absolute',
|
|
75
|
+
right: 8,
|
|
76
|
+
top: 8,
|
|
77
|
+
opacity: 0,
|
|
78
|
+
background: 'rgba(0,0,0,0.7)',
|
|
79
|
+
borderRadius: '4px',
|
|
80
|
+
}, children: showDownloadButton &&
|
|
81
|
+
getDownloadButton(index, {
|
|
82
|
+
size: 'small',
|
|
83
|
+
}) })] }) }, index));
|
|
84
|
+
}) }));
|
|
85
|
+
}
|
|
@@ -2,12 +2,24 @@ import { BoxProps } from '@mui/material';
|
|
|
2
2
|
import { ChatCompletionMessageParam } from 'openai/resources/index';
|
|
3
3
|
import { ReactNode } from 'react';
|
|
4
4
|
import { PromptProps } from './prompt';
|
|
5
|
+
export interface VideoResponse {
|
|
6
|
+
data?: string;
|
|
7
|
+
path?: string;
|
|
8
|
+
type?: string;
|
|
9
|
+
url?: string;
|
|
10
|
+
}
|
|
5
11
|
export interface MessageItem {
|
|
6
12
|
id: string;
|
|
7
13
|
prompt?: string | ChatCompletionMessageParam[];
|
|
8
14
|
response?: string | {
|
|
9
15
|
url: string;
|
|
10
|
-
}[]
|
|
16
|
+
}[] | {
|
|
17
|
+
videos: VideoResponse[];
|
|
18
|
+
} | {
|
|
19
|
+
images: {
|
|
20
|
+
url?: string;
|
|
21
|
+
}[];
|
|
22
|
+
};
|
|
11
23
|
loading?: boolean;
|
|
12
24
|
error?: {
|
|
13
25
|
message: string;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ChatCompletionMessageParam } from 'openai/resources/index';
|
|
2
|
-
import { MessageItem } from './conversation';
|
|
2
|
+
import type { MessageItem, VideoResponse } from './conversation';
|
|
3
3
|
export default function useConversation({ scrollToBottom, textCompletions, imageGenerations, enableCache, }: {
|
|
4
4
|
scrollToBottom?: (options?: {
|
|
5
5
|
force?: boolean;
|
|
@@ -14,6 +14,9 @@ export default function useConversation({ scrollToBottom, textCompletions, image
|
|
|
14
14
|
images: {
|
|
15
15
|
url: string;
|
|
16
16
|
}[];
|
|
17
|
+
} | {
|
|
18
|
+
type: 'video';
|
|
19
|
+
videos: VideoResponse[];
|
|
17
20
|
}>>;
|
|
18
21
|
imageGenerations?: (prompt: {
|
|
19
22
|
prompt: string;
|
|
@@ -36,8 +39,12 @@ export default function useConversation({ scrollToBottom, textCompletions, image
|
|
|
36
39
|
} | {
|
|
37
40
|
id: string;
|
|
38
41
|
text: string | {
|
|
39
|
-
|
|
40
|
-
}
|
|
42
|
+
videos: VideoResponse[];
|
|
43
|
+
} | {
|
|
44
|
+
images: {
|
|
45
|
+
url?: string;
|
|
46
|
+
}[];
|
|
47
|
+
};
|
|
41
48
|
data?: undefined;
|
|
42
49
|
} | null>;
|
|
43
50
|
cancel: ({ id }: Pick<MessageItem, "id">) => void;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export { default as LoadingImage } from './loading-image';
|
|
2
2
|
export { default as ImagePreview } from './image-preview';
|
|
3
|
+
export { default as VideoPreview } from './video-preview';
|
|
3
4
|
export { default as Conversation } from './conversation';
|
|
4
5
|
export * from './conversation';
|
|
5
6
|
export { default as SubscribeButton } from './subscribe/button';
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
interface VideoPreviewProps {
|
|
2
|
+
dataSource?: Array<{
|
|
3
|
+
src: string;
|
|
4
|
+
alt?: string;
|
|
5
|
+
width?: number;
|
|
6
|
+
height?: number;
|
|
7
|
+
}>;
|
|
8
|
+
spacing?: number;
|
|
9
|
+
itemWidth?: number;
|
|
10
|
+
itemHeight?: number;
|
|
11
|
+
transition?: string;
|
|
12
|
+
borderRadius?: number;
|
|
13
|
+
showDownloadButton?: boolean;
|
|
14
|
+
}
|
|
15
|
+
export default function VideoPreview({ dataSource, itemWidth, itemHeight, spacing, transition, borderRadius, showDownloadButton, }: VideoPreviewProps): import("react/jsx-runtime").JSX.Element;
|
|
16
|
+
export {};
|