@rexvane/piweb 0.3.15 → 0.3.16

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 (230) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/app-path-routes-manifest.json +9 -6
  3. package/.next/build-manifest.json +3 -3
  4. package/.next/prerender-manifest.json +6 -6
  5. package/.next/react-loadable-manifest.json +16 -4
  6. package/.next/routes-manifest.json +20 -0
  7. package/.next/server/app/_global-error/page.js +2 -2
  8. package/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  9. package/.next/server/app/_global-error.html +1 -1
  10. package/.next/server/app/_global-error.rsc +1 -1
  11. package/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  12. package/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  13. package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  14. package/.next/server/app/_not-found/page.js +1 -1
  15. package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  16. package/.next/server/app/_not-found.html +1 -1
  17. package/.next/server/app/_not-found.rsc +7 -4
  18. package/.next/server/app/_not-found.segments/_full.segment.rsc +7 -4
  19. package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +5 -2
  20. package/.next/server/app/_not-found.segments/_tree.segment.rsc +5 -2
  21. package/.next/server/app/api/agent/[id]/events/route.js +2 -2
  22. package/.next/server/app/api/agent/[id]/events/route.js.nft.json +1 -1
  23. package/.next/server/app/api/agent/[id]/route.js +1 -1
  24. package/.next/server/app/api/agent/[id]/route.js.nft.json +1 -1
  25. package/.next/server/app/api/agent/[id]/tree/route.js +1 -1
  26. package/.next/server/app/api/agent/[id]/tree/route.js.nft.json +1 -1
  27. package/.next/server/app/api/agent/new/route.js +1 -1
  28. package/.next/server/app/api/agent/new/route.js.nft.json +1 -1
  29. package/.next/server/app/api/config/route.js +1 -1
  30. package/.next/server/app/api/config/route.js.nft.json +1 -1
  31. package/.next/server/app/api/files/route.js +1 -1
  32. package/.next/server/app/api/files/route.js.nft.json +1 -1
  33. package/.next/server/app/api/git/route.js +2 -2
  34. package/.next/server/app/api/git/route.js.nft.json +1 -1
  35. package/.next/server/app/api/growth/route.js +1 -1
  36. package/.next/server/app/api/growth/route.js.nft.json +1 -1
  37. package/.next/server/app/api/health/route.js +1 -1
  38. package/.next/server/app/api/models/route.js +1 -1
  39. package/.next/server/app/api/models/route.js.nft.json +1 -1
  40. package/.next/server/app/api/pi-settings/route.js +1 -1
  41. package/.next/server/app/api/pi-settings/route.js.nft.json +1 -1
  42. package/.next/server/app/api/plugins/route.js +1 -1
  43. package/.next/server/app/api/plugins/route.js.nft.json +1 -1
  44. package/.next/server/app/api/prompts/route.js +1 -1
  45. package/.next/server/app/api/prompts/route.js.nft.json +1 -1
  46. package/.next/server/app/api/security/route.js +1 -1
  47. package/.next/server/app/api/security/route.js.nft.json +1 -1
  48. package/.next/server/app/api/sessions/[id]/export/route.js +1 -1
  49. package/.next/server/app/api/sessions/[id]/export/route.js.nft.json +1 -1
  50. package/.next/server/app/api/sessions/[id]/route.js +1 -1
  51. package/.next/server/app/api/sessions/[id]/route.js.nft.json +1 -1
  52. package/.next/server/app/api/sessions/import/route.js +3 -3
  53. package/.next/server/app/api/sessions/import/route.js.nft.json +1 -1
  54. package/.next/server/app/api/sessions/route.js +1 -1
  55. package/.next/server/app/api/sessions/route.js.nft.json +1 -1
  56. package/.next/server/app/api/skills/route.js +2 -2
  57. package/.next/server/app/api/skills/route.js.nft.json +1 -1
  58. package/.next/server/app/api/swarm/[id]/route.js +1 -0
  59. package/.next/server/app/api/swarm/[id]/route.js.nft.json +1 -0
  60. package/.next/server/app/api/swarm/[id]/route_client-reference-manifest.js +1 -0
  61. package/.next/server/app/api/swarm/route.js +1 -0
  62. package/.next/server/app/api/swarm/route.js.nft.json +1 -0
  63. package/.next/server/app/api/swarm/route_client-reference-manifest.js +1 -0
  64. package/.next/server/app/api/tests/route.js +2 -0
  65. package/.next/server/app/api/tests/route.js.nft.json +1 -0
  66. package/.next/server/app/api/tests/route_client-reference-manifest.js +1 -0
  67. package/.next/server/app/api/update/route.js +2 -2
  68. package/.next/server/app/api/update/route.js.nft.json +1 -1
  69. package/.next/server/app/api/version/route.js +1 -1
  70. package/.next/server/app/api/version/route.js.nft.json +1 -1
  71. package/.next/server/app/api/web-auth/route.js +1 -1
  72. package/.next/server/app/api/workspaces/route.js +1 -1
  73. package/.next/server/app/api/workspaces/route.js.nft.json +1 -1
  74. package/.next/server/app/icon.svg/route.js +1 -1
  75. package/.next/server/app/index.html +1 -1
  76. package/.next/server/app/index.rsc +9 -6
  77. package/.next/server/app/index.segments/__PAGE__.segment.rsc +7 -4
  78. package/.next/server/app/index.segments/_full.segment.rsc +9 -6
  79. package/.next/server/app/index.segments/_tree.segment.rsc +5 -2
  80. package/.next/server/app/login/page.js +1 -1
  81. package/.next/server/app/login/page_client-reference-manifest.js +1 -1
  82. package/.next/server/app/login.html +1 -1
  83. package/.next/server/app/login.rsc +8 -5
  84. package/.next/server/app/login.segments/_full.segment.rsc +8 -5
  85. package/.next/server/app/login.segments/_tree.segment.rsc +5 -2
  86. package/.next/server/app/login.segments/login/__PAGE__.segment.rsc +6 -3
  87. package/.next/server/app/page.js +1 -1
  88. package/.next/server/app/page_client-reference-manifest.js +1 -1
  89. package/.next/server/app-paths-manifest.json +9 -6
  90. package/.next/server/chunks/496.js +1 -1
  91. package/.next/server/chunks/532.js +1 -1
  92. package/.next/server/chunks/686.js +6 -2
  93. package/.next/server/chunks/757.js +1 -0
  94. package/.next/server/chunks/775.js +3 -0
  95. package/.next/server/middleware-build-manifest.js +1 -1
  96. package/.next/server/middleware-react-loadable-manifest.js +1 -1
  97. package/.next/server/pages/404.html +1 -1
  98. package/.next/server/pages/500.html +1 -1
  99. package/.next/server/server-reference-manifest.json +1 -1
  100. package/.next/static/0t1jBUAuVaTFTXxRyIo3q/_buildManifest.js +1 -0
  101. package/.next/static/chunks/2608.43c58236c9cce8e6.js +1 -0
  102. package/.next/static/chunks/{2716.a9e808190a4e2424.js → 2716.4ec42df1d51354c9.js} +1 -1
  103. package/.next/static/chunks/4632.62139b565b657aac.js +1 -0
  104. package/.next/static/chunks/{4673.d3d0a7f2599080fb.js → 4673.7796104395deb8d7.js} +1 -1
  105. package/.next/static/chunks/4686-17f21a006739a596.js +1 -0
  106. package/.next/static/chunks/7577.10af9bb661652da8.js +1 -0
  107. package/.next/static/chunks/8866.38ec4ac4ee5a1b74.js +1 -0
  108. package/.next/static/chunks/app/_global-error/page-5148173b3f55e415.js +1 -0
  109. package/.next/static/chunks/app/api/agent/[id]/events/route-5148173b3f55e415.js +1 -0
  110. package/.next/static/chunks/app/api/agent/[id]/route-5148173b3f55e415.js +1 -0
  111. package/.next/static/chunks/app/api/agent/[id]/tree/route-5148173b3f55e415.js +1 -0
  112. package/.next/static/chunks/app/api/agent/new/route-5148173b3f55e415.js +1 -0
  113. package/.next/static/chunks/app/api/config/route-5148173b3f55e415.js +1 -0
  114. package/.next/static/chunks/app/api/files/route-5148173b3f55e415.js +1 -0
  115. package/.next/static/chunks/app/api/git/route-5148173b3f55e415.js +1 -0
  116. package/.next/static/chunks/app/api/growth/route-5148173b3f55e415.js +1 -0
  117. package/.next/static/chunks/app/api/health/route-5148173b3f55e415.js +1 -0
  118. package/.next/static/chunks/app/api/models/route-5148173b3f55e415.js +1 -0
  119. package/.next/static/chunks/app/api/pi-settings/route-5148173b3f55e415.js +1 -0
  120. package/.next/static/chunks/app/api/plugins/route-5148173b3f55e415.js +1 -0
  121. package/.next/static/chunks/app/api/prompts/route-5148173b3f55e415.js +1 -0
  122. package/.next/static/chunks/app/api/security/route-5148173b3f55e415.js +1 -0
  123. package/.next/static/chunks/app/api/sessions/[id]/export/route-5148173b3f55e415.js +1 -0
  124. package/.next/static/chunks/app/api/sessions/[id]/route-5148173b3f55e415.js +1 -0
  125. package/.next/static/chunks/app/api/sessions/import/route-5148173b3f55e415.js +1 -0
  126. package/.next/static/chunks/app/api/sessions/route-5148173b3f55e415.js +1 -0
  127. package/.next/static/chunks/app/api/skills/route-5148173b3f55e415.js +1 -0
  128. package/.next/static/chunks/app/api/swarm/[id]/route-5148173b3f55e415.js +1 -0
  129. package/.next/static/chunks/app/api/swarm/route-5148173b3f55e415.js +1 -0
  130. package/.next/static/chunks/app/api/tests/route-5148173b3f55e415.js +1 -0
  131. package/.next/static/chunks/app/api/update/route-5148173b3f55e415.js +1 -0
  132. package/.next/static/chunks/app/api/version/route-5148173b3f55e415.js +1 -0
  133. package/.next/static/chunks/app/api/web-auth/route-5148173b3f55e415.js +1 -0
  134. package/.next/static/chunks/app/api/workspaces/route-5148173b3f55e415.js +1 -0
  135. package/.next/static/chunks/app/page-14379b8060d5cf07.js +1 -0
  136. package/.next/static/chunks/next/dist/client/components/builtin/app-error-5148173b3f55e415.js +1 -0
  137. package/.next/static/chunks/next/dist/client/components/builtin/forbidden-5148173b3f55e415.js +1 -0
  138. package/.next/static/chunks/next/dist/client/components/builtin/not-found-5148173b3f55e415.js +1 -0
  139. package/.next/static/chunks/next/dist/client/components/builtin/unauthorized-5148173b3f55e415.js +1 -0
  140. package/.next/static/chunks/webpack-d1a69ad9edb38b11.js +1 -0
  141. package/.next/static/css/d0a0162cb481abe8.css +1 -0
  142. package/.next/types/app/api/swarm/[id]/route.ts +353 -0
  143. package/.next/types/app/api/swarm/route.ts +353 -0
  144. package/.next/types/app/api/tests/route.ts +353 -0
  145. package/.next/types/routes.d.ts +4 -1
  146. package/.next/types/validator.ts +27 -0
  147. package/README.md +10 -0
  148. package/package.json +4 -3
  149. package/scripts/pick-folder.ps1 +11 -2
  150. package/scripts/release.mjs +11 -31
  151. package/scripts/run-test-file.mjs +34 -0
  152. package/scripts/verify-release-config.mjs +24 -0
  153. package/src/app/api/config/route.ts +4 -1
  154. package/src/app/api/security/route.ts +8 -3
  155. package/src/app/api/swarm/[id]/route.ts +28 -0
  156. package/src/app/api/swarm/route.ts +24 -0
  157. package/src/app/api/tests/route.ts +27 -0
  158. package/src/app/api/update/route.ts +8 -2
  159. package/src/app/globals.css +777 -0
  160. package/src/app/layout.tsx +13 -6
  161. package/src/components/AppShell.tsx +505 -199
  162. package/src/components/ChatInput.tsx +153 -26
  163. package/src/components/ChatWindow.tsx +46 -40
  164. package/src/components/ContextMeter.tsx +11 -17
  165. package/src/components/FileViewer.tsx +1 -1
  166. package/src/components/GitPanel.tsx +13 -11
  167. package/src/components/ModelSelector.tsx +19 -6
  168. package/src/components/PiMark.tsx +5 -38
  169. package/src/components/ProjectPanel.tsx +1 -0
  170. package/src/components/PromptPanel.tsx +132 -51
  171. package/src/components/SessionImportSection.tsx +17 -17
  172. package/src/components/SessionSidebar.tsx +159 -57
  173. package/src/components/SettingsPanel.tsx +330 -193
  174. package/src/components/SwarmCoordinatorPod.tsx +1021 -0
  175. package/src/components/TestInspectorPod.tsx +549 -0
  176. package/src/components/icons.tsx +23 -0
  177. package/src/hooks/usePiWeb.ts +112 -8
  178. package/src/i18n.tsx +59 -3
  179. package/src/lib/agent-manager.ts +180 -12
  180. package/src/lib/command-validation.ts +9 -1
  181. package/src/lib/delegation.ts +93 -0
  182. package/src/lib/managed-niubash.ts +8 -4
  183. package/src/lib/pi.ts +3 -2
  184. package/src/lib/runtime-activity.ts +27 -0
  185. package/src/lib/session-import/index.ts +5 -0
  186. package/src/lib/session-import/lock.ts +25 -0
  187. package/src/lib/session-import/writer.ts +8 -3
  188. package/src/lib/skills-service.ts +16 -9
  189. package/src/lib/swarm-service.ts +371 -0
  190. package/src/lib/test-run-service.ts +122 -0
  191. package/src/lib/theme.ts +68 -1
  192. package/src/lib/trajectory.ts +1 -0
  193. package/src/lib/types.ts +14 -0
  194. package/src/lib/workflow.ts +55 -0
  195. package/.next/static/DdBwPYoHBKtaJ97QIr6Co/_buildManifest.js +0 -1
  196. package/.next/static/chunks/2608.ec8ff1f5767c9056.js +0 -1
  197. package/.next/static/chunks/4632.0ed92ae42905ce40.js +0 -1
  198. package/.next/static/chunks/4686-7d694def920ff093.js +0 -1
  199. package/.next/static/chunks/app/_global-error/page-f77e9ede9808b18e.js +0 -1
  200. package/.next/static/chunks/app/api/agent/[id]/events/route-f77e9ede9808b18e.js +0 -1
  201. package/.next/static/chunks/app/api/agent/[id]/route-f77e9ede9808b18e.js +0 -1
  202. package/.next/static/chunks/app/api/agent/[id]/tree/route-f77e9ede9808b18e.js +0 -1
  203. package/.next/static/chunks/app/api/agent/new/route-f77e9ede9808b18e.js +0 -1
  204. package/.next/static/chunks/app/api/config/route-f77e9ede9808b18e.js +0 -1
  205. package/.next/static/chunks/app/api/files/route-f77e9ede9808b18e.js +0 -1
  206. package/.next/static/chunks/app/api/git/route-f77e9ede9808b18e.js +0 -1
  207. package/.next/static/chunks/app/api/growth/route-f77e9ede9808b18e.js +0 -1
  208. package/.next/static/chunks/app/api/health/route-f77e9ede9808b18e.js +0 -1
  209. package/.next/static/chunks/app/api/models/route-f77e9ede9808b18e.js +0 -1
  210. package/.next/static/chunks/app/api/pi-settings/route-f77e9ede9808b18e.js +0 -1
  211. package/.next/static/chunks/app/api/plugins/route-f77e9ede9808b18e.js +0 -1
  212. package/.next/static/chunks/app/api/prompts/route-f77e9ede9808b18e.js +0 -1
  213. package/.next/static/chunks/app/api/security/route-f77e9ede9808b18e.js +0 -1
  214. package/.next/static/chunks/app/api/sessions/[id]/export/route-f77e9ede9808b18e.js +0 -1
  215. package/.next/static/chunks/app/api/sessions/[id]/route-f77e9ede9808b18e.js +0 -1
  216. package/.next/static/chunks/app/api/sessions/import/route-f77e9ede9808b18e.js +0 -1
  217. package/.next/static/chunks/app/api/sessions/route-f77e9ede9808b18e.js +0 -1
  218. package/.next/static/chunks/app/api/skills/route-f77e9ede9808b18e.js +0 -1
  219. package/.next/static/chunks/app/api/update/route-f77e9ede9808b18e.js +0 -1
  220. package/.next/static/chunks/app/api/version/route-f77e9ede9808b18e.js +0 -1
  221. package/.next/static/chunks/app/api/web-auth/route-f77e9ede9808b18e.js +0 -1
  222. package/.next/static/chunks/app/api/workspaces/route-f77e9ede9808b18e.js +0 -1
  223. package/.next/static/chunks/app/page-9a5440047c5a415c.js +0 -1
  224. package/.next/static/chunks/next/dist/client/components/builtin/app-error-f77e9ede9808b18e.js +0 -1
  225. package/.next/static/chunks/next/dist/client/components/builtin/forbidden-f77e9ede9808b18e.js +0 -1
  226. package/.next/static/chunks/next/dist/client/components/builtin/not-found-f77e9ede9808b18e.js +0 -1
  227. package/.next/static/chunks/next/dist/client/components/builtin/unauthorized-f77e9ede9808b18e.js +0 -1
  228. package/.next/static/chunks/webpack-42c5aae79b0e5d8c.js +0 -1
  229. package/.next/static/css/b2d35abc828d4713.css +0 -1
  230. /package/.next/static/{DdBwPYoHBKtaJ97QIr6Co → 0t1jBUAuVaTFTXxRyIo3q}/_ssgManifest.js +0 -0
@@ -30,6 +30,8 @@ import {
30
30
  IconSettingsOutline16,
31
31
  IconTrashOutline16,
32
32
  IconTriangleRightFill14,
33
+ IconAgentPresetOutline16,
34
+ IconWorkflowAgent16,
33
35
  } from "@/components/icons";
34
36
  import { useI18n } from "@/i18n";
35
37
  import type { SessionListItem } from "@/hooks/usePiWeb";
@@ -97,6 +99,12 @@ export function SessionSidebar({
97
99
  onOpenSettings,
98
100
  onAddWorkspace,
99
101
  draftCwd,
102
+ gitBranch = "—",
103
+ gitStatus = "未选择",
104
+ canAskCommit = false,
105
+ onAskCommit,
106
+ activeTopView = "workbench",
107
+ onSelectTopView,
100
108
  }: {
101
109
  sessions: SessionListItem[];
102
110
  sessionListError?: string | null;
@@ -129,6 +137,12 @@ export function SessionSidebar({
129
137
  onAddWorkspace: () => void;
130
138
  /** 新会话草稿所在的工作区:条目显示在该分组内,发送第一条消息才真正创建 */
131
139
  draftCwd?: string | null;
140
+ gitBranch?: string;
141
+ gitStatus?: string;
142
+ canAskCommit?: boolean;
143
+ onAskCommit?: () => void;
144
+ activeTopView?: "workbench" | "diff" | "tests" | "swarm" | "archive";
145
+ onSelectTopView?: (view: "workbench" | "diff" | "tests" | "swarm" | "archive") => void;
132
146
  }) {
133
147
  const { t, lang } = useI18n();
134
148
  const [q, setQ] = useState("");
@@ -392,9 +406,6 @@ export function SessionSidebar({
392
406
  <IconSearchOutline16 size={17} />
393
407
  </button>
394
408
  <div className="flex-1" />
395
- <button className="rail-btn" title={t.settings} onClick={onOpenSettings}>
396
- <IconSettingsOutline16 size={17} />
397
- </button>
398
409
  </div>
399
410
  );
400
411
  }
@@ -406,16 +417,11 @@ export function SessionSidebar({
406
417
  return (
407
418
  <div
408
419
  key={s.path}
409
- className="group relative flex items-center rounded-xl transition-colors"
420
+ className="pw-session-row group relative flex items-center transition-colors"
421
+ data-selected={selected || undefined}
410
422
  style={{
411
423
  background: selected ? "var(--dsw-active)" : "transparent",
412
- paddingLeft: indented ? 38 : 10,
413
- paddingRight: 8,
414
- paddingTop: 6,
415
- paddingBottom: 6,
416
- // 选中/悬停框之间留缝,不互相粘连
417
- marginTop: 2,
418
- marginBottom: 2,
424
+ paddingLeft: indented ? 32 : s.streaming ? 16 : 8,
419
425
  }}
420
426
  onMouseEnter={(e) => {
421
427
  if (!selected) e.currentTarget.style.background = "var(--dsw-hover)";
@@ -448,8 +454,8 @@ export function SessionSidebar({
448
454
  <button
449
455
  className="icon-btn"
450
456
  style={{ width: 22, height: 22 }}
451
- title="..."
452
- aria-label="session-actions"
457
+ title={t.sessionActions}
458
+ aria-label={t.sessionActions}
453
459
  onClick={(e) => {
454
460
  e.stopPropagation();
455
461
  setSessionMenu(sessionMenu === s.path ? null : s.path);
@@ -525,16 +531,12 @@ export function SessionSidebar({
525
531
  return (
526
532
  <div key={g.cwd}>
527
533
  <div
528
- className="group relative flex items-center rounded-xl transition-colors"
534
+ className="pw-workspace-row group relative flex items-center transition-colors"
535
+ data-current={isCurrent || undefined}
536
+ data-open={open || undefined}
529
537
  style={{
530
538
  background: open || isCurrent ? "var(--dsw-hover)" : "transparent",
531
- paddingLeft: 10,
532
- paddingRight: 6,
533
- paddingTop: 7,
534
- paddingBottom: 7,
535
- // 悬停/展开框之间留缝,不互相粘连(与会话行一致)
536
- marginTop: 2,
537
- marginBottom: 2,
539
+ paddingLeft: 8,
538
540
  }}
539
541
  onMouseEnter={(e) => {
540
542
  if (!(open || isCurrent)) e.currentTarget.style.background = "var(--dsw-hover)";
@@ -570,8 +572,8 @@ export function SessionSidebar({
570
572
  <button
571
573
  className="icon-btn"
572
574
  style={{ width: 22, height: 22 }}
573
- title="..."
574
- aria-label="workspace-actions"
575
+ title={t.workspaceActions}
576
+ aria-label={t.workspaceActions}
575
577
  onClick={() => {
576
578
  setRowMenu(rowMenu === g.cwd ? null : g.cwd);
577
579
  setSessionMenu(null);
@@ -651,9 +653,9 @@ export function SessionSidebar({
651
653
  };
652
654
 
653
655
  return (
654
- <div className="flex h-full min-h-0 flex-col">
656
+ <div className="pw-sidebar flex h-full min-h-0 flex-col">
655
657
  {/* 品牌行 */}
656
- <div className="flex items-center gap-1 px-4 pb-2 pt-4">
658
+ <div className="pw-sidebar-head flex items-center gap-1 px-4 pb-2 pt-4">
657
659
  <BrandPi />
658
660
  <div className="flex-1" />
659
661
  <button className="icon-btn" onClick={onToggleCollapse} title="«">
@@ -661,14 +663,116 @@ export function SessionSidebar({
661
663
  </button>
662
664
  </div>
663
665
 
664
- {/* 新会话(dsh .newSession) */}
665
- <div className="px-3 pt-1">
666
- <button className="btn-new-session w-full" onClick={onNew}>
666
+ {/* 新会话(柔和粘土拟物风格) */}
667
+ <div className="pw-sidebar-new px-3 pt-1">
668
+ <button className="clay-btn clay-btn-mint w-full py-2.5 px-4 text-xs font-semibold tracking-wide flex items-center justify-center gap-2 text-white shadow-md rounded-2xl transition-all" onClick={onNew}>
667
669
  <IconNewChatOutline16 size={14} />
668
670
  {t.newChat}
669
671
  </button>
670
672
  </div>
671
673
 
674
+ {/* Git 状态卡片 (Screen 1 & 2) */}
675
+ <div className="px-3 pt-2">
676
+ <div className="clay-card p-2.5 rounded-2xl border border-slate-200/50 dark:border-slate-800/50 bg-white/70 dark:bg-slate-900/70">
677
+ <div className="flex items-center justify-between text-xs font-medium text-slate-700 dark:text-slate-200">
678
+ <div className="flex items-center gap-1.5">
679
+ <IconBranchOutline16 size={13} className="text-emerald-500" />
680
+ <span className="font-mono truncate max-w-[90px]" title={gitBranch}>{gitBranch}</span>
681
+ </div>
682
+ <span className={`text-[10px] font-mono font-semibold px-1.5 py-0.5 rounded ${gitStatus === "干净" ? "text-emerald-600 dark:text-emerald-400 bg-emerald-50 dark:bg-emerald-950/60" : "text-amber-700 dark:text-amber-300 bg-amber-50 dark:bg-amber-950/60"}`}>
683
+ {gitStatus}
684
+ </span>
685
+ </div>
686
+ <div className="flex items-center justify-between pt-2">
687
+ <span className="text-[11px] text-slate-400">就绪待审查</span>
688
+ <button
689
+ type="button"
690
+ className="px-2.5 py-1 text-[11px] font-medium text-emerald-700 dark:text-emerald-300 rounded-xl clay-btn clay-btn-soft transition-colors flex items-center gap-1 disabled:opacity-50 disabled:cursor-not-allowed"
691
+ disabled={!canAskCommit}
692
+ onClick={onAskCommit}
693
+ >
694
+ <span>让 pi 提交</span>
695
+ </button>
696
+ </div>
697
+ </div>
698
+ </div>
699
+
700
+ {/* 快速视图切换导航 (Screen 1 & 3 & 4) */}
701
+ <div className="px-3 pt-2">
702
+ <nav className="flex flex-col gap-1">
703
+ <button
704
+ type="button"
705
+ className={`flex items-center justify-between px-3 py-1.5 rounded-xl transition-all text-xs font-medium ${
706
+ activeTopView === "workbench"
707
+ ? "clay-card text-emerald-700 dark:text-emerald-300 font-bold bg-white dark:bg-slate-800 shadow-sm"
708
+ : "text-slate-600 dark:text-slate-300 hover:bg-slate-100 dark:hover:bg-slate-800/60"
709
+ }`}
710
+ onClick={() => onSelectTopView?.("workbench")}
711
+ >
712
+ <span className="flex items-center gap-2">
713
+ <IconFolderOpenOutline16 size={14} className={activeTopView === "workbench" ? "text-emerald-500" : "text-slate-400"} />
714
+ <span>工作区文件</span>
715
+ </span>
716
+ </button>
717
+ <button
718
+ type="button"
719
+ className={`flex items-center justify-between px-3 py-1.5 rounded-xl transition-all text-xs font-medium ${
720
+ activeTopView === "diff"
721
+ ? "clay-card text-emerald-700 dark:text-emerald-300 font-bold bg-white dark:bg-slate-800 shadow-sm"
722
+ : "text-slate-600 dark:text-slate-300 hover:bg-slate-100 dark:hover:bg-slate-800/60"
723
+ }`}
724
+ onClick={() => onSelectTopView?.("diff")}
725
+ >
726
+ <span className="flex items-center gap-2">
727
+ <IconEditOutline16 size={14} className={activeTopView === "diff" ? "text-emerald-500" : "text-slate-400"} />
728
+ <span>改动检查器</span>
729
+ </span>
730
+ </button>
731
+ <button
732
+ type="button"
733
+ className={`flex items-center justify-between px-3 py-1.5 rounded-xl transition-all text-xs font-medium ${
734
+ activeTopView === "tests"
735
+ ? "clay-card text-emerald-700 dark:text-emerald-300 font-bold bg-white dark:bg-slate-800 shadow-sm"
736
+ : "text-slate-600 dark:text-slate-300 hover:bg-slate-100 dark:hover:bg-slate-800/60"
737
+ }`}
738
+ onClick={() => onSelectTopView?.("tests")}
739
+ >
740
+ <span className="flex items-center gap-2">
741
+ <IconWorkflowAgent16 size={14} className={activeTopView === "tests" ? "text-emerald-500" : "text-slate-400"} />
742
+ <span>测试套件</span>
743
+ </span>
744
+ </button>
745
+ <button
746
+ type="button"
747
+ className={`flex items-center justify-between px-3 py-1.5 rounded-xl transition-all text-xs font-medium ${
748
+ activeTopView === "swarm"
749
+ ? "clay-card text-emerald-700 dark:text-emerald-300 font-bold bg-white dark:bg-slate-800 shadow-sm"
750
+ : "text-slate-600 dark:text-slate-300 hover:bg-slate-100 dark:hover:bg-slate-800/60"
751
+ }`}
752
+ onClick={() => onSelectTopView?.("swarm")}
753
+ >
754
+ <span className="flex items-center gap-2">
755
+ <IconAgentPresetOutline16 size={14} className={activeTopView === "swarm" ? "text-emerald-500" : "text-slate-400"} />
756
+ <span>智能体群组</span>
757
+ </span>
758
+ </button>
759
+ <button
760
+ type="button"
761
+ className={`flex items-center justify-between px-3 py-1.5 rounded-xl transition-all text-xs font-medium ${
762
+ activeTopView === "archive"
763
+ ? "clay-card text-emerald-700 dark:text-emerald-300 font-bold bg-white dark:bg-slate-800 shadow-sm"
764
+ : "text-slate-600 dark:text-slate-300 hover:bg-slate-100 dark:hover:bg-slate-800/60"
765
+ }`}
766
+ onClick={() => onSelectTopView?.("archive")}
767
+ >
768
+ <span className="flex items-center gap-2">
769
+ <IconArchiveOutline20 size={14} className={activeTopView === "archive" ? "text-emerald-500" : "text-slate-400"} />
770
+ <span>归档</span>
771
+ </span>
772
+ </button>
773
+ </nav>
774
+ </div>
775
+
672
776
  {/* 工作区标题行 / 搜索框(互替,照 dsh) */}
673
777
  {searchOpen ? (
674
778
  <div className="px-3 pb-1 pt-3">
@@ -676,6 +780,7 @@ export function SessionSidebar({
676
780
  <IconSearchOutline16 size={14} style={{ color: "var(--dsw-label-caption)" }} />
677
781
  <input
678
782
  autoFocus
783
+ aria-label={t.searchSessions}
679
784
  value={q}
680
785
  onChange={(e) => setQ(e.target.value)}
681
786
  placeholder={t.searchSessions}
@@ -685,6 +790,7 @@ export function SessionSidebar({
685
790
  <button
686
791
  className="icon-btn"
687
792
  style={{ width: 20, height: 20 }}
793
+ aria-label={t.close}
688
794
  onClick={() => {
689
795
  setQ("");
690
796
  setSearchOpen(false);
@@ -695,7 +801,7 @@ export function SessionSidebar({
695
801
  </div>
696
802
  </div>
697
803
  ) : (
698
- <div className="flex items-center gap-0.5 px-4 pb-1 pt-3">
804
+ <div className="pw-sidebar-section-head flex items-center gap-0.5 px-4 pb-1 pt-3">
699
805
  <span style={{ fontSize: 13, color: "var(--dsw-label-primary)" }}>{t.workspaces}</span>
700
806
  <div className="flex-1" />
701
807
  <button className="icon-btn" style={{ width: 26, height: 26 }} title={t.search} onClick={() => setSearchOpen(true)}>
@@ -733,7 +839,7 @@ export function SessionSidebar({
733
839
  )}
734
840
 
735
841
  {/* 列表区 */}
736
- <div className="min-h-0 flex-1 overflow-y-auto px-3 pb-3">
842
+ <div className="pw-sidebar-list min-h-0 flex-1 overflow-y-auto px-3 pb-3">
737
843
  {sessionListError && (
738
844
  <div role="alert" className="mb-2 rounded-xl px-3 py-2" style={{ background: "var(--dsw-hover)", color: "var(--dsw-danger)", fontSize: 12 }}>
739
845
  <div>{t.sessionListLoadFailed}: {sessionListError}</div>
@@ -864,28 +970,20 @@ export function SessionSidebar({
864
970
  )}
865
971
  </div>
866
972
 
867
- {/* 设置固定左下角 */}
868
- <div className="px-3 py-2.5">
869
- <button
870
- className="flex w-full items-center gap-2.5 rounded-xl px-4 py-2 text-left transition-colors"
871
- style={{ color: "var(--dsw-label-secondary)" }}
872
- onMouseEnter={(e) => (e.currentTarget.style.background = "var(--dsw-hover)")}
873
- onMouseLeave={(e) => (e.currentTarget.style.background = "transparent")}
874
- onClick={onOpenSettings}
875
- >
876
- <IconSettingsOutline16 size={16} />
877
- <span style={{ fontSize: "var(--dsh-content-font-size)" }}>{t.settings}</span>
878
- </button>
973
+ {/* 底部版本号 */}
974
+ <div className="pw-sidebar-footer px-3 py-2 border-t border-slate-200/60 dark:border-slate-800/60 flex items-center justify-center text-xs text-slate-400 font-mono text-[11px] shrink-0">
975
+ <span>PiWeb v0.3.15</span>
879
976
  </div>
880
977
 
881
978
  {/* 重命名弹窗(照用户截图 media_1788745548711.png,通过 createPortal 挂载到 document.body 确保浏览器正居中) */}
882
979
  {mounted && renameTarget && createPortal(
883
980
  <div
884
- className="fixed inset-0 z-[9999] flex items-center justify-center bg-black/60 backdrop-blur-sm p-4 animate-in fade-in duration-150"
981
+ className="modal-mask fixed inset-0 z-[9999] flex items-center justify-center p-4 animate-in fade-in duration-150"
885
982
  onClick={closeRename}
886
983
  >
887
984
  <div
888
- className="relative w-full max-w-[420px] rounded-[24px] bg-[#212121] p-6 text-white shadow-2xl border border-white/10"
985
+ className="glass-modal relative w-full max-w-[420px] rounded-[24px] p-6 shadow-2xl"
986
+ style={{ color: "var(--dsw-label-primary)", border: "0.5px solid var(--dsw-border-l2)" }}
889
987
  onClick={(e) => e.stopPropagation()}
890
988
  role="dialog"
891
989
  aria-modal="true"
@@ -896,14 +994,14 @@ export function SessionSidebar({
896
994
  type="button"
897
995
  onClick={closeRename}
898
996
  disabled={renamePending}
899
- className="absolute right-5 top-5 rounded-full p-1 text-zinc-400 hover:text-white hover:bg-white/10 transition"
997
+ className="icon-btn absolute right-5 top-5 rounded-full p-1"
900
998
  aria-label={t.close}
901
999
  >
902
1000
  <IconCloseOutline14 size={16} />
903
1001
  </button>
904
1002
 
905
1003
  {/* 标题 */}
906
- <h3 id="rename-dialog-title" className="text-lg font-medium text-white pr-8">
1004
+ <h3 id="rename-dialog-title" className="pr-8 text-lg font-medium">
907
1005
  {renameTarget.type === "session"
908
1006
  ? (t.renameSession || "重命名会话")
909
1007
  : (t.renameWorkspace || "重命名工作区")}
@@ -914,6 +1012,7 @@ export function SessionSidebar({
914
1012
  <input
915
1013
  ref={renameInputRef}
916
1014
  type="text"
1015
+ aria-label={renameTarget.type === "session" ? t.renameSession : t.renameWorkspace}
917
1016
  value={renameValue}
918
1017
  disabled={renamePending}
919
1018
  onChange={(e) => {
@@ -924,7 +1023,8 @@ export function SessionSidebar({
924
1023
  if (e.key === "Enter") void handleConfirmRename();
925
1024
  if (e.key === "Escape") closeRename();
926
1025
  }}
927
- className="w-full rounded-full bg-white/[0.05] border border-white/15 px-4 py-2.5 text-[14.5px] text-white outline-none focus:border-white/40 focus:ring-1 focus:ring-white/30 transition"
1026
+ className="w-full rounded-full px-4 py-2.5 text-[14.5px] outline-none transition"
1027
+ style={{ background: "var(--dsw-hover)", border: "1px solid var(--dsw-border-l2)", color: "var(--dsw-label-primary)" }}
928
1028
  />
929
1029
  </div>
930
1030
  {renameError && (
@@ -937,7 +1037,7 @@ export function SessionSidebar({
937
1037
  type="button"
938
1038
  onClick={closeRename}
939
1039
  disabled={renamePending}
940
- className="rounded-full bg-[#2c2c2c] px-5 py-2 text-sm font-medium text-zinc-200 hover:bg-[#383838] transition"
1040
+ className="btn-outline"
941
1041
  >
942
1042
  {t.cancel}
943
1043
  </button>
@@ -945,7 +1045,7 @@ export function SessionSidebar({
945
1045
  type="button"
946
1046
  onClick={() => void handleConfirmRename()}
947
1047
  disabled={renamePending || !renameValue.trim() || (renameTarget.type === "workspace" && (renameValue.trim() === renameTarget.name || renameWorkspaceConflict))}
948
- className="rounded-full bg-white px-5 py-2 text-sm font-medium text-black hover:bg-zinc-200 transition"
1048
+ className="btn-primary-white"
949
1049
  >
950
1050
  {t.rename}
951
1051
  </button>
@@ -958,11 +1058,12 @@ export function SessionSidebar({
958
1058
  {/* 删除工作区确认弹窗(照用户截图 media_1788745235179.png 与 media_1788746156687.png,通过 createPortal 挂载到 document.body 确保浏览器正居中) */}
959
1059
  {mounted && deleteWorkspaceTarget && createPortal(
960
1060
  <div
961
- className="fixed inset-0 z-[9999] flex items-center justify-center bg-black/60 backdrop-blur-sm p-4 animate-in fade-in duration-150"
1061
+ className="modal-mask fixed inset-0 z-[9999] flex items-center justify-center p-4 animate-in fade-in duration-150"
962
1062
  onClick={() => !deleteWorkspacePending && setDeleteWorkspaceTarget(null)}
963
1063
  >
964
1064
  <div
965
- className="relative w-full max-w-[420px] rounded-[24px] bg-[#212121] p-6 text-white shadow-2xl border border-white/10"
1065
+ className="glass-modal relative w-full max-w-[420px] rounded-[24px] p-6 shadow-2xl"
1066
+ style={{ color: "var(--dsw-label-primary)", border: "0.5px solid var(--dsw-border-l2)" }}
966
1067
  onClick={(e) => e.stopPropagation()}
967
1068
  role="dialog"
968
1069
  aria-modal="true"
@@ -973,26 +1074,26 @@ export function SessionSidebar({
973
1074
  type="button"
974
1075
  onClick={() => setDeleteWorkspaceTarget(null)}
975
1076
  disabled={deleteWorkspacePending}
976
- className="absolute right-5 top-5 rounded-full p-1 text-zinc-400 hover:text-white hover:bg-white/10 transition"
1077
+ className="icon-btn absolute right-5 top-5 rounded-full p-1"
977
1078
  aria-label={t.close}
978
1079
  >
979
1080
  <IconCloseOutline14 size={16} />
980
1081
  </button>
981
1082
 
982
1083
  {/* 标题 */}
983
- <h3 id="delete-workspace-dialog-title" className="text-lg font-medium text-white pr-8">
1084
+ <h3 id="delete-workspace-dialog-title" className="pr-8 text-lg font-medium">
984
1085
  {t.deleteWorkspace}
985
1086
  </h3>
986
1087
 
987
1088
  {/* 描述文本 */}
988
- <p className="mt-3 text-[14.5px] leading-relaxed text-zinc-300">
1089
+ <p className="mt-3 text-[14.5px] leading-relaxed" style={{ color: "var(--dsw-label-secondary)" }}>
989
1090
  {(t.deleteWorkspaceConfirm || "将把“{name}”从工作区列表中移除。文件夹与会话记录会保留,其会话将显示在“未分组”下。").replace(
990
1091
  "{name}",
991
1092
  deleteWorkspaceTarget.name,
992
1093
  )}
993
1094
  </p>
994
1095
  {deleteWorkspacePending && (
995
- <p className="mt-4 text-sm text-zinc-400" role="status">{t.deleteWorkspacePending}</p>
1096
+ <p className="mt-4 text-sm" style={{ color: "var(--dsw-label-caption)" }} role="status">{t.deleteWorkspacePending}</p>
996
1097
  )}
997
1098
  {deleteWorkspaceError && (
998
1099
  <p className="mt-4 text-sm" style={{ color: "var(--dsw-danger)" }} role="alert">{deleteWorkspaceError}</p>
@@ -1004,7 +1105,7 @@ export function SessionSidebar({
1004
1105
  type="button"
1005
1106
  onClick={() => setDeleteWorkspaceTarget(null)}
1006
1107
  disabled={deleteWorkspacePending}
1007
- className="rounded-xl border border-transparent bg-[#2c2c2c] px-5 py-2 text-sm font-medium text-zinc-200 transition hover:border-white/25 hover:bg-[#383838]"
1108
+ className="btn-outline"
1008
1109
  >
1009
1110
  {t.cancel}
1010
1111
  </button>
@@ -1012,7 +1113,8 @@ export function SessionSidebar({
1012
1113
  type="button"
1013
1114
  onClick={() => void handleConfirmDeleteWorkspace()}
1014
1115
  disabled={deleteWorkspacePending}
1015
- className="rounded-xl border border-transparent bg-[#2c2c2c] px-5 py-2 text-sm font-medium text-[#f87171] transition hover:border-[#f87171]/40 hover:bg-[#383838] hover:text-[#ef4444]"
1116
+ className="btn-outline"
1117
+ style={{ color: "var(--dsw-danger)" }}
1016
1118
  >
1017
1119
  {t.deleteWorkspace}
1018
1120
  </button>