@nextclaw/ui 0.12.35-beta.5 → 0.12.35-beta.7
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 +126 -0
- package/dist/assets/api-DruAcori.js +15 -0
- package/dist/assets/{book-open-qc0UVn6m.js → book-open-cPX0Za7Z.js} +1 -1
- package/dist/assets/channels-list-page-D-tv11dc.js +8 -0
- package/dist/assets/chat-page-MyXEZI8R.js +105 -0
- package/dist/assets/{config-split-page-DW8tP9Bc.js → config-split-page-BFh_Qixx.js} +1 -1
- package/dist/assets/confirm-dialog-B0TP-vPd.js +5 -0
- package/dist/assets/{createLucideIcon-CA0sgoMJ.js → createLucideIcon-Br1EFMEr.js} +1 -1
- package/dist/assets/{desktop-update-config-DARSwQG8.js → desktop-update-config-oZpIKKtS.js} +1 -1
- package/dist/assets/{dist-D5zsy7pX.js → dist-CpwDZDtv.js} +1 -1
- package/dist/assets/{dist-DNxIa3pe.js → dist-rzakR_bu.js} +1 -1
- package/dist/assets/doc-browser-B2OFjScU.js +3 -0
- package/dist/assets/doc-browser-BrUQEZ1j.js +1 -0
- package/dist/assets/doc-browser-context-DB6TTHeE.js +1 -0
- package/dist/assets/doc-browser-route-registry.utils-x5ijvk0u.js +1 -0
- package/dist/assets/{external-link-C9fbyi6k.js → external-link-C3usm3se.js} +1 -1
- package/dist/assets/index-D2H2CKYR.js +101 -0
- package/dist/assets/index-v0fAGXQW.css +1 -0
- package/dist/assets/{mcp-marketplace-page-BiExW5wq.js → mcp-marketplace-page-CegP14Hl.js} +2 -2
- package/dist/assets/mcp-marketplace-page-MjpFEEkY.js +1 -0
- package/dist/assets/model-config-fIE_Asps.js +1 -0
- package/dist/assets/play-CqNi0u02.js +1 -0
- package/dist/assets/plus-BcIC8dG1.js +1 -0
- package/dist/assets/{provider-scoped-model-input-Bk8jlpRm.js → provider-scoped-model-input-D4zRsGZ6.js} +1 -1
- package/dist/assets/providers-list-CqsOk2hr.js +1 -0
- package/dist/assets/react-B2X_ph0a.js +1 -0
- package/dist/assets/remote-BMWQUPDW.js +1 -0
- package/dist/assets/runtime-config-page-BJ86cC8a.js +1 -0
- package/dist/assets/{save-CG4Hb1u8.js → save-D10M5vQg.js} +1 -1
- package/dist/assets/{search-lWpOIHEn.js → search-CdhgFNwf.js} +1 -1
- package/dist/assets/search-config-TbCPlRE7.js +1 -0
- package/dist/assets/{secrets-config-ZbLzjIRx.js → secrets-config-DBIEKP_V.js} +1 -1
- package/dist/assets/{setting-row-CNfTKDjc.js → setting-row-8VkukJoH.js} +1 -1
- package/dist/assets/{tabs-custom-BbZoysAl.js → tabs-custom-BBXu1OGs.js} +1 -1
- package/dist/assets/{tag-chip-Dl8JbhC7.js → tag-chip-ix15P-r4.js} +1 -1
- package/dist/assets/wrench-qVIwShTF.js +1 -0
- package/dist/assets/x-Chb07O-Y.js +1 -0
- package/dist/index.html +17 -15
- package/package.json +8 -8
- package/src/features/panel-apps/components/panel-app-list-item.test.tsx +60 -0
- package/src/features/panel-apps/components/panel-app-list-item.tsx +120 -30
- package/src/features/panel-apps/components/panel-apps-list.tsx +8 -1
- package/src/features/panel-apps/hooks/use-panel-apps.ts +10 -0
- package/src/features/panel-apps/managers/panel-app-bridge.manager.test.ts +85 -1
- package/src/features/panel-apps/managers/panel-app-bridge.manager.ts +123 -6
- package/src/features/panel-apps/utils/panel-app-doc-browser.utils.tsx +7 -3
- package/src/features/panel-apps/utils/panel-app-view.utils.test.ts +66 -0
- package/src/features/panel-apps/utils/panel-app-view.utils.ts +13 -2
- package/src/index.css +23 -0
- package/src/shared/components/doc-browser/doc-browser-context.test.tsx +170 -0
- package/src/shared/components/doc-browser/doc-browser-context.tsx +3 -0
- package/src/shared/components/doc-browser/doc-browser-home-page.tsx +95 -0
- package/src/shared/components/doc-browser/doc-browser-panel-parts.tsx +0 -90
- package/src/shared/components/doc-browser/doc-browser-tab-strip.tsx +117 -44
- package/src/shared/components/doc-browser/doc-browser.test.tsx +159 -1
- package/src/shared/components/doc-browser/doc-browser.tsx +42 -23
- package/src/shared/components/doc-browser/managers/doc-browser.manager.ts +183 -71
- package/src/shared/components/doc-browser/stores/doc-browser.store.ts +52 -1
- package/src/shared/components/doc-browser/types/doc-browser.types.ts +9 -0
- package/src/shared/components/doc-browser/utils/doc-browser-route-registry.utils.ts +232 -0
- package/src/shared/components/doc-browser/utils/doc-browser-state.utils.ts +11 -0
- package/src/shared/components/doc-browser/utils/doc-browser-url.utils.ts +2 -0
- package/src/shared/lib/api/types.ts +1 -0
- package/src/shared/lib/i18n/runtime/doc-browser-labels.utils.ts +10 -0
- package/dist/assets/api-BTZui7nj.js +0 -15
- package/dist/assets/channels-list-page-DBtU_M1Y.js +0 -8
- package/dist/assets/chat-page-BcoJYo91.js +0 -105
- package/dist/assets/doc-browser-B9skxzVo.js +0 -1
- package/dist/assets/doc-browser-CwyITRXx.js +0 -1
- package/dist/assets/doc-browser-context-Do2qSrB8.js +0 -1
- package/dist/assets/index-BYRP_BMj.css +0 -1
- package/dist/assets/index-DckPGTdM.js +0 -103
- package/dist/assets/mcp-marketplace-page-Cj6MDSkE.js +0 -1
- package/dist/assets/model-config-DbdY7fF1.js +0 -1
- package/dist/assets/notice-card-CyiVcu_Z.js +0 -5
- package/dist/assets/play-D-s0C4MN.js +0 -1
- package/dist/assets/plus-DOwjM_kF.js +0 -1
- package/dist/assets/providers-list-CJjNStDV.js +0 -1
- package/dist/assets/react-BaRotFd6.js +0 -1
- package/dist/assets/remote-gl8QckNc.js +0 -1
- package/dist/assets/runtime-config-page-BMFc1yzf.js +0 -1
- package/dist/assets/search-config-DUOR26if.js +0 -1
- package/dist/assets/x-D6GwT_8k.js +0 -1
- /package/dist/assets/{config-hints-CTqBnCDp.js → config-hints-CY1lg9IE.js} +0 -0
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type {
|
|
2
|
+
DocBrowserActiveHistoryEntry,
|
|
2
3
|
DocBrowserOpenOptions,
|
|
3
4
|
DocBrowserState,
|
|
4
5
|
DocBrowserStateUpdate,
|
|
@@ -6,15 +7,17 @@ import type {
|
|
|
6
7
|
DocBrowserTabKind,
|
|
7
8
|
} from '@/shared/components/doc-browser/types/doc-browser.types';
|
|
8
9
|
import {
|
|
10
|
+
createDocBrowserActiveHistoryEntry,
|
|
9
11
|
createDefaultDocBrowserState,
|
|
10
12
|
createDocBrowserTab,
|
|
11
13
|
} from '@/shared/components/doc-browser/utils/doc-browser-state.utils';
|
|
12
14
|
import {
|
|
15
|
+
DOC_BROWSER_HOME_TAB_KIND,
|
|
13
16
|
getDefaultDocsUrl,
|
|
14
|
-
inferTabKind,
|
|
15
|
-
normalizeDocUrl,
|
|
16
|
-
normalizeUrlByKind,
|
|
17
17
|
} from '@/shared/components/doc-browser/utils/doc-browser-url.utils';
|
|
18
|
+
import {
|
|
19
|
+
docBrowserRouteRegistry,
|
|
20
|
+
} from '@/shared/components/doc-browser/utils/doc-browser-route-registry.utils';
|
|
18
21
|
import { useDocBrowserStore } from '@/shared/components/doc-browser/stores/doc-browser.store';
|
|
19
22
|
|
|
20
23
|
function updateTab(
|
|
@@ -32,61 +35,103 @@ function updateActiveTab(state: DocBrowserState, updater: (tab: DocBrowserTab) =
|
|
|
32
35
|
return updateTab(state, state.activeTabId, updater);
|
|
33
36
|
}
|
|
34
37
|
|
|
35
|
-
function
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
38
|
+
function appendManualNavigation(tab: DocBrowserTab, url: string): Pick<DocBrowserTab, 'history' | 'historyIndex'> {
|
|
39
|
+
const history = [...tab.history.slice(0, tab.historyIndex + 1), url];
|
|
40
|
+
return {
|
|
41
|
+
history,
|
|
42
|
+
historyIndex: history.length - 1,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function areActiveHistoryEntriesEquivalent(
|
|
47
|
+
current: DocBrowserActiveHistoryEntry,
|
|
48
|
+
next: DocBrowserActiveHistoryEntry,
|
|
40
49
|
): boolean {
|
|
41
|
-
|
|
42
|
-
|
|
50
|
+
return current.tabId === next.tabId
|
|
51
|
+
&& current.kind === next.kind
|
|
52
|
+
&& docBrowserRouteRegistry.areUrlsEquivalent(current.url, next.url, current.kind, next.kind);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function pushActiveHistory(state: DocBrowserState, entry: DocBrowserActiveHistoryEntry): DocBrowserState {
|
|
56
|
+
const current = state.activeHistory[state.activeHistoryIndex];
|
|
57
|
+
if (current && areActiveHistoryEntriesEquivalent(current, entry)) {
|
|
58
|
+
return state;
|
|
59
|
+
}
|
|
60
|
+
const activeHistory = [...state.activeHistory.slice(0, state.activeHistoryIndex + 1), entry];
|
|
61
|
+
return {
|
|
62
|
+
...state,
|
|
63
|
+
activeHistory,
|
|
64
|
+
activeHistoryIndex: activeHistory.length - 1,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function findTabHistoryIndex(tab: DocBrowserTab, url: string): number {
|
|
69
|
+
for (let index = tab.history.length - 1; index >= 0; index -= 1) {
|
|
70
|
+
if (docBrowserRouteRegistry.areUrlsEquivalent(tab.history[index], url, tab.kind, tab.kind)) {
|
|
71
|
+
return index;
|
|
72
|
+
}
|
|
43
73
|
}
|
|
44
|
-
return
|
|
74
|
+
return -1;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function restoreActiveHistoryEntry(state: DocBrowserState, entry: DocBrowserActiveHistoryEntry): DocBrowserState {
|
|
78
|
+
const tab = state.tabs.find((item) => item.id === entry.tabId);
|
|
79
|
+
if (!tab) {
|
|
80
|
+
return state;
|
|
81
|
+
}
|
|
82
|
+
const target = docBrowserRouteRegistry.resolveOpenTarget({
|
|
83
|
+
activeTab: tab,
|
|
84
|
+
kind: entry.kind,
|
|
85
|
+
url: entry.url,
|
|
86
|
+
});
|
|
87
|
+
const historyIndex = findTabHistoryIndex(tab, target.url);
|
|
88
|
+
return updateTab(
|
|
89
|
+
{
|
|
90
|
+
...state,
|
|
91
|
+
activeTabId: tab.id,
|
|
92
|
+
isOpen: true,
|
|
93
|
+
},
|
|
94
|
+
tab.id,
|
|
95
|
+
(currentTab) => ({
|
|
96
|
+
...currentTab,
|
|
97
|
+
currentUrl: target.url,
|
|
98
|
+
dedupeKey: target.dedupeKey,
|
|
99
|
+
historyIndex: historyIndex >= 0 ? historyIndex : currentTab.historyIndex,
|
|
100
|
+
kind: target.kind,
|
|
101
|
+
title: target.title,
|
|
102
|
+
}),
|
|
103
|
+
);
|
|
45
104
|
}
|
|
46
105
|
|
|
47
106
|
function updateTabForOpen(
|
|
48
107
|
tab: DocBrowserTab,
|
|
49
|
-
|
|
50
|
-
|
|
108
|
+
target: {
|
|
109
|
+
kind: DocBrowserTabKind;
|
|
110
|
+
title: string;
|
|
111
|
+
url: string;
|
|
112
|
+
},
|
|
51
113
|
options?: DocBrowserOpenOptions,
|
|
52
114
|
dedupeKey?: string,
|
|
53
115
|
): DocBrowserTab {
|
|
54
116
|
const baseTab = {
|
|
55
117
|
...tab,
|
|
56
|
-
title: options?.title || tab.title,
|
|
57
|
-
kind,
|
|
118
|
+
title: options?.title || target.title || tab.title,
|
|
119
|
+
kind: target.kind,
|
|
58
120
|
dedupeKey,
|
|
59
121
|
};
|
|
60
122
|
|
|
61
|
-
if (
|
|
123
|
+
if (docBrowserRouteRegistry.areUrlsEquivalent(tab.currentUrl, target.url, tab.kind, target.kind)) {
|
|
62
124
|
return baseTab;
|
|
63
125
|
}
|
|
64
126
|
|
|
65
127
|
return {
|
|
66
128
|
...baseTab,
|
|
67
|
-
currentUrl: url,
|
|
68
|
-
|
|
69
|
-
historyIndex: tab.historyIndex + 1,
|
|
129
|
+
currentUrl: target.url,
|
|
130
|
+
...appendManualNavigation(tab, target.url),
|
|
70
131
|
navVersion: tab.navVersion + 1,
|
|
71
132
|
};
|
|
72
133
|
}
|
|
73
134
|
|
|
74
|
-
function resolveOpenTargetUrl(params: {
|
|
75
|
-
url?: string;
|
|
76
|
-
kind: DocBrowserTabKind;
|
|
77
|
-
activeTab?: DocBrowserTab;
|
|
78
|
-
}): string {
|
|
79
|
-
const { activeTab, kind, url } = params;
|
|
80
|
-
if (url && url.trim().length > 0) {
|
|
81
|
-
return normalizeUrlByKind(url, kind);
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
if (kind === 'docs') {
|
|
85
|
-
return getDefaultDocsUrl();
|
|
86
|
-
}
|
|
87
|
-
return activeTab?.currentUrl ?? getDefaultDocsUrl();
|
|
88
|
-
}
|
|
89
|
-
|
|
90
135
|
function openDocBrowserState(
|
|
91
136
|
prev: DocBrowserState,
|
|
92
137
|
url?: string,
|
|
@@ -100,9 +145,12 @@ function openDocBrowserState(
|
|
|
100
145
|
title,
|
|
101
146
|
} = options ?? {};
|
|
102
147
|
const activeTab = prev.tabs.find((tab) => tab.id === prev.activeTabId) ?? prev.tabs[0];
|
|
103
|
-
const
|
|
104
|
-
|
|
105
|
-
|
|
148
|
+
const target = docBrowserRouteRegistry.resolveOpenTarget({
|
|
149
|
+
activeTab,
|
|
150
|
+
kind,
|
|
151
|
+
url,
|
|
152
|
+
});
|
|
153
|
+
const dedupeKey = rawDedupeKey?.trim() || target.dedupeKey;
|
|
106
154
|
const matchedTab = dedupeKey
|
|
107
155
|
? prev.tabs.find((tab) => tab.dedupeKey === dedupeKey)
|
|
108
156
|
: undefined;
|
|
@@ -111,30 +159,40 @@ function openDocBrowserState(
|
|
|
111
159
|
const next = updateTab(
|
|
112
160
|
prev,
|
|
113
161
|
matchedTab.id,
|
|
114
|
-
(tab) => updateTabForOpen(tab,
|
|
162
|
+
(tab) => updateTabForOpen(tab, target, options, dedupeKey),
|
|
115
163
|
);
|
|
116
|
-
|
|
164
|
+
const shouldActivate = activate !== false;
|
|
165
|
+
const activeTabId = shouldActivate ? matchedTab.id : prev.activeTabId;
|
|
166
|
+
const nextState = {
|
|
117
167
|
...next,
|
|
118
|
-
activeTabId
|
|
168
|
+
activeTabId,
|
|
119
169
|
isOpen: true,
|
|
120
170
|
};
|
|
171
|
+
const shouldPushActiveHistory = shouldActivate || matchedTab.id === prev.activeTabId;
|
|
172
|
+
return shouldPushActiveHistory
|
|
173
|
+
? pushActiveHistory(nextState, { kind: target.kind, tabId: matchedTab.id, url: target.url })
|
|
174
|
+
: nextState;
|
|
121
175
|
}
|
|
122
176
|
|
|
123
|
-
if (shouldForceNewTab || dedupeKey || !activeTab || activeTab.kind !==
|
|
124
|
-
const newTab = createDocBrowserTab(
|
|
125
|
-
|
|
177
|
+
if (shouldForceNewTab || dedupeKey || !activeTab || activeTab.kind !== target.kind) {
|
|
178
|
+
const newTab = createDocBrowserTab(target.url, target.kind, title ?? target.title, dedupeKey);
|
|
179
|
+
const nextState = {
|
|
126
180
|
...prev,
|
|
127
181
|
isOpen: true,
|
|
128
182
|
tabs: [...prev.tabs, newTab],
|
|
129
183
|
activeTabId: newTab.id,
|
|
130
184
|
};
|
|
185
|
+
return pushActiveHistory(nextState, createDocBrowserActiveHistoryEntry(newTab));
|
|
131
186
|
}
|
|
132
187
|
|
|
133
188
|
const next = updateActiveTab(
|
|
134
189
|
prev,
|
|
135
|
-
(tab) => updateTabForOpen(tab,
|
|
190
|
+
(tab) => updateTabForOpen(tab, target, options, dedupeKey),
|
|
191
|
+
);
|
|
192
|
+
return pushActiveHistory(
|
|
193
|
+
{ ...next, isOpen: true },
|
|
194
|
+
{ kind: target.kind, tabId: prev.activeTabId, url: target.url },
|
|
136
195
|
);
|
|
137
|
-
return { ...next, isOpen: true };
|
|
138
196
|
}
|
|
139
197
|
|
|
140
198
|
export class DocBrowserManager {
|
|
@@ -146,6 +204,10 @@ export class DocBrowserManager {
|
|
|
146
204
|
this.setSnapshot((prev) => openDocBrowserState(prev, url, options));
|
|
147
205
|
};
|
|
148
206
|
|
|
207
|
+
readonly openNewTab = (): void => {
|
|
208
|
+
this.open(undefined, { kind: DOC_BROWSER_HOME_TAB_KIND, newTab: true });
|
|
209
|
+
};
|
|
210
|
+
|
|
149
211
|
readonly close = (): void => {
|
|
150
212
|
this.setSnapshot((prev) => ({ ...prev, isOpen: false }));
|
|
151
213
|
};
|
|
@@ -166,24 +228,30 @@ export class DocBrowserManager {
|
|
|
166
228
|
};
|
|
167
229
|
}
|
|
168
230
|
|
|
169
|
-
|
|
170
|
-
if (tab
|
|
231
|
+
const next = updateActiveTab(prev, (tab) => {
|
|
232
|
+
if (!docBrowserRouteRegistry.usesManagedHistory(tab)) {
|
|
171
233
|
return tab;
|
|
172
234
|
}
|
|
235
|
+
const target = docBrowserRouteRegistry.resolveOpenTarget({ activeTab: tab, url });
|
|
173
236
|
|
|
174
|
-
|
|
175
|
-
if (normalizeDocUrl(targetUrl) === normalizeDocUrl(tab.currentUrl)) {
|
|
237
|
+
if (docBrowserRouteRegistry.areUrlsEquivalent(tab.currentUrl, target.url, tab.kind, target.kind)) {
|
|
176
238
|
return tab;
|
|
177
239
|
}
|
|
178
240
|
|
|
179
241
|
return {
|
|
180
242
|
...tab,
|
|
181
|
-
currentUrl:
|
|
182
|
-
|
|
183
|
-
|
|
243
|
+
currentUrl: target.url,
|
|
244
|
+
dedupeKey: target.dedupeKey,
|
|
245
|
+
...appendManualNavigation(tab, target.url),
|
|
246
|
+
kind: target.kind,
|
|
184
247
|
navVersion: tab.navVersion + 1,
|
|
248
|
+
title: target.title,
|
|
185
249
|
};
|
|
186
250
|
});
|
|
251
|
+
const currentTab = next.tabs.find((tab) => tab.id === next.activeTabId);
|
|
252
|
+
return currentTab
|
|
253
|
+
? pushActiveHistory(next, createDocBrowserActiveHistoryEntry(currentTab))
|
|
254
|
+
: next;
|
|
187
255
|
});
|
|
188
256
|
};
|
|
189
257
|
|
|
@@ -198,39 +266,58 @@ export class DocBrowserManager {
|
|
|
198
266
|
};
|
|
199
267
|
}
|
|
200
268
|
|
|
201
|
-
|
|
202
|
-
if (tab
|
|
269
|
+
const next = updateActiveTab(prev, (tab) => {
|
|
270
|
+
if (!docBrowserRouteRegistry.usesManagedHistory(tab)) {
|
|
203
271
|
return tab;
|
|
204
272
|
}
|
|
273
|
+
const target = docBrowserRouteRegistry.resolveOpenTarget({ activeTab: tab, kind: tab.kind, url });
|
|
205
274
|
|
|
206
|
-
if (
|
|
275
|
+
if (docBrowserRouteRegistry.areUrlsEquivalent(tab.currentUrl, target.url, tab.kind, target.kind)) {
|
|
207
276
|
return tab;
|
|
208
277
|
}
|
|
209
278
|
|
|
210
279
|
return {
|
|
211
280
|
...tab,
|
|
212
|
-
currentUrl: url,
|
|
213
|
-
|
|
214
|
-
|
|
281
|
+
currentUrl: target.url,
|
|
282
|
+
dedupeKey: target.dedupeKey,
|
|
283
|
+
...appendManualNavigation(tab, target.url),
|
|
284
|
+
kind: target.kind,
|
|
285
|
+
title: target.title,
|
|
215
286
|
};
|
|
216
287
|
});
|
|
288
|
+
const currentTab = next.tabs.find((tab) => tab.id === next.activeTabId);
|
|
289
|
+
return currentTab
|
|
290
|
+
? pushActiveHistory(next, createDocBrowserActiveHistoryEntry(currentTab))
|
|
291
|
+
: next;
|
|
217
292
|
});
|
|
218
293
|
};
|
|
219
294
|
|
|
220
295
|
readonly goBack = (): void => {
|
|
221
|
-
this.setSnapshot((prev) =>
|
|
222
|
-
if (
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
296
|
+
this.setSnapshot((prev) => {
|
|
297
|
+
if (prev.activeHistoryIndex <= 0) {
|
|
298
|
+
return prev;
|
|
299
|
+
}
|
|
300
|
+
const activeHistoryIndex = prev.activeHistoryIndex - 1;
|
|
301
|
+
const entry = prev.activeHistory[activeHistoryIndex];
|
|
302
|
+
if (!entry) {
|
|
303
|
+
return prev;
|
|
304
|
+
}
|
|
305
|
+
return restoreActiveHistoryEntry({ ...prev, activeHistoryIndex }, entry);
|
|
306
|
+
});
|
|
226
307
|
};
|
|
227
308
|
|
|
228
309
|
readonly goForward = (): void => {
|
|
229
|
-
this.setSnapshot((prev) =>
|
|
230
|
-
if (
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
310
|
+
this.setSnapshot((prev) => {
|
|
311
|
+
if (prev.activeHistoryIndex >= prev.activeHistory.length - 1) {
|
|
312
|
+
return prev;
|
|
313
|
+
}
|
|
314
|
+
const activeHistoryIndex = prev.activeHistoryIndex + 1;
|
|
315
|
+
const entry = prev.activeHistory[activeHistoryIndex];
|
|
316
|
+
if (!entry) {
|
|
317
|
+
return prev;
|
|
318
|
+
}
|
|
319
|
+
return restoreActiveHistoryEntry({ ...prev, activeHistoryIndex }, entry);
|
|
320
|
+
});
|
|
234
321
|
};
|
|
235
322
|
|
|
236
323
|
readonly closeTab = (tabId: string): void => {
|
|
@@ -253,11 +340,29 @@ export class DocBrowserManager {
|
|
|
253
340
|
? nextTabs[Math.max(0, index - 1)]?.id ?? nextTabs[0].id
|
|
254
341
|
: prev.activeTabId;
|
|
255
342
|
|
|
256
|
-
|
|
343
|
+
const nextState = {
|
|
257
344
|
...prev,
|
|
258
345
|
tabs: nextTabs,
|
|
259
346
|
activeTabId: nextActiveId,
|
|
260
347
|
};
|
|
348
|
+
const activeHistory = nextState.activeHistory.filter((entry) => entry.tabId !== tabId);
|
|
349
|
+
const activeHistoryBeforeIndex = nextState.activeHistory
|
|
350
|
+
.slice(0, nextState.activeHistoryIndex + 1)
|
|
351
|
+
.filter((entry) => entry.tabId !== tabId);
|
|
352
|
+
const nextActiveTab = nextTabs.find((tab) => tab.id === nextActiveId) ?? nextTabs[0];
|
|
353
|
+
const reconciledState = {
|
|
354
|
+
...nextState,
|
|
355
|
+
activeHistory: activeHistory.length > 0
|
|
356
|
+
? activeHistory
|
|
357
|
+
: [createDocBrowserActiveHistoryEntry(nextActiveTab)],
|
|
358
|
+
activeHistoryIndex: Math.min(
|
|
359
|
+
Math.max(0, activeHistoryBeforeIndex.length - 1),
|
|
360
|
+
Math.max(0, activeHistory.length - 1),
|
|
361
|
+
),
|
|
362
|
+
};
|
|
363
|
+
return prev.activeTabId === tabId
|
|
364
|
+
? pushActiveHistory(reconciledState, createDocBrowserActiveHistoryEntry(nextActiveTab))
|
|
365
|
+
: reconciledState;
|
|
261
366
|
});
|
|
262
367
|
};
|
|
263
368
|
|
|
@@ -266,7 +371,14 @@ export class DocBrowserManager {
|
|
|
266
371
|
if (!prev.tabs.some((tab) => tab.id === tabId)) {
|
|
267
372
|
return prev;
|
|
268
373
|
}
|
|
269
|
-
|
|
374
|
+
const tab = prev.tabs.find((item) => item.id === tabId);
|
|
375
|
+
if (!tab) {
|
|
376
|
+
return prev;
|
|
377
|
+
}
|
|
378
|
+
return pushActiveHistory(
|
|
379
|
+
{ ...prev, activeTabId: tabId, isOpen: true },
|
|
380
|
+
createDocBrowserActiveHistoryEntry(tab),
|
|
381
|
+
);
|
|
270
382
|
});
|
|
271
383
|
};
|
|
272
384
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { create } from 'zustand';
|
|
2
2
|
import { createJSONStorage, persist } from 'zustand/middleware';
|
|
3
3
|
import type {
|
|
4
|
+
DocBrowserActiveHistoryEntry,
|
|
4
5
|
DocBrowserMode,
|
|
5
6
|
DocBrowserState,
|
|
6
7
|
DocBrowserStateUpdate,
|
|
@@ -71,6 +72,36 @@ function normalizePersistedDocBrowserTab(value: unknown): DocBrowserTab | null {
|
|
|
71
72
|
};
|
|
72
73
|
}
|
|
73
74
|
|
|
75
|
+
function createActiveHistoryEntryFromTab(tab: DocBrowserTab): DocBrowserActiveHistoryEntry {
|
|
76
|
+
return {
|
|
77
|
+
kind: tab.kind,
|
|
78
|
+
tabId: tab.id,
|
|
79
|
+
url: tab.currentUrl,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function normalizePersistedActiveHistoryEntry(
|
|
84
|
+
value: unknown,
|
|
85
|
+
tabsById: Map<string, DocBrowserTab>,
|
|
86
|
+
): DocBrowserActiveHistoryEntry | null {
|
|
87
|
+
if (
|
|
88
|
+
!isRecord(value)
|
|
89
|
+
|| typeof value.tabId !== 'string'
|
|
90
|
+
|| typeof value.kind !== 'string'
|
|
91
|
+
|| typeof value.url !== 'string'
|
|
92
|
+
|| value.url.trim().length === 0
|
|
93
|
+
|| !tabsById.has(value.tabId)
|
|
94
|
+
) {
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return {
|
|
99
|
+
kind: value.kind,
|
|
100
|
+
tabId: value.tabId,
|
|
101
|
+
url: value.url,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
74
105
|
function normalizePersistedDocBrowserState(value: unknown): DocBrowserState | null {
|
|
75
106
|
if (!isRecord(value)) {
|
|
76
107
|
return null;
|
|
@@ -88,12 +119,30 @@ function normalizePersistedDocBrowserState(value: unknown): DocBrowserState | nu
|
|
|
88
119
|
const activeTabId = tabs.some((tab) => tab.id === persistedActiveTabId)
|
|
89
120
|
? persistedActiveTabId ?? tabs[0].id
|
|
90
121
|
: tabs[0].id;
|
|
122
|
+
const tabsById = new Map(tabs.map((tab) => [tab.id, tab]));
|
|
123
|
+
const activeHistory = Array.isArray(value.activeHistory)
|
|
124
|
+
? value.activeHistory
|
|
125
|
+
.map((entry) => normalizePersistedActiveHistoryEntry(entry, tabsById))
|
|
126
|
+
.filter((entry): entry is DocBrowserActiveHistoryEntry => entry !== null)
|
|
127
|
+
: [];
|
|
128
|
+
const fallbackHistory = [createActiveHistoryEntryFromTab(tabsById.get(activeTabId) ?? tabs[0])];
|
|
129
|
+
const resolvedActiveHistory = activeHistory.length > 0 ? activeHistory : fallbackHistory;
|
|
130
|
+
const activeHistoryIndex = normalizePersistedNumber(
|
|
131
|
+
value.activeHistoryIndex,
|
|
132
|
+
0,
|
|
133
|
+
resolvedActiveHistory.length - 1,
|
|
134
|
+
resolvedActiveHistory.length - 1,
|
|
135
|
+
);
|
|
136
|
+
const activeHistoryEntry = resolvedActiveHistory[activeHistoryIndex];
|
|
137
|
+
const resolvedActiveTabId = tabsById.has(activeHistoryEntry.tabId) ? activeHistoryEntry.tabId : activeTabId;
|
|
91
138
|
|
|
92
139
|
return {
|
|
93
140
|
isOpen: value.isOpen === true,
|
|
94
141
|
mode: isDocBrowserMode(value.mode) ? value.mode : 'docked',
|
|
95
142
|
tabs,
|
|
96
|
-
activeTabId,
|
|
143
|
+
activeTabId: resolvedActiveTabId,
|
|
144
|
+
activeHistory: resolvedActiveHistory,
|
|
145
|
+
activeHistoryIndex,
|
|
97
146
|
};
|
|
98
147
|
}
|
|
99
148
|
|
|
@@ -122,6 +171,8 @@ export const useDocBrowserStore = create<DocBrowserStore>()(
|
|
|
122
171
|
mode: state.snapshot.mode,
|
|
123
172
|
tabs: state.snapshot.tabs.slice(-DOC_BROWSER_MAX_PERSISTED_TABS),
|
|
124
173
|
activeTabId: state.snapshot.activeTabId,
|
|
174
|
+
activeHistory: state.snapshot.activeHistory,
|
|
175
|
+
activeHistoryIndex: state.snapshot.activeHistoryIndex,
|
|
125
176
|
},
|
|
126
177
|
}),
|
|
127
178
|
merge: (persistedState, currentState) => {
|
|
@@ -13,6 +13,12 @@ export type DocBrowserTab = {
|
|
|
13
13
|
navVersion: number;
|
|
14
14
|
};
|
|
15
15
|
|
|
16
|
+
export type DocBrowserActiveHistoryEntry = {
|
|
17
|
+
kind: DocBrowserTabKind;
|
|
18
|
+
tabId: string;
|
|
19
|
+
url: string;
|
|
20
|
+
};
|
|
21
|
+
|
|
16
22
|
export type DocBrowserOpenOptions = {
|
|
17
23
|
activate?: boolean;
|
|
18
24
|
dedupeKey?: string;
|
|
@@ -26,12 +32,15 @@ export type DocBrowserState = {
|
|
|
26
32
|
mode: DocBrowserMode;
|
|
27
33
|
tabs: DocBrowserTab[];
|
|
28
34
|
activeTabId: string;
|
|
35
|
+
activeHistory: DocBrowserActiveHistoryEntry[];
|
|
36
|
+
activeHistoryIndex: number;
|
|
29
37
|
};
|
|
30
38
|
|
|
31
39
|
export type DocBrowserStateUpdate = DocBrowserState | ((prev: DocBrowserState) => DocBrowserState);
|
|
32
40
|
|
|
33
41
|
export type DocBrowserActions = {
|
|
34
42
|
open: (url?: string, options?: DocBrowserOpenOptions) => void;
|
|
43
|
+
openNewTab: () => void;
|
|
35
44
|
close: () => void;
|
|
36
45
|
toggleMode: () => void;
|
|
37
46
|
navigate: (url: string) => void;
|