@midscene/visualizer 0.28.10-beta-20250922071252.0 → 0.28.11-beta-20250919104516.0
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/es/component/player/index.mjs +38 -15
- package/dist/es/component/universal-playground/index.css +1 -1
- package/dist/es/component/universal-playground/index.mjs +29 -5
- package/dist/es/component/universal-playground/providers/indexeddb-storage-provider.mjs +207 -0
- package/dist/es/component/universal-playground/providers/storage-provider.mjs +42 -3
- package/dist/es/hooks/usePlaygroundState.mjs +28 -3
- package/dist/es/index.mjs +2 -2
- package/dist/es/utils/replay-scripts.mjs +59 -32
- package/dist/lib/component/player/index.js +38 -15
- package/dist/lib/component/universal-playground/index.css +1 -1
- package/dist/lib/component/universal-playground/index.js +27 -3
- package/dist/lib/component/universal-playground/providers/indexeddb-storage-provider.js +247 -0
- package/dist/lib/component/universal-playground/providers/storage-provider.js +60 -3
- package/dist/lib/hooks/usePlaygroundState.js +28 -3
- package/dist/lib/index.js +14 -2
- package/dist/lib/utils/replay-scripts.js +59 -32
- package/dist/types/component/universal-playground/providers/indexeddb-storage-provider.d.ts +71 -0
- package/dist/types/component/universal-playground/providers/storage-provider.d.ts +16 -0
- package/dist/types/hooks/usePlaygroundExecution.d.ts +1 -1
- package/dist/types/hooks/usePlaygroundState.d.ts +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/utils/replay-scripts.d.ts +2 -0
- package/package.json +5 -5
|
@@ -172,9 +172,29 @@ function Player(props) {
|
|
|
172
172
|
const cameraState = useRef({
|
|
173
173
|
...basicCameraState
|
|
174
174
|
});
|
|
175
|
-
const
|
|
175
|
+
const resizeCanvasIfNeeded = async (newWidth, newHeight)=>{
|
|
176
|
+
if (app.screen.width !== newWidth || app.screen.height !== newHeight) {
|
|
177
|
+
app.renderer.resize(newWidth, newHeight);
|
|
178
|
+
if (divContainerRef.current) {
|
|
179
|
+
const aspectRatio = newWidth / newHeight;
|
|
180
|
+
divContainerRef.current.style.setProperty('--canvas-aspect-ratio', aspectRatio.toString());
|
|
181
|
+
}
|
|
182
|
+
const newBasicCameraState = {
|
|
183
|
+
left: 0,
|
|
184
|
+
top: 0,
|
|
185
|
+
width: newWidth,
|
|
186
|
+
pointerLeft: Math.round(newWidth / 2),
|
|
187
|
+
pointerTop: Math.round(newHeight / 2)
|
|
188
|
+
};
|
|
189
|
+
cameraState.current = newBasicCameraState;
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
const repaintImage = async (scriptWidth, scriptHeight)=>{
|
|
176
193
|
const imgToUpdate = currentImg.current;
|
|
177
194
|
if (!imgToUpdate) return void console.warn('no image to update');
|
|
195
|
+
const targetWidth = scriptWidth || imageWidth;
|
|
196
|
+
const targetHeight = scriptHeight || imageHeight;
|
|
197
|
+
await resizeCanvasIfNeeded(targetWidth, targetHeight);
|
|
178
198
|
if (!getTextureFromCache(imgToUpdate)) {
|
|
179
199
|
console.warn('image not loaded', imgToUpdate);
|
|
180
200
|
await loadTexture(imgToUpdate);
|
|
@@ -188,8 +208,8 @@ function Player(props) {
|
|
|
188
208
|
if (child) windowContentContainer.removeChild(child);
|
|
189
209
|
sprite.label = mainImgLabel;
|
|
190
210
|
sprite.zIndex = LAYER_ORDER_IMG;
|
|
191
|
-
sprite.width =
|
|
192
|
-
sprite.height =
|
|
211
|
+
sprite.width = targetWidth;
|
|
212
|
+
sprite.height = targetHeight;
|
|
193
213
|
windowContentContainer.addChild(sprite);
|
|
194
214
|
};
|
|
195
215
|
const spinningPointer = (frame)=>{
|
|
@@ -247,9 +267,10 @@ function Player(props) {
|
|
|
247
267
|
pointerSprite.current.zIndex = LAYER_ORDER_POINTER;
|
|
248
268
|
windowContentContainer.addChild(pointerSprite.current);
|
|
249
269
|
};
|
|
250
|
-
const updateCamera = (state)=>{
|
|
270
|
+
const updateCamera = (state, currentWidth)=>{
|
|
251
271
|
cameraState.current = state;
|
|
252
|
-
const
|
|
272
|
+
const effectiveWidth = currentWidth || app.screen.width || imageWidth;
|
|
273
|
+
const newScale = autoZoom ? Math.max(1, effectiveWidth / state.width) : 1;
|
|
253
274
|
windowContentContainer.scale.set(newScale);
|
|
254
275
|
windowContentContainer.x = autoZoom ? Math.round(canvasPaddingLeft - state.left * newScale) : canvasPaddingLeft;
|
|
255
276
|
windowContentContainer.y = autoZoom ? Math.round(canvasPaddingTop - state.top * newScale) : canvasPaddingTop;
|
|
@@ -263,6 +284,8 @@ function Player(props) {
|
|
|
263
284
|
}
|
|
264
285
|
};
|
|
265
286
|
const cameraAnimation = async (targetState, duration, frame)=>{
|
|
287
|
+
const currentCanvasWidth = app.screen.width || imageWidth;
|
|
288
|
+
const currentCanvasHeight = app.screen.height || imageHeight;
|
|
266
289
|
if (!autoZoom) {
|
|
267
290
|
const currentState = {
|
|
268
291
|
...cameraState.current
|
|
@@ -282,7 +305,7 @@ function Player(props) {
|
|
|
282
305
|
pointerLeft: startPointerLeft + (targetState.pointerLeft - startPointerLeft) * progress,
|
|
283
306
|
pointerTop: startPointerTop + (targetState.pointerTop - startPointerTop) * progress
|
|
284
307
|
};
|
|
285
|
-
updateCamera(nextState);
|
|
308
|
+
updateCamera(nextState, currentCanvasWidth);
|
|
286
309
|
if (elapsedTime < duration) frame(animate);
|
|
287
310
|
else resolve();
|
|
288
311
|
};
|
|
@@ -297,7 +320,7 @@ function Player(props) {
|
|
|
297
320
|
const startTop = currentState.top;
|
|
298
321
|
const startPointerLeft = currentState.pointerLeft;
|
|
299
322
|
const startPointerTop = currentState.pointerTop;
|
|
300
|
-
const startScale = currentState.width /
|
|
323
|
+
const startScale = currentState.width / currentCanvasWidth;
|
|
301
324
|
const startTime = performance.now();
|
|
302
325
|
const shouldMovePointer = 'number' == typeof targetState.pointerLeft && 'number' == typeof targetState.pointerTop && (targetState.pointerLeft !== startPointerLeft || targetState.pointerTop !== startPointerTop);
|
|
303
326
|
const pointerMoveDuration = shouldMovePointer ? 0.375 * duration : 0;
|
|
@@ -322,19 +345,19 @@ function Player(props) {
|
|
|
322
345
|
const cameraElapsedTime = elapsedTime - cameraMoveStart;
|
|
323
346
|
const rawCameraProgress = Math.min(cameraElapsedTime / cameraMoveDuration, 1);
|
|
324
347
|
const cameraProgress = cubicImage(rawCameraProgress);
|
|
325
|
-
const targetScale = targetState.width /
|
|
348
|
+
const targetScale = targetState.width / currentCanvasWidth;
|
|
326
349
|
const progressScale = startScale + (targetScale - startScale) * cameraProgress;
|
|
327
|
-
const progressWidth =
|
|
328
|
-
const progressHeight =
|
|
350
|
+
const progressWidth = currentCanvasWidth * progressScale;
|
|
351
|
+
const progressHeight = currentCanvasHeight * progressScale;
|
|
329
352
|
nextState.width = progressWidth;
|
|
330
353
|
const progressLeft = startLeft + (targetState.left - startLeft) * cameraProgress;
|
|
331
354
|
const progressTop = startTop + (targetState.top - startTop) * cameraProgress;
|
|
332
|
-
const horizontalExceed = progressLeft + progressWidth -
|
|
333
|
-
const verticalExceed = progressTop + progressHeight -
|
|
355
|
+
const horizontalExceed = progressLeft + progressWidth - currentCanvasWidth;
|
|
356
|
+
const verticalExceed = progressTop + progressHeight - currentCanvasHeight;
|
|
334
357
|
nextState.left = horizontalExceed > 0 ? progressLeft + horizontalExceed : progressLeft;
|
|
335
358
|
nextState.top = verticalExceed > 0 ? progressTop + verticalExceed : progressTop;
|
|
336
359
|
}
|
|
337
|
-
updateCamera(nextState);
|
|
360
|
+
updateCamera(nextState, currentCanvasWidth);
|
|
338
361
|
if (elapsedTime < duration) frame(animate);
|
|
339
362
|
else resolve();
|
|
340
363
|
};
|
|
@@ -471,7 +494,7 @@ function Player(props) {
|
|
|
471
494
|
var _item_context;
|
|
472
495
|
if (!item.img) throw new Error('img is required');
|
|
473
496
|
currentImg.current = item.img;
|
|
474
|
-
await repaintImage();
|
|
497
|
+
await repaintImage(item.imageWidth, item.imageHeight);
|
|
475
498
|
const elements = (null == (_item_context = item.context) ? void 0 : _item_context.tree) ? treeToList(item.context.tree) : [];
|
|
476
499
|
const highlightElements = item.highlightElement ? [
|
|
477
500
|
item.highlightElement
|
|
@@ -488,7 +511,7 @@ function Player(props) {
|
|
|
488
511
|
} else if ('img' === item.type) {
|
|
489
512
|
if (item.img && item.img !== currentImg.current) {
|
|
490
513
|
currentImg.current = item.img;
|
|
491
|
-
await repaintImage();
|
|
514
|
+
await repaintImage(item.imageWidth, item.imageHeight);
|
|
492
515
|
}
|
|
493
516
|
if (item.camera) await cameraAnimation(item.camera, item.duration, frame);
|
|
494
517
|
else await sleep(item.duration);
|
|
@@ -91,10 +91,10 @@
|
|
|
91
91
|
.playground-container .middle-dialog-area .scroll-to-bottom-button {
|
|
92
92
|
z-index: 10;
|
|
93
93
|
background: #fff;
|
|
94
|
+
border: 1px solid rgba(0, 0, 0, .08);
|
|
94
95
|
position: absolute;
|
|
95
96
|
bottom: 10px;
|
|
96
97
|
right: 0;
|
|
97
|
-
box-shadow: 0 4px 8px rgba(0, 0, 0, .04);
|
|
98
98
|
}
|
|
99
99
|
|
|
100
100
|
.playground-container .middle-dialog-area .scroll-to-bottom-button:hover {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
2
2
|
import icons, { ArrowDownOutlined, ClearOutlined, LoadingOutlined } from "@ant-design/icons";
|
|
3
3
|
import { Button, Form, List, Tooltip, Typography, message } from "antd";
|
|
4
|
-
import { useCallback, useEffect } from "react";
|
|
4
|
+
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
5
5
|
import { usePlaygroundExecution } from "../../hooks/usePlaygroundExecution.mjs";
|
|
6
6
|
import { usePlaygroundState } from "../../hooks/usePlaygroundState.mjs";
|
|
7
7
|
import { useEnvConfig } from "../../store/store.mjs";
|
|
@@ -11,7 +11,7 @@ import { PlaygroundResultView } from "../playground-result/index.mjs";
|
|
|
11
11
|
import "./index.css";
|
|
12
12
|
import avatar from "../../icons/avatar.mjs";
|
|
13
13
|
import { PromptInput } from "../prompt-input/index.mjs";
|
|
14
|
-
import {
|
|
14
|
+
import { createStorageProvider, detectBestStorageType } from "./providers/storage-provider.mjs";
|
|
15
15
|
const { Text } = Typography;
|
|
16
16
|
function getSDKId(sdk) {
|
|
17
17
|
if (sdk.id && 'string' == typeof sdk.id) return `agent-${sdk.id}`;
|
|
@@ -41,11 +41,35 @@ function UniversalPlayground(param) {
|
|
|
41
41
|
let { playgroundSDK, storage, contextProvider, config: componentConfig = {}, branding = {}, className = '', dryMode = false, showContextPreview = true } = param;
|
|
42
42
|
const [form] = Form.useForm();
|
|
43
43
|
const { config } = useEnvConfig();
|
|
44
|
-
const
|
|
44
|
+
const [sdkReady, setSdkReady] = useState(false);
|
|
45
|
+
useEffect(()=>{
|
|
46
|
+
const initializeSDK = async ()=>{
|
|
47
|
+
if (playgroundSDK && 'function' == typeof playgroundSDK.checkStatus) try {
|
|
48
|
+
await playgroundSDK.checkStatus();
|
|
49
|
+
setSdkReady(true);
|
|
50
|
+
} catch (error) {
|
|
51
|
+
console.warn('Failed to initialize SDK, using default namespace:', error);
|
|
52
|
+
setSdkReady(true);
|
|
53
|
+
}
|
|
54
|
+
else setSdkReady(true);
|
|
55
|
+
};
|
|
56
|
+
initializeSDK();
|
|
57
|
+
}, [
|
|
58
|
+
playgroundSDK
|
|
59
|
+
]);
|
|
60
|
+
const effectiveStorage = useMemo(()=>{
|
|
45
61
|
if (storage) return storage;
|
|
62
|
+
if (!sdkReady) return null;
|
|
46
63
|
const namespace = componentConfig.storageNamespace || getSDKId(playgroundSDK);
|
|
47
|
-
|
|
48
|
-
|
|
64
|
+
const bestStorageType = detectBestStorageType();
|
|
65
|
+
console.log(`Using ${bestStorageType} storage for namespace: ${namespace}`);
|
|
66
|
+
return createStorageProvider(bestStorageType, namespace);
|
|
67
|
+
}, [
|
|
68
|
+
storage,
|
|
69
|
+
sdkReady,
|
|
70
|
+
componentConfig.storageNamespace,
|
|
71
|
+
playgroundSDK
|
|
72
|
+
]);
|
|
49
73
|
const { loading, setLoading, infoList, setInfoList, actionSpace, actionSpaceLoading, uiContextPreview, setUiContextPreview, showScrollToBottomButton, verticalMode, replayCounter, setReplayCounter, infoListRef, currentRunningIdRef, interruptedFlagRef, clearInfoList, handleScrollToBottom } = usePlaygroundState(playgroundSDK, effectiveStorage, contextProvider);
|
|
50
74
|
const { handleRun: executeAction, handleStop, canStop } = usePlaygroundExecution(playgroundSDK, effectiveStorage, actionSpace, loading, setLoading, infoList, setInfoList, replayCounter, setReplayCounter, verticalMode, currentRunningIdRef, interruptedFlagRef);
|
|
51
75
|
useEffect(()=>{
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
import { IndexedDBManager, createCleanupFunction, withErrorHandling } from "@midscene/shared/baseDB";
|
|
2
|
+
function _define_property(obj, key, value) {
|
|
3
|
+
if (key in obj) Object.defineProperty(obj, key, {
|
|
4
|
+
value: value,
|
|
5
|
+
enumerable: true,
|
|
6
|
+
configurable: true,
|
|
7
|
+
writable: true
|
|
8
|
+
});
|
|
9
|
+
else obj[key] = value;
|
|
10
|
+
return obj;
|
|
11
|
+
}
|
|
12
|
+
const DB_NAME = 'midscene_playground';
|
|
13
|
+
const DB_VERSION = 1;
|
|
14
|
+
const MESSAGES_STORE = 'playground_messages';
|
|
15
|
+
const RESULTS_STORE = 'playground_results';
|
|
16
|
+
const MAX_STORED_MESSAGES = 100;
|
|
17
|
+
const MAX_STORED_RESULTS = 50;
|
|
18
|
+
class IndexedDBStorageProvider {
|
|
19
|
+
async saveMessages(messages) {
|
|
20
|
+
await withErrorHandling(async ()=>{
|
|
21
|
+
await this.dbManager.clear(MESSAGES_STORE);
|
|
22
|
+
const messagesToSave = messages.slice(-MAX_STORED_MESSAGES);
|
|
23
|
+
await Promise.all(messagesToSave.map((msg, index)=>{
|
|
24
|
+
const lightMessage = {
|
|
25
|
+
...msg,
|
|
26
|
+
result: void 0
|
|
27
|
+
};
|
|
28
|
+
const data = {
|
|
29
|
+
id: msg.id || `msg-${index}`,
|
|
30
|
+
data: lightMessage,
|
|
31
|
+
timestamp: msg.timestamp ? msg.timestamp.getTime() : Date.now() + index
|
|
32
|
+
};
|
|
33
|
+
return this.dbManager.put(MESSAGES_STORE, data);
|
|
34
|
+
}));
|
|
35
|
+
}, 'Failed to save messages to IndexedDB', void 0, this.messagesCleanup);
|
|
36
|
+
}
|
|
37
|
+
async loadMessages() {
|
|
38
|
+
const result = await withErrorHandling(async ()=>{
|
|
39
|
+
const messages = await this.dbManager.getAll(MESSAGES_STORE, true);
|
|
40
|
+
if (0 === messages.length) return [];
|
|
41
|
+
return Promise.all(messages.map(async (msg)=>{
|
|
42
|
+
const item = msg.data;
|
|
43
|
+
const restoredItem = {
|
|
44
|
+
...item,
|
|
45
|
+
timestamp: new Date(item.timestamp)
|
|
46
|
+
};
|
|
47
|
+
if ('result' === item.type && item.id) {
|
|
48
|
+
const fullResult = await this.loadResult(item.id);
|
|
49
|
+
if (fullResult) {
|
|
50
|
+
restoredItem.result = fullResult.result;
|
|
51
|
+
restoredItem.replayScriptsInfo = fullResult.replayScriptsInfo;
|
|
52
|
+
restoredItem.replayCounter = fullResult.replayCounter;
|
|
53
|
+
restoredItem.verticalMode = fullResult.verticalMode;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return restoredItem;
|
|
57
|
+
}));
|
|
58
|
+
}, 'Failed to load messages from IndexedDB', [], this.messagesCleanup);
|
|
59
|
+
return result || [];
|
|
60
|
+
}
|
|
61
|
+
async clearMessages() {
|
|
62
|
+
await withErrorHandling(async ()=>{
|
|
63
|
+
await Promise.all([
|
|
64
|
+
this.dbManager.clear(MESSAGES_STORE),
|
|
65
|
+
this.dbManager.clear(RESULTS_STORE)
|
|
66
|
+
]);
|
|
67
|
+
}, 'Failed to clear messages from IndexedDB');
|
|
68
|
+
}
|
|
69
|
+
async saveResult(id, result) {
|
|
70
|
+
await withErrorHandling(async ()=>{
|
|
71
|
+
const compressedResult = this.compressResultForStorage(result);
|
|
72
|
+
const data = {
|
|
73
|
+
id,
|
|
74
|
+
data: compressedResult,
|
|
75
|
+
timestamp: Date.now(),
|
|
76
|
+
size: JSON.stringify(compressedResult).length
|
|
77
|
+
};
|
|
78
|
+
await this.dbManager.put(RESULTS_STORE, data);
|
|
79
|
+
}, 'Failed to save result to IndexedDB', void 0, this.resultsCleanup);
|
|
80
|
+
}
|
|
81
|
+
async loadResult(id) {
|
|
82
|
+
const result = await withErrorHandling(async ()=>{
|
|
83
|
+
const data = await this.dbManager.get(RESULTS_STORE, id);
|
|
84
|
+
return (null == data ? void 0 : data.data) || null;
|
|
85
|
+
}, 'Failed to load result from IndexedDB', null);
|
|
86
|
+
return result || null;
|
|
87
|
+
}
|
|
88
|
+
compressResultForStorage(result) {
|
|
89
|
+
var _result_result_dump, _result_result;
|
|
90
|
+
if (!(null == (_result_result = result.result) ? void 0 : null == (_result_result_dump = _result_result.dump) ? void 0 : _result_result_dump.executions)) return result;
|
|
91
|
+
const compressedExecutions = result.result.dump.executions.map((execution)=>{
|
|
92
|
+
var _execution_tasks;
|
|
93
|
+
return {
|
|
94
|
+
...execution,
|
|
95
|
+
tasks: (null == (_execution_tasks = execution.tasks) ? void 0 : _execution_tasks.map((task)=>{
|
|
96
|
+
var _task_recorder;
|
|
97
|
+
var _this_compressScreenshotIfNeeded;
|
|
98
|
+
return {
|
|
99
|
+
...task,
|
|
100
|
+
uiContext: task.uiContext ? {
|
|
101
|
+
...task.uiContext,
|
|
102
|
+
screenshotBase64: null != (_this_compressScreenshotIfNeeded = this.compressScreenshotIfNeeded(task.uiContext.screenshotBase64)) ? _this_compressScreenshotIfNeeded : task.uiContext.screenshotBase64
|
|
103
|
+
} : task.uiContext,
|
|
104
|
+
recorder: null == (_task_recorder = task.recorder) ? void 0 : _task_recorder.map((record)=>({
|
|
105
|
+
...record,
|
|
106
|
+
screenshot: this.compressScreenshotIfNeeded(record.screenshot)
|
|
107
|
+
}))
|
|
108
|
+
};
|
|
109
|
+
})) || []
|
|
110
|
+
};
|
|
111
|
+
});
|
|
112
|
+
return {
|
|
113
|
+
...result,
|
|
114
|
+
result: {
|
|
115
|
+
...result.result,
|
|
116
|
+
dump: {
|
|
117
|
+
...result.result.dump,
|
|
118
|
+
executions: compressedExecutions
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
compressScreenshotIfNeeded(screenshot) {
|
|
124
|
+
if (!screenshot) return screenshot;
|
|
125
|
+
if (screenshot.length > 1048576) {
|
|
126
|
+
const sizeKB = Math.round(screenshot.length / 1024);
|
|
127
|
+
return `[COMPRESSED: ${sizeKB}KB screenshot removed for storage]`;
|
|
128
|
+
}
|
|
129
|
+
return screenshot;
|
|
130
|
+
}
|
|
131
|
+
async getStorageStats() {
|
|
132
|
+
const result = await withErrorHandling(async ()=>{
|
|
133
|
+
const [messageCount, resultCount] = await Promise.all([
|
|
134
|
+
this.dbManager.count(MESSAGES_STORE),
|
|
135
|
+
this.dbManager.count(RESULTS_STORE)
|
|
136
|
+
]);
|
|
137
|
+
return {
|
|
138
|
+
messageCount,
|
|
139
|
+
resultCount
|
|
140
|
+
};
|
|
141
|
+
}, 'Failed to get storage statistics', {
|
|
142
|
+
messageCount: 0,
|
|
143
|
+
resultCount: 0
|
|
144
|
+
});
|
|
145
|
+
return result || {
|
|
146
|
+
messageCount: 0,
|
|
147
|
+
resultCount: 0
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
async cleanup() {
|
|
151
|
+
await Promise.all([
|
|
152
|
+
this.messagesCleanup(),
|
|
153
|
+
this.resultsCleanup()
|
|
154
|
+
]);
|
|
155
|
+
}
|
|
156
|
+
constructor(namespace = 'playground'){
|
|
157
|
+
_define_property(this, "dbManager", void 0);
|
|
158
|
+
_define_property(this, "namespace", void 0);
|
|
159
|
+
_define_property(this, "messagesCleanup", void 0);
|
|
160
|
+
_define_property(this, "resultsCleanup", void 0);
|
|
161
|
+
this.namespace = namespace;
|
|
162
|
+
this.dbManager = new IndexedDBManager(`${DB_NAME}_${namespace}`, DB_VERSION, [
|
|
163
|
+
{
|
|
164
|
+
name: MESSAGES_STORE,
|
|
165
|
+
keyPath: 'id'
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
name: RESULTS_STORE,
|
|
169
|
+
keyPath: 'id'
|
|
170
|
+
}
|
|
171
|
+
]);
|
|
172
|
+
this.messagesCleanup = createCleanupFunction(this.dbManager, MESSAGES_STORE, MAX_STORED_MESSAGES);
|
|
173
|
+
this.resultsCleanup = createCleanupFunction(this.dbManager, RESULTS_STORE, MAX_STORED_RESULTS);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
class MemoryStorageProvider {
|
|
177
|
+
async saveMessages(messages) {
|
|
178
|
+
this.messages = [
|
|
179
|
+
...messages
|
|
180
|
+
];
|
|
181
|
+
}
|
|
182
|
+
async loadMessages() {
|
|
183
|
+
return [
|
|
184
|
+
...this.messages
|
|
185
|
+
];
|
|
186
|
+
}
|
|
187
|
+
async clearMessages() {
|
|
188
|
+
this.messages = [];
|
|
189
|
+
this.results.clear();
|
|
190
|
+
}
|
|
191
|
+
async saveResult(id, result) {
|
|
192
|
+
this.results.set(id, result);
|
|
193
|
+
}
|
|
194
|
+
constructor(){
|
|
195
|
+
_define_property(this, "messages", []);
|
|
196
|
+
_define_property(this, "results", new Map());
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
class NoOpStorageProvider {
|
|
200
|
+
async saveMessages(_messages) {}
|
|
201
|
+
async loadMessages() {
|
|
202
|
+
return [];
|
|
203
|
+
}
|
|
204
|
+
async clearMessages() {}
|
|
205
|
+
async saveResult(_id, _result) {}
|
|
206
|
+
}
|
|
207
|
+
export { IndexedDBStorageProvider, MemoryStorageProvider, NoOpStorageProvider };
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { IndexedDBStorageProvider, MemoryStorageProvider, NoOpStorageProvider } from "./indexeddb-storage-provider.mjs";
|
|
1
2
|
function _define_property(obj, key, value) {
|
|
2
3
|
if (key in obj) Object.defineProperty(obj, key, {
|
|
3
4
|
value: value,
|
|
@@ -137,7 +138,7 @@ class LocalStorageProvider {
|
|
|
137
138
|
this.resultsKey = `${namespace}-results`;
|
|
138
139
|
}
|
|
139
140
|
}
|
|
140
|
-
class
|
|
141
|
+
class storage_provider_MemoryStorageProvider {
|
|
141
142
|
async saveMessages(messages) {
|
|
142
143
|
this.messages = [
|
|
143
144
|
...messages
|
|
@@ -160,7 +161,7 @@ class MemoryStorageProvider {
|
|
|
160
161
|
_define_property(this, "results", new Map());
|
|
161
162
|
}
|
|
162
163
|
}
|
|
163
|
-
class
|
|
164
|
+
class storage_provider_NoOpStorageProvider {
|
|
164
165
|
async saveMessages(_messages) {}
|
|
165
166
|
async loadMessages() {
|
|
166
167
|
return [];
|
|
@@ -168,4 +169,42 @@ class NoOpStorageProvider {
|
|
|
168
169
|
async clearMessages() {}
|
|
169
170
|
async saveResult(_id, _result) {}
|
|
170
171
|
}
|
|
171
|
-
|
|
172
|
+
var storage_provider_StorageType = /*#__PURE__*/ function(StorageType) {
|
|
173
|
+
StorageType["INDEXEDDB"] = "indexeddb";
|
|
174
|
+
StorageType["LOCALSTORAGE"] = "localStorage";
|
|
175
|
+
StorageType["MEMORY"] = "memory";
|
|
176
|
+
StorageType["NONE"] = "none";
|
|
177
|
+
return StorageType;
|
|
178
|
+
}({});
|
|
179
|
+
function createStorageProvider() {
|
|
180
|
+
let type = arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : "indexeddb", namespace = arguments.length > 1 && void 0 !== arguments[1] ? arguments[1] : 'playground';
|
|
181
|
+
switch(type){
|
|
182
|
+
case "indexeddb":
|
|
183
|
+
if ('undefined' != typeof indexedDB) return new IndexedDBStorageProvider(namespace);
|
|
184
|
+
console.warn('IndexedDB not available, falling back to localStorage');
|
|
185
|
+
return createStorageProvider("localStorage", namespace);
|
|
186
|
+
case "localStorage":
|
|
187
|
+
if ('undefined' != typeof localStorage) return new LocalStorageProvider(namespace);
|
|
188
|
+
console.warn('localStorage not available, falling back to memory storage');
|
|
189
|
+
return createStorageProvider("memory", namespace);
|
|
190
|
+
case "memory":
|
|
191
|
+
return new storage_provider_MemoryStorageProvider();
|
|
192
|
+
case "none":
|
|
193
|
+
return new storage_provider_NoOpStorageProvider();
|
|
194
|
+
default:
|
|
195
|
+
throw new Error(`Unknown storage type: ${type}`);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
function detectBestStorageType() {
|
|
199
|
+
if ('undefined' != typeof indexedDB) try {
|
|
200
|
+
indexedDB.open('test', 1).onerror = ()=>{};
|
|
201
|
+
return "indexeddb";
|
|
202
|
+
} catch (e) {}
|
|
203
|
+
if ('undefined' != typeof localStorage) try {
|
|
204
|
+
localStorage.setItem('test', 'test');
|
|
205
|
+
localStorage.removeItem('test');
|
|
206
|
+
return "localStorage";
|
|
207
|
+
} catch (e) {}
|
|
208
|
+
return "memory";
|
|
209
|
+
}
|
|
210
|
+
export { MemoryStorageProvider as IndexedDBMemoryStorageProvider, NoOpStorageProvider as IndexedDBNoOpStorageProvider, IndexedDBStorageProvider, LocalStorageProvider, storage_provider_MemoryStorageProvider as MemoryStorageProvider, storage_provider_NoOpStorageProvider as NoOpStorageProvider, storage_provider_StorageType as StorageType, createStorageProvider, detectBestStorageType };
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { useCallback, useEffect, useRef, useState } from "react";
|
|
2
|
+
import { createStorageProvider, detectBestStorageType } from "../component/universal-playground/providers/storage-provider.mjs";
|
|
2
3
|
import { WELCOME_MESSAGE_TEMPLATE } from "../utils/constants.mjs";
|
|
3
4
|
function usePlaygroundState(playgroundSDK, storage, contextProvider) {
|
|
4
5
|
const [loading, setLoading] = useState(false);
|
|
@@ -12,7 +13,25 @@ function usePlaygroundState(playgroundSDK, storage, contextProvider) {
|
|
|
12
13
|
const infoListRef = useRef(null);
|
|
13
14
|
const currentRunningIdRef = useRef(null);
|
|
14
15
|
const interruptedFlagRef = useRef({});
|
|
16
|
+
const initializedRef = useRef(false);
|
|
15
17
|
useEffect(()=>{
|
|
18
|
+
const migrateFromOldNamespace = async ()=>{
|
|
19
|
+
const oldStorage = createStorageProvider(detectBestStorageType(), 'playground-default');
|
|
20
|
+
try {
|
|
21
|
+
if (null == oldStorage ? void 0 : oldStorage.loadMessages) {
|
|
22
|
+
const oldMessages = await oldStorage.loadMessages();
|
|
23
|
+
if (oldMessages.length > 1) {
|
|
24
|
+
console.log('Found data in old namespace, migrating...');
|
|
25
|
+
if (null == storage ? void 0 : storage.saveMessages) await storage.saveMessages(oldMessages);
|
|
26
|
+
if (oldStorage.clearMessages) await oldStorage.clearMessages();
|
|
27
|
+
return oldMessages;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
} catch (error) {
|
|
31
|
+
console.debug('No data found in old namespace:', error);
|
|
32
|
+
}
|
|
33
|
+
return [];
|
|
34
|
+
};
|
|
16
35
|
const initializeMessages = async ()=>{
|
|
17
36
|
const welcomeMessage = {
|
|
18
37
|
...WELCOME_MESSAGE_TEMPLATE,
|
|
@@ -20,7 +39,8 @@ function usePlaygroundState(playgroundSDK, storage, contextProvider) {
|
|
|
20
39
|
timestamp: new Date()
|
|
21
40
|
};
|
|
22
41
|
if (null == storage ? void 0 : storage.loadMessages) try {
|
|
23
|
-
|
|
42
|
+
let storedMessages = await storage.loadMessages();
|
|
43
|
+
if (0 === storedMessages.length) storedMessages = await migrateFromOldNamespace();
|
|
24
44
|
const hasWelcomeMessage = storedMessages.some((msg)=>'welcome' === msg.id);
|
|
25
45
|
hasWelcomeMessage ? setInfoList(storedMessages) : setInfoList([
|
|
26
46
|
welcomeMessage,
|
|
@@ -36,8 +56,13 @@ function usePlaygroundState(playgroundSDK, storage, contextProvider) {
|
|
|
36
56
|
welcomeMessage
|
|
37
57
|
]);
|
|
38
58
|
};
|
|
39
|
-
if (
|
|
40
|
-
|
|
59
|
+
if (storage && !initializedRef.current) {
|
|
60
|
+
initializedRef.current = true;
|
|
61
|
+
initializeMessages();
|
|
62
|
+
} else if (!storage && 0 === infoList.length) initializeMessages();
|
|
63
|
+
}, [
|
|
64
|
+
storage
|
|
65
|
+
]);
|
|
41
66
|
useEffect(()=>{
|
|
42
67
|
if ((null == storage ? void 0 : storage.saveMessages) && infoList.length > 1) storage.saveMessages(infoList).catch((error)=>{
|
|
43
68
|
if (error instanceof DOMException && 'QuotaExceededError' === error.name) console.warn('Storage quota exceeded - some messages may not be saved persistently');
|
package/dist/es/index.mjs
CHANGED
|
@@ -20,6 +20,6 @@ import { actionNameForType, getPlaceholderForType, staticAgentFromContext } from
|
|
|
20
20
|
import { filterBase64Value, timeStr } from "./utils/index.mjs";
|
|
21
21
|
import shiny_text from "./component/shiny-text/index.mjs";
|
|
22
22
|
import universal_playground, { UniversalPlayground } from "./component/universal-playground/index.mjs";
|
|
23
|
-
import { LocalStorageProvider, MemoryStorageProvider, NoOpStorageProvider } from "./component/universal-playground/providers/storage-provider.mjs";
|
|
23
|
+
import { IndexedDBStorageProvider, LocalStorageProvider, MemoryStorageProvider, NoOpStorageProvider, StorageType, createStorageProvider, detectBestStorageType } from "./component/universal-playground/providers/storage-provider.mjs";
|
|
24
24
|
import { AgentContextProvider, BaseContextProvider, NoOpContextProvider, StaticContextProvider } from "./component/universal-playground/providers/context-provider.mjs";
|
|
25
|
-
export { AgentContextProvider, BaseContextProvider, Blackboard, ContextPreview, EnvConfig, EnvConfigReminder, LocalStorageProvider, Logo, MemoryStorageProvider, NavActions, NoOpContextProvider, NoOpStorageProvider, Player, PlaygroundResultView, PromptInput, ServiceModeControl, shiny_text as ShinyText, StaticContextProvider, UniversalPlayground, universal_playground as UniversalPlaygroundDefault, actionNameForType, allScriptsFromDump, colorForName, filterBase64Value, generateAnimationScripts, getPlaceholderForType, globalThemeConfig, highlightColorForType, iconForStatus, safeOverrideAIConfig, staticAgentFromContext, timeCostStrElement, timeStr, useEnvConfig, useSafeOverrideAIConfig, useServerValid };
|
|
25
|
+
export { AgentContextProvider, BaseContextProvider, Blackboard, ContextPreview, EnvConfig, EnvConfigReminder, IndexedDBStorageProvider, LocalStorageProvider, Logo, MemoryStorageProvider, NavActions, NoOpContextProvider, NoOpStorageProvider, Player, PlaygroundResultView, PromptInput, ServiceModeControl, shiny_text as ShinyText, StaticContextProvider, StorageType, UniversalPlayground, universal_playground as UniversalPlaygroundDefault, actionNameForType, allScriptsFromDump, colorForName, createStorageProvider, detectBestStorageType, filterBase64Value, generateAnimationScripts, getPlaceholderForType, globalThemeConfig, highlightColorForType, iconForStatus, safeOverrideAIConfig, staticAgentFromContext, timeCostStrElement, timeStr, useEnvConfig, useSafeOverrideAIConfig, useServerValid };
|