@nextclaw/ui 0.15.11 → 0.15.13
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 +41 -0
- package/dist/assets/{api-pbx14YAL.js → api-DGG69ZcJ.js} +3 -3
- package/dist/assets/app-presenter-provider-B1RgR5ug.js +106 -0
- package/dist/assets/{appearance-settings-page-D9wJdf-0.js → appearance-settings-page-BZSd1SrH.js} +1 -1
- package/dist/assets/{book-open-DZyssdCc.js → book-open-BhaS5BJv.js} +1 -1
- package/dist/assets/{card-D7l-gWkY.js → card-Bcr5YM8F.js} +1 -1
- package/dist/assets/{channels-list-page-B3qW3T-B.js → channels-list-page-C_x83ZCT.js} +2 -2
- package/dist/assets/{chat-page-DUI_Jm6r.js → chat-page-D2xfR6TK.js} +1 -1
- package/dist/assets/{config-split-page-DFEKStD7.js → config-split-page-RXAoLeNv.js} +1 -1
- package/dist/assets/{confirm-dialog-7wpxfMCp.js → confirm-dialog-o_VPKvVD.js} +1 -1
- package/dist/assets/{desktop-update-config-DP6IbxBO.js → desktop-update-config-DEOMOsDN.js} +1 -1
- package/dist/assets/{dist-B9UN0HrS.js → dist-C87P4QWA.js} +1 -1
- package/dist/assets/doc-browser-BuGh646I.js +1 -0
- package/dist/assets/{doc-browser-D7klPMHs.js → doc-browser-DQ75iUkA.js} +1 -1
- package/dist/assets/{doc-browser-context-Bw9m83Jp.js → doc-browser-context-tE0--wSa.js} +1 -1
- package/dist/assets/{eye-IVMB0Rrz.js → eye-DL9QkrKg.js} +1 -1
- package/dist/assets/{form-actions-DrFAgO58.js → form-actions-J0tIaUdK.js} +1 -1
- package/dist/assets/index-B2YF-7Yw.css +1 -0
- package/dist/assets/index-B9QEjnoY.js +2 -0
- package/dist/assets/loader-circle-CAnHHSrY.js +1 -0
- package/dist/assets/{logo-badge-DoZOVuPr.js → logo-badge-BYTbZbOF.js} +1 -1
- package/dist/assets/{mcp-marketplace-page-C151EGP-.js → mcp-marketplace-page-COWlzeP1.js} +2 -2
- package/dist/assets/mcp-marketplace-page-CaUFv37E.js +1 -0
- package/dist/assets/{model-config-page-BmfQzc52.js → model-config-page-oNqnMXNY.js} +1 -1
- package/dist/assets/{navigation-link-CJw6FLOj.js → navigation-link-Dk6VdDkc.js} +1 -1
- package/dist/assets/plus-DjdTWPjQ.js +1 -0
- package/dist/assets/{popover-DitLJE6k.js → popover-Txwrum65.js} +1 -1
- package/dist/assets/{provider-scoped-model-input-CuAtduIc.js → provider-scoped-model-input-B7rQM4b6.js} +1 -1
- package/dist/assets/{providers-config-page-CrmuY4DC.js → providers-config-page-CP-OnvLK.js} +1 -1
- package/dist/assets/{react-CTDaaQ9V.js → react-B0LbRnAE.js} +1 -1
- package/dist/assets/{refresh-cw-E5qTlQY6.js → refresh-cw-C84mMku5.js} +1 -1
- package/dist/assets/remote-2gZkJSX4.js +1 -0
- package/dist/assets/rotate-cw-DQJhDnjg.js +1 -0
- package/dist/assets/{runtime-config-page-1fPJJvo5.js → runtime-config-page-u_fuHxc8.js} +1 -1
- package/dist/assets/{save-CTiZvAd2.js → save-R_qyrHpa.js} +1 -1
- package/dist/assets/{search-CztGmpnR.js → search-UKxiouNi.js} +1 -1
- package/dist/assets/{search-config-page-D3jVALZU.js → search-config-page-DzkpSGFH.js} +1 -1
- package/dist/assets/{secrets-config-page-CxWLQCjZ.js → secrets-config-page-DhoLYm7Z.js} +2 -2
- package/dist/assets/security-config-yraYoy9s.js +1 -0
- package/dist/assets/{select-BPVI2dQ_.js → select-CYHT8P5S.js} +1 -1
- package/dist/assets/{settings-2-Cd7QaBsq.js → settings-2-DGh4bGSi.js} +1 -1
- package/dist/assets/skeleton-BQuylYFO.js +1 -0
- package/dist/assets/{tabs-DrUmYwEQ.js → tabs-BCihm1zK.js} +1 -1
- package/dist/assets/{tag-chip-CmViAZKK.js → tag-chip-De0iz4o2.js} +1 -1
- package/dist/assets/{trash-2-DMRJ63AB.js → trash-2-Bo3DWiju.js} +1 -1
- package/dist/assets/use-config-DCzwgChy.js +1 -0
- package/dist/assets/{use-confirm-dialog-Du33bHPz.js → use-confirm-dialog-WZ94CehU.js} +1 -1
- package/dist/assets/{use-viewport-layout-D80O1d90.js → use-viewport-layout-CZ-3pMne.js} +1 -1
- package/dist/assets/x-NZxXyqW1.js +1 -0
- package/dist/index.html +28 -28
- package/package.json +9 -9
- package/src/features/chat/components/layout/__tests__/chat-sidebar.test.tsx +1 -0
- package/src/features/chat/components/layout/chat-sidebar-desktop-layout.tsx +4 -4
- package/src/features/chat/components/layout/chat-sidebar.tsx +27 -12
- package/src/features/chat/features/conversation/components/session-conversation-input.tsx +4 -19
- package/src/features/chat/features/conversation/hooks/__tests__/use-session-conversation-slash-commands.test.tsx +79 -0
- package/src/features/chat/features/conversation/hooks/use-session-conversation-slash-commands.ts +55 -0
- package/src/features/chat/features/message/components/__tests__/chat-inline-panel-app-card.test.tsx +28 -0
- package/src/features/chat/features/message/components/__tests__/chat-message-list.container.test.tsx +11 -12
- package/src/features/chat/features/message/components/chat-inline-panel-app-card.tsx +10 -7
- package/src/features/chat/features/message/components/chat-message-list.container.tsx +1 -0
- package/src/features/chat/features/message/utils/__tests__/chat-message-execution-summary.utils.test.ts +27 -15
- package/src/features/chat/features/message/utils/__tests__/chat-message-show-content-tool-card.utils.test.ts +3 -0
- package/src/features/chat/features/message/utils/chat-inline-panel-app-card.utils.ts +8 -6
- package/src/features/chat/features/message/utils/chat-message-execution-summary.utils.ts +1 -6
- package/src/features/chat/features/message/utils/chat-message-show-content-tool-card.utils.ts +2 -0
- package/src/features/chat/features/message/utils/chat-message-texts.utils.ts +0 -1
- package/src/features/chat/features/project/components/__tests__/chat-project-add-dialog.test.tsx +116 -0
- package/src/features/chat/features/project/components/chat-project-add-dialog.tsx +305 -0
- package/src/features/chat/features/session/components/__tests__/chat-sidebar-project-groups.test.tsx +3 -2
- package/src/features/chat/features/session/components/chat-sidebar-project-groups.tsx +2 -2
- package/src/features/chat/features/workspace/components/__tests__/workspace-file-preview-error-text.test.ts +22 -0
- package/src/features/chat/features/workspace/components/chat-session-workspace-file-preview.tsx +9 -5
- package/src/features/chat/managers/__tests__/chat-ui.manager.test.ts +24 -0
- package/src/features/chat/managers/chat-ui.manager.ts +7 -1
- package/src/features/cron/components/cron-task-discovery.tsx +1 -6
- package/src/features/right-panel-resources/configs/right-panel-resource-routes.config.ts +18 -4
- package/src/features/right-panel-resources/index.ts +1 -0
- package/src/features/right-panel-resources/utils/__tests__/right-panel-resource-route-resolver.utils.test.ts +33 -0
- package/src/features/right-panel-resources/utils/__tests__/right-panel-resource-uri.utils.test.ts +14 -1
- package/src/features/right-panel-resources/utils/right-panel-resource-uri.utils.ts +10 -2
- package/src/shared/components/path-picker/__tests__/server-path-picker-dialog.test.tsx +9 -0
- package/src/shared/components/path-picker/server-path-picker-dialog.tsx +8 -2
- package/src/shared/components/path-picker/server-path-picker-toolbar.tsx +8 -4
- package/src/shared/hooks/use-projects.ts +15 -1
- package/src/shared/lib/api/projects/project.utils.ts +7 -0
- package/src/shared/lib/api/types.ts +1 -1
- package/src/shared/lib/api/utils/ncp-session.utils.ts +7 -0
- package/src/shared/lib/i18n/locales/en-US/chat.json +26 -9
- package/src/shared/lib/i18n/locales/zh-CN/chat.json +26 -9
- package/src/shared/lib/resource-uri/utils/__tests__/resource-uri-resolver.utils.test.ts +13 -1
- package/src/shared/lib/resource-uri/utils/resource-uri.utils.ts +6 -3
- package/dist/assets/app-presenter-provider-Q6u2xBAn.js +0 -106
- package/dist/assets/doc-browser-wgEeLkn1.js +0 -1
- package/dist/assets/index-D297NGOb.js +0 -2
- package/dist/assets/index-DqZoodZL.css +0 -1
- package/dist/assets/loader-circle-DDgFo4Kv.js +0 -1
- package/dist/assets/mcp-marketplace-page-BtNExfMn.js +0 -1
- package/dist/assets/plus-CYxLSfLh.js +0 -1
- package/dist/assets/remote-YzPpOPmK.js +0 -1
- package/dist/assets/rotate-cw-BXeiDT6C.js +0 -1
- package/dist/assets/security-config-DEyj4WBh.js +0 -1
- package/dist/assets/skeleton-CUpAXGoe.js +0 -1
- package/dist/assets/use-config-gpXy2Iyc.js +0 -1
- package/dist/assets/x-CsLMuNFe.js +0 -1
- package/src/features/chat/features/project/components/__tests__/chat-project-create-dialog.test.tsx +0 -67
- package/src/features/chat/features/project/components/chat-project-create-dialog.tsx +0 -203
package/src/features/right-panel-resources/utils/__tests__/right-panel-resource-uri.utils.test.ts
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { describe, expect, it } from 'vitest';
|
|
2
2
|
import type { PanelAppEntryView } from '@/shared/lib/api';
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
createPanelAppContentPath,
|
|
5
|
+
createPanelAppResourceUri,
|
|
6
|
+
createPanelAppRightPanelResourceTarget,
|
|
7
|
+
} from '@/features/right-panel-resources/utils/right-panel-resource-uri.utils';
|
|
4
8
|
|
|
5
9
|
function createPanelAppEntry(overrides: Partial<PanelAppEntryView> = {}): PanelAppEntryView {
|
|
6
10
|
return {
|
|
@@ -22,6 +26,15 @@ function createPanelAppEntry(overrides: Partial<PanelAppEntryView> = {}): PanelA
|
|
|
22
26
|
}
|
|
23
27
|
|
|
24
28
|
describe('right-panel-resource uri utils', () => {
|
|
29
|
+
it('builds matching external panel app resource and content URLs', () => {
|
|
30
|
+
expect(createPanelAppResourceUri('demo', '/tmp/demo.panel')).toBe(
|
|
31
|
+
'nextclaw://panel-app/demo?path=%2Ftmp%2Fdemo.panel',
|
|
32
|
+
);
|
|
33
|
+
expect(createPanelAppContentPath('demo', '/tmp/demo.panel')).toBe(
|
|
34
|
+
'/api/panel-apps/demo/content?path=%2Ftmp%2Fdemo.panel',
|
|
35
|
+
);
|
|
36
|
+
});
|
|
37
|
+
|
|
25
38
|
it('keeps panel app image icons as dock image icons', () => {
|
|
26
39
|
const target = createPanelAppRightPanelResourceTarget(createPanelAppEntry({
|
|
27
40
|
icon: '/api/panel-apps/demo/assets/icon.png',
|
|
@@ -34,8 +34,16 @@ export function normalizeRightPanelAppsUrl(url: string): string {
|
|
|
34
34
|
return createRightPanelAppsUrl(getRightPanelAppsTabFromUrl(url));
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
export function createPanelAppResourceUri(appId: string): string {
|
|
38
|
-
|
|
37
|
+
export function createPanelAppResourceUri(appId: string, sourcePath?: string): string {
|
|
38
|
+
const uri = `nextclaw://panel-app/${encodeURIComponent(appId)}`;
|
|
39
|
+
const path = sourcePath?.trim();
|
|
40
|
+
return path ? `${uri}?${new URLSearchParams({ path }).toString()}` : uri;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function createPanelAppContentPath(appId: string, sourcePath?: string): string {
|
|
44
|
+
const url = `/api/panel-apps/${encodeURIComponent(appId)}/content`;
|
|
45
|
+
const path = sourcePath?.trim();
|
|
46
|
+
return path ? `${url}?${new URLSearchParams({ path }).toString()}` : url;
|
|
39
47
|
}
|
|
40
48
|
|
|
41
49
|
function isPanelAppImageIcon(icon: string): boolean {
|
|
@@ -20,6 +20,7 @@ vi.mock('@/shared/hooks/use-server-path-create-directory', () => ({
|
|
|
20
20
|
}));
|
|
21
21
|
|
|
22
22
|
type PickerOverrides = {
|
|
23
|
+
allowCreateDirectory?: boolean;
|
|
23
24
|
currentPath?: string;
|
|
24
25
|
defaultWorkspacePath?: string;
|
|
25
26
|
description?: string;
|
|
@@ -29,6 +30,7 @@ type PickerOverrides = {
|
|
|
29
30
|
const createPickerElement = (overrides?: PickerOverrides) => (
|
|
30
31
|
<ServerPathPickerDialog
|
|
31
32
|
open={overrides?.open ?? true}
|
|
33
|
+
allowCreateDirectory={overrides?.allowCreateDirectory}
|
|
32
34
|
currentPath={overrides?.currentPath ?? '/workspace'}
|
|
33
35
|
defaultWorkspacePath={overrides?.defaultWorkspacePath}
|
|
34
36
|
isSaving={false}
|
|
@@ -236,6 +238,13 @@ describe('ServerPathPickerDialog', () => {
|
|
|
236
238
|
);
|
|
237
239
|
});
|
|
238
240
|
|
|
241
|
+
it('hides directory creation when the caller only allows existing directories', () => {
|
|
242
|
+
renderPicker({ allowCreateDirectory: false });
|
|
243
|
+
|
|
244
|
+
expect(screen.queryByRole('button', { name: '新建文件夹' })).toBeNull();
|
|
245
|
+
expect(screen.getByText('单击选择,双击进入')).toBeTruthy();
|
|
246
|
+
});
|
|
247
|
+
|
|
239
248
|
it('uses single click to select and double click to enter a folder', async () => {
|
|
240
249
|
const user = userEvent.setup();
|
|
241
250
|
renderPicker();
|
|
@@ -20,6 +20,7 @@ import { t } from '@/shared/lib/i18n';
|
|
|
20
20
|
|
|
21
21
|
type ServerPathPickerDialogProps = {
|
|
22
22
|
open: boolean;
|
|
23
|
+
allowCreateDirectory?: boolean;
|
|
23
24
|
currentPath?: string | null;
|
|
24
25
|
defaultWorkspacePath?: string | null;
|
|
25
26
|
isSaving: boolean;
|
|
@@ -51,6 +52,7 @@ function resolveHomeRelativePath(path: string, homePath?: string | null): string
|
|
|
51
52
|
|
|
52
53
|
export function ServerPathPickerDialog({
|
|
53
54
|
open,
|
|
55
|
+
allowCreateDirectory = true,
|
|
54
56
|
currentPath,
|
|
55
57
|
defaultWorkspacePath,
|
|
56
58
|
isSaving,
|
|
@@ -88,6 +90,7 @@ export function ServerPathPickerDialog({
|
|
|
88
90
|
{open ? (
|
|
89
91
|
<ServerPathPickerSession
|
|
90
92
|
key={sessionKey}
|
|
93
|
+
allowCreateDirectory={allowCreateDirectory}
|
|
91
94
|
currentPath={normalizedCurrentPath}
|
|
92
95
|
defaultWorkspacePath={normalizedDefaultWorkspacePath}
|
|
93
96
|
isSaving={isSaving}
|
|
@@ -105,6 +108,7 @@ export function ServerPathPickerDialog({
|
|
|
105
108
|
}
|
|
106
109
|
|
|
107
110
|
type ServerPathPickerSessionProps = {
|
|
111
|
+
allowCreateDirectory: boolean;
|
|
108
112
|
currentPath: string;
|
|
109
113
|
defaultWorkspacePath: string;
|
|
110
114
|
isSaving: boolean;
|
|
@@ -117,6 +121,7 @@ type ServerPathPickerSessionProps = {
|
|
|
117
121
|
};
|
|
118
122
|
|
|
119
123
|
function ServerPathPickerSession({
|
|
124
|
+
allowCreateDirectory,
|
|
120
125
|
currentPath,
|
|
121
126
|
defaultWorkspacePath,
|
|
122
127
|
isSaving,
|
|
@@ -245,7 +250,7 @@ function ServerPathPickerSession({
|
|
|
245
250
|
const createDirectory = async () => {
|
|
246
251
|
const parentPath = browseQuery.data?.currentPath;
|
|
247
252
|
const name = newFolderName.trim();
|
|
248
|
-
if (!parentPath || !name || createDirectoryMutation.isPending) {
|
|
253
|
+
if (!allowCreateDirectory || !parentPath || !name || createDirectoryMutation.isPending) {
|
|
249
254
|
return;
|
|
250
255
|
}
|
|
251
256
|
try {
|
|
@@ -301,6 +306,7 @@ function ServerPathPickerSession({
|
|
|
301
306
|
/>
|
|
302
307
|
|
|
303
308
|
<ServerPathPickerToolbar
|
|
309
|
+
allowCreateDirectory={allowCreateDirectory}
|
|
304
310
|
disabled={navigationDisabled || !browseQuery.data?.currentPath}
|
|
305
311
|
onNewFolder={() => {
|
|
306
312
|
setNewFolderVisible(true);
|
|
@@ -320,7 +326,7 @@ function ServerPathPickerSession({
|
|
|
320
326
|
/>
|
|
321
327
|
</div>
|
|
322
328
|
|
|
323
|
-
{newFolderVisible ? (
|
|
329
|
+
{allowCreateDirectory && newFolderVisible ? (
|
|
324
330
|
<ServerPathPickerNewFolder
|
|
325
331
|
errorMessage={createDirectoryErrorMessage}
|
|
326
332
|
isCreating={createDirectoryMutation.isPending}
|
|
@@ -3,20 +3,24 @@ import { Button } from '@/shared/components/ui/button';
|
|
|
3
3
|
import { t } from '@/shared/lib/i18n';
|
|
4
4
|
|
|
5
5
|
type ServerPathPickerToolbarProps = {
|
|
6
|
+
allowCreateDirectory: boolean;
|
|
6
7
|
disabled: boolean;
|
|
7
8
|
onNewFolder: () => void;
|
|
8
9
|
};
|
|
9
10
|
|
|
10
11
|
export function ServerPathPickerToolbar({
|
|
12
|
+
allowCreateDirectory,
|
|
11
13
|
disabled,
|
|
12
14
|
onNewFolder,
|
|
13
15
|
}: ServerPathPickerToolbarProps) {
|
|
14
16
|
return (
|
|
15
17
|
<div className="flex items-center border-b border-border bg-background px-3 py-1.5">
|
|
16
|
-
|
|
17
|
-
<
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
{allowCreateDirectory ? (
|
|
19
|
+
<Button type="button" variant="ghost" size="sm" onClick={onNewFolder} disabled={disabled}>
|
|
20
|
+
<FolderPlus className="mr-1 h-4 w-4" />
|
|
21
|
+
{t('pathPickerNewFolder')}
|
|
22
|
+
</Button>
|
|
23
|
+
) : null}
|
|
20
24
|
<span className="ml-auto text-xs text-muted-foreground">
|
|
21
25
|
{t('pathPickerDoubleClickHint')}
|
|
22
26
|
</span>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
|
2
2
|
import { toast } from "sonner";
|
|
3
|
-
import { createProject, fetchProjects } from "@/shared/lib/api";
|
|
3
|
+
import { addExistingProject, createProject, fetchProjects } from "@/shared/lib/api";
|
|
4
4
|
import { t } from "@/shared/lib/i18n";
|
|
5
5
|
|
|
6
6
|
const PROJECTS_QUERY_KEY = ["projects"] as const;
|
|
@@ -26,3 +26,17 @@ export function useCreateProject() {
|
|
|
26
26
|
},
|
|
27
27
|
});
|
|
28
28
|
}
|
|
29
|
+
|
|
30
|
+
export function useAddExistingProject() {
|
|
31
|
+
const queryClient = useQueryClient();
|
|
32
|
+
return useMutation({
|
|
33
|
+
mutationFn: addExistingProject,
|
|
34
|
+
onSuccess: async () => {
|
|
35
|
+
await queryClient.invalidateQueries({ queryKey: PROJECTS_QUERY_KEY });
|
|
36
|
+
toast.success(t("chatProjectExistingAdded"));
|
|
37
|
+
},
|
|
38
|
+
onError: (error: Error) => {
|
|
39
|
+
toast.error(`${t("chatProjectExistingFailed")}: ${error.message}`);
|
|
40
|
+
},
|
|
41
|
+
});
|
|
42
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { nextclawClient } from "@/shared/lib/api/managers/client.manager";
|
|
2
2
|
import type {
|
|
3
|
+
ProjectAddExistingRequest,
|
|
3
4
|
ProjectCreateRequest,
|
|
4
5
|
ProjectListView,
|
|
5
6
|
ProjectView,
|
|
@@ -12,3 +13,9 @@ export async function fetchProjects(): Promise<ProjectListView> {
|
|
|
12
13
|
export async function createProject(input: ProjectCreateRequest): Promise<ProjectView> {
|
|
13
14
|
return await nextclawClient.projects.create(input);
|
|
14
15
|
}
|
|
16
|
+
|
|
17
|
+
export async function addExistingProject(
|
|
18
|
+
input: ProjectAddExistingRequest,
|
|
19
|
+
): Promise<ProjectView> {
|
|
20
|
+
return await nextclawClient.projects.addExisting(input);
|
|
21
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { NcpSessionStatus } from '@nextclaw/ncp';
|
|
2
2
|
import type { RuntimeEntryView, NcpSessionSummaryView } from './ncp-session.types';
|
|
3
|
-
export type { ProjectCreateRequest, ProjectListView, ProjectTemplateView, ProjectView } from '@nextclaw/client-sdk';
|
|
3
|
+
export type { ProjectAddExistingRequest, ProjectCreateRequest, ProjectListView, ProjectTemplateView, ProjectView } from '@nextclaw/client-sdk';
|
|
4
4
|
export type { SessionEntryView, RuntimeEntryView, SessionTypeIconView, SessionMessageView, SessionEventView, NcpSessionSummaryView, NcpSessionsListView, NcpMessageView, NcpSessionMessagesView, SessionContextWindowView } from './ncp-session.types';
|
|
5
5
|
|
|
6
6
|
// API Types - matching backend response format
|
|
@@ -45,6 +45,13 @@ export async function updateNcpSession(
|
|
|
45
45
|
)) as NcpSessionSummaryView;
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
+
// POST /api/ncp/sessions/:sessionId/context/compact
|
|
49
|
+
export async function compactNcpSessionContext(
|
|
50
|
+
sessionId: string,
|
|
51
|
+
): Promise<{ compacted: true; sessionId: string }> {
|
|
52
|
+
return await nextclawClient.sessions.compactContext(sessionId);
|
|
53
|
+
}
|
|
54
|
+
|
|
48
55
|
// DELETE /api/ncp/sessions/:sessionId
|
|
49
56
|
export async function deleteNcpSession(
|
|
50
57
|
sessionId: string,
|
|
@@ -76,6 +76,7 @@
|
|
|
76
76
|
"chatWorkspacePreviewUnsupported": "This file is not supported in the sidebar preview yet.",
|
|
77
77
|
"chatWorkspacePreviewUnsupportedHint": "Legacy Word or PowerPoint files, archives, and other unsupported formats can be downloaded and opened with a local app.",
|
|
78
78
|
"chatWorkspacePreviewFailed": "The file could not be previewed. You can still download it and open it with a local app.",
|
|
79
|
+
"chatWorkspacePreviewNotFound": "This file could not be found. It may have been moved, renamed, or deleted.",
|
|
79
80
|
"chatWorkspaceSpreadsheetPreviewTruncated": "This workbook is large. The preview shows up to 500 rows and 100 columns.",
|
|
80
81
|
"chatWorkspacePreviewDownload": "Download file",
|
|
81
82
|
"chatWorkspacePreviewOpenExternally": "Open with system app",
|
|
@@ -153,7 +154,6 @@
|
|
|
153
154
|
"chatAiExecutionOutput": "output",
|
|
154
155
|
"chatAiExecutionTokens": "tokens",
|
|
155
156
|
"chatAiExecutionPartial": "partial usage",
|
|
156
|
-
"chatAiExecutionUnavailable": "Token usage unavailable",
|
|
157
157
|
"chatMessageMoreActions": "More actions",
|
|
158
158
|
"chatAiExecutionViewMetadata": "View run metadata",
|
|
159
159
|
"chatAiExecutionMetadataTitle": "AI run metadata",
|
|
@@ -221,6 +221,11 @@
|
|
|
221
221
|
"chatSlashCommandSideChatTitle": "Side chat",
|
|
222
222
|
"chatSlashCommandSideChatDescription": "Open a draft side conversation that inherits this session context.",
|
|
223
223
|
"chatSlashCommandSideChatDetail": "Creates the child session only after your first message.",
|
|
224
|
+
"chatSlashCommandCompactContextTitle": "Compact context",
|
|
225
|
+
"chatSlashCommandCompactContextDescription": "Summarize older messages to free context space in this session.",
|
|
226
|
+
"chatSlashCommandCompactContextDetail": "Keeps the latest turn and the active task available for continuation.",
|
|
227
|
+
"chatSlashCommandCompactContextSuccess": "Context compacted",
|
|
228
|
+
"chatSlashCommandCompactContextFailed": "Couldn't compact context",
|
|
224
229
|
"chatSlashPanelAppHint": "Press Enter to open this panel app on the right.",
|
|
225
230
|
"chatSlashSkillHint": "Press Enter to add this skill for the next turn.",
|
|
226
231
|
"chatPanelAppReferenceLoading": "Loading panel apps...",
|
|
@@ -310,22 +315,34 @@
|
|
|
310
315
|
"chatSessionProjectUpdateHint": "The browser lists directories from the server machine. Changing the project directory only affects future messages and tool runs. History stays unchanged.",
|
|
311
316
|
"chatSessionProjectUpdated": "Project directory updated",
|
|
312
317
|
"chatSessionProjectCleared": "Project directory cleared",
|
|
313
|
-
"
|
|
314
|
-
"
|
|
315
|
-
"
|
|
318
|
+
"chatProjectAdd": "Add Project",
|
|
319
|
+
"chatProjectAddTitle": "Add Project",
|
|
320
|
+
"chatProjectAddDescription": "Create a new project directory or add an existing directory to the project list.",
|
|
321
|
+
"chatProjectCreateMode": "Create new",
|
|
322
|
+
"chatProjectCreateModeDescription": "Create a new project directory and optionally initialize it from a template.",
|
|
323
|
+
"chatProjectExistingMode": "Add existing directory",
|
|
324
|
+
"chatProjectExistingModeDescription": "Select an existing directory on the server machine. Its folder name becomes the project name, and its contents will not be changed or initialized.",
|
|
316
325
|
"chatProjectNameLabel": "Project name",
|
|
317
326
|
"chatProjectNamePlaceholder": "For example: Personal knowledge base",
|
|
318
327
|
"chatProjectTemplateLabel": "Project template",
|
|
319
328
|
"chatProjectTemplateEmpty": "Empty project",
|
|
320
329
|
"chatProjectTemplateKnowledgeBase": "Knowledge base",
|
|
321
|
-
"
|
|
322
|
-
"
|
|
323
|
-
"
|
|
324
|
-
"
|
|
325
|
-
"
|
|
330
|
+
"chatProjectCreatePathLabel": "Project directory (optional)",
|
|
331
|
+
"chatProjectCreatePathPlaceholder": "/path/to/new-project",
|
|
332
|
+
"chatProjectCreatePathHint": "Leave this blank to create a same-named folder under the default working directory, or select an empty directory.",
|
|
333
|
+
"chatProjectCreatePathPickerTitle": "Select or Create Project Directory",
|
|
334
|
+
"chatProjectCreatePathPickerDescription": "Select an empty directory or create a new folder for the project.",
|
|
335
|
+
"chatProjectExistingPathLabel": "Existing project directory",
|
|
336
|
+
"chatProjectExistingPathPlaceholder": "/path/to/existing-project",
|
|
337
|
+
"chatProjectExistingPathHint": "The directory must already exist. Its contents will remain unchanged.",
|
|
338
|
+
"chatProjectExistingPathPickerTitle": "Select Existing Project Directory",
|
|
339
|
+
"chatProjectExistingPathPickerDescription": "Select the server directory to add to the project list.",
|
|
326
340
|
"chatProjectCreateAction": "Create project",
|
|
341
|
+
"chatProjectExistingAction": "Add to project list",
|
|
327
342
|
"chatProjectCreated": "Project created",
|
|
328
343
|
"chatProjectCreateFailed": "Failed to create project",
|
|
344
|
+
"chatProjectExistingAdded": "Project added",
|
|
345
|
+
"chatProjectExistingFailed": "Failed to add project",
|
|
329
346
|
"chatSessionErrorTitle": "Something went wrong",
|
|
330
347
|
"chatSendFailed": "Failed to send message",
|
|
331
348
|
"chatErrorDetails": "Details",
|
|
@@ -76,6 +76,7 @@
|
|
|
76
76
|
"chatWorkspacePreviewUnsupported": "该文件暂不支持在侧栏预览。",
|
|
77
77
|
"chatWorkspacePreviewUnsupportedHint": "旧版 Word、PowerPoint、压缩包等暂不支持的格式,可以下载后用本机应用打开。",
|
|
78
78
|
"chatWorkspacePreviewFailed": "文件预览失败。你仍可以下载后用本机应用打开。",
|
|
79
|
+
"chatWorkspacePreviewNotFound": "找不到这个文件。它可能已被移动、重命名或删除。",
|
|
79
80
|
"chatWorkspaceSpreadsheetPreviewTruncated": "工作簿较大,当前最多显示 500 行和 100 列。",
|
|
80
81
|
"chatWorkspacePreviewDownload": "下载文件",
|
|
81
82
|
"chatWorkspacePreviewOpenExternally": "用系统应用打开",
|
|
@@ -153,7 +154,6 @@
|
|
|
153
154
|
"chatAiExecutionOutput": "输出",
|
|
154
155
|
"chatAiExecutionTokens": "Token",
|
|
155
156
|
"chatAiExecutionPartial": "部分用量",
|
|
156
|
-
"chatAiExecutionUnavailable": "Token 用量不可用",
|
|
157
157
|
"chatMessageMoreActions": "更多操作",
|
|
158
158
|
"chatAiExecutionViewMetadata": "查看运行元数据",
|
|
159
159
|
"chatAiExecutionMetadataTitle": "AI 运行元数据",
|
|
@@ -221,6 +221,11 @@
|
|
|
221
221
|
"chatSlashCommandSideChatTitle": "侧边对话",
|
|
222
222
|
"chatSlashCommandSideChatDescription": "打开一个继承当前会话上下文的右侧草稿会话。",
|
|
223
223
|
"chatSlashCommandSideChatDetail": "只有发送第一条消息后才会创建真实 child 会话。",
|
|
224
|
+
"chatSlashCommandCompactContextTitle": "压缩上下文",
|
|
225
|
+
"chatSlashCommandCompactContextDescription": "总结较早的消息,为当前会话释放上下文空间。",
|
|
226
|
+
"chatSlashCommandCompactContextDetail": "保留最近一轮和当前任务,供后续继续处理。",
|
|
227
|
+
"chatSlashCommandCompactContextSuccess": "上下文已压缩",
|
|
228
|
+
"chatSlashCommandCompactContextFailed": "无法压缩上下文",
|
|
224
229
|
"chatSlashPanelAppHint": "回车在右侧打开这个面板应用。",
|
|
225
230
|
"chatSlashSkillHint": "回车把该技能加入本轮请求。",
|
|
226
231
|
"chatPanelAppReferenceLoading": "加载面板应用中…",
|
|
@@ -310,22 +315,34 @@
|
|
|
310
315
|
"chatSessionProjectUpdateHint": "这里浏览和选择的是服务端机器上的目录。修改项目目录只影响后续消息和后续工具执行,不会改写历史会话。",
|
|
311
316
|
"chatSessionProjectUpdated": "项目目录已更新",
|
|
312
317
|
"chatSessionProjectCleared": "项目目录已清除",
|
|
313
|
-
"
|
|
314
|
-
"
|
|
315
|
-
"
|
|
318
|
+
"chatProjectAdd": "添加项目",
|
|
319
|
+
"chatProjectAddTitle": "添加项目",
|
|
320
|
+
"chatProjectAddDescription": "新建一个项目目录,或将已有目录添加到项目列表。",
|
|
321
|
+
"chatProjectCreateMode": "新建项目",
|
|
322
|
+
"chatProjectCreateModeDescription": "创建新的项目目录,并可使用模板初始化项目内容。",
|
|
323
|
+
"chatProjectExistingMode": "添加已有目录",
|
|
324
|
+
"chatProjectExistingModeDescription": "选择服务端机器上的已有目录。项目名称取目录名,不会修改或初始化目录内容。",
|
|
316
325
|
"chatProjectNameLabel": "项目名称",
|
|
317
326
|
"chatProjectNamePlaceholder": "例如:个人知识库",
|
|
318
327
|
"chatProjectTemplateLabel": "项目模板",
|
|
319
328
|
"chatProjectTemplateEmpty": "空项目",
|
|
320
329
|
"chatProjectTemplateKnowledgeBase": "知识库",
|
|
321
|
-
"
|
|
322
|
-
"
|
|
323
|
-
"
|
|
324
|
-
"
|
|
325
|
-
"
|
|
330
|
+
"chatProjectCreatePathLabel": "项目目录(可选)",
|
|
331
|
+
"chatProjectCreatePathPlaceholder": "/path/to/new-project",
|
|
332
|
+
"chatProjectCreatePathHint": "留空时,会在默认工作目录下新建与项目同名的文件夹;也可以选择一个空目录。",
|
|
333
|
+
"chatProjectCreatePathPickerTitle": "选择或新建项目目录",
|
|
334
|
+
"chatProjectCreatePathPickerDescription": "选择一个空目录,或新建文件夹作为项目目录。",
|
|
335
|
+
"chatProjectExistingPathLabel": "已有项目目录",
|
|
336
|
+
"chatProjectExistingPathPlaceholder": "/path/to/existing-project",
|
|
337
|
+
"chatProjectExistingPathHint": "目录必须已经存在;添加后目录内容保持不变。",
|
|
338
|
+
"chatProjectExistingPathPickerTitle": "选择已有项目目录",
|
|
339
|
+
"chatProjectExistingPathPickerDescription": "选择要添加到项目列表的服务端目录。",
|
|
326
340
|
"chatProjectCreateAction": "创建项目",
|
|
341
|
+
"chatProjectExistingAction": "添加到项目列表",
|
|
327
342
|
"chatProjectCreated": "项目已创建",
|
|
328
343
|
"chatProjectCreateFailed": "创建项目失败",
|
|
344
|
+
"chatProjectExistingAdded": "项目已添加",
|
|
345
|
+
"chatProjectExistingFailed": "添加项目失败",
|
|
329
346
|
"chatSessionErrorTitle": "出错了",
|
|
330
347
|
"chatSendFailed": "发送消息失败",
|
|
331
348
|
"chatErrorDetails": "查看详情",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, expect, it } from 'vitest';
|
|
2
|
-
import { ResourceUriResolver } from '@/shared/lib/resource-uri';
|
|
2
|
+
import { parseResourceUri, ResourceUriResolver } from '@/shared/lib/resource-uri';
|
|
3
3
|
import type { ResourceUriRouteDefinition } from '@/shared/lib/resource-uri';
|
|
4
4
|
|
|
5
5
|
type TestTarget = {
|
|
@@ -27,6 +27,18 @@ const routeDefinitions: ResourceUriRouteDefinition<TestTarget>[] = [
|
|
|
27
27
|
];
|
|
28
28
|
|
|
29
29
|
describe('ResourceUriResolver', () => {
|
|
30
|
+
it('parses query parameters from root-relative resource URLs', () => {
|
|
31
|
+
const uri = parseResourceUri('/api/panel-apps/timer/content?path=%2Ftmp%2Ftimer.panel');
|
|
32
|
+
|
|
33
|
+
expect(uri).toMatchObject({
|
|
34
|
+
authority: '',
|
|
35
|
+
pathSegments: ['api', 'panel-apps', 'timer', 'content'],
|
|
36
|
+
pathname: '/api/panel-apps/timer/content',
|
|
37
|
+
scheme: '',
|
|
38
|
+
});
|
|
39
|
+
expect(uri.searchParams.get('path')).toBe('/tmp/timer.panel');
|
|
40
|
+
});
|
|
41
|
+
|
|
30
42
|
it('resolves URI targets through registered route definitions', () => {
|
|
31
43
|
const resolver = new ResourceUriResolver(routeDefinitions, {
|
|
32
44
|
getNormalizedUri: (target) => target.normalizedUri,
|
|
@@ -10,13 +10,16 @@ function normalizePathSegments(pathname: string): string[] {
|
|
|
10
10
|
export function parseResourceUri(uri: string): ParsedResourceUri {
|
|
11
11
|
const raw = uri.trim();
|
|
12
12
|
try {
|
|
13
|
-
const
|
|
13
|
+
const isRootRelative = raw.startsWith('/');
|
|
14
|
+
const parsed = isRootRelative
|
|
15
|
+
? new URL(raw, 'http://nextclaw.local')
|
|
16
|
+
: new URL(raw);
|
|
14
17
|
return {
|
|
15
|
-
authority: parsed.hostname,
|
|
18
|
+
authority: isRootRelative ? '' : parsed.hostname,
|
|
16
19
|
pathSegments: normalizePathSegments(parsed.pathname),
|
|
17
20
|
pathname: parsed.pathname,
|
|
18
21
|
raw,
|
|
19
|
-
scheme: parsed.protocol.replace(/:$/, '').toLowerCase(),
|
|
22
|
+
scheme: isRootRelative ? '' : parsed.protocol.replace(/:$/, '').toLowerCase(),
|
|
20
23
|
searchParams: parsed.searchParams,
|
|
21
24
|
};
|
|
22
25
|
} catch {
|