@nextclaw/ui 0.15.19 → 0.15.21
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 +40 -0
- package/dist/assets/api-CHo2gekJ.js +13 -0
- package/dist/assets/app-presenter-provider-2_DmWYT9.js +108 -0
- package/dist/assets/appearance-settings-page-BEy2UE7R.js +1 -0
- package/dist/assets/{book-open-HtUSPwoS.js → book-open-BrZL4o9L.js} +1 -1
- package/dist/assets/card-B5rRLu_6.js +1 -0
- package/dist/assets/{channels-list-page-Bspr1Jp4.js → channels-list-page-C1dnm68I.js} +2 -2
- package/dist/assets/chat-page-Dz6tvZ91.js +1 -0
- package/dist/assets/config-split-page-6y6W6dz4.js +1 -0
- package/dist/assets/confirm-dialog-q1CpEmR8.js +5 -0
- package/dist/assets/desktop-update-config-DrdlwP4j.js +1 -0
- package/dist/assets/{dist-CZx9Q6jc.js → dist-C9EX46wM.js} +1 -1
- package/dist/assets/doc-browser-BfttDSti.js +1 -0
- package/dist/assets/{doc-browser-B4l0Xu7u.js → doc-browser-DH-3hDnI.js} +1 -1
- package/dist/assets/{doc-browser-context-Dy7EhLI8.js → doc-browser-context-TU2heon0.js} +1 -1
- package/dist/assets/{eye-DU7p1-7X.js → eye-B-3dyZNA.js} +1 -1
- package/dist/assets/form-actions-DvvxCpL4.js +1 -0
- package/dist/assets/index-CXDPLDFD.css +1 -0
- package/dist/assets/index-k_eWScvN.js +2 -0
- package/dist/assets/loader-circle-B35NlHTI.js +1 -0
- package/dist/assets/logo-badge-DB51c0cS.js +1 -0
- package/dist/assets/mcp-marketplace-page-CkPtwPBk.js +1 -0
- package/dist/assets/{mcp-marketplace-page-Dg03SZX1.js → mcp-marketplace-page-DWtsIqml.js} +3 -3
- package/dist/assets/{model-config-page-DOBqLZoQ.js → model-config-page-D6cGNnMM.js} +1 -1
- package/dist/assets/{navigation-link-Ct6xtnVM.js → navigation-link-BSJEUiwD.js} +1 -1
- package/dist/assets/plus-BRVe5jSJ.js +1 -0
- package/dist/assets/{popover-C2oIpGBF.js → popover-Bp9IpE_N.js} +1 -1
- package/dist/assets/provider-scoped-model-input-dk7fz9NL.js +1 -0
- package/dist/assets/{providers-config-page-BAVPJKpx.js → providers-config-page-DTP2YfxA.js} +1 -1
- package/dist/assets/react-D6ShNq95.js +8 -0
- package/dist/assets/{refresh-cw-BZ8giusN.js → refresh-cw-C2e94pmp.js} +1 -1
- package/dist/assets/remote-FllhXW3o.js +1 -0
- package/dist/assets/rotate-cw-CTC-Qszi.js +1 -0
- package/dist/assets/runtime-config-page-C57OLdPm.js +1 -0
- package/dist/assets/{save-CSzFGRse.js → save-CkwDOgvt.js} +1 -1
- package/dist/assets/{search-BmSWsiG2.js → search-CLXM2reO.js} +1 -1
- package/dist/assets/{search-config-page-CrL1pUzy.js → search-config-page-tnJXaaSr.js} +1 -1
- package/dist/assets/{secrets-config-page-BlgsD0-L.js → secrets-config-page-DQ_Curaj.js} +1 -1
- package/dist/assets/security-config-S00DjFfO.js +1 -0
- package/dist/assets/{select-CklWb6Rs.js → select-CNFBf00l.js} +1 -1
- package/dist/assets/{settings-2-BSZf8327.js → settings-2-B9IT2HGd.js} +1 -1
- package/dist/assets/skeleton-Dc06gatK.js +1 -0
- package/dist/assets/{tabs-Cah5klko.js → tabs-BuNr8LSO.js} +1 -1
- package/dist/assets/{tag-chip-DcrirDgN.js → tag-chip-CDkB5smU.js} +1 -1
- package/dist/assets/{trash-2-BLQOULWD.js → trash-2-GchaJuEM.js} +1 -1
- package/dist/assets/{use-config-CuFIsUVG.js → use-config-C69rR15i.js} +1 -1
- package/dist/assets/use-confirm-dialog-DF_EgPYx.js +1 -0
- package/dist/assets/{use-viewport-layout-DU29bUJS.js → use-viewport-layout-BACNZyMY.js} +1 -1
- package/dist/assets/x-BXFquY9e.js +1 -0
- package/dist/index.html +28 -28
- package/package.json +9 -9
- package/src/app/components/__tests__/app-notification-runtime.test.tsx +32 -0
- package/src/app/components/app-notification-runtime.tsx +15 -0
- package/src/app/components/layout/sidebar-items.tsx +23 -9
- package/src/app/configs/app-navigation.config.ts +21 -0
- package/src/app/index.tsx +22 -2
- package/src/app/presenters/app.presenter.ts +8 -0
- package/src/features/chat/components/layout/chat-page-shell.tsx +7 -2
- package/src/features/chat/components/layout/chat-sidebar-desktop-layout.tsx +12 -0
- 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-message-list.container.test.tsx +12 -1
- package/src/features/chat/features/message/components/chat-message-list.container.tsx +9 -5
- package/src/features/chat/index.ts +1 -0
- package/src/features/chat/managers/__tests__/chat-completion-notification.manager.test.ts +177 -0
- package/src/features/chat/managers/chat-completion-notification.manager.ts +146 -0
- package/src/features/chat/pages/__tests__/ncp-chat-page.test.ts +26 -0
- package/src/features/chat/pages/__tests__/ncp-chat-page.test.tsx +29 -2
- package/src/features/chat/pages/ncp-chat-page.tsx +9 -0
- package/src/features/inbox/components/inbox-delivery-content.test.tsx +57 -0
- package/src/features/inbox/components/inbox-delivery-content.tsx +118 -0
- package/src/features/inbox/components/inbox-reader-dialog.test.tsx +77 -0
- package/src/features/inbox/components/inbox-reader-dialog.tsx +224 -0
- package/src/features/inbox/components/inbox-runtime.tsx +14 -0
- package/src/features/inbox/hooks/use-inbox-deliveries.ts +14 -0
- package/src/features/inbox/index.ts +7 -0
- package/src/features/inbox/managers/inbox.manager.test.ts +86 -0
- package/src/features/inbox/managers/inbox.manager.ts +178 -0
- package/src/features/inbox/pages/inbox-page.test.ts +28 -0
- package/src/features/inbox/pages/inbox-page.tsx +385 -0
- package/src/features/inbox/stores/inbox.store.ts +23 -0
- package/src/features/notifications/components/__tests__/app-notification-toast.test.tsx +48 -0
- package/src/features/notifications/components/app-notification-toast.tsx +80 -0
- package/src/features/notifications/index.ts +8 -0
- package/src/features/notifications/managers/__tests__/app-notification.manager.test.ts +75 -0
- package/src/features/notifications/managers/app-notification.manager.ts +41 -0
- package/src/platforms/mobile/components/mobile-bottom-nav.tsx +14 -7
- package/src/shared/hooks/use-projects.ts +2 -1
- package/src/shared/lib/i18n/index.ts +4 -0
- package/src/shared/lib/i18n/locales/en-US/chat.json +16 -4
- package/src/shared/lib/i18n/locales/en-US/inbox.json +30 -0
- package/src/shared/lib/i18n/locales/zh-CN/chat.json +16 -4
- package/src/shared/lib/i18n/locales/zh-CN/inbox.json +30 -0
- package/dist/assets/api-O5yEgzBt.js +0 -13
- package/dist/assets/app-presenter-provider-DXTYtH78.js +0 -106
- package/dist/assets/appearance-settings-page-BuGxSvlJ.js +0 -1
- package/dist/assets/card-GkBc-UMw.js +0 -1
- package/dist/assets/chat-page-BKQ89f4s.js +0 -1
- package/dist/assets/config-split-page-CvLXSFSC.js +0 -1
- package/dist/assets/confirm-dialog-BVH7haN0.js +0 -5
- package/dist/assets/desktop-update-config-C9ZUI8UW.js +0 -1
- package/dist/assets/doc-browser-DsVF1DrS.js +0 -1
- package/dist/assets/form-actions-aM9_jSJC.js +0 -1
- package/dist/assets/index-6pHX0DZH.js +0 -2
- package/dist/assets/index-DN6KM7WD.css +0 -1
- package/dist/assets/loader-circle-Dd6jw700.js +0 -1
- package/dist/assets/logo-badge-2-iV1pkr.js +0 -1
- package/dist/assets/mcp-marketplace-page-C-d0RMfg.js +0 -1
- package/dist/assets/plus-CsgeRId8.js +0 -1
- package/dist/assets/provider-scoped-model-input-DDqCdnTU.js +0 -1
- package/dist/assets/react-CeMr9V52.js +0 -8
- package/dist/assets/remote-C-r0SHcz.js +0 -1
- package/dist/assets/rotate-cw-DVdRfMSO.js +0 -1
- package/dist/assets/runtime-config-page-D0244uxJ.js +0 -1
- package/dist/assets/security-config-cDrdpMJW.js +0 -1
- package/dist/assets/skeleton-DG9u1BmA.js +0 -1
- package/dist/assets/use-confirm-dialog-CcySmkWp.js +0 -1
- package/dist/assets/x-BVoeXny1.js +0 -1
|
@@ -86,6 +86,7 @@ describe('createSlashCommandInputSurfacePlugin', () => {
|
|
|
86
86
|
const state = resolveChatInputSurfaceState({
|
|
87
87
|
data: {
|
|
88
88
|
isPanelAppsLoading: false,
|
|
89
|
+
isProjectsLoading: false,
|
|
89
90
|
isServerPathSearchLoading: false,
|
|
90
91
|
isSkillsLoading: false,
|
|
91
92
|
panelApps: [
|
|
@@ -96,8 +97,10 @@ describe('createSlashCommandInputSurfacePlugin', () => {
|
|
|
96
97
|
}),
|
|
97
98
|
],
|
|
98
99
|
projectRoot: '/tmp/project',
|
|
100
|
+
projects: [],
|
|
101
|
+
projectsError: null,
|
|
99
102
|
recentSkillValues: [],
|
|
100
|
-
|
|
103
|
+
referenceLocation: { view: 'root' },
|
|
101
104
|
serverPathEntries: [],
|
|
102
105
|
serverPathSearchError: null,
|
|
103
106
|
skillRecords: [
|
|
@@ -153,12 +156,15 @@ describe('createSlashCommandInputSurfacePlugin', () => {
|
|
|
153
156
|
const state = resolveChatInputSurfaceState({
|
|
154
157
|
data: {
|
|
155
158
|
isPanelAppsLoading: false,
|
|
159
|
+
isProjectsLoading: false,
|
|
156
160
|
isServerPathSearchLoading: false,
|
|
157
161
|
isSkillsLoading: false,
|
|
158
162
|
panelApps: [],
|
|
159
163
|
projectRoot: '/tmp/project',
|
|
164
|
+
projects: [],
|
|
165
|
+
projectsError: null,
|
|
160
166
|
recentSkillValues: [],
|
|
161
|
-
|
|
167
|
+
referenceLocation: { view: 'root' },
|
|
162
168
|
serverPathEntries: [],
|
|
163
169
|
serverPathSearchError: null,
|
|
164
170
|
skillRecords: [
|
|
@@ -202,12 +208,15 @@ describe('createSlashCommandInputSurfacePlugin', () => {
|
|
|
202
208
|
const state = resolveChatInputSurfaceState({
|
|
203
209
|
data: {
|
|
204
210
|
isPanelAppsLoading: false,
|
|
211
|
+
isProjectsLoading: false,
|
|
205
212
|
isServerPathSearchLoading: false,
|
|
206
213
|
isSkillsLoading: false,
|
|
207
214
|
panelApps: [],
|
|
208
215
|
projectRoot: '/tmp/project',
|
|
216
|
+
projects: [],
|
|
217
|
+
projectsError: null,
|
|
209
218
|
recentSkillValues: [],
|
|
210
|
-
|
|
219
|
+
referenceLocation: { view: 'root' },
|
|
211
220
|
serverPathEntries: [],
|
|
212
221
|
serverPathSearchError: null,
|
|
213
222
|
skillRecords: [],
|
|
@@ -233,6 +242,7 @@ describe('createSlashCommandInputSurfacePlugin', () => {
|
|
|
233
242
|
const state = resolveChatInputSurfaceState({
|
|
234
243
|
data: {
|
|
235
244
|
isPanelAppsLoading: false,
|
|
245
|
+
isProjectsLoading: false,
|
|
236
246
|
isServerPathSearchLoading: false,
|
|
237
247
|
isSkillsLoading: false,
|
|
238
248
|
panelApps: [
|
|
@@ -243,8 +253,10 @@ describe('createSlashCommandInputSurfacePlugin', () => {
|
|
|
243
253
|
}),
|
|
244
254
|
],
|
|
245
255
|
projectRoot: '/tmp/project',
|
|
256
|
+
projects: [],
|
|
257
|
+
projectsError: null,
|
|
246
258
|
recentSkillValues: [],
|
|
247
|
-
|
|
259
|
+
referenceLocation: { view: 'root' },
|
|
248
260
|
serverPathEntries: [],
|
|
249
261
|
serverPathSearchError: null,
|
|
250
262
|
skillRecords: [],
|
|
@@ -1,14 +1,22 @@
|
|
|
1
1
|
import type { ChatSkillRecord } from '@/features/chat/types/chat-input-bar.types';
|
|
2
|
-
import type { PanelAppEntryView, ServerPathSearchEntryView } from '@/shared/lib/api';
|
|
2
|
+
import type { PanelAppEntryView, ProjectView, ServerPathSearchEntryView } from '@/shared/lib/api';
|
|
3
|
+
|
|
4
|
+
export type ContextReferenceLocation =
|
|
5
|
+
| { view: 'root' }
|
|
6
|
+
| { view: 'files'; path: string }
|
|
7
|
+
| { view: 'projects' };
|
|
3
8
|
|
|
4
9
|
export type ChatInputProductPluginData = {
|
|
5
10
|
isPanelAppsLoading: boolean;
|
|
11
|
+
isProjectsLoading: boolean;
|
|
6
12
|
isServerPathSearchLoading: boolean;
|
|
7
13
|
isSkillsLoading: boolean;
|
|
8
14
|
panelApps: readonly PanelAppEntryView[];
|
|
9
15
|
projectRoot: string;
|
|
16
|
+
projects: readonly ProjectView[];
|
|
17
|
+
projectsError: string | null;
|
|
10
18
|
recentSkillValues: readonly string[];
|
|
11
|
-
|
|
19
|
+
referenceLocation: ContextReferenceLocation;
|
|
12
20
|
serverPathEntries: readonly ServerPathSearchEntryView[];
|
|
13
21
|
serverPathSearchError: string | null;
|
|
14
22
|
skillRecords: readonly ChatSkillRecord[];
|
package/src/features/chat/features/input/input-surface-plugins/context-reference-items.utils.ts
ADDED
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
import type { ChatInputSurfaceItem } from '@nextclaw/agent-chat-ui';
|
|
2
|
+
import {
|
|
3
|
+
CHAT_PROJECT_TOKEN_KIND,
|
|
4
|
+
CHAT_WORKSPACE_DIRECTORY_TOKEN_KIND,
|
|
5
|
+
CHAT_WORKSPACE_FILE_TOKEN_KIND,
|
|
6
|
+
} from '@nextclaw/shared';
|
|
7
|
+
import type { ProjectView, ServerPathSearchEntryView } from '@/shared/lib/api';
|
|
8
|
+
import type { ContextReferenceLocation } from './chat-input-product-plugin-adapters.types';
|
|
9
|
+
import {
|
|
10
|
+
resolveInputSurfaceMatchTier,
|
|
11
|
+
scoreInputSurfaceSearchCandidate,
|
|
12
|
+
} from './input-surface-search.utils';
|
|
13
|
+
|
|
14
|
+
export const FILES_NAVIGATION_ITEM_KEY = 'context-reference:navigate:files';
|
|
15
|
+
export const PROJECTS_NAVIGATION_ITEM_KEY = 'context-reference:navigate:projects';
|
|
16
|
+
export const ROOT_NAVIGATION_ITEM_KEY = 'context-reference:navigate:root';
|
|
17
|
+
|
|
18
|
+
const WORKSPACE_SECTION_KEY = 'workspace';
|
|
19
|
+
const PROJECT_SECTION_KEY = 'projects';
|
|
20
|
+
|
|
21
|
+
export type ContextReferenceInputSurfaceTexts = {
|
|
22
|
+
backLabel: string;
|
|
23
|
+
backDescription: string;
|
|
24
|
+
backHintLabel: string;
|
|
25
|
+
currentDirectoryLabel: string;
|
|
26
|
+
directoryDescription: string;
|
|
27
|
+
fileDescription: string;
|
|
28
|
+
filesDescription: string;
|
|
29
|
+
filesHintLabel: string;
|
|
30
|
+
filesLabel: string;
|
|
31
|
+
filesSubtitle: string;
|
|
32
|
+
panelAppSectionLabel: string;
|
|
33
|
+
parentLabel: string;
|
|
34
|
+
parentDescription: string;
|
|
35
|
+
parentHintLabel: string;
|
|
36
|
+
projectDescription: string;
|
|
37
|
+
projectPathLabel: string;
|
|
38
|
+
projectSectionLabel: string;
|
|
39
|
+
projectSubtitle: string;
|
|
40
|
+
projectsDescription: string;
|
|
41
|
+
projectsHintLabel: string;
|
|
42
|
+
projectsLabel: string;
|
|
43
|
+
projectsLoadFailedLabel: string;
|
|
44
|
+
projectsSubtitle: string;
|
|
45
|
+
projectRootLabel: string;
|
|
46
|
+
searchFailedLabel: string;
|
|
47
|
+
workspaceSectionLabel: string;
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
function resolveProjectLabel(projectRoot: string): string {
|
|
51
|
+
return projectRoot.split(/[\\/]+/).filter(Boolean).at(-1) ?? projectRoot;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function buildPathPreview(params: {
|
|
55
|
+
kind: 'directory' | 'file';
|
|
56
|
+
projectRoot: string;
|
|
57
|
+
relativePath: string;
|
|
58
|
+
}) {
|
|
59
|
+
const { kind, projectRoot, relativePath } = params;
|
|
60
|
+
const pathSegments = relativePath.split('/').filter(Boolean);
|
|
61
|
+
return {
|
|
62
|
+
rootLabel: resolveProjectLabel(projectRoot),
|
|
63
|
+
segments: pathSegments.map((label, index) => ({
|
|
64
|
+
label,
|
|
65
|
+
kind: index === pathSegments.length - 1 ? kind : 'directory' as const,
|
|
66
|
+
})),
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function buildProjectReferenceItems(params: {
|
|
71
|
+
projects: readonly ProjectView[];
|
|
72
|
+
query: string;
|
|
73
|
+
texts: ContextReferenceInputSurfaceTexts;
|
|
74
|
+
}): ChatInputSurfaceItem[] {
|
|
75
|
+
const collator = new Intl.Collator(undefined, { sensitivity: 'base', numeric: true });
|
|
76
|
+
return params.projects
|
|
77
|
+
.map((project, order) => ({
|
|
78
|
+
order,
|
|
79
|
+
project,
|
|
80
|
+
score: scoreInputSurfaceSearchCandidate(
|
|
81
|
+
{
|
|
82
|
+
id: project.rootPath,
|
|
83
|
+
label: project.name,
|
|
84
|
+
description: project.rootPath,
|
|
85
|
+
aliases: project.template ? [project.template] : [],
|
|
86
|
+
},
|
|
87
|
+
params.query,
|
|
88
|
+
),
|
|
89
|
+
}))
|
|
90
|
+
.filter(({ score }) => score > 0)
|
|
91
|
+
.sort((left, right) => {
|
|
92
|
+
const leftTier = resolveInputSurfaceMatchTier(left.score);
|
|
93
|
+
const rightTier = resolveInputSurfaceMatchTier(right.score);
|
|
94
|
+
if (rightTier !== leftTier) {
|
|
95
|
+
return rightTier - leftTier;
|
|
96
|
+
}
|
|
97
|
+
if (right.score !== left.score) {
|
|
98
|
+
return right.score - left.score;
|
|
99
|
+
}
|
|
100
|
+
return collator.compare(left.project.name, right.project.name) || left.order - right.order;
|
|
101
|
+
})
|
|
102
|
+
.map(({ project }) => ({
|
|
103
|
+
key: `context-reference:project:${project.rootPath}`,
|
|
104
|
+
icon: 'project',
|
|
105
|
+
title: project.name,
|
|
106
|
+
subtitle: params.texts.projectSubtitle,
|
|
107
|
+
description: params.texts.projectDescription,
|
|
108
|
+
detailLines: [`${params.texts.projectPathLabel}: ${project.rootPath}`],
|
|
109
|
+
sectionKey: PROJECT_SECTION_KEY,
|
|
110
|
+
sectionLabel: params.texts.projectSectionLabel,
|
|
111
|
+
value: project.rootPath,
|
|
112
|
+
tokenKind: CHAT_PROJECT_TOKEN_KIND,
|
|
113
|
+
tokenKey: project.rootPath,
|
|
114
|
+
}));
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export function buildWorkspaceReferenceItems(params: {
|
|
118
|
+
entries: readonly ServerPathSearchEntryView[];
|
|
119
|
+
navigateDirectories: boolean;
|
|
120
|
+
projectRoot: string;
|
|
121
|
+
texts: ContextReferenceInputSurfaceTexts;
|
|
122
|
+
}): ChatInputSurfaceItem[] {
|
|
123
|
+
const projectLabel = resolveProjectLabel(params.projectRoot);
|
|
124
|
+
return params.entries.map((entry) => {
|
|
125
|
+
const navigates = params.navigateDirectories && entry.kind === 'directory';
|
|
126
|
+
return {
|
|
127
|
+
key: `workspace:${entry.kind}:${entry.relativePath}`,
|
|
128
|
+
icon: entry.kind === 'directory' ? 'folder' : 'file',
|
|
129
|
+
title: entry.name,
|
|
130
|
+
subtitle: entry.parentRelativePath || projectLabel,
|
|
131
|
+
description: entry.kind === 'directory'
|
|
132
|
+
? params.texts.directoryDescription
|
|
133
|
+
: params.texts.fileDescription,
|
|
134
|
+
detailLines: [
|
|
135
|
+
`${params.texts.projectRootLabel}: ${params.projectRoot}`,
|
|
136
|
+
entry.relativePath,
|
|
137
|
+
],
|
|
138
|
+
sectionKey: WORKSPACE_SECTION_KEY,
|
|
139
|
+
sectionLabel: params.texts.workspaceSectionLabel,
|
|
140
|
+
value: entry.relativePath,
|
|
141
|
+
tokenKind: navigates
|
|
142
|
+
? undefined
|
|
143
|
+
: entry.kind === 'directory'
|
|
144
|
+
? CHAT_WORKSPACE_DIRECTORY_TOKEN_KIND
|
|
145
|
+
: CHAT_WORKSPACE_FILE_TOKEN_KIND,
|
|
146
|
+
tokenKey: navigates ? undefined : entry.relativePath,
|
|
147
|
+
selectionBehavior: navigates ? 'navigate' : undefined,
|
|
148
|
+
pathPreview: buildPathPreview({
|
|
149
|
+
kind: entry.kind,
|
|
150
|
+
projectRoot: params.projectRoot,
|
|
151
|
+
relativePath: entry.relativePath,
|
|
152
|
+
}),
|
|
153
|
+
} satisfies ChatInputSurfaceItem;
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export function buildCurrentDirectoryReferenceItem(params: {
|
|
158
|
+
projectRoot: string;
|
|
159
|
+
referencePath: string;
|
|
160
|
+
texts: ContextReferenceInputSurfaceTexts;
|
|
161
|
+
}): ChatInputSurfaceItem {
|
|
162
|
+
const { projectRoot, referencePath, texts } = params;
|
|
163
|
+
const title = referencePath.split('/').filter(Boolean).at(-1) ?? referencePath;
|
|
164
|
+
return {
|
|
165
|
+
key: `context-reference:current-directory:${referencePath}`,
|
|
166
|
+
icon: 'folder',
|
|
167
|
+
title: texts.currentDirectoryLabel,
|
|
168
|
+
subtitle: title,
|
|
169
|
+
description: texts.directoryDescription,
|
|
170
|
+
detailLines: [
|
|
171
|
+
`${texts.projectRootLabel}: ${projectRoot}`,
|
|
172
|
+
referencePath,
|
|
173
|
+
],
|
|
174
|
+
sectionKey: WORKSPACE_SECTION_KEY,
|
|
175
|
+
sectionLabel: texts.workspaceSectionLabel,
|
|
176
|
+
value: referencePath,
|
|
177
|
+
tokenKind: CHAT_WORKSPACE_DIRECTORY_TOKEN_KIND,
|
|
178
|
+
tokenKey: referencePath,
|
|
179
|
+
pathPreview: buildPathPreview({
|
|
180
|
+
kind: 'directory',
|
|
181
|
+
projectRoot,
|
|
182
|
+
relativePath: referencePath,
|
|
183
|
+
}),
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
export function buildFilesNavigationItem(
|
|
188
|
+
texts: ContextReferenceInputSurfaceTexts,
|
|
189
|
+
): ChatInputSurfaceItem {
|
|
190
|
+
return {
|
|
191
|
+
key: FILES_NAVIGATION_ITEM_KEY,
|
|
192
|
+
icon: 'files',
|
|
193
|
+
title: texts.filesLabel,
|
|
194
|
+
subtitle: texts.filesSubtitle,
|
|
195
|
+
description: texts.filesDescription,
|
|
196
|
+
detailLines: [],
|
|
197
|
+
hintLabel: texts.filesHintLabel,
|
|
198
|
+
selectionBehavior: 'navigate',
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
export function buildProjectsNavigationItem(
|
|
203
|
+
texts: ContextReferenceInputSurfaceTexts,
|
|
204
|
+
): ChatInputSurfaceItem {
|
|
205
|
+
return {
|
|
206
|
+
key: PROJECTS_NAVIGATION_ITEM_KEY,
|
|
207
|
+
icon: 'project',
|
|
208
|
+
title: texts.projectsLabel,
|
|
209
|
+
subtitle: texts.projectsSubtitle,
|
|
210
|
+
description: texts.projectsDescription,
|
|
211
|
+
detailLines: [],
|
|
212
|
+
hintLabel: texts.projectsHintLabel,
|
|
213
|
+
selectionBehavior: 'navigate',
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function resolveParentReferencePath(referencePath: string): string | null {
|
|
218
|
+
const segments = referencePath.split('/').filter(Boolean);
|
|
219
|
+
return segments.length === 0 ? null : segments.slice(0, -1).join('/');
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
export function buildBackNavigationItem(params: {
|
|
223
|
+
location: ContextReferenceLocation;
|
|
224
|
+
texts: ContextReferenceInputSurfaceTexts;
|
|
225
|
+
}): ChatInputSurfaceItem {
|
|
226
|
+
const { location, texts } = params;
|
|
227
|
+
const referencePath = location.view === 'files' ? location.path : '';
|
|
228
|
+
const isRootView = location.view !== 'files' || !referencePath;
|
|
229
|
+
return {
|
|
230
|
+
key: ROOT_NAVIGATION_ITEM_KEY,
|
|
231
|
+
icon: 'back',
|
|
232
|
+
title: isRootView ? texts.backLabel : texts.parentLabel,
|
|
233
|
+
subtitle: '',
|
|
234
|
+
description: isRootView ? texts.backDescription : texts.parentDescription,
|
|
235
|
+
detailLines: [],
|
|
236
|
+
hintLabel: isRootView ? texts.backHintLabel : texts.parentHintLabel,
|
|
237
|
+
value: resolveParentReferencePath(referencePath) ?? '',
|
|
238
|
+
selectionBehavior: 'navigate',
|
|
239
|
+
};
|
|
240
|
+
}
|
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/',
|