@nextclaw/ui 0.24.0-beta.0 → 0.24.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/CHANGELOG.md +40 -0
  2. package/dist/assets/api-BqVIq0lF.js +13 -0
  3. package/dist/assets/{appearance-settings-page-CPoFZLw8.js → appearance-settings-page-CTWn1eAT.js} +1 -1
  4. package/dist/assets/{book-open-jcUt0rr9.js → book-open-o2x7OLiO.js} +1 -1
  5. package/dist/assets/{channels-list-page-anMJAkfK.js → channels-list-page-Bzn-pzyJ.js} +1 -1
  6. package/dist/assets/{config-split-page-AcuFnbIC.js → config-split-page-CnRbXXlh.js} +1 -1
  7. package/dist/assets/{confirm-dialog-B-atCfC5.js → confirm-dialog-B_Hbcra2.js} +1 -1
  8. package/dist/assets/{createLucideIcon-Blz0FzrV.js → createLucideIcon-rwwpqyks.js} +1 -1
  9. package/dist/assets/{desktop-capabilities-page-8lKF_Ob-.js → desktop-capabilities-page-C2ayYQTr.js} +1 -1
  10. package/dist/assets/{desktop-update-config-DABXDpbj.js → desktop-update-config-C13czpyL.js} +1 -1
  11. package/dist/assets/{dist-eH-_j8yU.js → dist-BsF3hfOi.js} +1 -1
  12. package/dist/assets/{dist-D8kcEpTf.js → dist-aNN3iQxV.js} +1 -1
  13. package/dist/assets/{doc-browser-DzXRehlW.js → doc-browser-D8dIOo9E.js} +1 -1
  14. package/dist/assets/doc-browser-DcuOOtpP.js +1 -0
  15. package/dist/assets/{doc-browser-context-BlbUcg2J.js → doc-browser-context-Dhsit2Ma.js} +1 -1
  16. package/dist/assets/{extensions-mquwAJ2Z.js → extensions-CD6xvKF_.js} +1 -1
  17. package/dist/assets/{form-actions-CaZDGEmB.js → form-actions-CC4Wz_lL.js} +1 -1
  18. package/dist/assets/index-BTsbdtv6.js +109 -0
  19. package/dist/assets/index-Bcfy2kTh.css +1 -0
  20. package/dist/assets/loader-circle-B6aFbB-8.js +1 -0
  21. package/dist/assets/mcp-marketplace-page-BtUAypA7.js +1 -0
  22. package/dist/assets/{mcp-marketplace-page-gFtgyueL.js → mcp-marketplace-page-r2MmD9xO.js} +1 -1
  23. package/dist/assets/{model-config-page-hFBpeRAO.js → model-config-page-DboxoT-I.js} +1 -1
  24. package/dist/assets/{navigation-link-Hb5Z0UxZ.js → navigation-link-C_6oj3zz.js} +1 -1
  25. package/dist/assets/plus-DRsq9Kvs.js +1 -0
  26. package/dist/assets/{privacy-settings-page-C18zo3ox.js → privacy-settings-page-C2sDBqhb.js} +1 -1
  27. package/dist/assets/projects-BcPcx-uD.js +1 -0
  28. package/dist/assets/{providers-config-page-YU0-AOXJ.js → providers-config-page-CmUFl9RF.js} +1 -1
  29. package/dist/assets/react-BjO9I4WZ.js +8 -0
  30. package/dist/assets/{refresh-cw-ZHKfE3yx.js → refresh-cw-DsT2kvXi.js} +1 -1
  31. package/dist/assets/remote-Ci6x91ES.js +1 -0
  32. package/dist/assets/{rotate-cw-CLhOXGwE.js → rotate-cw-BCAs6c3R.js} +1 -1
  33. package/dist/assets/{runtime-config-page-Dmy1A_0Y.js → runtime-config-page-cQKNeAjL.js} +1 -1
  34. package/dist/assets/{save-CWuuZs77.js → save-CadRMTMQ.js} +1 -1
  35. package/dist/assets/{search-DcmsKAn2.js → search-B6tB4fzB.js} +1 -1
  36. package/dist/assets/{search-config-page-CxBacx6W.js → search-config-page-C8lOIkxn.js} +1 -1
  37. package/dist/assets/{secrets-config-page-B4py8-KR.js → secrets-config-page-8clzAag2.js} +1 -1
  38. package/dist/assets/security-config-DPbM6IVT.js +1 -0
  39. package/dist/assets/{select-BEr0Iy_f.js → select-CO9S_C2C.js} +1 -1
  40. package/dist/assets/skeleton-IHILNhIW.js +1 -0
  41. package/dist/assets/{tag-chip-ByqDkn6g.js → tag-chip-BZKk0UkK.js} +1 -1
  42. package/dist/assets/x-BuvHIb1d.js +1 -0
  43. package/dist/index.html +21 -21
  44. package/package.json +6 -6
  45. package/src/features/apps/components/__tests__/app-packages-panel.test.tsx +362 -241
  46. package/src/features/apps/components/app-document-access-section.tsx +187 -0
  47. package/src/features/apps/components/app-package-card.tsx +150 -275
  48. package/src/features/apps/hooks/use-app-packages.ts +91 -35
  49. package/src/features/chat/index.ts +5 -1
  50. package/src/features/projects/AGENTS.md +3 -2
  51. package/src/features/projects/components/project-overview.tsx +70 -139
  52. package/src/features/projects/components/work/project-work-item-drawer.test.tsx +65 -0
  53. package/src/features/projects/components/work/project-work-item-drawer.tsx +367 -0
  54. package/src/features/projects/components/work/project-work-items.test.tsx +98 -0
  55. package/src/features/projects/components/work/project-work-items.tsx +199 -0
  56. package/src/features/projects/components/work/project-work-state-settings.tsx +285 -0
  57. package/src/features/projects/hooks/use-project-observation.ts +56 -26
  58. package/src/features/projects/hooks/use-project-work.test.tsx +58 -0
  59. package/src/features/projects/hooks/use-project-work.ts +215 -0
  60. package/src/features/projects/pages/project-home-page.tsx +119 -138
  61. package/src/features/projects/pages/projects-page.test.tsx +79 -308
  62. package/src/shared/lib/i18n/locales/en-US/doc-browser.json +16 -1
  63. package/src/shared/lib/i18n/locales/en-US/projects.json +60 -1
  64. package/src/shared/lib/i18n/locales/zh-CN/doc-browser.json +16 -1
  65. package/src/shared/lib/i18n/locales/zh-CN/projects.json +60 -1
  66. package/dist/assets/api-CoqPRKmF.js +0 -13
  67. package/dist/assets/doc-browser-DJEMr7Zk.js +0 -1
  68. package/dist/assets/index-DyR5iR0O.css +0 -1
  69. package/dist/assets/index-XxXj8WDS.js +0 -109
  70. package/dist/assets/loader-circle-0qPOOYQ5.js +0 -1
  71. package/dist/assets/mcp-marketplace-page-uanamJ6L.js +0 -1
  72. package/dist/assets/plus-CYOvNvcV.js +0 -1
  73. package/dist/assets/projects-C8Gm9RS8.js +0 -1
  74. package/dist/assets/react-DF-kP7iN.js +0 -8
  75. package/dist/assets/remote-B_utkBra.js +0 -1
  76. package/dist/assets/security-config-CTP4IQWw.js +0 -1
  77. package/dist/assets/skeleton-B2NHluWf.js +0 -1
  78. package/dist/assets/x-CENkFyK5.js +0 -1
  79. package/src/features/projects/components/project-work-items.test.tsx +0 -78
  80. package/src/features/projects/components/project-work-items.tsx +0 -272
@@ -2,14 +2,12 @@ import { fireEvent, render, screen } from "@testing-library/react";
2
2
  import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
3
3
  import { MemoryRouter, Route, Routes } from "react-router-dom";
4
4
  import { beforeEach, describe, expect, it, vi } from "vitest";
5
- import type { ProjectObservationSnapshot } from "@nextclaw/client-sdk";
6
5
  import { ProjectsPage } from "./project-home-page";
7
6
 
8
7
  const mocks = vi.hoisted(() => ({
9
- useProjects: vi.fn(),
8
+ drawerProps: vi.fn(),
10
9
  useProjectObservation: vi.fn(),
11
- openFilePreview: vi.fn(),
12
- createSession: vi.fn(),
10
+ useProjects: vi.fn(),
13
11
  }));
14
12
 
15
13
  vi.mock("@/shared/hooks/use-projects", () => ({
@@ -22,345 +20,118 @@ vi.mock("@/features/projects/hooks/use-project-observation", () => ({
22
20
  ],
23
21
  useProjectObservation: mocks.useProjectObservation,
24
22
  }));
25
- vi.mock("@/features/chat/components/providers/chat-presenter.provider", () => ({
26
- usePresenter: () => ({
27
- chatThreadManager: { openFilePreview: mocks.openFilePreview },
28
- chatSessionListManager: { createSession: mocks.createSession },
29
- }),
23
+ vi.mock("@/features/chat", () => ({
24
+ ChatConversationWorkspaceSection: () => <div data-testid="workspace" />,
25
+ usePresenter: () => ({ chatThreadManager: { openFilePreview: vi.fn() } }),
30
26
  }));
31
- vi.mock("@/features/chat/components/conversation/chat-conversation-workspace-section", () => ({
32
- ChatConversationWorkspaceSection: () => <div data-testid="project-workspace-preview" />,
27
+ vi.mock("@/app/hooks/use-viewport-layout", () => ({
28
+ useViewportLayout: () => ({ isMobile: false }),
33
29
  }));
34
-
35
- const observedAt = "2026-08-30T00:00:00.000Z";
36
- const snapshot: ProjectObservationSnapshot = {
37
- asOf: observedAt,
38
- project: {
39
- name: "Research",
40
- rootPath: "/tmp/research",
41
- context: [{
42
- id: "vision",
43
- role: "Project vision",
44
- source: "docs/VISION.md",
45
- accessible: true,
46
- reference: {
47
- kind: "file-observation",
48
- label: "File observation",
49
- observedAt,
50
- projectRelativePath: "docs/VISION.md",
51
- },
52
- }],
30
+ vi.mock("@/features/projects/components/project-overview", () => ({
31
+ ProjectOverview: ({
32
+ onOpenWorkItem,
33
+ }: {
34
+ onOpenWorkItem: (id: string) => void;
35
+ }) => (
36
+ <button onClick={() => onOpenWorkItem("work-overview")}>
37
+ Overview work
38
+ </button>
39
+ ),
40
+ }));
41
+ vi.mock("@/features/projects/components/work/project-work-items", () => ({
42
+ ProjectWorkItems: ({
43
+ onOpenWorkItem,
44
+ }: {
45
+ onOpenWorkItem: (id: string) => void;
46
+ }) => <button onClick={() => onOpenWorkItem("work-list")}>List work</button>,
47
+ }));
48
+ vi.mock("@/features/projects/components/work/project-work-item-drawer", () => ({
49
+ ProjectWorkItemDrawer: ({ workItemId }: { workItemId: string | null }) => {
50
+ mocks.drawerProps({ workItemId });
51
+ return <div data-testid="work-drawer">{workItemId}</div>;
53
52
  },
54
- sources: [
55
- {
56
- id: "config",
57
- label: "Project config",
58
- status: "error",
59
- itemCount: 0,
60
- observedAt,
61
- diagnosticIds: ["config:broken"],
62
- },
63
- {
64
- id: "files",
65
- label: "Project files",
66
- status: "empty",
67
- itemCount: 0,
68
- observedAt,
69
- diagnosticIds: [],
70
- },
71
- {
72
- id: "sessions",
73
- label: "Project sessions",
74
- status: "empty",
75
- itemCount: 0,
76
- observedAt,
77
- diagnosticIds: [],
78
- },
79
- {
80
- id: "skills",
81
- label: "Project Skills",
82
- status: "empty",
83
- itemCount: 0,
84
- observedAt,
85
- diagnosticIds: [],
86
- },
87
- ],
88
- workflows: [],
89
- runs: [],
90
- workItems: [],
91
- artifactCategories: [
92
- { id: "designs", label: "Designs" },
93
- { id: "plans", label: "Plans" },
94
- ],
95
- artifacts: [
96
- {
97
- id: "designs:docs/designs/observer.md",
98
- path: "docs/designs/observer.md",
99
- categoryId: "designs",
100
- categoryLabel: "Designs",
101
- exists: true,
102
- references: [],
103
- },
104
- {
105
- id: "plans:docs/plans/delivery.md",
106
- path: "docs/plans/delivery.md",
107
- categoryId: "plans",
108
- categoryLabel: "Plans",
109
- exists: true,
110
- references: [],
111
- },
112
- ],
113
- signals: [],
114
- requests: [],
115
- activity: [],
116
- skills: [
117
- {
118
- ref: "project:research-skill",
119
- name: "Research skill",
120
- description: "Collects and organizes source material.",
121
- source: "project",
122
- path: "/tmp/research/.agents/skills/research/SKILL.md",
123
- readable: true,
124
- reference: {
125
- kind: "project-config",
126
- label: "Project config",
127
- observedAt,
128
- },
129
- },
130
- {
131
- ref: "project:unavailable-skill",
132
- name: "Unavailable skill",
133
- source: "project",
134
- path: "/tmp/research/.agents/skills/unavailable/SKILL.md",
135
- readable: false,
136
- reference: {
137
- kind: "project-config",
138
- label: "Project config",
139
- observedAt,
140
- },
141
- },
142
- ],
143
- diagnostics: [
144
- {
145
- id: "config:broken",
146
- source: "config",
147
- level: "error",
148
- code: "PROJECT_CONFIG_PARSE_FAILED",
149
- message: "The project configuration is invalid.",
150
- },
151
- ],
152
- dataQuality: "partial",
153
- };
53
+ }));
54
+ vi.mock("@/features/projects/components/project-artifacts", () => ({
55
+ ProjectArtifacts: () => <div>Artifact content</div>,
56
+ }));
57
+ vi.mock("@/features/projects/components/project-skills", () => ({
58
+ ProjectSkills: () => <div>Skills content</div>,
59
+ }));
60
+ vi.mock("@/features/projects/components/project-agreement", () => ({
61
+ ProjectAgreement: () => <div>Agreement content</div>,
62
+ }));
63
+ vi.mock("@/features/projects/components/project-requests", () => ({
64
+ ProjectRequests: () => null,
65
+ }));
66
+
67
+ function renderPage(path: string) {
68
+ return render(
69
+ <QueryClientProvider client={new QueryClient()}>
70
+ <MemoryRouter initialEntries={[path]}>
71
+ <Routes>
72
+ <Route path="/projects" element={<ProjectsPage />} />
73
+ <Route path="/projects/:projectId/:tab" element={<ProjectsPage />} />
74
+ </Routes>
75
+ </MemoryRouter>
76
+ </QueryClientProvider>,
77
+ );
78
+ }
154
79
 
155
80
  describe("ProjectsPage", () => {
156
81
  beforeEach(() => {
157
- mocks.createSession.mockReset();
82
+ mocks.drawerProps.mockReset();
158
83
  mocks.useProjects.mockReturnValue({
159
84
  data: {
160
85
  projects: [
161
- {
162
- id: "project-research",
163
- name: "Research",
164
- rootPath: "/tmp/research",
165
- createdAt: observedAt,
166
- updatedAt: observedAt,
167
- },
86
+ { id: "project-1", name: "Research", rootPath: "/tmp/research" },
168
87
  ],
169
88
  },
170
89
  isLoading: false,
171
90
  isError: false,
172
91
  });
173
92
  mocks.useProjectObservation.mockReturnValue({
174
- data: snapshot,
93
+ data: undefined,
175
94
  isLoading: false,
176
95
  isError: false,
177
96
  });
178
97
  });
179
98
 
180
- it("uses the project selected by the sidebar route without rendering another selector", () => {
181
- render(
182
- <QueryClientProvider client={new QueryClient()}>
183
- <MemoryRouter initialEntries={["/projects/project-research/overview"]}>
184
- <Routes>
185
- <Route path="/projects/:projectId/:tab" element={<ProjectsPage />} />
186
- </Routes>
187
- </MemoryRouter>
188
- </QueryClientProvider>,
189
- );
99
+ it("keeps overview scan-free and opens its work item in the unified drawer", () => {
100
+ renderPage("/projects/project-1/overview");
190
101
 
191
102
  expect(mocks.useProjectObservation).toHaveBeenCalledWith(
192
- "project-research",
103
+ "project-1",
193
104
  "/tmp/research",
105
+ false,
194
106
  );
195
- expect(screen.queryByRole("combobox")).toBeNull();
196
107
  expect(screen.getAllByRole("tab")).toHaveLength(5);
197
- expect(screen.getByRole("tab", { name: "Skills" })).toBeTruthy();
198
- expect(
199
- screen.queryByText(
200
- /This project has no configured summary|项目没有配置摘要/,
201
- ),
202
- ).toBeNull();
203
- expect(screen.queryByText(/Partially available|部分可用/)).toBeNull();
204
- expect(screen.queryByText(observedAt)).toBeNull();
205
- expect(
206
- screen.getByText(
207
- /Set up project observation first|先建立项目观察配置/,
208
- ),
209
- ).toBeTruthy();
210
- fireEvent.click(
211
- screen.getByRole("button", {
212
- name: /Let AI set up project observation|让 AI 帮我建立项目观察/,
213
- }),
214
- );
215
- expect(mocks.createSession).toHaveBeenCalledWith({
216
- projectRoot: "/tmp/research",
217
- prompt: expect.stringMatching(/project observation|项目观察/),
218
- });
219
- expect(mocks.createSession).toHaveBeenCalledWith(
220
- expect.objectContaining({
221
- prompt: expect.stringContaining("AGENTS.md"),
222
- }),
223
- );
224
- fireEvent.click(screen.getByRole("button", { name: /docs\/designs\/observer\.md/ }));
225
- expect(mocks.openFilePreview).toHaveBeenCalledWith({
226
- path: "docs/designs/observer.md",
227
- label: "docs/designs/observer.md",
228
- viewMode: "preview",
229
- previewViewer: "rendered",
230
- });
231
-
232
- fireEvent.click(screen.getByRole("tab", { name: /Work items|工作项/ }));
233
- expect(
234
- screen.queryByRole("heading", { name: /Work items|工作项/ }),
235
- ).toBeNull();
236
-
237
- fireEvent.click(screen.getByRole("tab", { name: /Artifacts|产物/ }));
238
- expect(
239
- screen.queryByRole("heading", { name: /Artifacts|产物/ }),
240
- ).toBeNull();
241
- expect(screen.getByRole("button", { name: /Designs 1/ })).toBeTruthy();
242
- expect(screen.getByRole("button", { name: /docs\/plans\/delivery\.md/ })).toBeTruthy();
243
- expect(screen.queryByText(/File observed|文件已观测/)).toBeNull();
244
-
245
- fireEvent.click(screen.getByRole("tab", { name: /Skills/ }));
246
- expect(screen.getByText("2 skills")).toBeTruthy();
247
- expect(screen.queryByRole("heading", { name: "Skills" })).toBeNull();
248
- const skillPath = screen.getByText(
249
- "/tmp/research/.agents/skills/research/SKILL.md",
250
- );
251
- expect(skillPath.className).toContain("truncate");
252
- expect(skillPath.getAttribute("title")).toBe(
253
- "/tmp/research/.agents/skills/research/SKILL.md",
254
- );
255
- const skillDescription = screen.getByText(
256
- "Collects and organizes source material.",
257
- );
258
- expect(skillDescription.className).toContain("line-clamp-2");
259
- expect(skillDescription.getAttribute("title")).toBe(
260
- "Collects and organizes source material.",
261
- );
262
- expect(screen.queryByText(/Readable|可读取/)).toBeNull();
263
- expect(screen.getByText(/Unavailable|不可用/)).toBeTruthy();
264
- fireEvent.click(screen.getByRole("button", { name: /Research skill/ }));
265
- expect(mocks.openFilePreview).toHaveBeenCalledWith({
266
- path: "/tmp/research/.agents/skills/research/SKILL.md",
267
- label: "Research skill",
268
- viewMode: "preview",
269
- previewViewer: "rendered",
270
- });
271
-
272
- fireEvent.click(
273
- screen.getByRole("tab", { name: /Working rules|工作约定/ }),
274
- );
275
- expect(
276
- screen.queryByRole("heading", { name: /Working rules|工作约定/ }),
277
- ).toBeNull();
278
- expect(
279
- screen.getByRole("heading", { name: /Vision and context|愿景与上下文/ }),
280
- ).toBeTruthy();
281
- fireEvent.click(screen.getByRole("button", { name: /Project vision/ }));
282
- expect(mocks.openFilePreview).toHaveBeenCalledWith({
283
- path: "docs/VISION.md",
284
- label: "Project vision",
285
- viewMode: "preview",
286
- previewViewer: "rendered",
287
- });
288
- expect(screen.getByText("PROJECT_CONFIG_PARSE_FAILED")).toBeTruthy();
108
+ fireEvent.click(screen.getByRole("button", { name: "Overview work" }));
109
+ expect(screen.getByTestId("work-drawer").textContent).toBe("work-overview");
289
110
  });
290
111
 
291
- it("keeps rendering while an older server snapshot has no runs field", () => {
292
- const { runs: _runs, ...legacySnapshot } = snapshot;
112
+ it("enables legacy observation only for preserved artifact, skill, and agreement surfaces", () => {
293
113
  mocks.useProjectObservation.mockReturnValue({
294
- data: legacySnapshot as ProjectObservationSnapshot,
114
+ data: { requests: [] },
295
115
  isLoading: false,
296
116
  isError: false,
297
117
  });
118
+ renderPage("/projects/project-1/artifacts");
298
119
 
299
- render(
300
- <QueryClientProvider client={new QueryClient()}>
301
- <MemoryRouter initialEntries={["/projects/project-research/overview"]}>
302
- <Routes>
303
- <Route path="/projects/:projectId/:tab" element={<ProjectsPage />} />
304
- </Routes>
305
- </MemoryRouter>
306
- </QueryClientProvider>,
120
+ expect(mocks.useProjectObservation).toHaveBeenCalledWith(
121
+ "project-1",
122
+ "/tmp/research",
123
+ true,
307
124
  );
308
-
309
- expect(screen.getAllByRole("tab")).toHaveLength(5);
310
- expect(screen.getByText(/No AI-reported activity|暂无 AI 上报动态/)).toBeTruthy();
125
+ expect(screen.getByText("Artifact content")).toBeTruthy();
126
+ expect(screen.getByRole("tab", { name: /Skills/ })).toBeTruthy();
127
+ expect(
128
+ screen.getByRole("tab", { name: /Working rules|工作约定/ }),
129
+ ).toBeTruthy();
311
130
  });
312
131
 
313
- it("asks the user to choose from the sidebar when the route has no registered project", () => {
314
- render(
315
- <QueryClientProvider client={new QueryClient()}>
316
- <MemoryRouter initialEntries={["/projects"]}>
317
- <Routes>
318
- <Route path="/projects" element={<ProjectsPage />} />
319
- </Routes>
320
- </MemoryRouter>
321
- </QueryClientProvider>,
322
- );
323
-
324
- expect(mocks.useProjectObservation).toHaveBeenLastCalledWith(null, null);
132
+ it("asks the user to choose from the sidebar when no project route is selected", () => {
133
+ renderPage("/projects");
134
+ expect(mocks.useProjectObservation).toHaveBeenCalledWith(null, null, false);
325
135
  expect(screen.getByText(/Choose a project|选择项目/)).toBeTruthy();
326
- expect(screen.queryByRole("combobox")).toBeNull();
327
- });
328
-
329
- it("keeps the observation setup entry out of an already configured project", () => {
330
- mocks.useProjectObservation.mockReturnValue({
331
- data: {
332
- ...snapshot,
333
- sources: snapshot.sources.map((source) =>
334
- source.id === "config" ? { ...source, status: "available" as const } : source,
335
- ),
336
- },
337
- isLoading: false,
338
- isError: false,
339
- });
340
-
341
- render(
342
- <QueryClientProvider client={new QueryClient()}>
343
- <MemoryRouter initialEntries={["/projects/project-research/overview"]}>
344
- <Routes>
345
- <Route path="/projects/:projectId/:tab" element={<ProjectsPage />} />
346
- </Routes>
347
- </MemoryRouter>
348
- </QueryClientProvider>,
349
- );
350
-
351
- expect(
352
- screen.queryByRole("button", {
353
- name: /Let AI set up project observation|让 AI 帮我建立项目观察/,
354
- }),
355
- ).toBeNull();
356
- fireEvent.click(
357
- screen.getByRole("button", {
358
- name: /Start project work|开始项目工作/,
359
- }),
360
- );
361
- expect(mocks.createSession).toHaveBeenCalledWith({
362
- projectRoot: "/tmp/research",
363
- prompt: expect.stringMatching(/Start working on this project|请开始处理这个项目/),
364
- });
365
136
  });
366
137
  });
@@ -251,5 +251,20 @@
251
251
  "serviceActionAuthorizationRisk": "Risk",
252
252
  "serviceActionAuthorizationInput": "Input",
253
253
  "serviceActionAuthorizationReject": "Reject",
254
- "serviceActionAuthorizationAllow": "Allow"
254
+ "serviceActionAuthorizationAllow": "Allow",
255
+ "appPackagesDocumentAccessTitle": "Files and folders",
256
+ "appPackagesDocumentAccessUngranted": "Not granted",
257
+ "appPackagesDocumentAccessUnavailable": "Folder unavailable",
258
+ "appPackagesDocumentAccessRead": "Read only",
259
+ "appPackagesDocumentAccessReadWrite": "Read and write",
260
+ "appPackagesDocumentAccessMode": "Access mode",
261
+ "appPackagesDocumentAccessChoose": "Choose folder",
262
+ "appPackagesDocumentAccessReplace": "Replace",
263
+ "appPackagesDocumentAccessRevoke": "Revoke access",
264
+ "appPackagesDocumentAccessGrant": "Grant this folder",
265
+ "appPackagesDocumentAccessChooseTitle": "Choose a folder on the runtime host",
266
+ "appPackagesDocumentAccessChooseDescription": "Choose the folder this app may access.",
267
+ "appPackagesDocumentAccessPath": "Granted folder",
268
+ "appPackagesDocumentAccessWriteWarning": "Read and write access lets this app modify the original files in this folder. You can revoke it from the app card at any time.",
269
+ "appPackagesDocumentAccessReadHint": "Read-only access lets this app view the folder but not modify files through this mount."
255
270
  }
@@ -86,5 +86,64 @@
86
86
  "projectsStatus_cancelled": "Cancelled",
87
87
  "projectsSource_available": "Available",
88
88
  "projectsSource_empty": "No data",
89
- "projectsSource_error": "Failed"
89
+ "projectsSource_error": "Failed",
90
+ "projectsWorkNewItem": "Add a work item",
91
+ "projectsWorkEmpty": "No work items yet. Add one here or ask AI from a project conversation.",
92
+ "projectsWorkShowDeleted": "Show deleted work items",
93
+ "projectsWorkTitle": "Title",
94
+ "projectsWorkDescription": "Description",
95
+ "projectsWorkAttention": "Attention",
96
+ "projectsWorkAttention_none": "No special attention",
97
+ "projectsWorkAttention_blocked": "Blocked",
98
+ "projectsWorkAttention_awaiting-user": "Waiting for you",
99
+ "projectsWorkVersion": "Version",
100
+ "projectsWorkItemSaved": "Work item saved",
101
+ "projectsWorkItemSaveFailed": "Failed to save work item",
102
+ "projectsWorkItemLoadFailed": "Failed to load work item details",
103
+ "projectsWorkItemCreateFailed": "Failed to create work item",
104
+ "projectsDeleteWorkItem": "Delete work item?",
105
+ "projectsDeleteWorkItemDescription": "The work item will remain available under Show deleted and can be restored.",
106
+ "projectsDeleted": "Deleted",
107
+ "projectsDelete": "Delete",
108
+ "projectsRestore": "Restore",
109
+ "projectsCreate": "Create",
110
+ "projectsAdd": "Add",
111
+ "projectsWorkArtifacts": "Linked artifacts",
112
+ "projectsWorkNoArtifacts": "No artifacts linked",
113
+ "projectsWorkArtifactPath": "Project-relative file path",
114
+ "projectsWorkArtifactLabel": "Optional label",
115
+ "projectsWorkLinkArtifact": "Link",
116
+ "projectsWorkUnlinkArtifact": "Unlink artifact",
117
+ "projectsWorkArtifactLinkFailed": "Failed to link artifact",
118
+ "projectsWorkActionFailed": "Could not update project work",
119
+ "projectsWorkStateSettings": "Work states",
120
+ "projectsWorkStateSettingsDescription": "Customize names and ordering while keeping each state's lifecycle category explicit.",
121
+ "projectsWorkStateName": "State name",
122
+ "projectsWorkStateCategory": "Lifecycle category",
123
+ "projectsWorkDefaultState": "Use for new work items",
124
+ "projectsWorkNewState": "New state name",
125
+ "projectsWorkStateMigration": "Move existing work items to",
126
+ "projectsWorkStateMigrationOptional": "Choose when this state is in use",
127
+ "projectsDeleteState": "Delete state",
128
+ "projectsDeleteStateDescription": "Existing work items must be moved to another state before this state can be deleted.",
129
+ "projectsMoveUp": "Move state up",
130
+ "projectsMoveDown": "Move state down",
131
+ "projectsWorkCategory_backlog": "Backlog",
132
+ "projectsWorkCategory_unstarted": "Not started",
133
+ "projectsWorkCategory_started": "In progress",
134
+ "projectsWorkCategory_completed": "Completed",
135
+ "projectsWorkCategory_canceled": "Canceled",
136
+ "projectsWorkSummary": "Work summary",
137
+ "projectsWorkTotal": "Total",
138
+ "projectsWorkActive": "Active",
139
+ "projectsWorkNeedsAttention": "Needs attention",
140
+ "projectsWorkCompleted": "Completed",
141
+ "projectsWorkRecentlyUpdated": "Recently updated",
142
+ "projectsWorkActivity_created": "Created",
143
+ "projectsWorkActivity_updated": "Updated",
144
+ "projectsWorkActivity_state-changed": "State changed",
145
+ "projectsWorkActivity_deleted": "Deleted",
146
+ "projectsWorkActivity_restored": "Restored",
147
+ "projectsWorkActivity_artifact-linked": "Artifact linked",
148
+ "projectsWorkActivity_artifact-unlinked": "Artifact unlinked"
90
149
  }
@@ -251,5 +251,20 @@
251
251
  "serviceActionAuthorizationRisk": "风险",
252
252
  "serviceActionAuthorizationInput": "输入",
253
253
  "serviceActionAuthorizationReject": "拒绝",
254
- "serviceActionAuthorizationAllow": "允许"
254
+ "serviceActionAuthorizationAllow": "允许",
255
+ "appPackagesDocumentAccessTitle": "文件与文件夹",
256
+ "appPackagesDocumentAccessUngranted": "未授权",
257
+ "appPackagesDocumentAccessUnavailable": "目录不可用",
258
+ "appPackagesDocumentAccessRead": "只读",
259
+ "appPackagesDocumentAccessReadWrite": "读写",
260
+ "appPackagesDocumentAccessMode": "访问模式",
261
+ "appPackagesDocumentAccessChoose": "选择目录",
262
+ "appPackagesDocumentAccessReplace": "替换",
263
+ "appPackagesDocumentAccessRevoke": "撤销访问",
264
+ "appPackagesDocumentAccessGrant": "授权此目录",
265
+ "appPackagesDocumentAccessChooseTitle": "选择运行主机上的目录",
266
+ "appPackagesDocumentAccessChooseDescription": "选择此应用可以访问的目录。",
267
+ "appPackagesDocumentAccessPath": "授权目录",
268
+ "appPackagesDocumentAccessWriteWarning": "读写授权允许应用直接修改这个目录中的原始文件;你可以随时在应用卡片中撤销。",
269
+ "appPackagesDocumentAccessReadHint": "只读授权允许应用查看这个目录,但不能通过此挂载修改文件。"
255
270
  }
@@ -86,5 +86,64 @@
86
86
  "projectsStatus_cancelled": "已取消",
87
87
  "projectsSource_available": "可用",
88
88
  "projectsSource_empty": "暂无数据",
89
- "projectsSource_error": "读取失败"
89
+ "projectsSource_error": "读取失败",
90
+ "projectsWorkNewItem": "添加工作项",
91
+ "projectsWorkEmpty": "还没有工作项。你可以在这里添加,也可以在项目会话中让 AI 创建。",
92
+ "projectsWorkShowDeleted": "显示已删除的工作项",
93
+ "projectsWorkTitle": "标题",
94
+ "projectsWorkDescription": "说明",
95
+ "projectsWorkAttention": "关注状态",
96
+ "projectsWorkAttention_none": "无需特别关注",
97
+ "projectsWorkAttention_blocked": "受阻",
98
+ "projectsWorkAttention_awaiting-user": "等待你处理",
99
+ "projectsWorkVersion": "版本",
100
+ "projectsWorkItemSaved": "工作项已保存",
101
+ "projectsWorkItemSaveFailed": "工作项保存失败",
102
+ "projectsWorkItemLoadFailed": "工作项详情读取失败",
103
+ "projectsWorkItemCreateFailed": "工作项创建失败",
104
+ "projectsDeleteWorkItem": "删除这个工作项?",
105
+ "projectsDeleteWorkItemDescription": "删除后仍可通过“显示已删除的工作项”找到并恢复。",
106
+ "projectsDeleted": "已删除",
107
+ "projectsDelete": "删除",
108
+ "projectsRestore": "恢复",
109
+ "projectsCreate": "创建",
110
+ "projectsAdd": "添加",
111
+ "projectsWorkArtifacts": "关联产物",
112
+ "projectsWorkNoArtifacts": "还没有关联产物",
113
+ "projectsWorkArtifactPath": "项目内的文件路径",
114
+ "projectsWorkArtifactLabel": "显示名称(可选)",
115
+ "projectsWorkLinkArtifact": "关联",
116
+ "projectsWorkUnlinkArtifact": "解除关联",
117
+ "projectsWorkArtifactLinkFailed": "产物关联失败",
118
+ "projectsWorkActionFailed": "无法更新项目工作项",
119
+ "projectsWorkStateSettings": "工作项状态",
120
+ "projectsWorkStateSettingsDescription": "你可以修改名称和顺序;每个状态仍需归入明确的生命周期类别。",
121
+ "projectsWorkStateName": "状态名称",
122
+ "projectsWorkStateCategory": "生命周期类别",
123
+ "projectsWorkDefaultState": "新工作项默认使用此状态",
124
+ "projectsWorkNewState": "新状态名称",
125
+ "projectsWorkStateMigration": "把已有工作项迁移到",
126
+ "projectsWorkStateMigrationOptional": "状态被使用时必须选择",
127
+ "projectsDeleteState": "删除状态",
128
+ "projectsDeleteStateDescription": "如果已有工作项使用这个状态,需要先把它们迁移到另一个状态。",
129
+ "projectsMoveUp": "上移状态",
130
+ "projectsMoveDown": "下移状态",
131
+ "projectsWorkCategory_backlog": "待整理",
132
+ "projectsWorkCategory_unstarted": "未开始",
133
+ "projectsWorkCategory_started": "推进中",
134
+ "projectsWorkCategory_completed": "已完成",
135
+ "projectsWorkCategory_canceled": "已取消",
136
+ "projectsWorkSummary": "工作项概况",
137
+ "projectsWorkTotal": "全部工作项",
138
+ "projectsWorkActive": "推进中",
139
+ "projectsWorkNeedsAttention": "需要关注",
140
+ "projectsWorkCompleted": "已完成",
141
+ "projectsWorkRecentlyUpdated": "最近更新",
142
+ "projectsWorkActivity_created": "创建了工作项",
143
+ "projectsWorkActivity_updated": "更新了工作项",
144
+ "projectsWorkActivity_state-changed": "切换了状态",
145
+ "projectsWorkActivity_deleted": "删除了工作项",
146
+ "projectsWorkActivity_restored": "恢复了工作项",
147
+ "projectsWorkActivity_artifact-linked": "关联了产物",
148
+ "projectsWorkActivity_artifact-unlinked": "解除了产物关联"
90
149
  }