@nextclaw/ui 0.12.35-beta.1 → 0.12.35-beta.3
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/CHANGELOG.md +128 -0
- package/dist/assets/api-D3uoaOR8.js +15 -0
- package/dist/assets/{book-open-825Pcd0z.js → book-open--T9dJHnp.js} +1 -1
- package/dist/assets/channels-list-page-DHmz9iHg.js +8 -0
- package/dist/assets/{chat-page-RsUTK_bY.js → chat-page-DOKLoiqI.js} +2 -2
- package/dist/assets/{config-split-page-BvfxFQSb.js → config-split-page-Dutq3R5o.js} +1 -1
- package/dist/assets/{createLucideIcon-CeNHY6BW.js → createLucideIcon-Cwr2o6Zq.js} +1 -1
- package/dist/assets/{desktop-update-config-B_YIv5ZJ.js → desktop-update-config-ClbDsIOs.js} +1 -1
- package/dist/assets/{dist-uH7tiW6E.js → dist-e5WATjIW.js} +1 -1
- package/dist/assets/doc-browser-ChY6hMn8.js +1 -0
- package/dist/assets/doc-browser-cR1vWRs1.js +1 -0
- package/dist/assets/{doc-browser-context-CQAx_92e.js → doc-browser-context-CLczyX2Z.js} +1 -1
- package/dist/assets/{es2015-c9XYpli5.js → es2015-BJw-hrex.js} +1 -1
- package/dist/assets/{external-link-BE1deZ_c.js → external-link-CZ9bQY0L.js} +1 -1
- package/dist/assets/i18n-tWKGEGxB.js +1 -0
- package/dist/assets/{index-BjH_p3R6.js → index-DPz9DRZN.js} +44 -44
- package/dist/assets/index-HvJDzfSU.css +1 -0
- package/dist/assets/{key-round-CHo3gqpO.js → key-round-B8tTb3ZJ.js} +1 -1
- package/dist/assets/{mcp-marketplace-page-0C214hGi.js → mcp-marketplace-page-BYPxLdUR.js} +2 -2
- package/dist/assets/mcp-marketplace-page-DXCvk7wJ.js +1 -0
- package/dist/assets/model-config-DcnC_fSr.js +1 -0
- package/dist/assets/{notice-card-DO33_LzD.js → notice-card-DUDbOvqx.js} +1 -1
- package/dist/assets/play-CV_kwi9X.js +1 -0
- package/dist/assets/plus-BqfNq7Tu.js +1 -0
- package/dist/assets/{provider-scoped-model-input-B0_r_4EY.js → provider-scoped-model-input-eO4XY0iY.js} +1 -1
- package/dist/assets/providers-list-BgvjRVmp.js +1 -0
- package/dist/assets/remote-BCuZ1JJe.js +1 -0
- package/dist/assets/{rotate-cw-CYUByuqo.js → rotate-cw-BKEhWuWp.js} +1 -1
- package/dist/assets/runtime-config-page-B2XPXHrk.js +1 -0
- package/dist/assets/{save-B6Ga8JIq.js → save-B6wbBLkU.js} +1 -1
- package/dist/assets/{search-5kKpgNoP.js → search-Czito3Dk.js} +1 -1
- package/dist/assets/search-config-BFmG4_F7.js +1 -0
- package/dist/assets/{secrets-config-CrXA11bo.js → secrets-config-4uY5sk-V.js} +2 -2
- package/dist/assets/{select-C0hGOCOK.js → select-CmmDgG8o.js} +1 -1
- package/dist/assets/{setting-row-xsM_0nTd.js → setting-row-BUO6FeN1.js} +1 -1
- package/dist/assets/{tabs-custom-Bnqfhe4p.js → tabs-custom-D3IZL9WX.js} +1 -1
- package/dist/assets/{tag-chip-cktJJMzh.js → tag-chip-Dm_pOtE3.js} +1 -1
- package/dist/assets/x-DBP3WiC9.js +1 -0
- package/dist/index.html +17 -17
- package/package.json +9 -9
- package/src/app/components/layout/app-layout.tsx +3 -2
- package/src/features/chat/components/chat-session-workspace-panel.tsx +1 -1
- package/src/features/chat/components/layout/chat-sidebar-read-state.test.tsx +145 -0
- package/src/features/chat/components/layout/chat-sidebar-utility-menu.tsx +16 -0
- package/src/features/chat/components/layout/chat-sidebar.tsx +3 -2
- package/src/features/panel-apps/components/panel-app-list-item.tsx +83 -0
- package/src/features/panel-apps/components/panel-app-toolbar.tsx +34 -0
- package/src/features/panel-apps/components/panel-apps-list.tsx +109 -0
- package/src/features/panel-apps/hooks/use-panel-apps.ts +39 -0
- package/src/features/panel-apps/index.ts +4 -0
- package/src/features/panel-apps/utils/panel-app-doc-browser.utils.tsx +56 -0
- package/src/features/panel-apps/utils/panel-app-view.utils.ts +43 -0
- package/src/platforms/desktop/components/desktop-app-shell.tsx +5 -2
- package/src/platforms/mobile/components/mobile-app-shell.tsx +4 -1
- package/src/shared/components/doc-browser/doc-browser-context.tsx +7 -7
- package/src/shared/components/doc-browser/doc-browser-panel-parts.tsx +201 -0
- package/src/shared/components/doc-browser/doc-browser-renderer.types.ts +19 -0
- package/src/shared/components/doc-browser/doc-browser-tab-strip.tsx +67 -0
- package/src/shared/components/doc-browser/doc-browser.test.tsx +82 -2
- package/src/shared/components/doc-browser/doc-browser.tsx +205 -279
- package/src/shared/lib/api/types.ts +24 -0
- package/src/shared/lib/i18n/index.ts +2 -12
- package/src/shared/lib/i18n/runtime/doc-browser-labels.utils.ts +29 -0
- package/dist/assets/api-De1PAch0.js +0 -15
- package/dist/assets/channels-list-page-CGjXDSD-.js +0 -8
- package/dist/assets/doc-browser-CDe9yz9n.js +0 -1
- package/dist/assets/doc-browser-DwT64GuF.js +0 -1
- package/dist/assets/i18n-DnubSHoK.js +0 -1
- package/dist/assets/index-6WZ6JjaU.css +0 -1
- package/dist/assets/mcp-marketplace-page-amWcMncu.js +0 -1
- package/dist/assets/model-config-B8zNrkag.js +0 -1
- package/dist/assets/play-CqlWbVNo.js +0 -1
- package/dist/assets/plus-p61tQh2-.js +0 -1
- package/dist/assets/providers-list-B6v3foi7.js +0 -1
- package/dist/assets/remote-k671rLmY.js +0 -1
- package/dist/assets/runtime-config-page-DgH-XJwK.js +0 -1
- package/dist/assets/search-config-DPZ6d9y6.js +0 -1
- package/dist/assets/x-DDQw-C2x.js +0 -1
|
@@ -1,52 +1,47 @@
|
|
|
1
|
-
import { useState, useRef, useCallback, useEffect
|
|
2
|
-
import {
|
|
1
|
+
import { useState, useRef, useCallback, useEffect } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
DOCS_DEFAULT_BASE_URL,
|
|
4
|
+
useDocBrowser,
|
|
5
|
+
} from './doc-browser-context';
|
|
6
|
+
import type { DocBrowserCustomTabRenderers } from './doc-browser-renderer.types';
|
|
7
|
+
import {
|
|
8
|
+
DocBrowserDocsToolbar,
|
|
9
|
+
DocBrowserExternalLink,
|
|
10
|
+
DocBrowserFrameContent,
|
|
11
|
+
DocBrowserPanelHeader,
|
|
12
|
+
} from './doc-browser-panel-parts';
|
|
13
|
+
import { DocBrowserTabStrip } from './doc-browser-tab-strip';
|
|
3
14
|
import { ResizableRightPanel } from '@/shared/components/resizable-right-panel/resizable-right-panel';
|
|
4
15
|
import { cn } from '@/shared/lib/utils';
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
ArrowLeft,
|
|
8
|
-
ArrowRight,
|
|
9
|
-
X,
|
|
10
|
-
ExternalLink,
|
|
11
|
-
PanelRightOpen,
|
|
12
|
-
Maximize2,
|
|
13
|
-
GripVertical,
|
|
14
|
-
Search,
|
|
15
|
-
BookOpen,
|
|
16
|
-
Plus,
|
|
17
|
-
} from 'lucide-react';
|
|
16
|
+
import { GripVertical } from 'lucide-react';
|
|
18
17
|
|
|
19
18
|
type DocBrowserProps = {
|
|
19
|
+
customTabRenderers?: DocBrowserCustomTabRenderers;
|
|
20
20
|
displayMode?: 'desktop' | 'fullscreen';
|
|
21
21
|
};
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
23
|
+
type FloatingPanelRect = { x: number; y: number; w: number; h: number };
|
|
24
|
+
type FloatingPanelResizeEdge = 'left' | 'right' | 'top' | 'bottom' | 'bottom-right';
|
|
25
|
+
type FloatingPanelInteraction = {
|
|
26
|
+
kind: 'drag' | 'resize';
|
|
27
|
+
edge?: FloatingPanelResizeEdge;
|
|
28
|
+
startX: number;
|
|
29
|
+
startY: number;
|
|
30
|
+
startRect: FloatingPanelRect;
|
|
31
|
+
};
|
|
31
32
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
if (isFullscreen) return undefined;
|
|
39
|
-
return {
|
|
40
|
-
position: 'fixed',
|
|
41
|
-
left: floatPos.x,
|
|
42
|
-
top: floatPos.y,
|
|
43
|
-
width: floatSize.w,
|
|
44
|
-
height: floatSize.h,
|
|
45
|
-
zIndex: 9999,
|
|
46
|
-
};
|
|
33
|
+
const FLOATING_PANEL_MARGIN = 40;
|
|
34
|
+
const FLOATING_PANEL_MIN_WIDTH = 360;
|
|
35
|
+
const FLOATING_PANEL_MIN_HEIGHT = 400;
|
|
36
|
+
|
|
37
|
+
function clamp(value: number, min: number, max: number): number {
|
|
38
|
+
return Math.min(max, Math.max(min, value));
|
|
47
39
|
}
|
|
48
40
|
|
|
49
|
-
export function DocBrowser({
|
|
41
|
+
export function DocBrowser({
|
|
42
|
+
customTabRenderers = {},
|
|
43
|
+
displayMode = 'desktop',
|
|
44
|
+
}: DocBrowserProps) {
|
|
50
45
|
const {
|
|
51
46
|
isOpen,
|
|
52
47
|
mode,
|
|
@@ -65,15 +60,14 @@ export function DocBrowser({ displayMode = 'desktop' }: DocBrowserProps) {
|
|
|
65
60
|
} = useDocBrowser();
|
|
66
61
|
|
|
67
62
|
const [urlInput, setUrlInput] = useState('');
|
|
68
|
-
const [
|
|
69
|
-
const [
|
|
70
|
-
|
|
71
|
-
x: Math.max(40, window.innerWidth - 520),
|
|
63
|
+
const [iframeReloadVersion, setIframeReloadVersion] = useState(0);
|
|
64
|
+
const [floatRect, setFloatRect] = useState<FloatingPanelRect>(() => ({
|
|
65
|
+
x: Math.max(FLOATING_PANEL_MARGIN, window.innerWidth - 520),
|
|
72
66
|
y: 80,
|
|
67
|
+
w: 480,
|
|
68
|
+
h: 600,
|
|
73
69
|
}));
|
|
74
|
-
const [
|
|
75
|
-
const dragRef = useRef<{ startX: number; startY: number; startPosX: number; startPosY: number } | null>(null);
|
|
76
|
-
const resizeRef = useRef<{ startX: number; startY: number; startW: number; startH: number } | null>(null);
|
|
70
|
+
const [floatInteraction, setFloatInteraction] = useState<FloatingPanelInteraction | null>(null);
|
|
77
71
|
const iframeRef = useRef<HTMLIFrameElement>(null);
|
|
78
72
|
const currentUrl = currentTab?.currentUrl ?? DOCS_DEFAULT_BASE_URL;
|
|
79
73
|
const navVersion = currentTab?.navVersion ?? 0;
|
|
@@ -114,15 +108,6 @@ export function DocBrowser({ displayMode = 'desktop' }: DocBrowserProps) {
|
|
|
114
108
|
}
|
|
115
109
|
}, [currentUrl, navVersion, isDocsTab]);
|
|
116
110
|
|
|
117
|
-
useEffect(() => {
|
|
118
|
-
if (mode === 'floating') {
|
|
119
|
-
setFloatPos((prev) => ({
|
|
120
|
-
x: Math.max(40, window.innerWidth - floatSize.w - 40),
|
|
121
|
-
y: prev.y,
|
|
122
|
-
}));
|
|
123
|
-
}
|
|
124
|
-
}, [mode, floatSize.w]);
|
|
125
|
-
|
|
126
111
|
useEffect(() => {
|
|
127
112
|
const handler = (e: MessageEvent) => {
|
|
128
113
|
if (!isDocsTab) {
|
|
@@ -136,6 +121,76 @@ export function DocBrowser({ displayMode = 'desktop' }: DocBrowserProps) {
|
|
|
136
121
|
return () => window.removeEventListener('message', handler);
|
|
137
122
|
}, [syncUrl, isDocsTab]);
|
|
138
123
|
|
|
124
|
+
const startFloatDrag = useCallback((event: React.PointerEvent<HTMLElement>) => {
|
|
125
|
+
event.preventDefault();
|
|
126
|
+
setFloatInteraction({
|
|
127
|
+
kind: 'drag',
|
|
128
|
+
startX: event.clientX,
|
|
129
|
+
startY: event.clientY,
|
|
130
|
+
startRect: floatRect,
|
|
131
|
+
});
|
|
132
|
+
}, [floatRect]);
|
|
133
|
+
|
|
134
|
+
const startFloatResize = useCallback((edge: FloatingPanelResizeEdge) => (event: React.PointerEvent<HTMLElement>) => {
|
|
135
|
+
event.preventDefault();
|
|
136
|
+
event.stopPropagation();
|
|
137
|
+
setFloatInteraction({
|
|
138
|
+
kind: 'resize',
|
|
139
|
+
edge,
|
|
140
|
+
startX: event.clientX,
|
|
141
|
+
startY: event.clientY,
|
|
142
|
+
startRect: floatRect,
|
|
143
|
+
});
|
|
144
|
+
}, [floatRect]);
|
|
145
|
+
|
|
146
|
+
useEffect(() => {
|
|
147
|
+
if (!floatInteraction) return;
|
|
148
|
+
|
|
149
|
+
const onMove = (event: PointerEvent) => {
|
|
150
|
+
const { startRect, startX, startY } = floatInteraction;
|
|
151
|
+
const dx = event.clientX - startX;
|
|
152
|
+
const dy = event.clientY - startY;
|
|
153
|
+
|
|
154
|
+
if (floatInteraction.kind === 'drag') {
|
|
155
|
+
setFloatRect({
|
|
156
|
+
...startRect,
|
|
157
|
+
x: clamp(startRect.x + dx, FLOATING_PANEL_MARGIN, window.innerWidth - FLOATING_PANEL_MARGIN - startRect.w),
|
|
158
|
+
y: clamp(startRect.y + dy, FLOATING_PANEL_MARGIN, window.innerHeight - FLOATING_PANEL_MARGIN - startRect.h),
|
|
159
|
+
});
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
if (floatInteraction.edge === 'left' || floatInteraction.edge === 'top') {
|
|
164
|
+
const isLeftEdge = floatInteraction.edge === 'left';
|
|
165
|
+
const fixedEdge = isLeftEdge ? startRect.x + startRect.w : startRect.y + startRect.h;
|
|
166
|
+
const movingEdge = isLeftEdge ? startRect.x + dx : startRect.y + dy;
|
|
167
|
+
const minSize = isLeftEdge ? FLOATING_PANEL_MIN_WIDTH : FLOATING_PANEL_MIN_HEIGHT;
|
|
168
|
+
const nextEdge = clamp(movingEdge, FLOATING_PANEL_MARGIN, fixedEdge - minSize);
|
|
169
|
+
setFloatRect(isLeftEdge ? { ...startRect, x: nextEdge, w: fixedEdge - nextEdge } : { ...startRect, y: nextEdge, h: fixedEdge - nextEdge });
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const right = clamp(startRect.x + startRect.w + dx, startRect.x + FLOATING_PANEL_MIN_WIDTH, window.innerWidth - FLOATING_PANEL_MARGIN);
|
|
174
|
+
const bottom = clamp(startRect.y + startRect.h + dy, startRect.y + FLOATING_PANEL_MIN_HEIGHT, window.innerHeight - FLOATING_PANEL_MARGIN);
|
|
175
|
+
setFloatRect({
|
|
176
|
+
...startRect,
|
|
177
|
+
w: floatInteraction.edge === 'bottom' ? startRect.w : right - startRect.x,
|
|
178
|
+
h: floatInteraction.edge === 'right' ? startRect.h : bottom - startRect.y,
|
|
179
|
+
});
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
const onEnd = () => setFloatInteraction(null);
|
|
183
|
+
|
|
184
|
+
window.addEventListener('pointermove', onMove);
|
|
185
|
+
window.addEventListener('pointerup', onEnd);
|
|
186
|
+
window.addEventListener('pointercancel', onEnd);
|
|
187
|
+
return () => {
|
|
188
|
+
window.removeEventListener('pointermove', onMove);
|
|
189
|
+
window.removeEventListener('pointerup', onEnd);
|
|
190
|
+
window.removeEventListener('pointercancel', onEnd);
|
|
191
|
+
};
|
|
192
|
+
}, [floatInteraction]);
|
|
193
|
+
|
|
139
194
|
const handleUrlSubmit = useCallback((e: React.FormEvent) => {
|
|
140
195
|
e.preventDefault();
|
|
141
196
|
if (!isDocsTab) return;
|
|
@@ -150,230 +205,73 @@ export function DocBrowser({ displayMode = 'desktop' }: DocBrowserProps) {
|
|
|
150
205
|
}
|
|
151
206
|
}, [urlInput, navigate, isDocsTab]);
|
|
152
207
|
|
|
153
|
-
const
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
dragRef.current = {
|
|
157
|
-
startX: e.clientX,
|
|
158
|
-
startY: e.clientY,
|
|
159
|
-
startPosX: floatPos.x,
|
|
160
|
-
startPosY: floatPos.y,
|
|
161
|
-
};
|
|
162
|
-
}, [mode, floatPos]);
|
|
163
|
-
|
|
164
|
-
useEffect(() => {
|
|
165
|
-
if (!isDragging) return;
|
|
166
|
-
const onMove = (e: MouseEvent) => {
|
|
167
|
-
if (!dragRef.current) return;
|
|
168
|
-
setFloatPos({
|
|
169
|
-
x: dragRef.current.startPosX + (e.clientX - dragRef.current.startX),
|
|
170
|
-
y: dragRef.current.startPosY + (e.clientY - dragRef.current.startY),
|
|
171
|
-
});
|
|
172
|
-
};
|
|
173
|
-
const onUp = () => {
|
|
174
|
-
setIsDragging(false);
|
|
175
|
-
dragRef.current = null;
|
|
176
|
-
};
|
|
177
|
-
window.addEventListener('mousemove', onMove);
|
|
178
|
-
window.addEventListener('mouseup', onUp);
|
|
179
|
-
return () => {
|
|
180
|
-
window.removeEventListener('mousemove', onMove);
|
|
181
|
-
window.removeEventListener('mouseup', onUp);
|
|
182
|
-
};
|
|
183
|
-
}, [isDragging]);
|
|
184
|
-
|
|
185
|
-
const onResizeStart = useCallback((e: React.MouseEvent) => {
|
|
186
|
-
e.preventDefault();
|
|
187
|
-
e.stopPropagation();
|
|
188
|
-
setIsResizing(true);
|
|
189
|
-
const { axis } = (e.currentTarget as HTMLElement).dataset;
|
|
190
|
-
resizeRef.current = {
|
|
191
|
-
startX: e.clientX,
|
|
192
|
-
startY: e.clientY,
|
|
193
|
-
startW: floatSize.w,
|
|
194
|
-
startH: floatSize.h,
|
|
195
|
-
};
|
|
196
|
-
const onMove = (ev: MouseEvent) => {
|
|
197
|
-
if (!resizeRef.current) return;
|
|
198
|
-
setFloatSize((prev) => ({
|
|
199
|
-
w: axis === 'y' ? prev.w : Math.max(360, resizeRef.current!.startW + (ev.clientX - resizeRef.current!.startX)),
|
|
200
|
-
h: axis === 'x' ? prev.h : Math.max(400, resizeRef.current!.startH + (ev.clientY - resizeRef.current!.startY)),
|
|
201
|
-
}));
|
|
202
|
-
};
|
|
203
|
-
const onUp = () => {
|
|
204
|
-
setIsResizing(false);
|
|
205
|
-
resizeRef.current = null;
|
|
206
|
-
window.removeEventListener('mousemove', onMove);
|
|
207
|
-
window.removeEventListener('mouseup', onUp);
|
|
208
|
-
};
|
|
209
|
-
window.addEventListener('mousemove', onMove);
|
|
210
|
-
window.addEventListener('mouseup', onUp);
|
|
211
|
-
}, [floatSize]);
|
|
212
|
-
|
|
213
|
-
const onLeftResizeStart = useCallback((e: React.MouseEvent) => {
|
|
214
|
-
e.preventDefault();
|
|
215
|
-
e.stopPropagation();
|
|
216
|
-
setIsResizing(true);
|
|
217
|
-
const startX = e.clientX;
|
|
218
|
-
const startW = floatSize.w;
|
|
219
|
-
const startPosX = floatPos.x;
|
|
220
|
-
const onMove = (ev: MouseEvent) => {
|
|
221
|
-
const delta = startX - ev.clientX;
|
|
222
|
-
const newW = Math.max(360, startW + delta);
|
|
223
|
-
setFloatSize((prev) => ({ ...prev, w: newW }));
|
|
224
|
-
setFloatPos((prev) => ({ ...prev, x: startPosX - (newW - startW) }));
|
|
225
|
-
};
|
|
226
|
-
const onUp = () => {
|
|
227
|
-
setIsResizing(false);
|
|
228
|
-
window.removeEventListener('mousemove', onMove);
|
|
229
|
-
window.removeEventListener('mouseup', onUp);
|
|
230
|
-
};
|
|
231
|
-
window.addEventListener('mousemove', onMove);
|
|
232
|
-
window.addEventListener('mouseup', onUp);
|
|
233
|
-
}, [floatSize.w, floatPos.x]);
|
|
208
|
+
const refreshIframe = useCallback(() => {
|
|
209
|
+
setIframeReloadVersion((version) => version + 1);
|
|
210
|
+
}, []);
|
|
234
211
|
|
|
235
212
|
if (!isOpen) return null;
|
|
236
213
|
|
|
237
214
|
const isDocked = mode === 'docked';
|
|
238
215
|
const isFullscreen = displayMode === 'fullscreen';
|
|
216
|
+
const customRenderer = currentTab ? customTabRenderers[currentTab.kind] : undefined;
|
|
217
|
+
const customRenderParams = currentTab ? {
|
|
218
|
+
currentUrl,
|
|
219
|
+
open,
|
|
220
|
+
refreshIframe,
|
|
221
|
+
tab: currentTab,
|
|
222
|
+
} : undefined;
|
|
223
|
+
const customToolbar = customRenderParams ? customRenderer?.renderToolbar?.(customRenderParams) : null;
|
|
224
|
+
const customContent = customRenderParams ? customRenderer?.renderContent?.(customRenderParams) : null;
|
|
225
|
+
const iframeSandbox = currentTab
|
|
226
|
+
? customRenderer?.getIframeSandbox?.(currentTab) ?? 'allow-same-origin allow-scripts allow-popups allow-forms'
|
|
227
|
+
: 'allow-same-origin allow-scripts allow-popups allow-forms';
|
|
239
228
|
|
|
240
229
|
const panelContent = (
|
|
241
230
|
<>
|
|
242
|
-
<
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
<BookOpen className="w-4 h-4 text-primary shrink-0" />
|
|
252
|
-
<span className="text-sm font-semibold text-gray-900 truncate">{t('docBrowserTitle')}</span>
|
|
253
|
-
</div>
|
|
254
|
-
<div className="flex items-center gap-1">
|
|
255
|
-
{!isFullscreen ? (
|
|
256
|
-
<button
|
|
257
|
-
onClick={toggleMode}
|
|
258
|
-
className="hover:bg-gray-200 rounded-md p-1.5 text-gray-500 hover:text-gray-700 transition-colors"
|
|
259
|
-
title={isDocked ? t('docBrowserFloatMode') : t('docBrowserDockMode')}
|
|
260
|
-
>
|
|
261
|
-
{isDocked ? <Maximize2 className="w-3.5 h-3.5" /> : <PanelRightOpen className="w-3.5 h-3.5" />}
|
|
262
|
-
</button>
|
|
263
|
-
) : null}
|
|
264
|
-
<button
|
|
265
|
-
onClick={close}
|
|
266
|
-
className="hover:bg-gray-200 rounded-md p-1.5 text-gray-500 hover:text-gray-700 transition-colors"
|
|
267
|
-
title={t('docBrowserClose')}
|
|
268
|
-
>
|
|
269
|
-
<X className="w-3.5 h-3.5" />
|
|
270
|
-
</button>
|
|
271
|
-
</div>
|
|
272
|
-
</div>
|
|
273
|
-
|
|
274
|
-
<div className="flex items-center gap-1.5 px-2.5 py-2 bg-background border-b border-[#f1e7d4] overflow-x-auto custom-scrollbar">
|
|
275
|
-
{tabs.map((tab) => {
|
|
276
|
-
const isActive = tab.id === activeTabId;
|
|
277
|
-
return (
|
|
278
|
-
<div
|
|
279
|
-
key={tab.id}
|
|
280
|
-
className={cn(
|
|
281
|
-
'inline-flex items-center gap-1 h-7 px-1.5 rounded-lg text-xs border max-w-[220px] shrink-0 transition-colors',
|
|
282
|
-
isActive
|
|
283
|
-
? 'bg-amber-50/80 border-amber-200 text-amber-900 shadow-[0_1px_2px_rgba(30,20,10,0.04)]'
|
|
284
|
-
: 'bg-[#f9f8f5] border-[#eee3d1] text-[#78644d] hover:bg-[#fff7ea] hover:text-[#2f2212]'
|
|
285
|
-
)}
|
|
286
|
-
>
|
|
287
|
-
<button
|
|
288
|
-
type="button"
|
|
289
|
-
onClick={() => setActiveTab(tab.id)}
|
|
290
|
-
className="truncate text-left px-1"
|
|
291
|
-
title={tab.title}
|
|
292
|
-
>
|
|
293
|
-
{tab.title || t('docBrowserTabUntitled')}
|
|
294
|
-
</button>
|
|
295
|
-
<button
|
|
296
|
-
type="button"
|
|
297
|
-
onClick={(event) => {
|
|
298
|
-
event.stopPropagation();
|
|
299
|
-
closeTab(tab.id);
|
|
300
|
-
}}
|
|
301
|
-
className="rounded p-0.5 hover:bg-black/10"
|
|
302
|
-
aria-label={t('docBrowserCloseTab')}
|
|
303
|
-
>
|
|
304
|
-
<X className="w-3 h-3" />
|
|
305
|
-
</button>
|
|
306
|
-
</div>
|
|
307
|
-
);
|
|
308
|
-
})}
|
|
309
|
-
<button
|
|
310
|
-
onClick={() => open(undefined, { kind: 'docs', newTab: true, title: 'Docs' })}
|
|
311
|
-
className="inline-flex items-center justify-center w-7 h-7 rounded-lg border border-[#eee3d1] bg-white text-[#78644d] hover:bg-[#fff7ea] hover:text-[#2f2212] shrink-0"
|
|
312
|
-
title={t('docBrowserNewTab')}
|
|
313
|
-
>
|
|
314
|
-
<Plus className="w-3.5 h-3.5" />
|
|
315
|
-
</button>
|
|
316
|
-
</div>
|
|
317
|
-
|
|
318
|
-
{isDocsTab && (
|
|
319
|
-
<div className="flex items-center gap-2 px-3.5 py-2 bg-white border-b border-gray-100 shrink-0">
|
|
320
|
-
<button
|
|
321
|
-
onClick={goBack}
|
|
322
|
-
disabled={!isDocsTab || (currentTab?.historyIndex ?? 0) <= 0}
|
|
323
|
-
className="p-1.5 rounded-md hover:bg-gray-100 disabled:opacity-30 disabled:cursor-not-allowed text-gray-600 transition-colors"
|
|
324
|
-
>
|
|
325
|
-
<ArrowLeft className="w-4 h-4" />
|
|
326
|
-
</button>
|
|
327
|
-
<button
|
|
328
|
-
onClick={goForward}
|
|
329
|
-
disabled={!isDocsTab || (currentTab?.historyIndex ?? 0) >= (currentTab?.history.length ?? 0) - 1}
|
|
330
|
-
className="p-1.5 rounded-md hover:bg-gray-100 disabled:opacity-30 disabled:cursor-not-allowed text-gray-600 transition-colors"
|
|
331
|
-
>
|
|
332
|
-
<ArrowRight className="w-4 h-4" />
|
|
333
|
-
</button>
|
|
334
|
-
|
|
335
|
-
<form onSubmit={handleUrlSubmit} className="flex-1 relative">
|
|
336
|
-
<Search className="w-3.5 h-3.5 absolute left-3 top-1/2 -translate-y-1/2 text-gray-400" />
|
|
337
|
-
<input
|
|
338
|
-
type="text"
|
|
339
|
-
value={urlInput}
|
|
340
|
-
onChange={(e) => setUrlInput(e.target.value)}
|
|
341
|
-
placeholder={t('docBrowserSearchPlaceholder')}
|
|
342
|
-
className="w-full h-8 pl-8 pr-3 rounded-lg bg-gray-50 border border-gray-200 text-xs text-gray-700 focus:outline-none focus:ring-1 focus:ring-primary/30 focus:border-primary/40 transition-colors placeholder:text-gray-400"
|
|
343
|
-
/>
|
|
344
|
-
</form>
|
|
345
|
-
</div>
|
|
346
|
-
)}
|
|
231
|
+
<DocBrowserPanelHeader
|
|
232
|
+
currentTab={currentTab}
|
|
233
|
+
customRenderer={customRenderer}
|
|
234
|
+
isDocked={isDocked}
|
|
235
|
+
isFullscreen={isFullscreen}
|
|
236
|
+
onClose={close}
|
|
237
|
+
onDragStart={startFloatDrag}
|
|
238
|
+
onToggleMode={toggleMode}
|
|
239
|
+
/>
|
|
347
240
|
|
|
348
|
-
<
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
{
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
241
|
+
<DocBrowserTabStrip
|
|
242
|
+
tabs={tabs}
|
|
243
|
+
activeTabId={activeTabId}
|
|
244
|
+
onOpenDocs={() => open(undefined, { kind: 'docs', newTab: true, title: 'Docs' })}
|
|
245
|
+
onSetActiveTab={setActiveTab}
|
|
246
|
+
onCloseTab={closeTab}
|
|
247
|
+
/>
|
|
248
|
+
|
|
249
|
+
<DocBrowserDocsToolbar
|
|
250
|
+
currentTab={currentTab}
|
|
251
|
+
isDocsTab={isDocsTab}
|
|
252
|
+
onBack={goBack}
|
|
253
|
+
onForward={goForward}
|
|
254
|
+
onSubmit={handleUrlSubmit}
|
|
255
|
+
onUrlInputChange={setUrlInput}
|
|
256
|
+
urlInput={urlInput}
|
|
257
|
+
/>
|
|
258
|
+
|
|
259
|
+
{customToolbar}
|
|
260
|
+
|
|
261
|
+
<DocBrowserFrameContent
|
|
262
|
+
activeTabId={activeTabId}
|
|
263
|
+
currentTab={currentTab}
|
|
264
|
+
currentUrl={currentUrl}
|
|
265
|
+
customContent={customContent}
|
|
266
|
+
iframeRef={iframeRef}
|
|
267
|
+
iframeReloadVersion={iframeReloadVersion}
|
|
268
|
+
iframeSandbox={iframeSandbox}
|
|
269
|
+
isDragging={floatInteraction?.kind === 'drag'}
|
|
270
|
+
isResizing={floatInteraction?.kind === 'resize'}
|
|
271
|
+
navVersion={navVersion}
|
|
272
|
+
/>
|
|
273
|
+
|
|
274
|
+
<DocBrowserExternalLink currentUrl={currentUrl} isDocsTab={isDocsTab} />
|
|
377
275
|
</>
|
|
378
276
|
);
|
|
379
277
|
|
|
@@ -390,22 +288,52 @@ export function DocBrowser({ displayMode = 'desktop' }: DocBrowserProps) {
|
|
|
390
288
|
);
|
|
391
289
|
}
|
|
392
290
|
|
|
393
|
-
|
|
291
|
+
return (
|
|
394
292
|
<div
|
|
395
293
|
data-testid="doc-browser-panel"
|
|
396
|
-
className={
|
|
397
|
-
|
|
294
|
+
className={cn(
|
|
295
|
+
'flex flex-col bg-white overflow-hidden relative',
|
|
296
|
+
isFullscreen
|
|
297
|
+
? 'fixed inset-0 z-[9999] h-[100dvh] w-screen rounded-none border-0 shadow-2xl'
|
|
298
|
+
: 'rounded-2xl shadow-2xl border border-gray-200',
|
|
299
|
+
)}
|
|
300
|
+
style={
|
|
301
|
+
isFullscreen
|
|
302
|
+
? undefined
|
|
303
|
+
: {
|
|
304
|
+
position: 'fixed',
|
|
305
|
+
left: floatRect.x,
|
|
306
|
+
top: floatRect.y,
|
|
307
|
+
width: floatRect.w,
|
|
308
|
+
height: floatRect.h,
|
|
309
|
+
zIndex: 9999,
|
|
310
|
+
}
|
|
311
|
+
}
|
|
398
312
|
>
|
|
399
313
|
{panelContent}
|
|
400
314
|
|
|
401
315
|
{!isDocked && !isFullscreen && (
|
|
402
316
|
<>
|
|
403
|
-
<div className="absolute top-0 left-0
|
|
404
|
-
<div
|
|
405
|
-
|
|
317
|
+
<div className="absolute top-0 left-0 h-1.5 w-full cursor-ns-resize z-20 hover:bg-primary/10 transition-colors" data-testid="doc-browser-resize-top" onPointerDown={startFloatResize('top')} />
|
|
318
|
+
<div
|
|
319
|
+
className="absolute top-0 left-0 w-1.5 h-full cursor-ew-resize z-20 hover:bg-primary/10 transition-colors"
|
|
320
|
+
data-testid="doc-browser-resize-left"
|
|
321
|
+
onPointerDown={startFloatResize('left')}
|
|
322
|
+
/>
|
|
323
|
+
<div
|
|
324
|
+
className="absolute top-0 right-0 w-1.5 h-full cursor-ew-resize z-20 hover:bg-primary/10 transition-colors"
|
|
325
|
+
data-testid="doc-browser-resize-right"
|
|
326
|
+
onPointerDown={startFloatResize('right')}
|
|
327
|
+
/>
|
|
328
|
+
<div
|
|
329
|
+
className="absolute bottom-0 left-0 h-1.5 w-full cursor-ns-resize z-20 hover:bg-primary/10 transition-colors"
|
|
330
|
+
data-testid="doc-browser-resize-bottom"
|
|
331
|
+
onPointerDown={startFloatResize('bottom')}
|
|
332
|
+
/>
|
|
406
333
|
<div
|
|
407
334
|
className="absolute bottom-0 right-0 w-4 h-4 cursor-se-resize z-30 flex items-center justify-center text-gray-300 hover:text-gray-500 transition-colors"
|
|
408
|
-
|
|
335
|
+
data-testid="doc-browser-resize-bottom-right"
|
|
336
|
+
onPointerDown={startFloatResize('bottom-right')}
|
|
409
337
|
>
|
|
410
338
|
<GripVertical className="w-3 h-3 rotate-[-45deg]" />
|
|
411
339
|
</div>
|
|
@@ -413,6 +341,4 @@ export function DocBrowser({ displayMode = 'desktop' }: DocBrowserProps) {
|
|
|
413
341
|
)}
|
|
414
342
|
</div>
|
|
415
343
|
);
|
|
416
|
-
|
|
417
|
-
return panel;
|
|
418
344
|
}
|
|
@@ -351,6 +351,30 @@ export type ServerPathBrowseView = {
|
|
|
351
351
|
|
|
352
352
|
export type ServerPathReadView = { requestedPath: string; resolvedPath: string; kind: "text" | "markdown" | "binary"; sizeBytes: number; truncated: boolean; text?: string; languageHint?: string | null };
|
|
353
353
|
|
|
354
|
+
export type PanelAppEntryView = {
|
|
355
|
+
id: string;
|
|
356
|
+
fileName: string;
|
|
357
|
+
title: string;
|
|
358
|
+
description?: string;
|
|
359
|
+
icon?: string;
|
|
360
|
+
contentPath: string;
|
|
361
|
+
updatedAt: string;
|
|
362
|
+
sizeBytes: number;
|
|
363
|
+
favorite: boolean;
|
|
364
|
+
lastOpenedAt?: string;
|
|
365
|
+
openCount: number;
|
|
366
|
+
};
|
|
367
|
+
|
|
368
|
+
export type PanelAppListView = {
|
|
369
|
+
workspacePath: string;
|
|
370
|
+
panelsPath: string;
|
|
371
|
+
entries: PanelAppEntryView[];
|
|
372
|
+
};
|
|
373
|
+
|
|
374
|
+
export type PanelAppPreferencesUpdateView = {
|
|
375
|
+
favorite?: boolean;
|
|
376
|
+
};
|
|
377
|
+
|
|
354
378
|
export type {
|
|
355
379
|
ChatSessionTypeCtaView,
|
|
356
380
|
ChatSessionTypeOptionView,
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
type I18nLanguage
|
|
14
14
|
} from './runtime/i18n-language-owner';
|
|
15
15
|
import { DESKTOP_UPDATE_LABELS } from './desktop-update-labels.utils';
|
|
16
|
+
import { DOC_BROWSER_LABELS } from './runtime/doc-browser-labels.utils';
|
|
16
17
|
import { MARKETPLACE_LABELS } from './marketplace';
|
|
17
18
|
import { PATH_PICKER_LABELS } from './runtime/i18n.path-picker';
|
|
18
19
|
import { PWA_LABELS } from './pwa';
|
|
@@ -476,18 +477,7 @@ export const LABELS: Record<string, { zh: string; en: string }> = {
|
|
|
476
477
|
enterTag: { zh: '输入后按回车...', en: 'Type and press Enter...' },
|
|
477
478
|
headerName: { zh: 'Header 名称', en: 'Header Name' },
|
|
478
479
|
headerValue: { zh: 'Header 值', en: 'Header Value' },
|
|
479
|
-
|
|
480
|
-
docBrowserTitle: { zh: '内嵌浏览器', en: 'Embedded Browser' },
|
|
481
|
-
docBrowserSearchPlaceholder: { zh: '搜索,也可以输入文档地址直接打开', en: 'Search, or enter a doc URL to open' },
|
|
482
|
-
docBrowserUrlPlaceholder: { zh: '输入文档路径,如 /guide/channels', en: 'Enter a doc path, e.g. /guide/channels' },
|
|
483
|
-
docBrowserOpenExternal: { zh: '文档中心打开', en: 'Open in Docs' },
|
|
484
|
-
docBrowserFloatMode: { zh: '悬浮窗口', en: 'Float Window' },
|
|
485
|
-
docBrowserDockMode: { zh: '固定到侧栏', en: 'Dock to Sidebar' },
|
|
486
|
-
docBrowserClose: { zh: '关闭', en: 'Close' },
|
|
487
|
-
docBrowserHelp: { zh: '帮助文档', en: 'Help Docs' },
|
|
488
|
-
docBrowserNewTab: { zh: '新建标签', en: 'New Tab' },
|
|
489
|
-
docBrowserCloseTab: { zh: '关闭标签', en: 'Close Tab' },
|
|
490
|
-
docBrowserTabUntitled: { zh: '未命名', en: 'Untitled' },
|
|
480
|
+
...DOC_BROWSER_LABELS,
|
|
491
481
|
...PATH_PICKER_LABELS,
|
|
492
482
|
...PWA_LABELS,
|
|
493
483
|
...CHANNEL_AUTH_LABELS,
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export const DOC_BROWSER_LABELS = {
|
|
2
|
+
docBrowserTitle: { zh: '内嵌浏览器', en: 'Embedded Browser' },
|
|
3
|
+
docBrowserSearchPlaceholder: { zh: '搜索,也可以输入文档地址直接打开', en: 'Search, or enter a doc URL to open' },
|
|
4
|
+
docBrowserUrlPlaceholder: { zh: '输入文档路径,如 /guide/channels', en: 'Enter a doc path, e.g. /guide/channels' },
|
|
5
|
+
docBrowserOpenExternal: { zh: '文档中心打开', en: 'Open in Docs' },
|
|
6
|
+
docBrowserFloatMode: { zh: '悬浮窗口', en: 'Float Window' },
|
|
7
|
+
docBrowserDockMode: { zh: '固定到侧栏', en: 'Dock to Sidebar' },
|
|
8
|
+
docBrowserClose: { zh: '关闭', en: 'Close' },
|
|
9
|
+
docBrowserHelp: { zh: '帮助文档', en: 'Help Docs' },
|
|
10
|
+
docBrowserNewTab: { zh: '新建标签', en: 'New Tab' },
|
|
11
|
+
docBrowserCloseTab: { zh: '关闭标签', en: 'Close Tab' },
|
|
12
|
+
docBrowserTabUntitled: { zh: '未命名', en: 'Untitled' },
|
|
13
|
+
panelAppsTitle: { zh: '面板应用', en: 'Panel Apps' },
|
|
14
|
+
panelAppsLoading: { zh: '加载面板应用中...', en: 'Loading panel apps...' },
|
|
15
|
+
panelAppsLoadFailed: { zh: '面板应用加载失败', en: 'Failed to load panel apps' },
|
|
16
|
+
panelAppsEmpty: { zh: '暂无面板应用', en: 'No panel apps yet' },
|
|
17
|
+
panelAppsRefresh: { zh: '刷新面板应用', en: 'Refresh panel apps' },
|
|
18
|
+
panelAppsRefreshCurrent: { zh: '刷新当前面板应用', en: 'Refresh current panel app' },
|
|
19
|
+
panelAppsSortLabel: { zh: '排序', en: 'Sort' },
|
|
20
|
+
panelAppsSortSmart: { zh: '智能排序', en: 'Smart' },
|
|
21
|
+
panelAppsFavorites: { zh: '收藏', en: 'Favorites' },
|
|
22
|
+
panelAppsSortRecentOpen: { zh: '最近打开', en: 'Recently Opened' },
|
|
23
|
+
panelAppsSortUpdated: { zh: '最近更新', en: 'Recently Updated' },
|
|
24
|
+
panelAppsSortName: { zh: '名称', en: 'Name' },
|
|
25
|
+
panelAppsFavorite: { zh: '收藏面板应用', en: 'Favorite panel app' },
|
|
26
|
+
panelAppsUnfavorite: { zh: '取消收藏面板应用', en: 'Unfavorite panel app' },
|
|
27
|
+
panelAppsLastOpened: { zh: '上次打开', en: 'Last opened' },
|
|
28
|
+
panelAppsUpdated: { zh: '更新于', en: 'Updated' },
|
|
29
|
+
} as const;
|