@nextclaw/ui 0.15.18 → 0.15.20
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 +39 -0
- package/dist/assets/api-tWUa6l9J.js +13 -0
- package/dist/assets/app-presenter-provider-s-FrOinZ.js +106 -0
- package/dist/assets/{appearance-settings-page-BkQaPtQ-.js → appearance-settings-page-dMRBD7ni.js} +1 -1
- package/dist/assets/{book-open-WsEnuW9P.js → book-open-BQ29ZmDE.js} +1 -1
- package/dist/assets/{card-xVrS1b1T.js → card-CtKueklV.js} +1 -1
- package/dist/assets/{channels-list-page-CAWuTHSM.js → channels-list-page-C8y3rqYt.js} +2 -2
- package/dist/assets/chat-page-MXBltS3G.js +1 -0
- package/dist/assets/{config-split-page-xj0UtMow.js → config-split-page-uC6d_r9c.js} +1 -1
- package/dist/assets/{confirm-dialog-e2YCUzt1.js → confirm-dialog-C1pbqHZX.js} +1 -1
- package/dist/assets/desktop-update-config-DssP8LCk.js +1 -0
- package/dist/assets/{dist-Ca-jiFrK.js → dist-rYHnBo8t.js} +1 -1
- package/dist/assets/doc-browser-AXF1zRsM.js +1 -0
- package/dist/assets/doc-browser-D8ng4EUc.js +1 -0
- package/dist/assets/doc-browser-context-CPHNzQ8m.js +1 -0
- package/dist/assets/{eye-B30J471A.js → eye-CiJz3ODY.js} +1 -1
- package/dist/assets/{form-actions-CnSzu3WD.js → form-actions-BnZVzLZf.js} +1 -1
- package/dist/assets/index-CocHWEsI.js +2 -0
- package/dist/assets/index-DN6KM7WD.css +1 -0
- package/dist/assets/loader-circle-AOpK5kkW.js +1 -0
- package/dist/assets/{logo-badge-CISsltAo.js → logo-badge-BXI-Kdhd.js} +1 -1
- package/dist/assets/mcp-marketplace-page-5ogeBHnl.js +1 -0
- package/dist/assets/mcp-marketplace-page-CxNe-Q9Q.js +9 -0
- package/dist/assets/model-config-page-CR_mgEVp.js +1 -0
- package/dist/assets/{navigation-link-qPPUiLt0.js → navigation-link-C1cvB7y4.js} +1 -1
- package/dist/assets/plus-DI37QuHc.js +1 -0
- package/dist/assets/{popover-DHx55dbc.js → popover-ZoDwVwwd.js} +1 -1
- package/dist/assets/{provider-scoped-model-input-1TJxAhx6.js → provider-scoped-model-input-DIL6Ex_d.js} +1 -1
- package/dist/assets/{providers-config-page-BwHtHIPe.js → providers-config-page-D2NXVctW.js} +1 -1
- package/dist/assets/react-39CDNZlM.js +8 -0
- package/dist/assets/{refresh-cw-C7bynrWX.js → refresh-cw-DKewcdAl.js} +1 -1
- package/dist/assets/remote-DYOOO1YG.js +1 -0
- package/dist/assets/rotate-cw-PmqA8LQT.js +1 -0
- package/dist/assets/{runtime-config-page-BLrTVO3n.js → runtime-config-page-0lb5NyKc.js} +1 -1
- package/dist/assets/{save-CsRsFtHS.js → save-BrckuAXY.js} +1 -1
- package/dist/assets/{search-D0HtJIL-.js → search-BhXQB2TO.js} +1 -1
- package/dist/assets/{search-config-page-n1GElgij.js → search-config-page-DFOQ6SDJ.js} +1 -1
- package/dist/assets/{secrets-config-page-DbUc3Ggi.js → secrets-config-page-BqqeClqE.js} +2 -2
- package/dist/assets/security-config-13AtPIlm.js +1 -0
- package/dist/assets/{select-C1zfp9zM.js → select-CVzGGT38.js} +1 -1
- package/dist/assets/{settings-2-Ba3cLSN2.js → settings-2-BP6L2enG.js} +1 -1
- package/dist/assets/skeleton-OwCNQJ7M.js +1 -0
- package/dist/assets/{tabs-DmKgMKzb.js → tabs-B-QWjKCL.js} +1 -1
- package/dist/assets/{tag-chip-2vwlVUwM.js → tag-chip-C88mxjwx.js} +1 -1
- package/dist/assets/{trash-2-CUpWjNHu.js → trash-2-ck3t33I3.js} +1 -1
- package/dist/assets/use-config-BAr5bH2c.js +1 -0
- package/dist/assets/{use-confirm-dialog-CeUEE4p5.js → use-confirm-dialog-C4e0nG_4.js} +1 -1
- package/dist/assets/{use-viewport-layout-DaKJ-kap.js → use-viewport-layout-k1isXfNm.js} +1 -1
- package/dist/assets/x-C1ijNWcE.js +1 -0
- package/dist/index.html +28 -28
- package/package.json +9 -9
- package/src/features/chat/features/conversation/hooks/__tests__/use-session-run-queue.test.tsx +28 -0
- package/src/features/chat/features/conversation/hooks/use-session-run-queue.ts +4 -5
- package/src/features/chat/features/input/hooks/__tests__/use-chat-input-surface-state.test.tsx +13 -0
- package/src/features/chat/features/input/hooks/use-chat-input-surface-state.ts +31 -8
- package/src/features/chat/features/input/input-surface-plugins/__tests__/context-reference-plugin.test.ts +79 -9
- package/src/features/chat/features/input/input-surface-plugins/__tests__/slash-command-plugin.test.ts +16 -4
- package/src/features/chat/features/input/input-surface-plugins/chat-input-product-plugin-adapters.types.ts +10 -2
- package/src/features/chat/features/input/input-surface-plugins/context-reference-items.utils.ts +240 -0
- package/src/features/chat/features/input/input-surface-plugins/context-reference-plugin.utils.ts +62 -173
- package/src/features/chat/features/input/utils/__tests__/chat-inline-token.utils.test.ts +29 -0
- package/src/features/chat/features/input/utils/chat-composer-token-protocol.utils.ts +5 -0
- package/src/features/chat/features/input/utils/chat-inline-token.utils.ts +26 -5
- package/src/features/chat/features/message/components/__tests__/chat-inline-panel-app-card.test.tsx +35 -0
- package/src/features/chat/features/message/components/__tests__/chat-message-list.container.test.tsx +12 -1
- package/src/features/chat/features/message/components/chat-inline-file-preview.tsx +3 -1
- package/src/features/chat/features/message/components/chat-inline-panel-app-card.tsx +13 -5
- package/src/features/chat/features/message/components/chat-message-list.container.tsx +10 -5
- package/src/features/chat/features/message/utils/__tests__/chat-message-show-content-tool-card.utils.test.ts +9 -0
- package/src/features/chat/features/message/utils/chat-inline-panel-app-card.utils.ts +4 -1
- package/src/features/chat/features/message/utils/chat-message-show-content-tool-card.utils.ts +29 -0
- package/src/features/chat/features/message/utils/chat-message-texts.utils.ts +1 -0
- package/src/features/chat/features/ncp/hooks/__tests__/use-ncp-agent-runtime-queue.test.tsx +32 -14
- package/src/features/chat/features/workspace/components/__tests__/workspace-file-content-preview.test.tsx +41 -0
- package/src/features/chat/features/workspace/components/chat-session-workspace-file-preview.tsx +4 -0
- package/src/features/chat/features/workspace/components/workspace-file-content-preview.tsx +15 -2
- package/src/features/chat/features/workspace/utils/chat-workspace-file-tab-persistence.utils.ts +11 -0
- package/src/features/chat/features/workspace/utils/chat-workspace-file-tab.utils.ts +1 -0
- package/src/features/chat/managers/__tests__/chat-thread.manager.test.ts +11 -8
- package/src/features/chat/managers/__tests__/chat-ui.manager.test.ts +6 -0
- package/src/features/chat/managers/chat-thread.manager.ts +2 -0
- package/src/features/chat/managers/chat-ui.manager.ts +10 -4
- package/src/features/chat/pages/__tests__/ncp-chat-page.test.ts +26 -0
- package/src/features/chat/stores/__tests__/chat-thread.store.test.ts +4 -0
- package/src/features/chat/stores/chat-thread.store.ts +6 -1
- package/src/shared/components/doc-browser/__tests__/doc-browser-context.test.tsx +32 -0
- package/src/shared/components/doc-browser/doc-browser-panel-parts.tsx +2 -0
- package/src/shared/components/doc-browser/managers/doc-browser.manager.ts +20 -38
- package/src/shared/components/doc-browser/types/doc-browser.types.ts +5 -0
- package/src/shared/components/doc-browser/utils/doc-browser-state.utils.ts +63 -0
- package/src/shared/hooks/use-projects.ts +2 -1
- package/src/shared/lib/i18n/locales/en-US/chat.json +14 -4
- package/src/shared/lib/i18n/locales/zh-CN/chat.json +14 -4
- package/dist/assets/api-D5UCLVE_.js +0 -13
- package/dist/assets/app-presenter-provider-De7Zwitu.js +0 -106
- package/dist/assets/chat-page-AvOuaxuv.js +0 -1
- package/dist/assets/desktop-update-config-D5NIaimo.js +0 -1
- package/dist/assets/doc-browser-B2tNEKrJ.js +0 -1
- package/dist/assets/doc-browser-CZX6I6CU.js +0 -1
- package/dist/assets/doc-browser-context-CGz0LULR.js +0 -1
- package/dist/assets/index-B5AoUrMf.js +0 -2
- package/dist/assets/index-QveCjxCj.css +0 -1
- package/dist/assets/loader-circle-DjwSRQHW.js +0 -1
- package/dist/assets/mcp-marketplace-page-BZa7p7Qf.js +0 -1
- package/dist/assets/mcp-marketplace-page-CPcPzMUQ.js +0 -9
- package/dist/assets/model-config-page-D6ex3_NM.js +0 -1
- package/dist/assets/plus-C45awBb1.js +0 -1
- package/dist/assets/react-DerwD2EA.js +0 -8
- package/dist/assets/remote-B6TXClIg.js +0 -1
- package/dist/assets/rotate-cw-Bhx0XkQP.js +0 -1
- package/dist/assets/security-config-n2WHhHXV.js +0 -1
- package/dist/assets/skeleton-DL5npsVL.js +0 -1
- package/dist/assets/use-config-BvxDWV-M.js +0 -1
- package/dist/assets/x-DGdYT7rH.js +0 -1
package/src/features/chat/features/input/input-surface-plugins/context-reference-plugin.utils.ts
CHANGED
|
@@ -5,20 +5,28 @@ import {
|
|
|
5
5
|
type ChatInputSurfacePlugin,
|
|
6
6
|
type ChatInputSurfaceTriggerSpec,
|
|
7
7
|
} from '@nextclaw/agent-chat-ui';
|
|
8
|
+
import type { PanelAppEntryView } from '@/shared/lib/api';
|
|
9
|
+
import type {
|
|
10
|
+
ChatInputProductPluginData,
|
|
11
|
+
ContextReferenceLocation,
|
|
12
|
+
} from './chat-input-product-plugin-adapters.types';
|
|
8
13
|
import {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
+
buildBackNavigationItem,
|
|
15
|
+
buildCurrentDirectoryReferenceItem,
|
|
16
|
+
buildFilesNavigationItem,
|
|
17
|
+
buildProjectReferenceItems,
|
|
18
|
+
buildProjectsNavigationItem,
|
|
19
|
+
buildWorkspaceReferenceItems,
|
|
20
|
+
FILES_NAVIGATION_ITEM_KEY,
|
|
21
|
+
PROJECTS_NAVIGATION_ITEM_KEY,
|
|
22
|
+
ROOT_NAVIGATION_ITEM_KEY,
|
|
23
|
+
type ContextReferenceInputSurfaceTexts,
|
|
24
|
+
} from './context-reference-items.utils';
|
|
14
25
|
import {
|
|
15
26
|
scoreInputSurfaceSearchCandidate,
|
|
16
27
|
resolveInputSurfaceMatchTier,
|
|
17
28
|
} from './input-surface-search.utils';
|
|
18
29
|
|
|
19
|
-
const FILES_NAVIGATION_ITEM_KEY = 'context-reference:navigate:files';
|
|
20
|
-
const ROOT_NAVIGATION_ITEM_KEY = 'context-reference:navigate:root';
|
|
21
|
-
const WORKSPACE_SECTION_KEY = 'workspace';
|
|
22
30
|
const PANEL_APP_SECTION_KEY = 'panel-apps';
|
|
23
31
|
|
|
24
32
|
export const CONTEXT_REFERENCE_TRIGGER_SPEC: ChatInputSurfaceTriggerSpec = {
|
|
@@ -33,34 +41,10 @@ export type PanelAppInputSurfaceItemTexts = {
|
|
|
33
41
|
subtitle: string;
|
|
34
42
|
};
|
|
35
43
|
|
|
36
|
-
export type ContextReferenceInputSurfaceTexts = {
|
|
37
|
-
backLabel: string;
|
|
38
|
-
backDescription: string;
|
|
39
|
-
backHintLabel: string;
|
|
40
|
-
currentDirectoryLabel: string;
|
|
41
|
-
directoryDescription: string;
|
|
42
|
-
fileDescription: string;
|
|
43
|
-
filesDescription: string;
|
|
44
|
-
filesHintLabel: string;
|
|
45
|
-
filesLabel: string;
|
|
46
|
-
filesSubtitle: string;
|
|
47
|
-
panelAppSectionLabel: string;
|
|
48
|
-
parentLabel: string;
|
|
49
|
-
parentDescription: string;
|
|
50
|
-
parentHintLabel: string;
|
|
51
|
-
projectRootLabel: string;
|
|
52
|
-
searchFailedLabel: string;
|
|
53
|
-
workspaceSectionLabel: string;
|
|
54
|
-
};
|
|
55
|
-
|
|
56
44
|
function getPanelAppActivityTime(entry: PanelAppEntryView): number {
|
|
57
45
|
return Date.parse(entry.lastOpenedAt ?? entry.updatedAt) || 0;
|
|
58
46
|
}
|
|
59
47
|
|
|
60
|
-
function resolveProjectLabel(projectRoot: string): string {
|
|
61
|
-
return projectRoot.split(/[\\/]+/).filter(Boolean).at(-1) ?? projectRoot;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
48
|
function resolvePanelAppInputSurfaceEntries(params: {
|
|
65
49
|
entries: readonly PanelAppEntryView[];
|
|
66
50
|
query: string;
|
|
@@ -155,171 +139,64 @@ function buildPanelAppReferenceItems(params: {
|
|
|
155
139
|
}));
|
|
156
140
|
}
|
|
157
141
|
|
|
158
|
-
function buildPathPreview(params: {
|
|
159
|
-
kind: 'directory' | 'file';
|
|
160
|
-
projectRoot: string;
|
|
161
|
-
relativePath: string;
|
|
162
|
-
}) {
|
|
163
|
-
const { kind, projectRoot, relativePath } = params;
|
|
164
|
-
const pathSegments = relativePath.split('/').filter(Boolean);
|
|
165
|
-
return {
|
|
166
|
-
rootLabel: resolveProjectLabel(projectRoot),
|
|
167
|
-
segments: pathSegments.map((label, index) => ({
|
|
168
|
-
label,
|
|
169
|
-
kind: index === pathSegments.length - 1 ? kind : 'directory' as const,
|
|
170
|
-
})),
|
|
171
|
-
};
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
function buildWorkspaceReferenceItems(params: {
|
|
175
|
-
entries: readonly ServerPathSearchEntryView[];
|
|
176
|
-
navigateDirectories: boolean;
|
|
177
|
-
projectRoot: string;
|
|
178
|
-
texts: ContextReferenceInputSurfaceTexts;
|
|
179
|
-
}): ChatInputSurfaceItem[] {
|
|
180
|
-
const projectLabel = resolveProjectLabel(params.projectRoot);
|
|
181
|
-
return params.entries.map((entry) => {
|
|
182
|
-
const navigates = params.navigateDirectories && entry.kind === 'directory';
|
|
183
|
-
return {
|
|
184
|
-
key: `workspace:${entry.kind}:${entry.relativePath}`,
|
|
185
|
-
icon: entry.kind === 'directory' ? 'folder' : 'file',
|
|
186
|
-
title: entry.name,
|
|
187
|
-
subtitle: entry.parentRelativePath || projectLabel,
|
|
188
|
-
description: entry.kind === 'directory'
|
|
189
|
-
? params.texts.directoryDescription
|
|
190
|
-
: params.texts.fileDescription,
|
|
191
|
-
detailLines: [
|
|
192
|
-
`${params.texts.projectRootLabel}: ${params.projectRoot}`,
|
|
193
|
-
entry.relativePath,
|
|
194
|
-
],
|
|
195
|
-
sectionKey: WORKSPACE_SECTION_KEY,
|
|
196
|
-
sectionLabel: params.texts.workspaceSectionLabel,
|
|
197
|
-
value: entry.relativePath,
|
|
198
|
-
tokenKind: navigates
|
|
199
|
-
? undefined
|
|
200
|
-
: entry.kind === 'directory'
|
|
201
|
-
? CHAT_WORKSPACE_DIRECTORY_TOKEN_KIND
|
|
202
|
-
: CHAT_WORKSPACE_FILE_TOKEN_KIND,
|
|
203
|
-
tokenKey: navigates ? undefined : entry.relativePath,
|
|
204
|
-
selectionBehavior: navigates ? 'navigate' : undefined,
|
|
205
|
-
pathPreview: buildPathPreview({
|
|
206
|
-
kind: entry.kind,
|
|
207
|
-
projectRoot: params.projectRoot,
|
|
208
|
-
relativePath: entry.relativePath,
|
|
209
|
-
}),
|
|
210
|
-
} satisfies ChatInputSurfaceItem;
|
|
211
|
-
});
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
function resolveParentReferencePath(referencePath: string): string | null {
|
|
215
|
-
const segments = referencePath.split('/').filter(Boolean);
|
|
216
|
-
if (segments.length === 0) {
|
|
217
|
-
return null;
|
|
218
|
-
}
|
|
219
|
-
return segments.slice(0, -1).join('/');
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
function buildCurrentDirectoryReferenceItem(params: {
|
|
223
|
-
projectRoot: string;
|
|
224
|
-
referencePath: string;
|
|
225
|
-
texts: ContextReferenceInputSurfaceTexts;
|
|
226
|
-
}): ChatInputSurfaceItem {
|
|
227
|
-
const { projectRoot, referencePath, texts } = params;
|
|
228
|
-
const title = referencePath.split('/').filter(Boolean).at(-1) ?? referencePath;
|
|
229
|
-
return {
|
|
230
|
-
key: `context-reference:current-directory:${referencePath}`,
|
|
231
|
-
icon: 'folder',
|
|
232
|
-
title: texts.currentDirectoryLabel,
|
|
233
|
-
subtitle: title,
|
|
234
|
-
description: texts.directoryDescription,
|
|
235
|
-
detailLines: [
|
|
236
|
-
`${texts.projectRootLabel}: ${projectRoot}`,
|
|
237
|
-
referencePath,
|
|
238
|
-
],
|
|
239
|
-
sectionKey: WORKSPACE_SECTION_KEY,
|
|
240
|
-
sectionLabel: texts.workspaceSectionLabel,
|
|
241
|
-
value: referencePath,
|
|
242
|
-
tokenKind: CHAT_WORKSPACE_DIRECTORY_TOKEN_KIND,
|
|
243
|
-
tokenKey: referencePath,
|
|
244
|
-
pathPreview: buildPathPreview({
|
|
245
|
-
kind: 'directory',
|
|
246
|
-
projectRoot,
|
|
247
|
-
relativePath: referencePath,
|
|
248
|
-
}),
|
|
249
|
-
};
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
function buildFilesNavigationItem(texts: ContextReferenceInputSurfaceTexts): ChatInputSurfaceItem {
|
|
253
|
-
return {
|
|
254
|
-
key: FILES_NAVIGATION_ITEM_KEY,
|
|
255
|
-
icon: 'files',
|
|
256
|
-
title: texts.filesLabel,
|
|
257
|
-
subtitle: texts.filesSubtitle,
|
|
258
|
-
description: texts.filesDescription,
|
|
259
|
-
detailLines: [],
|
|
260
|
-
hintLabel: texts.filesHintLabel,
|
|
261
|
-
selectionBehavior: 'navigate',
|
|
262
|
-
};
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
function buildBackNavigationItem(params: {
|
|
266
|
-
referencePath: string;
|
|
267
|
-
texts: ContextReferenceInputSurfaceTexts;
|
|
268
|
-
}): ChatInputSurfaceItem {
|
|
269
|
-
const { referencePath, texts } = params;
|
|
270
|
-
const isProjectRoot = !referencePath;
|
|
271
|
-
return {
|
|
272
|
-
key: ROOT_NAVIGATION_ITEM_KEY,
|
|
273
|
-
icon: 'back',
|
|
274
|
-
title: isProjectRoot ? texts.backLabel : texts.parentLabel,
|
|
275
|
-
subtitle: '',
|
|
276
|
-
description: isProjectRoot ? texts.backDescription : texts.parentDescription,
|
|
277
|
-
detailLines: [],
|
|
278
|
-
hintLabel: isProjectRoot ? texts.backHintLabel : texts.parentHintLabel,
|
|
279
|
-
value: resolveParentReferencePath(referencePath) ?? '',
|
|
280
|
-
selectionBehavior: 'navigate',
|
|
281
|
-
};
|
|
282
|
-
}
|
|
283
|
-
|
|
284
142
|
export function createContextReferenceInputSurfacePlugin(params: {
|
|
285
143
|
itemTexts: {
|
|
286
144
|
context: ContextReferenceInputSurfaceTexts;
|
|
287
145
|
panelApp: PanelAppInputSurfaceItemTexts;
|
|
288
146
|
};
|
|
289
147
|
menuTexts: ChatInputSurfaceMenuTexts;
|
|
290
|
-
onNavigate: (
|
|
148
|
+
onNavigate: (location: ContextReferenceLocation) => void;
|
|
291
149
|
}): ChatInputSurfacePlugin<ChatInputProductPluginData> {
|
|
292
150
|
return createInputSurfaceTriggeredPanelPlugin({
|
|
293
151
|
key: 'context-reference',
|
|
294
152
|
trigger: CONTEXT_REFERENCE_TRIGGER_SPEC,
|
|
295
153
|
resolvePanel: ({ data, trigger }) => {
|
|
296
|
-
const isFilesMode = data.
|
|
154
|
+
const isFilesMode = data.referenceLocation.view === 'files';
|
|
155
|
+
const isProjectsMode = data.referenceLocation.view === 'projects';
|
|
297
156
|
const isBrowsing = isFilesMode && !trigger.query.trim();
|
|
157
|
+
const referencePath = data.referenceLocation.view === 'files'
|
|
158
|
+
? data.referenceLocation.path
|
|
159
|
+
: '';
|
|
298
160
|
const workspaceItems = buildWorkspaceReferenceItems({
|
|
299
161
|
entries: data.serverPathEntries,
|
|
300
162
|
navigateDirectories: isBrowsing,
|
|
301
163
|
projectRoot: data.projectRoot,
|
|
302
164
|
texts: params.itemTexts.context,
|
|
303
165
|
});
|
|
304
|
-
const
|
|
166
|
+
const projectItems = buildProjectReferenceItems({
|
|
167
|
+
projects: data.projects,
|
|
168
|
+
query: trigger.query,
|
|
169
|
+
texts: params.itemTexts.context,
|
|
170
|
+
});
|
|
171
|
+
const currentDirectoryItem = isBrowsing && referencePath
|
|
305
172
|
? buildCurrentDirectoryReferenceItem({
|
|
306
173
|
projectRoot: data.projectRoot,
|
|
307
|
-
referencePath
|
|
174
|
+
referencePath,
|
|
308
175
|
texts: params.itemTexts.context,
|
|
309
176
|
})
|
|
310
177
|
: null;
|
|
311
178
|
const items = isFilesMode
|
|
312
179
|
? [
|
|
313
180
|
buildBackNavigationItem({
|
|
314
|
-
|
|
181
|
+
location: data.referenceLocation,
|
|
315
182
|
texts: params.itemTexts.context,
|
|
316
183
|
}),
|
|
317
184
|
...(currentDirectoryItem ? [currentDirectoryItem] : []),
|
|
318
185
|
...workspaceItems,
|
|
319
186
|
]
|
|
320
|
-
:
|
|
187
|
+
: isProjectsMode
|
|
188
|
+
? [
|
|
189
|
+
buildBackNavigationItem({
|
|
190
|
+
location: data.referenceLocation,
|
|
191
|
+
texts: params.itemTexts.context,
|
|
192
|
+
}),
|
|
193
|
+
...projectItems,
|
|
194
|
+
]
|
|
195
|
+
: [
|
|
321
196
|
buildFilesNavigationItem(params.itemTexts.context),
|
|
197
|
+
buildProjectsNavigationItem(params.itemTexts.context),
|
|
322
198
|
...(trigger.query ? workspaceItems : []),
|
|
199
|
+
...(trigger.query ? projectItems : []),
|
|
323
200
|
...buildPanelAppReferenceItems({
|
|
324
201
|
entries: data.panelApps,
|
|
325
202
|
query: trigger.query,
|
|
@@ -329,25 +206,37 @@ export function createContextReferenceInputSurfacePlugin(params: {
|
|
|
329
206
|
];
|
|
330
207
|
const relevantLoading = isFilesMode
|
|
331
208
|
? data.isServerPathSearchLoading
|
|
332
|
-
:
|
|
209
|
+
: isProjectsMode
|
|
210
|
+
? data.isProjectsLoading
|
|
211
|
+
: data.isPanelAppsLoading ||
|
|
212
|
+
(Boolean(trigger.query) && (data.isProjectsLoading || data.isServerPathSearchLoading));
|
|
213
|
+
const errorMessage = isProjectsMode && data.projectsError
|
|
214
|
+
? `${params.itemTexts.context.projectsLoadFailedLabel}: ${data.projectsError}`
|
|
215
|
+
: data.serverPathSearchError
|
|
216
|
+
? `${params.itemTexts.context.searchFailedLabel}: ${data.serverPathSearchError}`
|
|
217
|
+
: null;
|
|
333
218
|
return {
|
|
334
|
-
isLoading:
|
|
335
|
-
? relevantLoading
|
|
336
|
-
: relevantLoading && items.length === 0,
|
|
219
|
+
isLoading: isProjectsMode ? relevantLoading : relevantLoading && items.length === 0,
|
|
337
220
|
items,
|
|
338
|
-
notice:
|
|
221
|
+
notice: errorMessage
|
|
339
222
|
? {
|
|
340
|
-
message:
|
|
223
|
+
message: errorMessage,
|
|
341
224
|
tone: 'error',
|
|
342
225
|
}
|
|
343
226
|
: undefined,
|
|
344
227
|
onSelectItem: (item) => {
|
|
345
228
|
if (item.key === FILES_NAVIGATION_ITEM_KEY) {
|
|
346
|
-
params.onNavigate('');
|
|
229
|
+
params.onNavigate({ view: 'files', path: '' });
|
|
230
|
+
} else if (item.key === PROJECTS_NAVIGATION_ITEM_KEY) {
|
|
231
|
+
params.onNavigate({ view: 'projects' });
|
|
347
232
|
} else if (item.key === ROOT_NAVIGATION_ITEM_KEY) {
|
|
348
|
-
params.onNavigate(
|
|
233
|
+
params.onNavigate(
|
|
234
|
+
isFilesMode && referencePath
|
|
235
|
+
? { view: 'files', path: item.value ?? '' }
|
|
236
|
+
: { view: 'root' },
|
|
237
|
+
);
|
|
349
238
|
} else if (item.selectionBehavior === 'navigate' && item.value) {
|
|
350
|
-
params.onNavigate(item.value);
|
|
239
|
+
params.onNavigate({ view: 'files', path: item.value });
|
|
351
240
|
}
|
|
352
241
|
},
|
|
353
242
|
texts: params.menuTexts,
|
|
@@ -226,6 +226,35 @@ describe('chat inline token workspace references', () => {
|
|
|
226
226
|
]);
|
|
227
227
|
});
|
|
228
228
|
|
|
229
|
+
it('serializes project references with their registered path and display name', () => {
|
|
230
|
+
expect(
|
|
231
|
+
buildInlineTokensFromComposer([
|
|
232
|
+
createChatComposerTokenNode({
|
|
233
|
+
tokenKind: 'project',
|
|
234
|
+
tokenKey: '/tmp/NextClaw Project',
|
|
235
|
+
label: 'NextClaw',
|
|
236
|
+
}),
|
|
237
|
+
]),
|
|
238
|
+
).toEqual([
|
|
239
|
+
{
|
|
240
|
+
kind: 'project',
|
|
241
|
+
key: '/tmp/NextClaw Project',
|
|
242
|
+
label: 'NextClaw',
|
|
243
|
+
rawText: '@project:%2Ftmp%2FNextClaw%20Project',
|
|
244
|
+
},
|
|
245
|
+
]);
|
|
246
|
+
expect(
|
|
247
|
+
buildInlineTokensFromTextProtocol('review @project:%2Ftmp%2FNextClaw%20Project'),
|
|
248
|
+
).toEqual([
|
|
249
|
+
{
|
|
250
|
+
kind: 'project',
|
|
251
|
+
key: '/tmp/NextClaw Project',
|
|
252
|
+
label: 'NextClaw Project',
|
|
253
|
+
rawText: '@project:%2Ftmp%2FNextClaw%20Project',
|
|
254
|
+
},
|
|
255
|
+
]);
|
|
256
|
+
});
|
|
257
|
+
|
|
229
258
|
it('resolves workspace token paths inside POSIX and Windows project roots', () => {
|
|
230
259
|
expect(resolveWorkspaceReferencePath({
|
|
231
260
|
projectRoot: '/tmp/project/',
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
|
+
CHAT_PROJECT_TOKEN_KIND,
|
|
2
3
|
CHAT_WORKSPACE_DIRECTORY_TOKEN_KIND,
|
|
3
4
|
CHAT_WORKSPACE_FILE_TOKEN_KIND,
|
|
4
5
|
} from '@nextclaw/shared';
|
|
5
6
|
|
|
6
7
|
const CHAT_SKILL_TOKEN_PREFIX = '$';
|
|
7
8
|
const CHAT_PANEL_APP_TOKEN_PREFIX = '@panel-app:';
|
|
9
|
+
const CHAT_PROJECT_TOKEN_PREFIX = '@project:';
|
|
8
10
|
const CHAT_WORKSPACE_FILE_TOKEN_PREFIX = '@file:';
|
|
9
11
|
const CHAT_WORKSPACE_DIRECTORY_TOKEN_PREFIX = '@folder:';
|
|
10
12
|
|
|
@@ -20,6 +22,9 @@ export function serializeChatComposerTokenText(params: {
|
|
|
20
22
|
if (tokenKind === 'panel_app') {
|
|
21
23
|
return `${CHAT_PANEL_APP_TOKEN_PREFIX}${tokenKey}`;
|
|
22
24
|
}
|
|
25
|
+
if (tokenKind === CHAT_PROJECT_TOKEN_KIND) {
|
|
26
|
+
return `${CHAT_PROJECT_TOKEN_PREFIX}${encodeURIComponent(tokenKey)}`;
|
|
27
|
+
}
|
|
23
28
|
if (tokenKind === CHAT_WORKSPACE_FILE_TOKEN_KIND) {
|
|
24
29
|
return `${CHAT_WORKSPACE_FILE_TOKEN_PREFIX}${encodeURIComponent(tokenKey)}`;
|
|
25
30
|
}
|
|
@@ -2,6 +2,7 @@ import type { ChatComposerNode } from '@nextclaw/agent-chat-ui';
|
|
|
2
2
|
import {
|
|
3
3
|
CHAT_INLINE_TOKENS_METADATA_KEY,
|
|
4
4
|
CHAT_INLINE_TOKENS_SCHEMA_VERSION,
|
|
5
|
+
CHAT_PROJECT_TOKEN_KIND,
|
|
5
6
|
CHAT_WORKSPACE_DIRECTORY_TOKEN_KIND,
|
|
6
7
|
CHAT_WORKSPACE_FILE_TOKEN_KIND,
|
|
7
8
|
type ChatInlineTokenMetadata,
|
|
@@ -13,6 +14,7 @@ import { serializeChatComposerTokenText } from './chat-composer-token-protocol.u
|
|
|
13
14
|
export { CHAT_INLINE_TOKENS_METADATA_KEY };
|
|
14
15
|
const CHAT_PANEL_APP_TOKEN_PREFIX = '@panel-app:';
|
|
15
16
|
const CHAT_PANEL_APP_TOKEN_PATTERN = /@panel-app:([A-Za-z0-9_-]+)/g;
|
|
17
|
+
const CHAT_PROJECT_TOKEN_PATTERN = /@project:([^\s]+)/g;
|
|
16
18
|
const CHAT_WORKSPACE_FILE_TOKEN_PATTERN = /@file:([^\s]+)/g;
|
|
17
19
|
const CHAT_WORKSPACE_DIRECTORY_TOKEN_PATTERN = /@folder:([^\s]+)/g;
|
|
18
20
|
|
|
@@ -96,6 +98,7 @@ export function buildInlineTokensFromComposer(
|
|
|
96
98
|
}
|
|
97
99
|
if (
|
|
98
100
|
node.tokenKind !== 'skill' &&
|
|
101
|
+
node.tokenKind !== CHAT_PROJECT_TOKEN_KIND &&
|
|
99
102
|
node.tokenKind !== CHAT_WORKSPACE_FILE_TOKEN_KIND &&
|
|
100
103
|
node.tokenKind !== CHAT_WORKSPACE_DIRECTORY_TOKEN_KIND
|
|
101
104
|
) {
|
|
@@ -121,6 +124,15 @@ export function buildInlineTokensFromComposer(
|
|
|
121
124
|
});
|
|
122
125
|
continue;
|
|
123
126
|
}
|
|
127
|
+
if (node.tokenKind === CHAT_PROJECT_TOKEN_KIND) {
|
|
128
|
+
tokens.push({
|
|
129
|
+
kind: CHAT_PROJECT_TOKEN_KIND,
|
|
130
|
+
key: node.tokenKey,
|
|
131
|
+
label: node.label,
|
|
132
|
+
rawText,
|
|
133
|
+
});
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
124
136
|
const workspaceKind = node.tokenKind === CHAT_WORKSPACE_FILE_TOKEN_KIND
|
|
125
137
|
? CHAT_WORKSPACE_FILE_TOKEN_KIND
|
|
126
138
|
: CHAT_WORKSPACE_DIRECTORY_TOKEN_KIND;
|
|
@@ -134,8 +146,11 @@ export function buildInlineTokensFromComposer(
|
|
|
134
146
|
return dedupeInlineTokens(tokens);
|
|
135
147
|
}
|
|
136
148
|
|
|
137
|
-
function
|
|
138
|
-
kind:
|
|
149
|
+
function appendEncodedKeyTokens(params: {
|
|
150
|
+
kind:
|
|
151
|
+
| typeof CHAT_PROJECT_TOKEN_KIND
|
|
152
|
+
| typeof CHAT_WORKSPACE_FILE_TOKEN_KIND
|
|
153
|
+
| typeof CHAT_WORKSPACE_DIRECTORY_TOKEN_KIND;
|
|
139
154
|
pattern: RegExp;
|
|
140
155
|
text: string;
|
|
141
156
|
tokens: ChatInlineTokenSource[];
|
|
@@ -155,7 +170,7 @@ function appendWorkspaceTokens(params: {
|
|
|
155
170
|
tokens.push({
|
|
156
171
|
kind,
|
|
157
172
|
key,
|
|
158
|
-
label: key.split(
|
|
173
|
+
label: key.split(/[\\/]+/).filter(Boolean).at(-1) ?? key,
|
|
159
174
|
rawText: match[0],
|
|
160
175
|
});
|
|
161
176
|
}
|
|
@@ -175,13 +190,19 @@ export function buildInlineTokensFromTextProtocol(text: string): ChatInlineToken
|
|
|
175
190
|
rawText: `${CHAT_PANEL_APP_TOKEN_PREFIX}${key}`
|
|
176
191
|
});
|
|
177
192
|
}
|
|
178
|
-
|
|
193
|
+
appendEncodedKeyTokens({
|
|
194
|
+
kind: CHAT_PROJECT_TOKEN_KIND,
|
|
195
|
+
pattern: CHAT_PROJECT_TOKEN_PATTERN,
|
|
196
|
+
text,
|
|
197
|
+
tokens,
|
|
198
|
+
});
|
|
199
|
+
appendEncodedKeyTokens({
|
|
179
200
|
kind: CHAT_WORKSPACE_FILE_TOKEN_KIND,
|
|
180
201
|
pattern: CHAT_WORKSPACE_FILE_TOKEN_PATTERN,
|
|
181
202
|
text,
|
|
182
203
|
tokens,
|
|
183
204
|
});
|
|
184
|
-
|
|
205
|
+
appendEncodedKeyTokens({
|
|
185
206
|
kind: CHAT_WORKSPACE_DIRECTORY_TOKEN_KIND,
|
|
186
207
|
pattern: CHAT_WORKSPACE_DIRECTORY_TOKEN_PATTERN,
|
|
187
208
|
text,
|
package/src/features/chat/features/message/components/__tests__/chat-inline-panel-app-card.test.tsx
CHANGED
|
@@ -72,6 +72,9 @@ it("renders panel apps through the shared adaptive inline surface", () => {
|
|
|
72
72
|
<ChatInlinePanelAppCard
|
|
73
73
|
panelApp={{
|
|
74
74
|
appId: "weather-card",
|
|
75
|
+
params: {
|
|
76
|
+
city: "Shanghai",
|
|
77
|
+
},
|
|
75
78
|
title: "Weather",
|
|
76
79
|
}}
|
|
77
80
|
/>,
|
|
@@ -99,6 +102,8 @@ it("renders panel apps through the shared adaptive inline surface", () => {
|
|
|
99
102
|
expect(iframe.getAttribute("src")).toBe(
|
|
100
103
|
"/api/panel-apps/weather-card/content?nextclawDisplayMode=card&nextclawPlacement=inline",
|
|
101
104
|
);
|
|
105
|
+
expect(iframe.name).toContain('"city":"Shanghai"');
|
|
106
|
+
expect(iframe.getAttribute("src")).not.toContain("Shanghai");
|
|
102
107
|
|
|
103
108
|
fireEvent(
|
|
104
109
|
window,
|
|
@@ -118,6 +123,9 @@ it("renders panel apps through the shared adaptive inline surface", () => {
|
|
|
118
123
|
|
|
119
124
|
expect(mocks.openTarget).toHaveBeenCalledWith(
|
|
120
125
|
expect.objectContaining({
|
|
126
|
+
contentParams: {
|
|
127
|
+
city: "Shanghai",
|
|
128
|
+
},
|
|
121
129
|
dedupeKey: "panel-app:weather-card",
|
|
122
130
|
kind: "panel-app",
|
|
123
131
|
title: "Weather",
|
|
@@ -126,6 +134,33 @@ it("renders panel apps through the shared adaptive inline surface", () => {
|
|
|
126
134
|
);
|
|
127
135
|
});
|
|
128
136
|
|
|
137
|
+
it("replaces the inline iframe when params change at the same URL", () => {
|
|
138
|
+
const { rerender } = render(
|
|
139
|
+
<ChatInlinePanelAppCard
|
|
140
|
+
panelApp={{
|
|
141
|
+
appId: "weather-card",
|
|
142
|
+
params: { city: "Shanghai" },
|
|
143
|
+
title: "Weather",
|
|
144
|
+
}}
|
|
145
|
+
/>,
|
|
146
|
+
);
|
|
147
|
+
const firstIframe = screen.getByTitle("Weather");
|
|
148
|
+
|
|
149
|
+
rerender(
|
|
150
|
+
<ChatInlinePanelAppCard
|
|
151
|
+
panelApp={{
|
|
152
|
+
appId: "weather-card",
|
|
153
|
+
params: { city: "Beijing" },
|
|
154
|
+
title: "Weather",
|
|
155
|
+
}}
|
|
156
|
+
/>,
|
|
157
|
+
);
|
|
158
|
+
|
|
159
|
+
const nextIframe = screen.getByTitle("Weather") as HTMLIFrameElement;
|
|
160
|
+
expect(nextIframe).not.toBe(firstIframe);
|
|
161
|
+
expect(nextIframe.name).toContain('"city":"Beijing"');
|
|
162
|
+
});
|
|
163
|
+
|
|
129
164
|
it("renders and expands a panel app from an explicit external path", () => {
|
|
130
165
|
render(
|
|
131
166
|
<ChatInlinePanelAppCard
|
package/src/features/chat/features/message/components/__tests__/chat-message-list.container.test.tsx
CHANGED
|
@@ -453,7 +453,7 @@ it("wires markdown file link actions to the workspace file preview manager", ()
|
|
|
453
453
|
expect(captures.openFilePreview).toHaveBeenCalledWith(action);
|
|
454
454
|
});
|
|
455
455
|
|
|
456
|
-
it("opens workspace
|
|
456
|
+
it("opens workspace and project reference tokens from the message", () => {
|
|
457
457
|
captures.selectedSession = {
|
|
458
458
|
projectRoot: "/tmp/project",
|
|
459
459
|
workingDir: "/tmp/project/packages/ui",
|
|
@@ -474,6 +474,12 @@ it("opens workspace file and directory tokens from the message", () => {
|
|
|
474
474
|
label: "server",
|
|
475
475
|
rawText: "@folder:packages%2Fserver",
|
|
476
476
|
});
|
|
477
|
+
onInlineTokenClick?.({
|
|
478
|
+
kind: "project",
|
|
479
|
+
key: "/tmp/nextclaw",
|
|
480
|
+
label: "NextClaw",
|
|
481
|
+
rawText: "@project:%2Ftmp%2Fnextclaw",
|
|
482
|
+
});
|
|
477
483
|
|
|
478
484
|
expect(captures.openFilePreview).toHaveBeenNthCalledWith(1, {
|
|
479
485
|
path: "/tmp/project/docs/guide.md",
|
|
@@ -485,6 +491,11 @@ it("opens workspace file and directory tokens from the message", () => {
|
|
|
485
491
|
label: "server",
|
|
486
492
|
viewMode: "preview",
|
|
487
493
|
});
|
|
494
|
+
expect(captures.openFilePreview).toHaveBeenNthCalledWith(3, {
|
|
495
|
+
path: "/tmp/nextclaw",
|
|
496
|
+
label: "NextClaw",
|
|
497
|
+
viewMode: "preview",
|
|
498
|
+
});
|
|
488
499
|
});
|
|
489
500
|
|
|
490
501
|
it("opens panel app tokens through the existing content owner", () => {
|
|
@@ -31,7 +31,7 @@ export function ChatInlineFilePreview({
|
|
|
31
31
|
if (display.target.type !== "file") {
|
|
32
32
|
return null;
|
|
33
33
|
}
|
|
34
|
-
const { path, line, column, viewer } = display.target.payload;
|
|
34
|
+
const { path, line, column, params, viewer } = display.target.payload;
|
|
35
35
|
const file = createWorkspaceFileTab(
|
|
36
36
|
{
|
|
37
37
|
path,
|
|
@@ -40,6 +40,7 @@ export function ChatInlineFilePreview({
|
|
|
40
40
|
previewViewer: viewer,
|
|
41
41
|
line,
|
|
42
42
|
column,
|
|
43
|
+
params,
|
|
43
44
|
},
|
|
44
45
|
parentSessionKey,
|
|
45
46
|
);
|
|
@@ -58,6 +59,7 @@ export function ChatInlineFilePreview({
|
|
|
58
59
|
previewViewer,
|
|
59
60
|
line: file.line ?? undefined,
|
|
60
61
|
column: file.column ?? undefined,
|
|
62
|
+
params: file.params ?? undefined,
|
|
61
63
|
});
|
|
62
64
|
};
|
|
63
65
|
|
|
@@ -19,10 +19,11 @@ import { ChatInlineContentSurface } from "@/features/chat/features/message/compo
|
|
|
19
19
|
import { useDocBrowser } from "@/shared/components/doc-browser";
|
|
20
20
|
import { IconActionButton } from "@/shared/components/ui/actions/icon-action-button";
|
|
21
21
|
import { t } from "@/shared/lib/i18n";
|
|
22
|
+
import { createUiContentParamsWindowName } from "@nextclaw/shared";
|
|
22
23
|
|
|
23
24
|
type ChatInlinePanelAppDescriptor = Pick<
|
|
24
25
|
ChatPanelAppCardViewModel,
|
|
25
|
-
"appId" | "path" | "title"
|
|
26
|
+
"appId" | "params" | "path" | "title"
|
|
26
27
|
>;
|
|
27
28
|
|
|
28
29
|
export function ChatInlinePanelAppCard({
|
|
@@ -60,21 +61,27 @@ export function ChatInlinePanelAppCard({
|
|
|
60
61
|
() =>
|
|
61
62
|
createInlinePanelAppTab({
|
|
62
63
|
appId: entry?.id ?? panelApp.appId,
|
|
64
|
+
params: panelApp.params,
|
|
63
65
|
path: panelApp.path,
|
|
64
66
|
title,
|
|
65
67
|
url,
|
|
66
|
-
|
|
67
|
-
[entry?.id, panelApp.appId, panelApp.path, title, url],
|
|
68
|
+
}),
|
|
69
|
+
[entry?.id, panelApp.appId, panelApp.params, panelApp.path, title, url],
|
|
68
70
|
);
|
|
69
|
-
const
|
|
71
|
+
const contentParamsWindowName = createUiContentParamsWindowName(panelApp.params);
|
|
72
|
+
const iframeInstanceId = `${tab.id}:${tab.navVersion}:${iframeId}:${contentParamsWindowName ?? ""}`;
|
|
70
73
|
const openExpanded = () => {
|
|
71
74
|
if (target) {
|
|
72
|
-
docBrowser.openTarget(
|
|
75
|
+
docBrowser.openTarget({
|
|
76
|
+
...target,
|
|
77
|
+
contentParams: panelApp.params,
|
|
78
|
+
});
|
|
73
79
|
return;
|
|
74
80
|
}
|
|
75
81
|
docBrowser.open(url, {
|
|
76
82
|
dedupeKey: tab.dedupeKey,
|
|
77
83
|
kind: tab.kind,
|
|
84
|
+
contentParams: panelApp.params,
|
|
78
85
|
title,
|
|
79
86
|
});
|
|
80
87
|
};
|
|
@@ -130,6 +137,7 @@ export function ChatInlinePanelAppCard({
|
|
|
130
137
|
ref={iframeRef}
|
|
131
138
|
key={iframeInstanceId}
|
|
132
139
|
src={cardUrl}
|
|
140
|
+
name={contentParamsWindowName}
|
|
133
141
|
title={title}
|
|
134
142
|
sandbox={PANEL_APP_IFRAME_SANDBOX}
|
|
135
143
|
tabIndex={0}
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
type RefObject,
|
|
7
7
|
} from "react";
|
|
8
8
|
import type { NcpMessage } from "@nextclaw/ncp";
|
|
9
|
+
import { CHAT_PROJECT_TOKEN_KIND } from "@nextclaw/shared";
|
|
9
10
|
import { toast } from "sonner";
|
|
10
11
|
import {
|
|
11
12
|
type ChatInlineDisplayViewModel,
|
|
@@ -145,6 +146,7 @@ function renderChatInlineDisplay(display: ChatInlineDisplayViewModel) {
|
|
|
145
146
|
<ChatInlinePanelAppCard
|
|
146
147
|
panelApp={{
|
|
147
148
|
appId: display.target.payload.appId,
|
|
149
|
+
params: display.target.payload.params,
|
|
148
150
|
path: display.target.payload.path,
|
|
149
151
|
title: display.title,
|
|
150
152
|
}}
|
|
@@ -345,13 +347,16 @@ export function ChatMessageListContainer({
|
|
|
345
347
|
}
|
|
346
348
|
if (
|
|
347
349
|
"key" in token &&
|
|
348
|
-
(token.kind ===
|
|
350
|
+
(token.kind === CHAT_PROJECT_TOKEN_KIND ||
|
|
351
|
+
token.kind === "workspace_file" ||
|
|
349
352
|
token.kind === "workspace_directory")
|
|
350
353
|
) {
|
|
351
|
-
const path =
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
354
|
+
const path = token.kind === CHAT_PROJECT_TOKEN_KIND
|
|
355
|
+
? token.key
|
|
356
|
+
: resolveWorkspaceReferencePath({
|
|
357
|
+
projectRoot: selectedSession?.projectRoot,
|
|
358
|
+
relativePath: token.key,
|
|
359
|
+
});
|
|
355
360
|
if (path) {
|
|
356
361
|
presenter.chatThreadManager.openFilePreview({
|
|
357
362
|
path,
|