@nextclaw/ui 0.13.4 → 0.13.6
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-BCDb13Uv.js +8 -0
- package/dist/assets/app-presenter-provider-C-QMjK7m.js +3 -0
- package/dist/assets/arrow-left-rIXFA2Zy.js +1 -0
- package/dist/assets/channels-list-page-DnqwsKGe.js +8 -0
- package/dist/assets/chat-page-CsQx20xL.js +105 -0
- package/dist/assets/config-split-page-BwhGqmUS.js +1 -0
- package/dist/assets/confirm-dialog-DBkKcQ1q.js +5 -0
- package/dist/assets/desktop-update-config-BE9KjiXi.js +1 -0
- package/dist/assets/{dist-CpwDZDtv.js → dist-BI0bX8jv.js} +1 -1
- package/dist/assets/dist-C8JRt-tf.js +1 -0
- package/dist/assets/doc-browser-context-VHUOIUXL.js +1 -0
- package/dist/assets/doc-browser-eqdNEerh.js +1 -0
- package/dist/assets/doc-browser-lpF_pyx4.js +1 -0
- package/dist/assets/ellipsis-C9VB2CxT.js +1 -0
- package/dist/assets/es2015-BCw3Q7nY.js +41 -0
- package/dist/assets/external-link-Bp7_4s9t.js +1 -0
- package/dist/assets/index-BCq7kbC8.css +1 -0
- package/dist/assets/index-DTdPDqto.js +101 -0
- package/dist/assets/key-round-Ccy6oND3.js +1 -0
- package/dist/assets/loader-circle-OpH4EErP.js +1 -0
- package/dist/assets/{mcp-marketplace-page-Dvi8mCBd.js → mcp-marketplace-page-B-GxKOqS.js} +2 -2
- package/dist/assets/mcp-marketplace-page-Dm-0-w51.js +1 -0
- package/dist/assets/model-config-BXPnV_7n.js +1 -0
- package/dist/assets/notice-card-Dsbkl7u7.js +1 -0
- package/dist/assets/play-CvgYhNbo.js +1 -0
- package/dist/assets/plus-DHcP-K1U.js +1 -0
- package/dist/assets/popover-BTLZE5Iz.js +1 -0
- package/dist/assets/provider-models-C_yOh6DE.js +1 -0
- package/dist/assets/provider-scoped-model-input-DKI9KN7N.js +1 -0
- package/dist/assets/providers-list-Bko6B7dE.js +1 -0
- package/dist/assets/react-BqS8oMyM.js +8 -0
- package/dist/assets/refresh-cw-BIaj7txb.js +1 -0
- package/dist/assets/remote-DHn_b8QU.js +1 -0
- package/dist/assets/rotate-cw-DMqSkRJQ.js +1 -0
- package/dist/assets/runtime-config-page-DX_wSK1J.js +1 -0
- package/dist/assets/save-BEoORten.js +1 -0
- package/dist/assets/search-COFh_SFI.js +1 -0
- package/dist/assets/{search-config-G2O1fcRb.js → search-config-DA5By-tE.js} +1 -1
- package/dist/assets/{secrets-config-TenzxYbE.js → secrets-config-eDxG7iN4.js} +2 -2
- package/dist/assets/{status-dot-BSMG7ZaC.js → status-dot-oWBjTovo.js} +1 -1
- package/dist/assets/{tabs-D1CjpWNX.js → tabs-CGaY0BVO.js} +1 -1
- package/dist/assets/{tabs-custom-CRSN1ekg.js → tabs-custom-D2fPYb4G.js} +1 -1
- package/dist/assets/{tag-chip-DEashiw6.js → tag-chip-TVkgofS1.js} +1 -1
- package/dist/assets/tooltip-BwHPKBv9.js +1 -0
- package/dist/assets/trash-2-D3psAGrz.js +1 -0
- package/dist/assets/x-CHAxzyp2.js +1 -0
- package/dist/index.html +25 -23
- package/dist/logos/mimo.svg +9 -0
- package/package.json +8 -8
- package/public/logos/mimo.svg +9 -0
- package/src/app/components/layout/app-layout.tsx +10 -2
- package/src/features/agents/components/agents-page.tsx +6 -4
- package/src/features/chat/components/chat-welcome.test.tsx +15 -0
- package/src/features/chat/components/chat-welcome.tsx +4 -7
- package/src/features/chat/hooks/use-ncp-chat-page-data.ts +17 -10
- package/src/features/marketplace/components/marketplace-list-card.tsx +22 -1
- package/src/features/marketplace/components/marketplace-page.tsx +3 -1
- package/src/features/marketplace/hooks/use-marketplace.ts +3 -1
- package/src/features/marketplace/utils/marketplace-installed-cache.utils.ts +8 -0
- package/src/features/right-panel-resources/configs/right-panel-resource-routes.config.ts +70 -6
- package/src/features/right-panel-resources/utils/right-panel-resource-route-resolver.utils.test.ts +25 -1
- package/src/features/right-panel-resources/utils/right-panel-resource-route-resolver.utils.ts +1 -1
- package/src/features/right-panel-resources/utils/right-panel-resource-uri.utils.test.ts +42 -0
- package/src/features/right-panel-resources/utils/right-panel-resource-uri.utils.ts +22 -0
- package/src/features/service-apps/components/service-apps-panel.test.tsx +88 -0
- package/src/features/service-apps/components/service-apps-panel.tsx +246 -56
- package/src/features/service-apps/hooks/use-service-apps.ts +14 -0
- package/src/features/side-dock/components/side-dock.test.tsx +58 -3
- package/src/features/side-dock/components/side-dock.tsx +77 -24
- package/src/features/side-dock/configs/side-dock-built-in-items.config.ts +37 -34
- package/src/features/side-dock/index.ts +2 -0
- package/src/features/side-dock/managers/side-dock.manager.test.ts +84 -3
- package/src/features/side-dock/managers/side-dock.manager.ts +135 -12
- package/src/features/side-dock/types/side-dock.types.ts +11 -4
- package/src/features/side-dock/utils/side-dock-item.utils.test.ts +8 -8
- package/src/features/side-dock/utils/side-dock-item.utils.ts +13 -5
- 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/config/provider-form-support.test.ts +21 -0
- package/src/shared/components/config/provider-form-support.ts +38 -78
- package/src/shared/components/config/provider-form.tsx +54 -67
- package/src/shared/components/config/providers-list.tsx +211 -73
- package/src/shared/components/doc-browser/doc-browser-context.test.tsx +73 -0
- package/src/shared/components/doc-browser/doc-browser-context.tsx +3 -0
- package/src/shared/components/doc-browser/doc-browser-tab-strip.tsx +184 -98
- package/src/shared/components/doc-browser/doc-browser.test.tsx +168 -43
- package/src/shared/components/doc-browser/doc-browser.tsx +38 -7
- package/src/shared/components/doc-browser/index.ts +1 -0
- package/src/shared/components/doc-browser/managers/doc-browser.manager.ts +30 -6
- package/src/shared/components/doc-browser/stores/doc-browser.store.ts +29 -0
- package/src/shared/components/doc-browser/types/doc-browser.types.ts +23 -0
- package/src/shared/components/doc-browser/utils/doc-browser-state.utils.ts +7 -0
- package/src/shared/components/model-config.tsx +5 -4
- package/src/shared/components/provider-auth-section.tsx +2 -2
- package/src/shared/components/ui/switch.tsx +5 -2
- package/src/shared/hooks/use-config.ts +27 -6
- package/src/shared/lib/api/marketplace.types.ts +193 -0
- package/src/shared/lib/api/types.ts +23 -200
- package/src/shared/lib/api/utils/config.utils.ts +17 -7
- package/src/shared/lib/i18n/index.ts +10 -12
- package/src/shared/lib/i18n/{marketplace.ts → marketplace-labels.utils.ts} +3 -0
- package/src/shared/lib/i18n/runtime/doc-browser-labels.utils.ts +46 -2
- package/src/shared/lib/provider-models/index.test.ts +93 -15
- package/src/shared/lib/provider-models/index.ts +35 -20
- package/src/shared/lib/transport/request-raw-api-response.utils.ts +1 -1
- package/dist/assets/api-DruAcori.js +0 -15
- package/dist/assets/app-presenter-provider-DIcnNB9s.js +0 -3
- package/dist/assets/book-open-CRotBOF2.js +0 -1
- package/dist/assets/boxes-BvA-I63l.js +0 -1
- package/dist/assets/channels-list-page-wK3l7p5R.js +0 -8
- package/dist/assets/chat-page-DgehvdJT.js +0 -105
- package/dist/assets/config-split-page-BnTO1mAM.js +0 -1
- package/dist/assets/confirm-dialog-CGrKHbAW.js +0 -5
- package/dist/assets/createLucideIcon-Br1EFMEr.js +0 -1
- package/dist/assets/desktop-update-config-CehWxSSg.js +0 -1
- package/dist/assets/dist-rzakR_bu.js +0 -41
- package/dist/assets/doc-browser-BgxmVsTx.js +0 -1
- package/dist/assets/doc-browser-CZn5-Qbu.js +0 -1
- package/dist/assets/doc-browser-context-B8gk7HMr.js +0 -1
- package/dist/assets/external-link-DHjFdCpy.js +0 -1
- package/dist/assets/index-DORmh0Cy.css +0 -1
- package/dist/assets/index-DcFpvnge.js +0 -101
- package/dist/assets/key-round-D41Oe35x.js +0 -1
- package/dist/assets/mcp-marketplace-page-DBLhJWOJ.js +0 -1
- package/dist/assets/model-config-B-EJXGtO.js +0 -1
- package/dist/assets/play-C-0vo7hi.js +0 -1
- package/dist/assets/plus-Cr_akLp2.js +0 -1
- package/dist/assets/popover-YieElO8c.js +0 -1
- package/dist/assets/provider-scoped-model-input-BPRNGqYn.js +0 -1
- package/dist/assets/providers-list-CtBpL1ZY.js +0 -1
- package/dist/assets/react-B2X_ph0a.js +0 -1
- package/dist/assets/remote-BGHbHoHf.js +0 -1
- package/dist/assets/rotate-cw-jueVCNJQ.js +0 -1
- package/dist/assets/runtime-config-page-C1Hv7mad.js +0 -1
- package/dist/assets/save-aYdSdK97.js +0 -1
- package/dist/assets/search-CdhgFNwf.js +0 -1
- package/dist/assets/setting-row-BNu0qhOM.js +0 -1
- package/dist/assets/trash-2-DSWTsARW.js +0 -1
- package/dist/assets/x-D1v_vb54.js +0 -1
- package/src/shared/components/provider-enabled-field.tsx +0 -23
- /package/dist/assets/{config-hints-A7G_jMTU.js → config-hints-Ceiol9x4.js} +0 -0
|
@@ -2,6 +2,9 @@ import { useState, useRef, useCallback, useEffect } from 'react';
|
|
|
2
2
|
import {
|
|
3
3
|
DOCS_DEFAULT_BASE_URL,
|
|
4
4
|
DOC_BROWSER_HOME_TAB_KIND,
|
|
5
|
+
type DocBrowserDockControls,
|
|
6
|
+
type DocBrowserDockState,
|
|
7
|
+
type DocBrowserTab,
|
|
5
8
|
useDocBrowser,
|
|
6
9
|
} from './doc-browser-context';
|
|
7
10
|
import { normalizeDocUrl } from './utils/doc-browser-url.utils';
|
|
@@ -20,6 +23,7 @@ import { GripVertical } from 'lucide-react';
|
|
|
20
23
|
type DocBrowserProps = {
|
|
21
24
|
customTabRenderers?: DocBrowserCustomTabRenderers;
|
|
22
25
|
displayMode?: 'desktop' | 'fullscreen';
|
|
26
|
+
dockControls?: DocBrowserDockControls;
|
|
23
27
|
};
|
|
24
28
|
|
|
25
29
|
type FloatingPanelRect = { x: number; y: number; w: number; h: number };
|
|
@@ -35,6 +39,33 @@ type FloatingPanelInteraction = {
|
|
|
35
39
|
const FLOATING_PANEL_MARGIN = 40;
|
|
36
40
|
const FLOATING_PANEL_MIN_WIDTH = 360;
|
|
37
41
|
const FLOATING_PANEL_MIN_HEIGHT = 400;
|
|
42
|
+
const DEFAULT_IFRAME_SANDBOX = 'allow-same-origin allow-scripts allow-popups allow-forms';
|
|
43
|
+
|
|
44
|
+
function resolveIframeSandbox(
|
|
45
|
+
customRenderer: DocBrowserCustomTabRenderers[string] | undefined,
|
|
46
|
+
currentTab: DocBrowserTab | undefined,
|
|
47
|
+
): string {
|
|
48
|
+
return currentTab ? customRenderer?.getIframeSandbox?.(currentTab) ?? DEFAULT_IFRAME_SANDBOX : DEFAULT_IFRAME_SANDBOX;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function useDocBrowserDockAction(
|
|
52
|
+
dockControls: DocBrowserDockControls | undefined,
|
|
53
|
+
currentTab: DocBrowserTab | undefined,
|
|
54
|
+
dockState: DocBrowserDockState | undefined,
|
|
55
|
+
) {
|
|
56
|
+
return useCallback(() => {
|
|
57
|
+
if (!dockState?.canDock) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
if (dockState.isDocked) {
|
|
61
|
+
if (dockState.removable) {
|
|
62
|
+
dockControls?.unpinTab(currentTab);
|
|
63
|
+
}
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
dockControls?.pinTab(currentTab);
|
|
67
|
+
}, [currentTab, dockControls, dockState]);
|
|
68
|
+
}
|
|
38
69
|
|
|
39
70
|
function clamp(value: number, min: number, max: number): number {
|
|
40
71
|
return Math.min(max, Math.max(min, value));
|
|
@@ -49,10 +80,7 @@ function createInitialFloatingPanelRect(): FloatingPanelRect {
|
|
|
49
80
|
};
|
|
50
81
|
}
|
|
51
82
|
|
|
52
|
-
export function DocBrowser({
|
|
53
|
-
customTabRenderers = {},
|
|
54
|
-
displayMode = 'desktop',
|
|
55
|
-
}: DocBrowserProps) {
|
|
83
|
+
export function DocBrowser({ customTabRenderers = {}, displayMode = 'desktop', dockControls }: DocBrowserProps) {
|
|
56
84
|
const {
|
|
57
85
|
isOpen,
|
|
58
86
|
mode,
|
|
@@ -89,6 +117,7 @@ export function DocBrowser({
|
|
|
89
117
|
const canGoBack = activeHistoryIndex > 0;
|
|
90
118
|
const canGoForward = activeHistoryIndex < activeHistory.length - 1;
|
|
91
119
|
const customRenderer = currentTab ? customTabRenderers[currentTab.kind] : undefined;
|
|
120
|
+
const dockState = dockControls?.getDockState(currentTab);
|
|
92
121
|
|
|
93
122
|
useEffect(() => {
|
|
94
123
|
if (!isDocsTab) {
|
|
@@ -249,6 +278,8 @@ export function DocBrowser({
|
|
|
249
278
|
setIframeReloadVersion((version) => version + 1);
|
|
250
279
|
}, []);
|
|
251
280
|
|
|
281
|
+
const handleToggleDock = useDocBrowserDockAction(dockControls, currentTab, dockState);
|
|
282
|
+
|
|
252
283
|
if (!isOpen) return null;
|
|
253
284
|
|
|
254
285
|
const isDocked = mode === 'docked';
|
|
@@ -264,9 +295,7 @@ export function DocBrowser({
|
|
|
264
295
|
const customContent = customRenderParams
|
|
265
296
|
? customRenderer?.renderContent?.(customRenderParams) ?? (isHomeTab ? <DocBrowserHomePage /> : null)
|
|
266
297
|
: null;
|
|
267
|
-
const iframeSandbox = currentTab
|
|
268
|
-
? customRenderer?.getIframeSandbox?.(currentTab) ?? 'allow-same-origin allow-scripts allow-popups allow-forms'
|
|
269
|
-
: 'allow-same-origin allow-scripts allow-popups allow-forms';
|
|
298
|
+
const iframeSandbox = resolveIframeSandbox(customRenderer, currentTab);
|
|
270
299
|
|
|
271
300
|
const panelContent = (
|
|
272
301
|
<>
|
|
@@ -275,11 +304,13 @@ export function DocBrowser({
|
|
|
275
304
|
activeTabId={activeTabId}
|
|
276
305
|
canGoBack={canGoBack}
|
|
277
306
|
canGoForward={canGoForward}
|
|
307
|
+
dockState={dockState}
|
|
278
308
|
isDocked={isDocked}
|
|
279
309
|
isFullscreen={isFullscreen}
|
|
280
310
|
onGoBack={goBack}
|
|
281
311
|
onGoForward={goForward}
|
|
282
312
|
onOpenNewTab={openNewTab}
|
|
313
|
+
onToggleDock={handleToggleDock}
|
|
283
314
|
onSetActiveTab={setActiveTab}
|
|
284
315
|
onCloseTab={closeTab}
|
|
285
316
|
onClose={close}
|
|
@@ -50,9 +50,23 @@ function areActiveHistoryEntriesEquivalent(
|
|
|
50
50
|
current: DocBrowserActiveHistoryEntry,
|
|
51
51
|
next: DocBrowserActiveHistoryEntry,
|
|
52
52
|
): boolean {
|
|
53
|
+
const currentUri = current.resourceUri ?? current.url;
|
|
54
|
+
const nextUri = next.resourceUri ?? next.url;
|
|
53
55
|
return current.tabId === next.tabId
|
|
54
56
|
&& current.kind === next.kind
|
|
55
|
-
&& routeResolver.areUrlsEquivalent(
|
|
57
|
+
&& routeResolver.areUrlsEquivalent(currentUri, nextUri, current.kind, next.kind);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function createActiveHistoryEntryFromTarget(
|
|
61
|
+
tabId: string,
|
|
62
|
+
target: DocBrowserRouteTarget,
|
|
63
|
+
): DocBrowserActiveHistoryEntry {
|
|
64
|
+
return {
|
|
65
|
+
kind: target.kind,
|
|
66
|
+
resourceUri: target.resourceUri ?? target.url,
|
|
67
|
+
tabId,
|
|
68
|
+
url: target.url,
|
|
69
|
+
};
|
|
56
70
|
}
|
|
57
71
|
|
|
58
72
|
function pushActiveHistory(
|
|
@@ -90,12 +104,14 @@ function restoreActiveHistoryEntry(
|
|
|
90
104
|
if (!tab) {
|
|
91
105
|
return state;
|
|
92
106
|
}
|
|
107
|
+
const restoreUri = entry.resourceUri ?? entry.url;
|
|
93
108
|
const target = routeResolver.resolveOpenTarget({
|
|
94
109
|
activeTab: tab,
|
|
95
110
|
kind: entry.kind,
|
|
96
|
-
url:
|
|
111
|
+
url: restoreUri,
|
|
97
112
|
});
|
|
98
113
|
const historyIndex = findTabHistoryIndex(routeResolver, tab, target.url);
|
|
114
|
+
const shouldPreserveCurrentTitle = entry.resourceUri !== undefined && entry.resourceUri === tab.resourceUri;
|
|
99
115
|
return updateTab(
|
|
100
116
|
{
|
|
101
117
|
...state,
|
|
@@ -107,9 +123,11 @@ function restoreActiveHistoryEntry(
|
|
|
107
123
|
...currentTab,
|
|
108
124
|
currentUrl: target.url,
|
|
109
125
|
dedupeKey: target.dedupeKey,
|
|
126
|
+
dockIcon: target.dockIcon ?? currentTab.dockIcon,
|
|
110
127
|
historyIndex: historyIndex >= 0 ? historyIndex : currentTab.historyIndex,
|
|
111
128
|
kind: target.kind,
|
|
112
|
-
|
|
129
|
+
resourceUri: target.resourceUri ?? target.url,
|
|
130
|
+
title: shouldPreserveCurrentTitle ? currentTab.title : target.title,
|
|
113
131
|
}),
|
|
114
132
|
);
|
|
115
133
|
}
|
|
@@ -125,6 +143,8 @@ function updateTabForOpen(
|
|
|
125
143
|
...tab,
|
|
126
144
|
title: options?.title || target.title || tab.title,
|
|
127
145
|
kind: target.kind,
|
|
146
|
+
resourceUri: target.resourceUri ?? target.url,
|
|
147
|
+
dockIcon: options?.dockIcon ?? target.dockIcon,
|
|
128
148
|
dedupeKey,
|
|
129
149
|
};
|
|
130
150
|
|
|
@@ -180,12 +200,12 @@ function openResolvedDocBrowserState(
|
|
|
180
200
|
};
|
|
181
201
|
const shouldPushActiveHistory = shouldActivate || matchedTab.id === prev.activeTabId;
|
|
182
202
|
return shouldPushActiveHistory
|
|
183
|
-
? pushActiveHistory(routeResolver, nextState,
|
|
203
|
+
? pushActiveHistory(routeResolver, nextState, createActiveHistoryEntryFromTarget(matchedTab.id, target))
|
|
184
204
|
: nextState;
|
|
185
205
|
}
|
|
186
206
|
|
|
187
207
|
if (shouldForceNewTab || dedupeKey || !activeTab || activeTab.kind !== target.kind) {
|
|
188
|
-
const newTab = createDocBrowserTab(target.url, target.kind, title ?? target.title, dedupeKey);
|
|
208
|
+
const newTab = createDocBrowserTab(target.url, target.kind, title ?? target.title, dedupeKey, target.resourceUri ?? target.url, options?.dockIcon ?? target.dockIcon);
|
|
189
209
|
if (isClosedDefaultHomeState(prev, activeTab)) {
|
|
190
210
|
const nextState = {
|
|
191
211
|
...prev,
|
|
@@ -213,7 +233,7 @@ function openResolvedDocBrowserState(
|
|
|
213
233
|
return pushActiveHistory(
|
|
214
234
|
routeResolver,
|
|
215
235
|
{ ...next, isOpen: true },
|
|
216
|
-
|
|
236
|
+
createActiveHistoryEntryFromTarget(prev.activeTabId, target),
|
|
217
237
|
);
|
|
218
238
|
}
|
|
219
239
|
|
|
@@ -290,9 +310,11 @@ export class DocBrowserManager {
|
|
|
290
310
|
...tab,
|
|
291
311
|
currentUrl: target.url,
|
|
292
312
|
dedupeKey: target.dedupeKey,
|
|
313
|
+
dockIcon: target.dockIcon,
|
|
293
314
|
...appendManualNavigation(tab, target.url),
|
|
294
315
|
kind: target.kind,
|
|
295
316
|
navVersion: tab.navVersion + 1,
|
|
317
|
+
resourceUri: target.resourceUri ?? target.url,
|
|
296
318
|
title: target.title,
|
|
297
319
|
};
|
|
298
320
|
});
|
|
@@ -328,8 +350,10 @@ export class DocBrowserManager {
|
|
|
328
350
|
...tab,
|
|
329
351
|
currentUrl: target.url,
|
|
330
352
|
dedupeKey: target.dedupeKey,
|
|
353
|
+
dockIcon: target.dockIcon,
|
|
331
354
|
...appendManualNavigation(tab, target.url),
|
|
332
355
|
kind: target.kind,
|
|
356
|
+
resourceUri: target.resourceUri ?? target.url,
|
|
333
357
|
title: target.title,
|
|
334
358
|
};
|
|
335
359
|
});
|
|
@@ -2,6 +2,7 @@ import { create } from 'zustand';
|
|
|
2
2
|
import { createJSONStorage, persist } from 'zustand/middleware';
|
|
3
3
|
import type {
|
|
4
4
|
DocBrowserActiveHistoryEntry,
|
|
5
|
+
DocBrowserDockIcon,
|
|
5
6
|
DocBrowserMode,
|
|
6
7
|
DocBrowserState,
|
|
7
8
|
DocBrowserStateUpdate,
|
|
@@ -42,6 +43,22 @@ function normalizePersistedNumber(value: unknown, min: number, max: number, fall
|
|
|
42
43
|
: fallback;
|
|
43
44
|
}
|
|
44
45
|
|
|
46
|
+
function normalizePersistedDockIcon(value: unknown): DocBrowserDockIcon | undefined {
|
|
47
|
+
if (!isRecord(value) || typeof value.type !== 'string') {
|
|
48
|
+
return undefined;
|
|
49
|
+
}
|
|
50
|
+
if (value.type === 'builtin' && typeof value.name === 'string' && value.name.trim().length > 0) {
|
|
51
|
+
return { type: 'builtin', name: value.name.trim() };
|
|
52
|
+
}
|
|
53
|
+
if (value.type === 'url' && typeof value.url === 'string' && value.url.trim().length > 0) {
|
|
54
|
+
return { type: 'url', url: value.url.trim() };
|
|
55
|
+
}
|
|
56
|
+
if (value.type === 'text' && typeof value.value === 'string' && value.value.trim().length > 0) {
|
|
57
|
+
return { type: 'text', value: value.value.trim() };
|
|
58
|
+
}
|
|
59
|
+
return undefined;
|
|
60
|
+
}
|
|
61
|
+
|
|
45
62
|
function normalizePersistedDocBrowserTab(value: unknown): DocBrowserTab | null {
|
|
46
63
|
if (
|
|
47
64
|
!isRecord(value)
|
|
@@ -59,12 +76,18 @@ function normalizePersistedDocBrowserTab(value: unknown): DocBrowserTab | null {
|
|
|
59
76
|
const dedupeKey = typeof value.dedupeKey === 'string' && value.dedupeKey.trim().length > 0
|
|
60
77
|
? value.dedupeKey
|
|
61
78
|
: undefined;
|
|
79
|
+
const resourceUri = typeof value.resourceUri === 'string' && value.resourceUri.trim().length > 0
|
|
80
|
+
? value.resourceUri.trim()
|
|
81
|
+
: undefined;
|
|
82
|
+
const dockIcon = normalizePersistedDockIcon(value.dockIcon);
|
|
62
83
|
|
|
63
84
|
return {
|
|
64
85
|
id: value.id,
|
|
65
86
|
kind: value.kind,
|
|
66
87
|
title: value.title,
|
|
67
88
|
currentUrl: history[historyIndex] ?? value.currentUrl,
|
|
89
|
+
resourceUri,
|
|
90
|
+
dockIcon,
|
|
68
91
|
dedupeKey,
|
|
69
92
|
history,
|
|
70
93
|
historyIndex,
|
|
@@ -75,6 +98,7 @@ function normalizePersistedDocBrowserTab(value: unknown): DocBrowserTab | null {
|
|
|
75
98
|
function createActiveHistoryEntryFromTab(tab: DocBrowserTab): DocBrowserActiveHistoryEntry {
|
|
76
99
|
return {
|
|
77
100
|
kind: tab.kind,
|
|
101
|
+
resourceUri: tab.resourceUri,
|
|
78
102
|
tabId: tab.id,
|
|
79
103
|
url: tab.currentUrl,
|
|
80
104
|
};
|
|
@@ -95,8 +119,13 @@ function normalizePersistedActiveHistoryEntry(
|
|
|
95
119
|
return null;
|
|
96
120
|
}
|
|
97
121
|
|
|
122
|
+
const resourceUri = typeof value.resourceUri === 'string' && value.resourceUri.trim().length > 0
|
|
123
|
+
? value.resourceUri.trim()
|
|
124
|
+
: undefined;
|
|
125
|
+
|
|
98
126
|
return {
|
|
99
127
|
kind: value.kind,
|
|
128
|
+
resourceUri,
|
|
100
129
|
tabId: value.tabId,
|
|
101
130
|
url: value.url,
|
|
102
131
|
};
|
|
@@ -6,6 +6,8 @@ export type DocBrowserTab = {
|
|
|
6
6
|
kind: DocBrowserTabKind;
|
|
7
7
|
title: string;
|
|
8
8
|
currentUrl: string;
|
|
9
|
+
resourceUri?: string;
|
|
10
|
+
dockIcon?: DocBrowserDockIcon;
|
|
9
11
|
dedupeKey?: string;
|
|
10
12
|
history: string[];
|
|
11
13
|
historyIndex: number;
|
|
@@ -14,13 +16,32 @@ export type DocBrowserTab = {
|
|
|
14
16
|
};
|
|
15
17
|
|
|
16
18
|
export type DocBrowserRouteTarget = {
|
|
19
|
+
dockIcon?: DocBrowserDockIcon;
|
|
17
20
|
dedupeKey?: string;
|
|
18
21
|
historyPolicy: 'managed' | 'none';
|
|
19
22
|
kind: DocBrowserTabKind;
|
|
23
|
+
resourceUri?: string;
|
|
20
24
|
title: string;
|
|
21
25
|
url: string;
|
|
22
26
|
};
|
|
23
27
|
|
|
28
|
+
export type DocBrowserDockIcon =
|
|
29
|
+
| { type: 'builtin'; name: string }
|
|
30
|
+
| { type: 'url'; url: string }
|
|
31
|
+
| { type: 'text'; value: string };
|
|
32
|
+
|
|
33
|
+
export type DocBrowserDockState = {
|
|
34
|
+
canDock: boolean;
|
|
35
|
+
isDocked: boolean;
|
|
36
|
+
removable: boolean;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export type DocBrowserDockControls = {
|
|
40
|
+
getDockState: (tab?: DocBrowserTab) => DocBrowserDockState;
|
|
41
|
+
pinTab: (tab?: DocBrowserTab) => void;
|
|
42
|
+
unpinTab: (tab?: DocBrowserTab) => void;
|
|
43
|
+
};
|
|
44
|
+
|
|
24
45
|
export type DocBrowserRouteResolver = {
|
|
25
46
|
areUrlsEquivalent: (
|
|
26
47
|
currentUrl: string,
|
|
@@ -38,6 +59,7 @@ export type DocBrowserRouteResolver = {
|
|
|
38
59
|
|
|
39
60
|
export type DocBrowserActiveHistoryEntry = {
|
|
40
61
|
kind: DocBrowserTabKind;
|
|
62
|
+
resourceUri?: string;
|
|
41
63
|
tabId: string;
|
|
42
64
|
url: string;
|
|
43
65
|
};
|
|
@@ -45,6 +67,7 @@ export type DocBrowserActiveHistoryEntry = {
|
|
|
45
67
|
export type DocBrowserOpenOptions = {
|
|
46
68
|
activate?: boolean;
|
|
47
69
|
dedupeKey?: string;
|
|
70
|
+
dockIcon?: DocBrowserDockIcon;
|
|
48
71
|
newTab?: boolean;
|
|
49
72
|
title?: string;
|
|
50
73
|
kind?: DocBrowserTabKind;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type {
|
|
2
2
|
DocBrowserActiveHistoryEntry,
|
|
3
|
+
DocBrowserDockIcon,
|
|
3
4
|
DocBrowserState,
|
|
4
5
|
DocBrowserTab,
|
|
5
6
|
DocBrowserTabKind,
|
|
@@ -23,14 +24,19 @@ export function createDocBrowserTab(
|
|
|
23
24
|
kind: DocBrowserTabKind,
|
|
24
25
|
title?: string,
|
|
25
26
|
dedupeKey?: string,
|
|
27
|
+
resourceUri?: string,
|
|
28
|
+
dockIcon?: DocBrowserDockIcon,
|
|
26
29
|
): DocBrowserTab {
|
|
27
30
|
const tabTitle = title?.trim() || inferTabTitle(url, kind, kind === 'docs' ? t('docBrowserHelp') : 'Detail');
|
|
31
|
+
const normalizedResourceUri = resourceUri?.trim();
|
|
28
32
|
|
|
29
33
|
return {
|
|
30
34
|
id: nextTabId(),
|
|
31
35
|
kind,
|
|
32
36
|
title: tabTitle,
|
|
33
37
|
currentUrl: url,
|
|
38
|
+
resourceUri: normalizedResourceUri ? normalizedResourceUri : undefined,
|
|
39
|
+
dockIcon,
|
|
34
40
|
dedupeKey,
|
|
35
41
|
history: [url],
|
|
36
42
|
historyIndex: 0,
|
|
@@ -41,6 +47,7 @@ export function createDocBrowserTab(
|
|
|
41
47
|
export function createDocBrowserActiveHistoryEntry(tab: DocBrowserTab): DocBrowserActiveHistoryEntry {
|
|
42
48
|
return {
|
|
43
49
|
kind: tab.kind,
|
|
50
|
+
resourceUri: tab.resourceUri,
|
|
44
51
|
tabId: tab.id,
|
|
45
52
|
url: tab.currentUrl,
|
|
46
53
|
};
|
|
@@ -4,7 +4,7 @@ import { Input } from '@/shared/components/ui/input';
|
|
|
4
4
|
import { Label } from '@/shared/components/ui/label';
|
|
5
5
|
import { Skeleton } from '@/shared/components/ui/skeleton';
|
|
6
6
|
import { ProviderScopedModelInput } from '@/shared/components/common/provider-scoped-model-input';
|
|
7
|
-
import { useConfig,
|
|
7
|
+
import { useConfig, useConfigSchema, useProviderTemplates, useProviders, useUpdateModel } from '@/shared/hooks/use-config';
|
|
8
8
|
import { hintForPath } from '@/shared/lib/config-hints';
|
|
9
9
|
import { t } from '@/shared/lib/i18n';
|
|
10
10
|
import { buildProviderModelCatalog } from '@/shared/lib/provider-models';
|
|
@@ -85,7 +85,8 @@ function ModelConfigForm(props: {
|
|
|
85
85
|
|
|
86
86
|
export function ModelConfig() {
|
|
87
87
|
const { data: config, isLoading } = useConfig();
|
|
88
|
-
const { data:
|
|
88
|
+
const { data: providersView } = useProviders();
|
|
89
|
+
const { data: templatesView } = useProviderTemplates();
|
|
89
90
|
const { data: schema } = useConfigSchema();
|
|
90
91
|
const updateModel = useUpdateModel();
|
|
91
92
|
const uiHints = schema?.uiHints;
|
|
@@ -93,8 +94,8 @@ export function ModelConfig() {
|
|
|
93
94
|
const workspaceHint = hintForPath('agents.defaults.workspace', uiHints);
|
|
94
95
|
|
|
95
96
|
const providerCatalog = useMemo(
|
|
96
|
-
() => buildProviderModelCatalog({
|
|
97
|
-
[config,
|
|
97
|
+
() => buildProviderModelCatalog({ providersView, templatesView, config, onlyConfigured: true }),
|
|
98
|
+
[config, providersView, templatesView]
|
|
98
99
|
);
|
|
99
100
|
const modelHelpText = t('modelIdentifierHelp') || modelHint?.help || '';
|
|
100
101
|
const initialModel = (config?.agents?.defaults?.model || '').trim();
|
|
@@ -2,10 +2,10 @@ import { Button } from '@/shared/components/ui/button';
|
|
|
2
2
|
import { Label } from '@/shared/components/ui/label';
|
|
3
3
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/shared/components/ui/select';
|
|
4
4
|
import { t } from '@/shared/lib/i18n';
|
|
5
|
-
import type {
|
|
5
|
+
import type { ProviderTemplateView } from '@/shared/lib/api';
|
|
6
6
|
import { ProviderPillSelector } from './provider-pill-selector';
|
|
7
7
|
|
|
8
|
-
type ProviderAuthSectionProps = { providerAuth:
|
|
8
|
+
type ProviderAuthSectionProps = { providerAuth: ProviderTemplateView['auth']; providerAuthNote: string; providerAuthMethodOptions: Array<{ value: string; label: string }>; providerAuthMethodsCount: number; selectedAuthMethodHint: string; shouldUseAuthMethodPills: boolean; resolvedAuthMethodId: string; onAuthMethodChange: (value: string) => void; onStartProviderAuth: () => void; onImportProviderAuthFromCli: () => void; startPending: boolean; importPending: boolean; authSessionId: string | null; authStatusMessage: string };
|
|
9
9
|
|
|
10
10
|
export function ProviderAuthSection(props: ProviderAuthSectionProps) {
|
|
11
11
|
const {
|
|
@@ -4,10 +4,11 @@ import { cn } from '@/shared/lib/utils';
|
|
|
4
4
|
interface SwitchProps extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, 'onChange'> {
|
|
5
5
|
checked?: boolean;
|
|
6
6
|
onCheckedChange?: (checked: boolean) => void;
|
|
7
|
+
thumbClassName?: string;
|
|
7
8
|
}
|
|
8
9
|
|
|
9
10
|
const Switch = React.forwardRef<HTMLButtonElement, SwitchProps>(
|
|
10
|
-
({ className, checked = false, onCheckedChange, ...props }, ref) => {
|
|
11
|
+
({ className, checked = false, onCheckedChange, thumbClassName, ...props }, ref) => {
|
|
11
12
|
return (
|
|
12
13
|
<button
|
|
13
14
|
type="button"
|
|
@@ -23,9 +24,11 @@ const Switch = React.forwardRef<HTMLButtonElement, SwitchProps>(
|
|
|
23
24
|
{...props}
|
|
24
25
|
>
|
|
25
26
|
<span
|
|
27
|
+
data-state={checked ? 'checked' : 'unchecked'}
|
|
26
28
|
className={cn(
|
|
27
29
|
'pointer-events-none block h-5 w-5 rounded-full bg-white shadow-md ring-0 transition-transform duration-fast',
|
|
28
|
-
checked ? 'translate-x-5' : 'translate-x-0'
|
|
30
|
+
checked ? 'translate-x-5' : 'translate-x-0',
|
|
31
|
+
thumbClassName
|
|
29
32
|
)}
|
|
30
33
|
/>
|
|
31
34
|
</button>
|
|
@@ -8,6 +8,8 @@ import {
|
|
|
8
8
|
fetchConfig,
|
|
9
9
|
fetchConfigMeta,
|
|
10
10
|
fetchConfigSchema,
|
|
11
|
+
fetchProviders,
|
|
12
|
+
fetchProviderTemplates,
|
|
11
13
|
updateModel,
|
|
12
14
|
updateSearch,
|
|
13
15
|
createProvider,
|
|
@@ -61,6 +63,23 @@ export function useConfigMeta() {
|
|
|
61
63
|
});
|
|
62
64
|
}
|
|
63
65
|
|
|
66
|
+
export function useProviders() {
|
|
67
|
+
return useQuery({
|
|
68
|
+
queryKey: ['providers'],
|
|
69
|
+
queryFn: fetchProviders,
|
|
70
|
+
staleTime: 30_000,
|
|
71
|
+
refetchOnWindowFocus: true
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export function useProviderTemplates() {
|
|
76
|
+
return useQuery({
|
|
77
|
+
queryKey: ['provider-templates'],
|
|
78
|
+
queryFn: fetchProviderTemplates,
|
|
79
|
+
staleTime: Infinity
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
|
|
64
83
|
export function useConfigSchema() {
|
|
65
84
|
return useQuery({
|
|
66
85
|
queryKey: ['config-schema'],
|
|
@@ -104,12 +123,14 @@ export function useUpdateProvider() {
|
|
|
104
123
|
const queryClient = useQueryClient();
|
|
105
124
|
|
|
106
125
|
return useMutation({
|
|
107
|
-
mutationFn: ({ provider, data }: { provider: string; data: unknown }) =>
|
|
126
|
+
mutationFn: ({ provider, data }: { provider: string; data: unknown; silentSuccess?: boolean }) =>
|
|
108
127
|
updateProvider(provider, data as Parameters<typeof updateProvider>[1]),
|
|
109
|
-
onSuccess: () => {
|
|
128
|
+
onSuccess: (_data, variables) => {
|
|
129
|
+
queryClient.invalidateQueries({ queryKey: ['providers'] });
|
|
110
130
|
queryClient.invalidateQueries({ queryKey: ['config'] });
|
|
111
|
-
|
|
112
|
-
|
|
131
|
+
if (!variables.silentSuccess) {
|
|
132
|
+
toast.success(t('configSaved'));
|
|
133
|
+
}
|
|
113
134
|
},
|
|
114
135
|
onError: (error: Error) => {
|
|
115
136
|
toast.error(t('configSaveFailed') + ': ' + error.message);
|
|
@@ -124,8 +145,8 @@ export function useCreateProvider() {
|
|
|
124
145
|
mutationFn: ({ data }: { data?: unknown }) =>
|
|
125
146
|
createProvider((data ?? {}) as Parameters<typeof createProvider>[0]),
|
|
126
147
|
onSuccess: () => {
|
|
148
|
+
queryClient.invalidateQueries({ queryKey: ['providers'] });
|
|
127
149
|
queryClient.invalidateQueries({ queryKey: ['config'] });
|
|
128
|
-
queryClient.invalidateQueries({ queryKey: ['config-meta'] });
|
|
129
150
|
toast.success(t('configSaved'));
|
|
130
151
|
},
|
|
131
152
|
onError: (error: Error) => {
|
|
@@ -140,8 +161,8 @@ export function useDeleteProvider() {
|
|
|
140
161
|
return useMutation({
|
|
141
162
|
mutationFn: ({ provider }: { provider: string }) => deleteProvider(provider),
|
|
142
163
|
onSuccess: () => {
|
|
164
|
+
queryClient.invalidateQueries({ queryKey: ['providers'] });
|
|
143
165
|
queryClient.invalidateQueries({ queryKey: ['config'] });
|
|
144
|
-
queryClient.invalidateQueries({ queryKey: ['config-meta'] });
|
|
145
166
|
toast.success(t('configSaved'));
|
|
146
167
|
},
|
|
147
168
|
onError: (error: Error) => {
|