@nextclaw/ui 0.24.0-beta.0 → 0.24.0-beta.1

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 +17 -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 +5 -5
  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
@@ -1 +0,0 @@
1
- import{gt as e}from"./index-XxXj8WDS.js";export{e as RemoteAccessPage};
@@ -1 +0,0 @@
1
- import{Rt as e}from"./api-CoqPRKmF.js";export{e as SecurityConfig};
@@ -1 +0,0 @@
1
- import{B as e,L as t}from"./react-DF-kP7iN.js";var n=e();function r({className:e,...r}){return(0,n.jsx)(`div`,{className:t(`animate-pulse rounded-md bg-slate-200`,e),...r})}export{r as t};
@@ -1 +0,0 @@
1
- import{t as e}from"./createLucideIcon-Blz0FzrV.js";var t=e(`X`,[[`path`,{d:`M18 6 6 18`,key:`1bl5f8`}],[`path`,{d:`m6 6 12 12`,key:`d8bk6v`}]]);export{t};
@@ -1,78 +0,0 @@
1
- import { fireEvent, render, screen } from "@testing-library/react";
2
- import { describe, expect, it, vi } from "vitest";
3
- import type { ProjectObservationSnapshot } from "@nextclaw/client-sdk";
4
- import { ProjectWorkItems } from "./project-work-items";
5
-
6
- const reference = {
7
- kind: "ai-report" as const,
8
- label: "AI report",
9
- observedAt: "2026-08-30T00:00:00.000Z",
10
- sessionId: "session-1",
11
- };
12
-
13
- function createSnapshot(): ProjectObservationSnapshot {
14
- return {
15
- asOf: reference.observedAt,
16
- project: { name: "Demo", rootPath: "/tmp/demo", context: [] },
17
- sources: [],
18
- workflows: [{
19
- id: "research",
20
- label: "Research",
21
- stages: [{ id: "draft", label: "Draft" }],
22
- reference,
23
- }],
24
- runs: [],
25
- workItems: [{
26
- id: "report",
27
- name: "Write the report",
28
- status: "active",
29
- workflowId: "research",
30
- stageId: "draft",
31
- updatedAt: reference.observedAt,
32
- reference,
33
- }],
34
- artifactCategories: [], artifacts: [], signals: [], requests: [], activity: [], skills: [], diagnostics: [],
35
- dataQuality: "complete",
36
- };
37
- }
38
-
39
- describe("ProjectWorkItems", () => {
40
- it("switches list, board, and gantt over one snapshot without fabricating a schedule", () => {
41
- render(
42
- <ProjectWorkItems
43
- hasProjectSessions
44
- snapshot={createSnapshot()}
45
- shouldOfferObservationSetup={false}
46
- onStartObservationSetup={vi.fn()}
47
- onStartProjectWork={vi.fn()}
48
- />,
49
- );
50
- expect(screen.getByText("Write the report")).toBeTruthy();
51
-
52
- fireEvent.click(screen.getByRole("button", { name: /Board|看板/ }));
53
- expect(screen.getByText("Draft")).toBeTruthy();
54
- expect(screen.getByText("Write the report")).toBeTruthy();
55
-
56
- fireEvent.click(screen.getByRole("button", { name: /Gantt|甘特图/ }));
57
- expect(screen.getByText(/No schedule markers|没有 schedule Marker/)).toBeTruthy();
58
- });
59
-
60
- it("offers an ordinary project conversation before rendering empty view controls", () => {
61
- const onStartProjectWork = vi.fn();
62
- render(
63
- <ProjectWorkItems
64
- hasProjectSessions={false}
65
- snapshot={{ ...createSnapshot(), workItems: [] }}
66
- shouldOfferObservationSetup={false}
67
- onStartObservationSetup={vi.fn()}
68
- onStartProjectWork={onStartProjectWork}
69
- />,
70
- );
71
-
72
- expect(screen.queryByRole("group", { name: /Work item views|工作项视图/ })).toBeNull();
73
- fireEvent.click(
74
- screen.getByRole("button", { name: /Start project work|开始项目工作/ }),
75
- );
76
- expect(onStartProjectWork).toHaveBeenCalledOnce();
77
- });
78
- });
@@ -1,272 +0,0 @@
1
- import { useState } from "react";
2
- import type {
3
- ObservedWorkItem,
4
- ProjectObservationSnapshot,
5
- } from "@nextclaw/client-sdk";
6
- import { Button } from "@/shared/components/ui/button";
7
- import { t } from "@/shared/lib/i18n";
8
- import {
9
- createProjectBoardColumns,
10
- getScheduledWorkItems,
11
- getUnstagedWorkItems,
12
- type ProjectWorkView,
13
- } from "@/features/projects/presenters/project-home.presenter";
14
- import { ProjectEmptyState } from "./project-section";
15
- import { ProjectRuns } from "./project-runs";
16
-
17
- function WorkItemCard({
18
- item,
19
- onSelect,
20
- }: {
21
- item: ObservedWorkItem;
22
- onSelect: () => void;
23
- }) {
24
- return (
25
- <button
26
- type="button"
27
- className="w-full rounded-xl bg-card p-3 text-left text-sm shadow-sm hover:bg-[var(--interaction-hover)] focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-border"
28
- onClick={onSelect}
29
- >
30
- <span className="block font-medium">{item.name}</span>
31
- <span className="mt-1 block text-xs text-muted-foreground">
32
- {item.reference.label} · {t(`projectsStatus_${item.status}`)}
33
- </span>
34
- </button>
35
- );
36
- }
37
-
38
- function ProjectWorkItemsEmptyState({
39
- hasProjectSessions,
40
- onStartObservationSetup,
41
- onStartProjectWork,
42
- shouldOfferObservationSetup,
43
- }: {
44
- hasProjectSessions: boolean;
45
- onStartObservationSetup: () => void;
46
- onStartProjectWork: () => void;
47
- shouldOfferObservationSetup: boolean;
48
- }) {
49
- const messageKey = shouldOfferObservationSetup
50
- ? "projectsWorkNeedsSetup"
51
- : hasProjectSessions
52
- ? "projectsWorkNoReports"
53
- : "projectsWorkNoSessions";
54
- return (
55
- <div className="min-w-0">
56
- <ProjectEmptyState>{t(messageKey)}</ProjectEmptyState>
57
- <Button
58
- size="sm"
59
- variant="outline"
60
- onClick={
61
- shouldOfferObservationSetup
62
- ? onStartObservationSetup
63
- : onStartProjectWork
64
- }
65
- >
66
- {t(
67
- shouldOfferObservationSetup
68
- ? "projectsObservationSetupAction"
69
- : "projectsStartProjectWork",
70
- )}
71
- </Button>
72
- </div>
73
- );
74
- }
75
-
76
- export function ProjectWorkItems({
77
- hasProjectSessions,
78
- onStartObservationSetup,
79
- onStartProjectWork,
80
- snapshot,
81
- shouldOfferObservationSetup,
82
- }: {
83
- hasProjectSessions: boolean;
84
- onStartObservationSetup: () => void;
85
- onStartProjectWork: () => void;
86
- snapshot: ProjectObservationSnapshot;
87
- shouldOfferObservationSetup: boolean;
88
- }) {
89
- const [view, setView] = useState<ProjectWorkView>("list");
90
- const [selectedId, setSelectedId] = useState<string | null>(null);
91
- const selected =
92
- snapshot.workItems.find((item) => item.id === selectedId) ?? null;
93
- const columns = createProjectBoardColumns(
94
- snapshot.workflows,
95
- snapshot.workItems,
96
- );
97
- const unstaged = getUnstagedWorkItems(snapshot.workItems);
98
- const scheduled = getScheduledWorkItems(snapshot.workItems);
99
- return (
100
- <section className="min-w-0" aria-label={t("projectsWork")}>
101
- {(snapshot.runs?.length ?? 0) > 0 ? (
102
- <div className="mb-5">
103
- <h2 className="mb-2 text-sm font-semibold">{t("projectsAiActivity")}</h2>
104
- <ProjectRuns snapshot={snapshot} />
105
- </div>
106
- ) : null}
107
- {snapshot.workItems.length === 0 ? (
108
- <ProjectWorkItemsEmptyState
109
- hasProjectSessions={hasProjectSessions}
110
- onStartObservationSetup={onStartObservationSetup}
111
- onStartProjectWork={onStartProjectWork}
112
- shouldOfferObservationSetup={shouldOfferObservationSetup}
113
- />
114
- ) : (
115
- <>
116
- <div
117
- className="mb-4 flex flex-wrap gap-1"
118
- role="group"
119
- aria-label={t("projectsWorkViews")}
120
- >
121
- {(["list", "board", "gantt"] as const).map((key) => (
122
- <Button
123
- key={key}
124
- size="sm"
125
- variant={view === key ? "secondary" : "ghost"}
126
- aria-pressed={view === key}
127
- onClick={() => setView(key)}
128
- >
129
- {t(`projectsWork${key[0].toUpperCase()}${key.slice(1)}`)}
130
- </Button>
131
- ))}
132
- </div>
133
- {view === "list" ? (
134
- <div className="space-y-2">
135
- {snapshot.workItems.map((item) => (
136
- <WorkItemCard
137
- key={item.id}
138
- item={item}
139
- onSelect={() => setSelectedId(item.id)}
140
- />
141
- ))}
142
- </div>
143
- ) : null}
144
- {view === "board" ? (
145
- columns.length ? (
146
- <div className="grid min-w-0 gap-3 md:grid-cols-2 xl:grid-cols-4">
147
- {columns.map((column) => (
148
- <section
149
- key={column.id}
150
- className="min-w-0 rounded-xl bg-muted/35 p-3"
151
- >
152
- <h3 className="mb-3 text-xs font-semibold">{column.label}</h3>
153
- <div className="space-y-2">
154
- {column.items.map((item) => (
155
- <WorkItemCard
156
- key={item.id}
157
- item={item}
158
- onSelect={() => setSelectedId(item.id)}
159
- />
160
- ))}
161
- {!column.items.length ? (
162
- <p className="py-3 text-xs text-muted-foreground">
163
- {t("projectsNoData")}
164
- </p>
165
- ) : null}
166
- </div>
167
- </section>
168
- ))}
169
- {unstaged.length ? (
170
- <section className="min-w-0 rounded-xl bg-muted/35 p-3">
171
- <h3 className="mb-3 text-xs font-semibold">
172
- {t("projectsUnstaged")}
173
- </h3>
174
- <div className="space-y-2">
175
- {unstaged.map((item) => (
176
- <WorkItemCard
177
- key={item.id}
178
- item={item}
179
- onSelect={() => setSelectedId(item.id)}
180
- />
181
- ))}
182
- </div>
183
- </section>
184
- ) : null}
185
- </div>
186
- ) : (
187
- <ProjectEmptyState>
188
- {t("projectsBoardNeedsWorkflow")}
189
- </ProjectEmptyState>
190
- )
191
- ) : null}
192
- {view === "gantt" ? (
193
- scheduled.length ? (
194
- <div className="overflow-x-auto rounded-xl border border-border/60">
195
- <div className="min-w-[42rem] divide-y divide-border/60">
196
- {scheduled.map((item) => (
197
- <button
198
- key={item.id}
199
- type="button"
200
- className="grid w-full grid-cols-[minmax(12rem,1fr)_9rem_9rem] gap-3 px-4 py-3 text-left text-sm hover:bg-[var(--interaction-hover)]"
201
- onClick={() => setSelectedId(item.id)}
202
- >
203
- <span className="truncate font-medium">{item.name}</span>
204
- <span>{item.schedule?.start ?? "—"}</span>
205
- <span>{item.schedule?.end ?? "—"}</span>
206
- </button>
207
- ))}
208
- </div>
209
- </div>
210
- ) : (
211
- <ProjectEmptyState>
212
- {t("projectsGanttNeedsSchedule")}
213
- </ProjectEmptyState>
214
- )
215
- ) : null}
216
- {selected ? (
217
- <aside
218
- className="mt-4 rounded-xl border border-border/60 bg-muted/25 p-4"
219
- aria-label={t("projectsWorkDetails")}
220
- >
221
- <div className="flex items-start justify-between gap-3">
222
- <div>
223
- <h3 className="font-semibold">{selected.name}</h3>
224
- <p className="mt-1 text-xs text-muted-foreground">
225
- {selected.id} · {selected.reference.label}
226
- </p>
227
- </div>
228
- <Button
229
- size="sm"
230
- variant="ghost"
231
- onClick={() => setSelectedId(null)}
232
- >
233
- {t("projectsCloseDetails")}
234
- </Button>
235
- </div>
236
- <dl className="mt-3 grid gap-2 text-sm sm:grid-cols-2">
237
- <div>
238
- <dt className="text-xs text-muted-foreground">
239
- {t("projectsStatus")}
240
- </dt>
241
- <dd>{t(`projectsStatus_${selected.status}`)}</dd>
242
- </div>
243
- <div>
244
- <dt className="text-xs text-muted-foreground">
245
- {t("projectsStage")}
246
- </dt>
247
- <dd>{selected.stageId ?? t("projectsNoData")}</dd>
248
- </div>
249
- <div>
250
- <dt className="text-xs text-muted-foreground">
251
- {t("projectsUpdated")}
252
- </dt>
253
- <dd>{selected.updatedAt}</dd>
254
- </div>
255
- <div>
256
- <dt className="text-xs text-muted-foreground">
257
- {t("projectsSchedule")}
258
- </dt>
259
- <dd>
260
- {selected.schedule
261
- ? `${selected.schedule.start ?? "—"} → ${selected.schedule.end ?? "—"}`
262
- : t("projectsNoData")}
263
- </dd>
264
- </div>
265
- </dl>
266
- </aside>
267
- ) : null}
268
- </>
269
- )}
270
- </section>
271
- );
272
- }