@nextclaw/ui 0.15.17 → 0.15.19

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 (120) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/dist/assets/api-O5yEgzBt.js +13 -0
  3. package/dist/assets/app-presenter-provider-DXTYtH78.js +106 -0
  4. package/dist/assets/appearance-settings-page-BuGxSvlJ.js +1 -0
  5. package/dist/assets/{book-open-WsEnuW9P.js → book-open-HtUSPwoS.js} +1 -1
  6. package/dist/assets/card-GkBc-UMw.js +1 -0
  7. package/dist/assets/{channels-list-page-CAWuTHSM.js → channels-list-page-Bspr1Jp4.js} +2 -2
  8. package/dist/assets/chat-page-BKQ89f4s.js +1 -0
  9. package/dist/assets/config-split-page-CvLXSFSC.js +1 -0
  10. package/dist/assets/confirm-dialog-BVH7haN0.js +5 -0
  11. package/dist/assets/desktop-update-config-C9ZUI8UW.js +1 -0
  12. package/dist/assets/{dist-Ca-jiFrK.js → dist-CZx9Q6jc.js} +1 -1
  13. package/dist/assets/doc-browser-B4l0Xu7u.js +1 -0
  14. package/dist/assets/doc-browser-DsVF1DrS.js +1 -0
  15. package/dist/assets/doc-browser-context-Dy7EhLI8.js +1 -0
  16. package/dist/assets/{eye-B30J471A.js → eye-DU7p1-7X.js} +1 -1
  17. package/dist/assets/form-actions-aM9_jSJC.js +1 -0
  18. package/dist/assets/index-6pHX0DZH.js +2 -0
  19. package/dist/assets/index-DN6KM7WD.css +1 -0
  20. package/dist/assets/loader-circle-Dd6jw700.js +1 -0
  21. package/dist/assets/logo-badge-2-iV1pkr.js +1 -0
  22. package/dist/assets/mcp-marketplace-page-C-d0RMfg.js +1 -0
  23. package/dist/assets/mcp-marketplace-page-Dg03SZX1.js +9 -0
  24. package/dist/assets/model-config-page-DOBqLZoQ.js +1 -0
  25. package/dist/assets/{navigation-link-qPPUiLt0.js → navigation-link-Ct6xtnVM.js} +1 -1
  26. package/dist/assets/plus-CsgeRId8.js +1 -0
  27. package/dist/assets/{popover-DHx55dbc.js → popover-C2oIpGBF.js} +1 -1
  28. package/dist/assets/provider-scoped-model-input-DDqCdnTU.js +1 -0
  29. package/dist/assets/{providers-config-page-BwHtHIPe.js → providers-config-page-BAVPJKpx.js} +1 -1
  30. package/dist/assets/react-CeMr9V52.js +8 -0
  31. package/dist/assets/{refresh-cw-C7bynrWX.js → refresh-cw-BZ8giusN.js} +1 -1
  32. package/dist/assets/remote-C-r0SHcz.js +1 -0
  33. package/dist/assets/rotate-cw-DVdRfMSO.js +1 -0
  34. package/dist/assets/{runtime-config-page-CbSrKeJt.js → runtime-config-page-D0244uxJ.js} +1 -1
  35. package/dist/assets/{save-CsRsFtHS.js → save-CSzFGRse.js} +1 -1
  36. package/dist/assets/{search-D0HtJIL-.js → search-BmSWsiG2.js} +1 -1
  37. package/dist/assets/{search-config-page-n1GElgij.js → search-config-page-CrL1pUzy.js} +1 -1
  38. package/dist/assets/{secrets-config-page-DbUc3Ggi.js → secrets-config-page-BlgsD0-L.js} +2 -2
  39. package/dist/assets/security-config-cDrdpMJW.js +1 -0
  40. package/dist/assets/{select-C1zfp9zM.js → select-CklWb6Rs.js} +1 -1
  41. package/dist/assets/{settings-2-Ba3cLSN2.js → settings-2-BSZf8327.js} +1 -1
  42. package/dist/assets/skeleton-DG9u1BmA.js +1 -0
  43. package/dist/assets/{tabs-DmKgMKzb.js → tabs-Cah5klko.js} +1 -1
  44. package/dist/assets/{tag-chip-2vwlVUwM.js → tag-chip-DcrirDgN.js} +1 -1
  45. package/dist/assets/{trash-2-CUpWjNHu.js → trash-2-BLQOULWD.js} +1 -1
  46. package/dist/assets/use-config-CuFIsUVG.js +1 -0
  47. package/dist/assets/use-confirm-dialog-CcySmkWp.js +1 -0
  48. package/dist/assets/{use-viewport-layout-DaKJ-kap.js → use-viewport-layout-DU29bUJS.js} +1 -1
  49. package/dist/assets/x-BVoeXny1.js +1 -0
  50. package/dist/index.html +28 -28
  51. package/package.json +9 -9
  52. package/src/features/chat/components/conversation/__tests__/chat-conversation-content.test.tsx +11 -0
  53. package/src/features/chat/components/conversation/chat-conversation-content.tsx +7 -1
  54. package/src/features/chat/features/conversation/components/__tests__/session-conversation-area.test.tsx +50 -4
  55. package/src/features/chat/features/conversation/components/session-conversation-area.tsx +25 -1
  56. package/src/features/chat/features/conversation/components/session-conversation-input.tsx +3 -0
  57. package/src/features/chat/features/conversation/hooks/__tests__/use-session-conversation-input-state.test.tsx +41 -0
  58. package/src/features/chat/features/conversation/hooks/__tests__/use-session-conversation-slash-commands.test.tsx +28 -0
  59. package/src/features/chat/features/conversation/hooks/use-session-conversation-input-query.ts +1 -1
  60. package/src/features/chat/features/conversation/hooks/use-session-conversation-input-state.ts +0 -1
  61. package/src/features/chat/features/conversation/hooks/use-session-conversation-preference-actions.ts +10 -11
  62. package/src/features/chat/features/conversation/hooks/use-session-conversation-slash-commands.ts +5 -2
  63. package/src/features/chat/features/message/components/__tests__/chat-inline-panel-app-card.test.tsx +35 -0
  64. package/src/features/chat/features/message/components/chat-inline-file-preview.tsx +3 -1
  65. package/src/features/chat/features/message/components/chat-inline-panel-app-card.tsx +13 -5
  66. package/src/features/chat/features/message/components/chat-message-list.container.tsx +13 -9
  67. package/src/features/chat/features/message/utils/__tests__/chat-message-show-content-tool-card.utils.test.ts +9 -0
  68. package/src/features/chat/features/message/utils/chat-inline-panel-app-card.utils.ts +4 -1
  69. package/src/features/chat/features/message/utils/chat-message-show-content-tool-card.utils.ts +29 -0
  70. package/src/features/chat/features/message/utils/chat-message-texts.utils.ts +1 -0
  71. package/src/features/chat/features/ncp/hooks/__tests__/use-hydrated-ncp-agent.test.tsx +122 -1
  72. package/src/features/chat/features/ncp/hooks/__tests__/use-ncp-agent-runtime-queue.test.tsx +31 -6
  73. package/src/features/chat/features/ncp/hooks/__tests__/use-ncp-agent-runtime.test.tsx +107 -1
  74. package/src/features/chat/features/workspace/components/__tests__/workspace-file-content-preview.test.tsx +41 -0
  75. package/src/features/chat/features/workspace/components/chat-session-workspace-file-preview.tsx +4 -0
  76. package/src/features/chat/features/workspace/components/workspace-file-content-preview.tsx +15 -2
  77. package/src/features/chat/features/workspace/utils/chat-workspace-file-tab-persistence.utils.ts +11 -0
  78. package/src/features/chat/features/workspace/utils/chat-workspace-file-tab.utils.ts +1 -0
  79. package/src/features/chat/managers/__tests__/chat-thread.manager.test.ts +11 -8
  80. package/src/features/chat/managers/__tests__/chat-ui.manager.test.ts +6 -0
  81. package/src/features/chat/managers/chat-thread.manager.ts +2 -0
  82. package/src/features/chat/managers/chat-ui.manager.ts +10 -4
  83. package/src/features/chat/stores/__tests__/chat-thread.store.test.ts +4 -0
  84. package/src/features/chat/stores/chat-thread.store.ts +6 -1
  85. package/src/shared/components/doc-browser/__tests__/doc-browser-context.test.tsx +32 -0
  86. package/src/shared/components/doc-browser/doc-browser-panel-parts.tsx +2 -0
  87. package/src/shared/components/doc-browser/managers/doc-browser.manager.ts +20 -38
  88. package/src/shared/components/doc-browser/types/doc-browser.types.ts +5 -0
  89. package/src/shared/components/doc-browser/utils/doc-browser-state.utils.ts +63 -0
  90. package/src/shared/lib/i18n/locales/en-US/chat.json +1 -0
  91. package/src/shared/lib/i18n/locales/zh-CN/chat.json +1 -0
  92. package/dist/assets/api-D5UCLVE_.js +0 -13
  93. package/dist/assets/app-presenter-provider-C9AcZKKs.js +0 -106
  94. package/dist/assets/appearance-settings-page-CiD9Ey8f.js +0 -1
  95. package/dist/assets/card-xVrS1b1T.js +0 -1
  96. package/dist/assets/chat-page-B0cC3jsB.js +0 -1
  97. package/dist/assets/config-split-page-xj0UtMow.js +0 -1
  98. package/dist/assets/confirm-dialog-e2YCUzt1.js +0 -5
  99. package/dist/assets/desktop-update-config-D5NIaimo.js +0 -1
  100. package/dist/assets/doc-browser-B2tNEKrJ.js +0 -1
  101. package/dist/assets/doc-browser-CZX6I6CU.js +0 -1
  102. package/dist/assets/doc-browser-context-CGz0LULR.js +0 -1
  103. package/dist/assets/form-actions-CnSzu3WD.js +0 -1
  104. package/dist/assets/index-QveCjxCj.css +0 -1
  105. package/dist/assets/index-bOqpao-w.js +0 -2
  106. package/dist/assets/loader-circle-DjwSRQHW.js +0 -1
  107. package/dist/assets/logo-badge-CISsltAo.js +0 -1
  108. package/dist/assets/mcp-marketplace-page-BZa7p7Qf.js +0 -1
  109. package/dist/assets/mcp-marketplace-page-CPcPzMUQ.js +0 -9
  110. package/dist/assets/model-config-page-D6ex3_NM.js +0 -1
  111. package/dist/assets/plus-C45awBb1.js +0 -1
  112. package/dist/assets/provider-scoped-model-input-1TJxAhx6.js +0 -1
  113. package/dist/assets/react-DerwD2EA.js +0 -8
  114. package/dist/assets/remote-B0nlynVp.js +0 -1
  115. package/dist/assets/rotate-cw-Bhx0XkQP.js +0 -1
  116. package/dist/assets/security-config-n2WHhHXV.js +0 -1
  117. package/dist/assets/skeleton-DL5npsVL.js +0 -1
  118. package/dist/assets/use-config-BvxDWV-M.js +0 -1
  119. package/dist/assets/use-confirm-dialog-CeUEE4p5.js +0 -1
  120. package/dist/assets/x-DGdYT7rH.js +0 -1
@@ -88,6 +88,9 @@ describe('ChatUiManager content display', () => {
88
88
  type: 'panel_app',
89
89
  payload: {
90
90
  appId: 'reader',
91
+ params: {
92
+ file: { path: '/tmp/photo.png' },
93
+ },
91
94
  },
92
95
  },
93
96
  title: 'Reader',
@@ -95,6 +98,9 @@ describe('ChatUiManager content display', () => {
95
98
 
96
99
  expect(docBrowserManager.openTarget).toHaveBeenCalledWith({
97
100
  dedupeKey: 'panel-app:encoded-reader',
101
+ contentParams: {
102
+ file: { path: '/tmp/photo.png' },
103
+ },
98
104
  historyPolicy: 'managed',
99
105
  kind: 'panel-app',
100
106
  resourceUri: 'nextclaw://panel-app/encoded-reader',
@@ -347,6 +347,7 @@ export class ChatThreadManager {
347
347
  previewViewer: nextViewer,
348
348
  line: sourceTab.line ?? undefined,
349
349
  column: sourceTab.column ?? undefined,
350
+ params: sourceTab.params ?? undefined,
350
351
  rawText: sourceTab.rawText ?? undefined,
351
352
  contentUrl: sourceTab.contentUrl ?? undefined,
352
353
  mimeType: sourceTab.mimeType ?? undefined,
@@ -419,6 +420,7 @@ export class ChatThreadManager {
419
420
  previewViewer: request.target.payload.viewer,
420
421
  line: request.target.payload.line,
421
422
  column: request.target.payload.column,
423
+ params: request.target.payload.params,
422
424
  });
423
425
  return;
424
426
  }
@@ -96,21 +96,27 @@ export class ChatUiManager {
96
96
  });
97
97
  return;
98
98
  }
99
- const { appId, path } = target.payload;
99
+ const { appId, params: contentParams, path } = target.payload;
100
100
  if (path) {
101
101
  this.docBrowserManager.open(createPanelAppResourceUri(appId, path), {
102
+ contentParams,
102
103
  title,
103
104
  });
104
105
  return;
105
106
  }
106
107
  const entry = await this.resolvePanelAppEntry(appId);
107
108
  if (entry) {
108
- this.docBrowserManager.openTarget(createPanelAppRightPanelResourceTarget(entry), {
109
- title,
110
- });
109
+ this.docBrowserManager.openTarget(
110
+ {
111
+ ...createPanelAppRightPanelResourceTarget(entry),
112
+ contentParams,
113
+ },
114
+ { title },
115
+ );
111
116
  return;
112
117
  }
113
118
  this.docBrowserManager.open(createPanelAppResourceUri(appId), {
119
+ contentParams,
114
120
  title,
115
121
  });
116
122
  };
@@ -60,6 +60,7 @@ describe('chat thread workspace panel persistence', () => {
60
60
  label: 'README.md',
61
61
  viewMode: 'preview',
62
62
  previewViewer: 'rendered',
63
+ params: { chart: { series: [3, 5, 8] } },
63
64
  rawText: '# Hello',
64
65
  fullLines: [],
65
66
  },
@@ -98,6 +99,9 @@ describe('chat thread workspace panel persistence', () => {
98
99
  expect(persisted.state.snapshot.isSending).toBeUndefined();
99
100
  expect(persisted.state.snapshot.workspaceFileTabs[0].rawText).toBe('# Hello');
100
101
  expect(persisted.state.snapshot.workspaceFileTabs[0].previewViewer).toBe('rendered');
102
+ expect(persisted.state.snapshot.workspaceFileTabs[0].params).toEqual({
103
+ chart: { series: [3, 5, 8] },
104
+ });
101
105
  expect(persisted.state.snapshot.workspaceFileTabs[0].fullLines).toBeUndefined();
102
106
  });
103
107
 
@@ -1,7 +1,11 @@
1
1
  import { create } from 'zustand';
2
2
  import { createJSONStorage, persist } from 'zustand/middleware';
3
3
  import type { NcpMessage } from '@nextclaw/ncp';
4
- import type { ChatFileOperationLineViewModel, ChatFilePreviewViewer } from '@nextclaw/agent-chat-ui';
4
+ import type {
5
+ ChatContentParams,
6
+ ChatFileOperationLineViewModel,
7
+ ChatFilePreviewViewer,
8
+ } from '@nextclaw/agent-chat-ui';
5
9
  import type {
6
10
  SessionContextWindowView,
7
11
  SessionTypeIconView
@@ -38,6 +42,7 @@ export type ChatWorkspaceFileTab = {
38
42
  previewViewer?: ChatFilePreviewViewer | null;
39
43
  line?: number | null;
40
44
  column?: number | null;
45
+ params?: ChatContentParams | null;
41
46
  rawText?: string | null;
42
47
  /** Attachment/binary content URL for workspace-native media preview. */
43
48
  contentUrl?: string | null;
@@ -269,6 +269,9 @@ describe('DocBrowserProvider panel app resources', () => {
269
269
 
270
270
  act(() => {
271
271
  result.current.openTarget({
272
+ contentParams: {
273
+ file: { path: '/tmp/photo.png' },
274
+ },
272
275
  dedupeKey: 'panel-app:demo',
273
276
  dockIcon: { type: 'url', url: '/api/panel-apps/demo/assets/icon.png' },
274
277
  historyPolicy: 'managed',
@@ -281,6 +284,9 @@ describe('DocBrowserProvider panel app resources', () => {
281
284
 
282
285
  expect(result.current.currentTab).toMatchObject({
283
286
  currentUrl: '/api/panel-apps/demo/content',
287
+ contentParams: {
288
+ file: { path: '/tmp/photo.png' },
289
+ },
284
290
  dedupeKey: 'panel-app:demo',
285
291
  dockIcon: { type: 'url', url: '/api/panel-apps/demo/assets/icon.png' },
286
292
  kind: 'panel-app',
@@ -288,6 +294,32 @@ describe('DocBrowserProvider panel app resources', () => {
288
294
  title: 'Demo Panel App',
289
295
  });
290
296
  });
297
+
298
+ it('reloads a reused Panel App tab when its params change', () => {
299
+ const { result } = renderHook(() => useDocBrowser(), { wrapper });
300
+ const openPanelApp = (path: string) => {
301
+ result.current.openTarget({
302
+ contentParams: { file: { path } },
303
+ dedupeKey: 'panel-app:demo',
304
+ historyPolicy: 'managed',
305
+ kind: 'panel-app',
306
+ resourceUri: 'nextclaw://panel-app/demo',
307
+ title: 'Demo Panel App',
308
+ url: '/api/panel-apps/demo/content',
309
+ });
310
+ };
311
+
312
+ act(() => openPanelApp('/tmp/first.png'));
313
+ const firstNavVersion = result.current.currentTab?.navVersion;
314
+ act(() => openPanelApp('/tmp/second.png'));
315
+
316
+ expect(result.current.currentTab).toMatchObject({
317
+ contentParams: {
318
+ file: { path: '/tmp/second.png' },
319
+ },
320
+ navVersion: (firstNavVersion ?? 0) + 1,
321
+ });
322
+ });
291
323
  });
292
324
 
293
325
  describe('DocBrowserProvider managed history', () => {
@@ -10,6 +10,7 @@ import {
10
10
  import { IconActionButton } from '@/shared/components/ui/actions/icon-action-button';
11
11
  import { NavigationLink } from '@/shared/components/actions/navigation-link';
12
12
  import { t } from '@/shared/lib/i18n';
13
+ import { createUiContentParamsWindowName } from '@nextclaw/shared';
13
14
 
14
15
  type DocBrowserAddressToolbarProps = {
15
16
  isVisible: boolean;
@@ -86,6 +87,7 @@ export function DocBrowserFrameContent({
86
87
  ref={iframeRef}
87
88
  key={iframeInstanceId}
88
89
  src={currentUrl}
90
+ name={createUiContentParamsWindowName(currentTab?.contentParams)}
89
91
  className="absolute inset-0 w-full h-full border-0"
90
92
  title={currentTab?.title || 'NextClaw Docs'}
91
93
  sandbox={iframeSandbox}
@@ -8,10 +8,12 @@ import type {
8
8
  DocBrowserTab,
9
9
  } from '@/shared/components/doc-browser/types/doc-browser.types';
10
10
  import {
11
+ appendManualNavigation,
11
12
  createDocBrowserActiveHistoryEntry,
12
13
  createDefaultDocBrowserState,
13
14
  createDocBrowserTab,
14
15
  normalizeDocBrowserDockedWidth,
16
+ updateTabForOpen,
15
17
  } from '@/shared/components/doc-browser/utils/doc-browser-state.utils';
16
18
  import {
17
19
  DOC_BROWSER_HOME_TAB_KIND,
@@ -45,14 +47,6 @@ function updateActiveTab(state: DocBrowserState, updater: (tab: DocBrowserTab) =
45
47
  return updateTab(state, state.activeTabId, updater);
46
48
  }
47
49
 
48
- function appendManualNavigation(tab: DocBrowserTab, url: string): Pick<DocBrowserTab, 'history' | 'historyIndex'> {
49
- const history = [...tab.history.slice(0, tab.historyIndex + 1), url];
50
- return {
51
- history,
52
- historyIndex: history.length - 1,
53
- };
54
- }
55
-
56
50
  function areActiveHistoryEntriesEquivalent(
57
51
  routeResolver: DocBrowserRouteResolver,
58
52
  current: DocBrowserActiveHistoryEntry,
@@ -140,34 +134,6 @@ function restoreActiveHistoryEntry(
140
134
  );
141
135
  }
142
136
 
143
- function updateTabForOpen(
144
- routeResolver: DocBrowserRouteResolver,
145
- tab: DocBrowserTab,
146
- target: DocBrowserRouteTarget,
147
- options?: DocBrowserOpenOptions,
148
- dedupeKey?: string,
149
- ): DocBrowserTab {
150
- const baseTab = {
151
- ...tab,
152
- title: options?.title || target.title || tab.title,
153
- kind: target.kind,
154
- resourceUri: target.resourceUri ?? target.url,
155
- dockIcon: options?.dockIcon ?? target.dockIcon,
156
- dedupeKey,
157
- };
158
-
159
- if (routeResolver.areUrlsEquivalent(tab.currentUrl, target.url, tab.kind, target.kind)) {
160
- return baseTab;
161
- }
162
-
163
- return {
164
- ...baseTab,
165
- currentUrl: target.url,
166
- ...appendManualNavigation(tab, target.url),
167
- navVersion: tab.navVersion + 1,
168
- };
169
- }
170
-
171
137
  function isClosedDefaultHomeState(state: DocBrowserState, activeTab?: DocBrowserTab): boolean {
172
138
  return !state.isOpen
173
139
  && state.tabs.length === 1
@@ -213,7 +179,15 @@ function openResolvedDocBrowserState(
213
179
  }
214
180
 
215
181
  if (shouldForceNewTab || dedupeKey || !activeTab || activeTab.kind !== target.kind) {
216
- const newTab = createDocBrowserTab(target.url, target.kind, title ?? target.title, dedupeKey, target.resourceUri ?? target.url, options?.dockIcon ?? target.dockIcon);
182
+ const newTab = createDocBrowserTab(
183
+ target.url,
184
+ target.kind,
185
+ title ?? target.title,
186
+ dedupeKey,
187
+ target.resourceUri ?? target.url,
188
+ options?.dockIcon ?? target.dockIcon,
189
+ target.contentParams,
190
+ );
217
191
  if (isClosedDefaultHomeState(prev, activeTab)) {
218
192
  const nextState = {
219
193
  ...prev,
@@ -257,7 +231,15 @@ function openDocBrowserState(
257
231
  kind: options?.kind,
258
232
  url,
259
233
  });
260
- return openResolvedDocBrowserState(routeResolver, prev, target, options);
234
+ return openResolvedDocBrowserState(
235
+ routeResolver,
236
+ prev,
237
+ {
238
+ ...target,
239
+ contentParams: options?.contentParams,
240
+ },
241
+ options,
242
+ );
261
243
  }
262
244
 
263
245
  export class DocBrowserManager {
@@ -1,3 +1,5 @@
1
+ import type { UiContentParams } from '@nextclaw/shared';
2
+
1
3
  export type DocBrowserMode = 'floating' | 'docked';
2
4
  export type DocBrowserTabKind = 'docs' | 'content' | (string & {});
3
5
 
@@ -7,6 +9,7 @@ export type DocBrowserTab = {
7
9
  title: string;
8
10
  currentUrl: string;
9
11
  resourceUri?: string;
12
+ contentParams?: UiContentParams;
10
13
  dockIcon?: DocBrowserDockIcon;
11
14
  dedupeKey?: string;
12
15
  history: string[];
@@ -21,6 +24,7 @@ export type DocBrowserRouteTarget = {
21
24
  historyPolicy: 'managed' | 'none';
22
25
  kind: DocBrowserTabKind;
23
26
  resourceUri?: string;
27
+ contentParams?: UiContentParams;
24
28
  title: string;
25
29
  url: string;
26
30
  };
@@ -71,6 +75,7 @@ export type DocBrowserOpenOptions = {
71
75
  newTab?: boolean;
72
76
  title?: string;
73
77
  kind?: DocBrowserTabKind;
78
+ contentParams?: UiContentParams;
74
79
  };
75
80
 
76
81
  export type DocBrowserState = {
@@ -1,6 +1,9 @@
1
1
  import type {
2
2
  DocBrowserActiveHistoryEntry,
3
3
  DocBrowserDockIcon,
4
+ DocBrowserOpenOptions,
5
+ DocBrowserRouteResolver,
6
+ DocBrowserRouteTarget,
4
7
  DocBrowserState,
5
8
  DocBrowserTab,
6
9
  DocBrowserTabKind,
@@ -11,6 +14,10 @@ import {
11
14
  inferTabTitle,
12
15
  } from './doc-browser-url.utils';
13
16
  import { t } from '@/shared/lib/i18n';
17
+ import {
18
+ createUiContentParamsWindowName,
19
+ type UiContentParams,
20
+ } from '@nextclaw/shared';
14
21
 
15
22
  export const DOC_BROWSER_DOCKED_DEFAULT_WIDTH = 420;
16
23
  export const DOC_BROWSER_DOCKED_MIN_WIDTH = 320;
@@ -36,6 +43,7 @@ export function createDocBrowserTab(
36
43
  dedupeKey?: string,
37
44
  resourceUri?: string,
38
45
  dockIcon?: DocBrowserDockIcon,
46
+ contentParams?: UiContentParams,
39
47
  ): DocBrowserTab {
40
48
  const tabTitle = title?.trim() || inferTabTitle(url, kind, kind === 'docs' ? t('docBrowserHelp') : 'Detail');
41
49
  const normalizedResourceUri = resourceUri?.trim();
@@ -46,6 +54,7 @@ export function createDocBrowserTab(
46
54
  title: tabTitle,
47
55
  currentUrl: url,
48
56
  resourceUri: normalizedResourceUri ? normalizedResourceUri : undefined,
57
+ contentParams,
49
58
  dockIcon,
50
59
  dedupeKey,
51
60
  history: [url],
@@ -54,6 +63,60 @@ export function createDocBrowserTab(
54
63
  };
55
64
  }
56
65
 
66
+ export function appendManualNavigation(
67
+ tab: DocBrowserTab,
68
+ url: string,
69
+ ): Pick<DocBrowserTab, 'history' | 'historyIndex'> {
70
+ const history = [...tab.history.slice(0, tab.historyIndex + 1), url];
71
+ return {
72
+ history,
73
+ historyIndex: history.length - 1,
74
+ };
75
+ }
76
+
77
+ export function updateTabForOpen(
78
+ routeResolver: DocBrowserRouteResolver,
79
+ tab: DocBrowserTab,
80
+ target: DocBrowserRouteTarget,
81
+ options?: DocBrowserOpenOptions,
82
+ dedupeKey?: string,
83
+ ): DocBrowserTab {
84
+ const baseTab = {
85
+ ...tab,
86
+ title: options?.title || target.title || tab.title,
87
+ kind: target.kind,
88
+ resourceUri: target.resourceUri ?? target.url,
89
+ dockIcon: options?.dockIcon ?? target.dockIcon,
90
+ dedupeKey,
91
+ contentParams: target.contentParams,
92
+ };
93
+ const hasSameUrl = routeResolver.areUrlsEquivalent(
94
+ tab.currentUrl,
95
+ target.url,
96
+ tab.kind,
97
+ target.kind,
98
+ );
99
+ if (
100
+ hasSameUrl &&
101
+ createUiContentParamsWindowName(tab.contentParams) ===
102
+ createUiContentParamsWindowName(target.contentParams)
103
+ ) {
104
+ return baseTab;
105
+ }
106
+ if (hasSameUrl) {
107
+ return {
108
+ ...baseTab,
109
+ navVersion: tab.navVersion + 1,
110
+ };
111
+ }
112
+ return {
113
+ ...baseTab,
114
+ currentUrl: target.url,
115
+ ...appendManualNavigation(tab, target.url),
116
+ navVersion: tab.navVersion + 1,
117
+ };
118
+ }
119
+
57
120
  export function createDocBrowserActiveHistoryEntry(tab: DocBrowserTab): DocBrowserActiveHistoryEntry {
58
121
  return {
59
122
  kind: tab.kind,
@@ -388,6 +388,7 @@
388
388
  "chatMessageCopy": "Copy",
389
389
  "chatMessageCopied": "Copied",
390
390
  "chatMermaidDiagram": "Diagram",
391
+ "chatMermaidExpand": "Expand diagram",
391
392
  "chatMermaidLoading": "Rendering diagram…",
392
393
  "chatMermaidRenderError": "Diagram could not be rendered; showing source instead",
393
394
  "chatSidebarNewTask": "New Task",
@@ -388,6 +388,7 @@
388
388
  "chatMessageCopy": "复制",
389
389
  "chatMessageCopied": "已复制",
390
390
  "chatMermaidDiagram": "图表",
391
+ "chatMermaidExpand": "放大图表",
391
392
  "chatMermaidLoading": "正在渲染图表…",
392
393
  "chatMermaidRenderError": "图表无法渲染,已显示源代码",
393
394
  "chatSidebarNewTask": "新任务",