@midscene/visualizer 0.28.9 → 0.28.10-beta-20250917142241.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.
Files changed (33) hide show
  1. package/dist/es/component/history-selector/index.css +17 -15
  2. package/dist/es/component/history-selector/index.mjs +20 -4
  3. package/dist/es/component/nav-actions/index.mjs +32 -0
  4. package/dist/es/component/nav-actions/style.css +35 -0
  5. package/dist/es/component/player/index.css +2 -2
  6. package/dist/es/component/playground/index.css +22 -6
  7. package/dist/es/component/prompt-input/index.css +22 -6
  8. package/dist/es/component/prompt-input/index.mjs +39 -7
  9. package/dist/es/component/universal-playground/index.css +2 -2
  10. package/dist/es/component/universal-playground/providers/storage-provider.mjs +68 -4
  11. package/dist/es/hooks/usePlaygroundState.mjs +2 -1
  12. package/dist/es/index.mjs +2 -2
  13. package/dist/lib/component/history-selector/index.css +17 -15
  14. package/dist/lib/component/history-selector/index.js +19 -3
  15. package/dist/lib/component/nav-actions/index.js +66 -0
  16. package/dist/lib/component/nav-actions/style.css +35 -0
  17. package/dist/lib/component/player/index.css +2 -2
  18. package/dist/lib/component/playground/index.css +22 -6
  19. package/dist/lib/component/prompt-input/index.css +22 -6
  20. package/dist/lib/component/prompt-input/index.js +38 -6
  21. package/dist/lib/component/universal-playground/index.css +2 -2
  22. package/dist/lib/component/universal-playground/providers/storage-provider.js +68 -4
  23. package/dist/lib/hooks/usePlaygroundState.js +2 -1
  24. package/dist/lib/index.js +4 -4
  25. package/dist/types/component/nav-actions/index.d.ts +10 -0
  26. package/dist/types/component/universal-playground/providers/storage-provider.d.ts +9 -0
  27. package/dist/types/index.d.ts +2 -1
  28. package/package.json +6 -6
  29. package/dist/es/component/github-star/index.css +0 -4
  30. package/dist/es/component/github-star/index.mjs +0 -20
  31. package/dist/lib/component/github-star/index.css +0 -4
  32. package/dist/lib/component/github-star/index.js +0 -54
  33. package/dist/types/component/github-star/index.d.ts +0 -2
@@ -1,26 +1,28 @@
1
+ .history-selector-wrapper {
2
+ position: relative;
3
+ }
4
+
1
5
  .history-modal-overlay {
2
- z-index: 1000;
3
- background: rgba(0, 0, 0, .45);
4
- justify-content: stretch;
5
- align-items: flex-end;
6
- display: flex;
7
- position: absolute;
8
- top: 0;
9
- bottom: 0;
10
- left: 0;
11
- right: 0;
6
+ z-index: 9999;
7
+ background: #fff;
8
+ border: 1px solid rgba(0, 0, 0, .08);
9
+ border-radius: 12px;
10
+ width: 320px;
11
+ height: 400px;
12
+ position: fixed;
13
+ top: auto;
14
+ bottom: 20px;
15
+ right: 20px;
16
+ box-shadow: 0 8px 24px rgba(0, 0, 0, .12);
12
17
  }
13
18
 
14
19
  .history-modal-container {
15
- z-index: 1001;
16
- background: #fff;
17
- border-radius: 12px 12px 0 0;
20
+ border-radius: 12px;
18
21
  flex-direction: column;
19
22
  width: 100%;
20
- height: 400px;
23
+ height: 100%;
21
24
  display: flex;
22
25
  overflow: hidden;
23
- box-shadow: 0 8px 24px rgba(0, 0, 0, .12);
24
26
  }
25
27
 
26
28
  .history-modal-container .history-modal-header {
@@ -1,6 +1,6 @@
1
1
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
2
2
  import { Button, Input, Typography } from "antd";
3
- import { useMemo, useState } from "react";
3
+ import { useEffect, useMemo, useRef, useState } from "react";
4
4
  import icons_close from "../../icons/close.mjs";
5
5
  import icons_history from "../../icons/history.mjs";
6
6
  import magnifying_glass from "../../icons/magnifying-glass.mjs";
@@ -12,6 +12,7 @@ const HistorySelector = (param)=>{
12
12
  const [isModalOpen, setIsModalOpen] = useState(false);
13
13
  const [searchText, setSearchText] = useState('');
14
14
  const clearHistory = useHistoryStore((state)=>state.clearHistory);
15
+ const modalRef = useRef(null);
15
16
  const groupedHistory = useMemo(()=>{
16
17
  const now = Date.now();
17
18
  const sevenDaysAgo = now - 604800000;
@@ -36,6 +37,21 @@ const HistorySelector = (param)=>{
36
37
  setSearchText('');
37
38
  setIsModalOpen(false);
38
39
  };
40
+ useEffect(()=>{
41
+ if (!isModalOpen) return;
42
+ const handleClickOutside = (event)=>{
43
+ if (modalRef.current && !modalRef.current.contains(event.target)) setIsModalOpen(false);
44
+ };
45
+ const timer = setTimeout(()=>{
46
+ document.addEventListener('click', handleClickOutside);
47
+ }, 100);
48
+ return ()=>{
49
+ clearTimeout(timer);
50
+ document.removeEventListener('click', handleClickOutside);
51
+ };
52
+ }, [
53
+ isModalOpen
54
+ ]);
39
55
  const renderHistoryGroup = (title, items)=>{
40
56
  if (0 === items.length) return null;
41
57
  return /*#__PURE__*/ jsxs("div", {
@@ -53,7 +69,8 @@ const HistorySelector = (param)=>{
53
69
  ]
54
70
  }, title);
55
71
  };
56
- return /*#__PURE__*/ jsxs(Fragment, {
72
+ return /*#__PURE__*/ jsxs("div", {
73
+ className: "history-selector-wrapper",
57
74
  children: [
58
75
  /*#__PURE__*/ jsx("div", {
59
76
  className: "selector-trigger",
@@ -65,10 +82,9 @@ const HistorySelector = (param)=>{
65
82
  }),
66
83
  isModalOpen && /*#__PURE__*/ jsx("div", {
67
84
  className: "history-modal-overlay",
68
- onClick: ()=>setIsModalOpen(false),
85
+ ref: modalRef,
69
86
  children: /*#__PURE__*/ jsxs("div", {
70
87
  className: "history-modal-container",
71
- onClick: (e)=>e.stopPropagation(),
72
88
  children: [
73
89
  /*#__PURE__*/ jsxs("div", {
74
90
  className: "history-modal-header",
@@ -0,0 +1,32 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { GithubOutlined, QuestionCircleOutlined } from "@ant-design/icons";
3
+ import { Typography } from "antd";
4
+ import { EnvConfig } from "../env-config/index.mjs";
5
+ import "./style.css";
6
+ function NavActions(param) {
7
+ let { showEnvConfig = true, showTooltipWhenEmpty = false, showModelName = false, githubUrl = 'https://github.com/web-infra-dev/midscene', helpUrl = 'https://midscenejs.com/quick-experience.html', className = '' } = param;
8
+ return /*#__PURE__*/ jsxs("div", {
9
+ className: `nav-actions ${className}`,
10
+ children: [
11
+ /*#__PURE__*/ jsx(Typography.Link, {
12
+ href: githubUrl,
13
+ target: "_blank",
14
+ children: /*#__PURE__*/ jsx(GithubOutlined, {
15
+ className: "nav-icon"
16
+ })
17
+ }),
18
+ /*#__PURE__*/ jsx(Typography.Link, {
19
+ href: helpUrl,
20
+ target: "_blank",
21
+ children: /*#__PURE__*/ jsx(QuestionCircleOutlined, {
22
+ className: "nav-icon"
23
+ })
24
+ }),
25
+ showEnvConfig && /*#__PURE__*/ jsx(EnvConfig, {
26
+ showTooltipWhenEmpty: showTooltipWhenEmpty,
27
+ showModelName: showModelName
28
+ })
29
+ ]
30
+ });
31
+ }
32
+ export { NavActions };
@@ -0,0 +1,35 @@
1
+ .nav-actions {
2
+ align-items: center;
3
+ gap: 8px;
4
+ display: flex;
5
+ }
6
+
7
+ .nav-actions .nav-icon {
8
+ color: rgba(0, 0, 0, .65);
9
+ cursor: pointer;
10
+ font-size: 16px;
11
+ transition: color .3s;
12
+ }
13
+
14
+ .nav-actions .nav-icon:hover {
15
+ color: #2b83ff;
16
+ }
17
+
18
+ .nav-actions a {
19
+ align-items: center;
20
+ text-decoration: none;
21
+ display: flex;
22
+ }
23
+
24
+ .nav-actions a:hover .nav-icon {
25
+ color: #2b83ff;
26
+ }
27
+
28
+ [data-theme="dark"] .nav-actions .nav-icon {
29
+ color: rgba(255, 255, 255, .65);
30
+ }
31
+
32
+ [data-theme="dark"] .nav-actions .nav-icon:hover {
33
+ color: #2b83ff;
34
+ }
35
+
@@ -30,8 +30,8 @@
30
30
  min-height: 200px;
31
31
  aspect-ratio: var(--canvas-aspect-ratio, 16 / 9);
32
32
  background-color: #fff;
33
- border-top-left-radius: 16px;
34
- border-top-right-radius: 16px;
33
+ border-top-left-radius: 4px;
34
+ border-top-right-radius: 4px;
35
35
  flex: none;
36
36
  justify-content: center;
37
37
  align-items: center;
@@ -44,21 +44,34 @@
44
44
  }
45
45
 
46
46
  .prompt-input-wrapper .mode-radio-group-wrapper .mode-radio-group {
47
- scrollbar-width: none;
48
- -ms-overflow-style: none;
47
+ scrollbar-width: thin;
49
48
  flex: 1;
50
49
  align-items: center;
51
50
  gap: 8px;
52
51
  min-width: 0;
53
52
  height: 100%;
54
- margin-right: 80px;
53
+ margin-right: 60px;
55
54
  display: flex;
56
55
  overflow-x: auto;
57
56
  overflow-y: hidden;
58
57
  }
59
58
 
60
59
  .prompt-input-wrapper .mode-radio-group-wrapper .mode-radio-group::-webkit-scrollbar {
61
- display: none;
60
+ height: 6px;
61
+ }
62
+
63
+ .prompt-input-wrapper .mode-radio-group-wrapper .mode-radio-group::-webkit-scrollbar-track {
64
+ background: #f1f1f1;
65
+ border-radius: 3px;
66
+ }
67
+
68
+ .prompt-input-wrapper .mode-radio-group-wrapper .mode-radio-group::-webkit-scrollbar-thumb {
69
+ background: #c1c1c1;
70
+ border-radius: 3px;
71
+ }
72
+
73
+ .prompt-input-wrapper .mode-radio-group-wrapper .mode-radio-group::-webkit-scrollbar-thumb:hover {
74
+ background: #a8a8a8;
62
75
  }
63
76
 
64
77
  .prompt-input-wrapper .mode-radio-group-wrapper .mode-radio-group .ant-form-item {
@@ -146,10 +159,13 @@
146
159
 
147
160
  .prompt-input-wrapper .mode-radio-group-wrapper .action-icons {
148
161
  z-index: 10;
149
- background: #fff;
162
+ background: linear-gradient(to right, rgba(0, 0, 0, 0) 0%, rgba(255, 255, 255, .5) 20%, rgba(255, 255, 255, .8) 40%, rgba(255, 255, 255, .95) 60%, #fff 70%);
150
163
  flex-shrink: 0;
164
+ justify-content: flex-end;
151
165
  align-items: center;
152
- padding-left: 8px;
166
+ gap: 8px;
167
+ width: 80px;
168
+ padding-left: 20px;
153
169
  display: flex;
154
170
  position: absolute;
155
171
  top: 50%;
@@ -10,21 +10,34 @@
10
10
  }
11
11
 
12
12
  .prompt-input-wrapper .mode-radio-group-wrapper .mode-radio-group {
13
- scrollbar-width: none;
14
- -ms-overflow-style: none;
13
+ scrollbar-width: thin;
15
14
  flex: 1;
16
15
  align-items: center;
17
16
  gap: 8px;
18
17
  min-width: 0;
19
18
  height: 100%;
20
- margin-right: 80px;
19
+ margin-right: 60px;
21
20
  display: flex;
22
21
  overflow-x: auto;
23
22
  overflow-y: hidden;
24
23
  }
25
24
 
26
25
  .prompt-input-wrapper .mode-radio-group-wrapper .mode-radio-group::-webkit-scrollbar {
27
- display: none;
26
+ height: 6px;
27
+ }
28
+
29
+ .prompt-input-wrapper .mode-radio-group-wrapper .mode-radio-group::-webkit-scrollbar-track {
30
+ background: #f1f1f1;
31
+ border-radius: 3px;
32
+ }
33
+
34
+ .prompt-input-wrapper .mode-radio-group-wrapper .mode-radio-group::-webkit-scrollbar-thumb {
35
+ background: #c1c1c1;
36
+ border-radius: 3px;
37
+ }
38
+
39
+ .prompt-input-wrapper .mode-radio-group-wrapper .mode-radio-group::-webkit-scrollbar-thumb:hover {
40
+ background: #a8a8a8;
28
41
  }
29
42
 
30
43
  .prompt-input-wrapper .mode-radio-group-wrapper .mode-radio-group .ant-form-item {
@@ -112,10 +125,13 @@
112
125
 
113
126
  .prompt-input-wrapper .mode-radio-group-wrapper .action-icons {
114
127
  z-index: 10;
115
- background: #fff;
128
+ background: linear-gradient(to right, rgba(0, 0, 0, 0) 0%, rgba(255, 255, 255, .5) 20%, rgba(255, 255, 255, .8) 40%, rgba(255, 255, 255, .95) 60%, #fff 70%);
116
129
  flex-shrink: 0;
130
+ justify-content: flex-end;
117
131
  align-items: center;
118
- padding-left: 8px;
132
+ gap: 8px;
133
+ width: 80px;
134
+ padding-left: 20px;
119
135
  display: flex;
120
136
  position: absolute;
121
137
  top: 50%;
@@ -1,7 +1,7 @@
1
1
  import { jsx, jsxs } from "react/jsx-runtime";
2
2
  import { BorderOutlined, DownOutlined, SendOutlined } from "@ant-design/icons";
3
3
  import "./index.css";
4
- import { Button, Dropdown, Form, Input, Radio, Space, Tooltip } from "antd";
4
+ import { Button, Dropdown, Form, Input, Radio, Tooltip } from "antd";
5
5
  import react, { useCallback, useEffect, useMemo, useRef, useState } from "react";
6
6
  import { useHistoryStore } from "../../store/history.mjs";
7
7
  import { extractDefaultValue, isLocateField, isZodObjectSchema, unwrapZodType } from "../../types.mjs";
@@ -17,6 +17,7 @@ const PromptInput = (param)=>{
17
17
  const [promptValue, setPromptValue] = useState('');
18
18
  const placeholder = getPlaceholderForType(selectedType);
19
19
  const textAreaRef = useRef(null);
20
+ const modeRadioGroupRef = useRef(null);
20
21
  const params = Form.useWatch('params', form);
21
22
  const lastHistoryRef = useRef(null);
22
23
  const history = useHistoryStore((state)=>state.history);
@@ -160,6 +161,27 @@ const PromptInput = (param)=>{
160
161
  lastSelectedType,
161
162
  setLastSelectedType
162
163
  ]);
164
+ const scrollToSelectedItem = useCallback(()=>{
165
+ const container = modeRadioGroupRef.current;
166
+ if (!container) return;
167
+ let targetElement = null;
168
+ const selectedRadioButton = container.querySelector('.ant-radio-button-wrapper-checked');
169
+ const dropdownButton = container.querySelector('.more-apis-button.selected-from-dropdown');
170
+ if (selectedRadioButton) targetElement = selectedRadioButton;
171
+ else if (dropdownButton) targetElement = dropdownButton;
172
+ if (targetElement) {
173
+ const containerRect = container.getBoundingClientRect();
174
+ const targetRect = targetElement.getBoundingClientRect();
175
+ const targetLeft = targetRect.left - containerRect.left + container.scrollLeft;
176
+ const targetWidth = targetRect.width;
177
+ const containerWidth = containerRect.width;
178
+ const optimalScrollLeft = targetLeft - (containerWidth - targetWidth) / 2;
179
+ container.scrollTo({
180
+ left: Math.max(0, optimalScrollLeft),
181
+ behavior: 'smooth'
182
+ });
183
+ }
184
+ }, []);
163
185
  useEffect(()=>{
164
186
  const lastHistory = historyForSelectedType[0];
165
187
  if (lastHistory && lastHistoryRef.current && lastHistory.timestamp === lastHistoryRef.current.timestamp) return;
@@ -186,6 +208,15 @@ const PromptInput = (param)=>{
186
208
  form,
187
209
  getDefaultParams
188
210
  ]);
211
+ useEffect(()=>{
212
+ const timeoutId = setTimeout(()=>{
213
+ scrollToSelectedItem();
214
+ }, 100);
215
+ return ()=>clearTimeout(timeoutId);
216
+ }, [
217
+ selectedType,
218
+ scrollToSelectedItem
219
+ ]);
189
220
  const formPromptValue = Form.useWatch('prompt', form);
190
221
  useEffect(()=>{
191
222
  if (formPromptValue !== promptValue) setPromptValue(formPromptValue || '');
@@ -501,11 +532,12 @@ const PromptInput = (param)=>{
501
532
  return /*#__PURE__*/ jsxs("div", {
502
533
  className: "prompt-input-wrapper",
503
534
  children: [
504
- /*#__PURE__*/ jsxs(Space, {
535
+ /*#__PURE__*/ jsxs("div", {
505
536
  className: "mode-radio-group-wrapper",
506
537
  children: [
507
538
  /*#__PURE__*/ jsxs("div", {
508
539
  className: "mode-radio-group",
540
+ ref: modeRadioGroupRef,
509
541
  children: [
510
542
  /*#__PURE__*/ jsx(Form.Item, {
511
543
  name: "type",
@@ -632,11 +664,6 @@ const PromptInput = (param)=>{
632
664
  /*#__PURE__*/ jsxs("div", {
633
665
  className: "action-icons",
634
666
  children: [
635
- /*#__PURE__*/ jsx(HistorySelector, {
636
- onSelect: handleSelectHistory,
637
- history: historyForSelectedType,
638
- currentType: selectedType
639
- }),
640
667
  hasConfigOptions && /*#__PURE__*/ jsx("div", {
641
668
  className: hoveringSettings ? 'settings-wrapper settings-wrapper-hover' : 'settings-wrapper',
642
669
  onMouseEnter: handleMouseEnter,
@@ -647,6 +674,11 @@ const PromptInput = (param)=>{
647
674
  showDataExtractionOptions: showDataExtractionOptions,
648
675
  hideDomAndScreenshotOptions: hideDomAndScreenshotOptions
649
676
  })
677
+ }),
678
+ /*#__PURE__*/ jsx(HistorySelector, {
679
+ onSelect: handleSelectHistory,
680
+ history: historyForSelectedType,
681
+ currentType: selectedType
650
682
  })
651
683
  ]
652
684
  })
@@ -32,8 +32,8 @@
32
32
  .playground-container .middle-dialog-area .clear-button-container {
33
33
  z-index: 10;
34
34
  position: absolute;
35
- top: 8px;
36
- right: 12px;
35
+ top: 16px;
36
+ right: 0;
37
37
  }
38
38
 
39
39
  .playground-container .middle-dialog-area .clear-button-container .clear-button {
@@ -9,15 +9,48 @@ function _define_property(obj, key, value) {
9
9
  return obj;
10
10
  }
11
11
  class LocalStorageProvider {
12
+ checkStorageSpace() {
13
+ try {
14
+ const testKey = 'storage-test';
15
+ const testData = 'x'.repeat(102400);
16
+ localStorage.setItem(testKey, testData);
17
+ localStorage.removeItem(testKey);
18
+ return true;
19
+ } catch (e) {
20
+ return false;
21
+ }
22
+ }
12
23
  async saveMessages(messages) {
13
24
  try {
14
- const lightMessages = messages.map((msg)=>({
25
+ if (!this.checkStorageSpace()) {
26
+ console.warn('Low storage space detected, clearing old data...');
27
+ await this.handleQuotaExceeded();
28
+ }
29
+ const messagesToSave = messages.slice(-this.maxStorageItems);
30
+ const lightMessages = messagesToSave.map((msg)=>({
15
31
  ...msg,
16
32
  result: void 0
17
33
  }));
18
- localStorage.setItem(this.messagesKey, JSON.stringify(lightMessages));
34
+ const messageData = JSON.stringify(lightMessages);
35
+ localStorage.setItem(this.messagesKey, messageData);
19
36
  } catch (error) {
20
- console.error('Failed to save messages to localStorage:', error);
37
+ if (error instanceof DOMException && 'QuotaExceededError' === error.name) {
38
+ console.warn('LocalStorage quota exceeded, attempting to clear old data and retry...');
39
+ await this.handleQuotaExceeded();
40
+ try {
41
+ const recentMessages = messages.slice(-10);
42
+ const lightRecentMessages = recentMessages.map((msg)=>({
43
+ ...msg,
44
+ result: void 0
45
+ }));
46
+ const messageData = JSON.stringify(lightRecentMessages);
47
+ localStorage.setItem(this.messagesKey, messageData);
48
+ console.info('Successfully saved recent messages after clearing storage');
49
+ } catch (retryError) {
50
+ console.error('Failed to save even after clearing storage:', retryError);
51
+ await this.clearMessages();
52
+ }
53
+ } else console.error('Failed to save messages to localStorage:', error);
21
54
  }
22
55
  }
23
56
  async loadMessages() {
@@ -63,12 +96,43 @@ class LocalStorageProvider {
63
96
  const resultKey = `${this.resultsKey}-${id}`;
64
97
  localStorage.setItem(resultKey, JSON.stringify(result));
65
98
  } catch (error) {
66
- console.error('Failed to save result to localStorage:', error);
99
+ if (error instanceof DOMException && 'QuotaExceededError' === error.name) {
100
+ console.warn('LocalStorage quota exceeded when saving result, clearing old results...');
101
+ await this.handleQuotaExceeded();
102
+ try {
103
+ const resultKey = `${this.resultsKey}-${id}`;
104
+ localStorage.setItem(resultKey, JSON.stringify(result));
105
+ } catch (retryError) {
106
+ console.error('Failed to save result even after clearing storage:', retryError);
107
+ }
108
+ } else console.error('Failed to save result to localStorage:', error);
109
+ }
110
+ }
111
+ async handleQuotaExceeded() {
112
+ try {
113
+ const keys = Object.keys(localStorage);
114
+ const resultKeys = keys.filter((key)=>key.startsWith(this.resultsKey));
115
+ const keysToRemove = resultKeys.slice(0, Math.max(1, Math.floor(resultKeys.length / 2)));
116
+ keysToRemove.forEach((key)=>{
117
+ localStorage.removeItem(key);
118
+ });
119
+ console.info(`Cleared ${keysToRemove.length} old result entries to free up storage space`);
120
+ const playgroundKeys = keys.filter((key)=>key.includes('playground') || key.includes('agent') || key.startsWith('midscene'));
121
+ if (playgroundKeys.length > 10) {
122
+ const additionalKeysToRemove = playgroundKeys.slice(0, Math.floor(playgroundKeys.length / 3));
123
+ additionalKeysToRemove.forEach((key)=>{
124
+ if (key !== this.messagesKey) localStorage.removeItem(key);
125
+ });
126
+ console.info(`Cleared ${additionalKeysToRemove.length} additional playground-related entries`);
127
+ }
128
+ } catch (error) {
129
+ console.error('Failed to handle quota exceeded:', error);
67
130
  }
68
131
  }
69
132
  constructor(namespace = 'playground'){
70
133
  _define_property(this, "messagesKey", void 0);
71
134
  _define_property(this, "resultsKey", void 0);
135
+ _define_property(this, "maxStorageItems", 50);
72
136
  this.messagesKey = `${namespace}-messages`;
73
137
  this.resultsKey = `${namespace}-results`;
74
138
  }
@@ -40,7 +40,8 @@ function usePlaygroundState(playgroundSDK, storage, contextProvider) {
40
40
  }, []);
41
41
  useEffect(()=>{
42
42
  if ((null == storage ? void 0 : storage.saveMessages) && infoList.length > 1) storage.saveMessages(infoList).catch((error)=>{
43
- console.error('Failed to save messages:', error);
43
+ if (error instanceof DOMException && 'QuotaExceededError' === error.name) console.warn('Storage quota exceeded - some messages may not be saved persistently');
44
+ else console.error('Failed to save messages:', error);
44
45
  });
45
46
  }, [
46
47
  infoList,
package/dist/es/index.mjs CHANGED
@@ -5,6 +5,7 @@ import { useEnvConfig } from "./store/store.mjs";
5
5
  import { colorForName, globalThemeConfig, highlightColorForType } from "./utils/color.mjs";
6
6
  import { EnvConfig } from "./component/env-config/index.mjs";
7
7
  import { EnvConfigReminder } from "./component/env-config-reminder/index.mjs";
8
+ import { NavActions } from "./component/nav-actions/index.mjs";
8
9
  import { Logo } from "./component/logo/index.mjs";
9
10
  import { iconForStatus, timeCostStrElement } from "./component/misc/index.mjs";
10
11
  import { useServerValid } from "./hooks/useServerValid.mjs";
@@ -15,11 +16,10 @@ import { ContextPreview } from "./component/context-preview/index.mjs";
15
16
  import { PromptInput } from "./component/prompt-input/index.mjs";
16
17
  import { Player } from "./component/player/index.mjs";
17
18
  import { Blackboard } from "./component/blackboard/index.mjs";
18
- import { GithubStar } from "./component/github-star/index.mjs";
19
19
  import { actionNameForType, getPlaceholderForType, staticAgentFromContext } from "./utils/playground-utils.mjs";
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
23
  import { LocalStorageProvider, MemoryStorageProvider, NoOpStorageProvider } 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, GithubStar, LocalStorageProvider, Logo, MemoryStorageProvider, 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, 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 };
@@ -1,26 +1,28 @@
1
+ .history-selector-wrapper {
2
+ position: relative;
3
+ }
4
+
1
5
  .history-modal-overlay {
2
- z-index: 1000;
3
- background: rgba(0, 0, 0, .45);
4
- justify-content: stretch;
5
- align-items: flex-end;
6
- display: flex;
7
- position: absolute;
8
- top: 0;
9
- bottom: 0;
10
- left: 0;
11
- right: 0;
6
+ z-index: 9999;
7
+ background: #fff;
8
+ border: 1px solid rgba(0, 0, 0, .08);
9
+ border-radius: 12px;
10
+ width: 320px;
11
+ height: 400px;
12
+ position: fixed;
13
+ top: auto;
14
+ bottom: 20px;
15
+ right: 20px;
16
+ box-shadow: 0 8px 24px rgba(0, 0, 0, .12);
12
17
  }
13
18
 
14
19
  .history-modal-container {
15
- z-index: 1001;
16
- background: #fff;
17
- border-radius: 12px 12px 0 0;
20
+ border-radius: 12px;
18
21
  flex-direction: column;
19
22
  width: 100%;
20
- height: 400px;
23
+ height: 100%;
21
24
  display: flex;
22
25
  overflow: hidden;
23
- box-shadow: 0 8px 24px rgba(0, 0, 0, .12);
24
26
  }
25
27
 
26
28
  .history-modal-container .history-modal-header {
@@ -52,6 +52,7 @@ const HistorySelector = (param)=>{
52
52
  const [isModalOpen, setIsModalOpen] = (0, external_react_namespaceObject.useState)(false);
53
53
  const [searchText, setSearchText] = (0, external_react_namespaceObject.useState)('');
54
54
  const clearHistory = (0, external_store_history_js_namespaceObject.useHistoryStore)((state)=>state.clearHistory);
55
+ const modalRef = (0, external_react_namespaceObject.useRef)(null);
55
56
  const groupedHistory = (0, external_react_namespaceObject.useMemo)(()=>{
56
57
  const now = Date.now();
57
58
  const sevenDaysAgo = now - 604800000;
@@ -76,6 +77,21 @@ const HistorySelector = (param)=>{
76
77
  setSearchText('');
77
78
  setIsModalOpen(false);
78
79
  };
80
+ (0, external_react_namespaceObject.useEffect)(()=>{
81
+ if (!isModalOpen) return;
82
+ const handleClickOutside = (event)=>{
83
+ if (modalRef.current && !modalRef.current.contains(event.target)) setIsModalOpen(false);
84
+ };
85
+ const timer = setTimeout(()=>{
86
+ document.addEventListener('click', handleClickOutside);
87
+ }, 100);
88
+ return ()=>{
89
+ clearTimeout(timer);
90
+ document.removeEventListener('click', handleClickOutside);
91
+ };
92
+ }, [
93
+ isModalOpen
94
+ ]);
79
95
  const renderHistoryGroup = (title, items)=>{
80
96
  if (0 === items.length) return null;
81
97
  return /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsxs)("div", {
@@ -93,7 +109,8 @@ const HistorySelector = (param)=>{
93
109
  ]
94
110
  }, title);
95
111
  };
96
- return /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsxs)(jsx_runtime_namespaceObject.Fragment, {
112
+ return /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsxs)("div", {
113
+ className: "history-selector-wrapper",
97
114
  children: [
98
115
  /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)("div", {
99
116
  className: "selector-trigger",
@@ -105,10 +122,9 @@ const HistorySelector = (param)=>{
105
122
  }),
106
123
  isModalOpen && /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)("div", {
107
124
  className: "history-modal-overlay",
108
- onClick: ()=>setIsModalOpen(false),
125
+ ref: modalRef,
109
126
  children: /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsxs)("div", {
110
127
  className: "history-modal-container",
111
- onClick: (e)=>e.stopPropagation(),
112
128
  children: [
113
129
  /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsxs)("div", {
114
130
  className: "history-modal-header",