@rexvane/piweb 0.3.16 → 0.3.17
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/.next/BUILD_ID +1 -1
- package/.next/app-path-routes-manifest.json +8 -11
- package/.next/build-manifest.json +3 -3
- package/.next/prerender-manifest.json +6 -6
- package/.next/react-loadable-manifest.json +4 -16
- package/.next/required-server-files.js +1 -3
- package/.next/required-server-files.json +1 -3
- package/.next/routes-manifest.json +0 -20
- package/.next/server/app/_global-error/page.js +2 -2
- package/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
- package/.next/server/app/_global-error.html +1 -1
- package/.next/server/app/_global-error.rsc +1 -1
- package/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/_not-found/page.js +1 -1
- package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/server/app/_not-found.html +1 -1
- package/.next/server/app/_not-found.rsc +4 -7
- package/.next/server/app/_not-found.segments/_full.segment.rsc +4 -7
- package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -5
- package/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -5
- package/.next/server/app/api/agent/[id]/events/route.js +2 -2
- package/.next/server/app/api/agent/[id]/events/route.js.nft.json +1 -1
- package/.next/server/app/api/agent/[id]/route.js +1 -1
- package/.next/server/app/api/agent/[id]/route.js.nft.json +1 -1
- package/.next/server/app/api/agent/[id]/tree/route.js +1 -1
- package/.next/server/app/api/agent/[id]/tree/route.js.nft.json +1 -1
- package/.next/server/app/api/agent/new/route.js +1 -1
- package/.next/server/app/api/agent/new/route.js.nft.json +1 -1
- package/.next/server/app/api/config/route.js +1 -1
- package/.next/server/app/api/config/route.js.nft.json +1 -1
- package/.next/server/app/api/files/route.js +1 -1
- package/.next/server/app/api/files/route.js.nft.json +1 -1
- package/.next/server/app/api/git/route.js +2 -2
- package/.next/server/app/api/git/route.js.nft.json +1 -1
- package/.next/server/app/api/growth/route.js +1 -1
- package/.next/server/app/api/growth/route.js.nft.json +1 -1
- package/.next/server/app/api/health/route.js +1 -1
- package/.next/server/app/api/models/route.js +1 -1
- package/.next/server/app/api/models/route.js.nft.json +1 -1
- package/.next/server/app/api/pi-settings/route.js +1 -1
- package/.next/server/app/api/pi-settings/route.js.nft.json +1 -1
- package/.next/server/app/api/plugins/route.js +1 -1
- package/.next/server/app/api/plugins/route.js.nft.json +1 -1
- package/.next/server/app/api/prompts/route.js +1 -1
- package/.next/server/app/api/prompts/route.js.nft.json +1 -1
- package/.next/server/app/api/security/route.js +1 -1
- package/.next/server/app/api/security/route.js.nft.json +1 -1
- package/.next/server/app/api/sessions/[id]/export/route.js +1 -1
- package/.next/server/app/api/sessions/[id]/export/route.js.nft.json +1 -1
- package/.next/server/app/api/sessions/[id]/route.js +1 -1
- package/.next/server/app/api/sessions/[id]/route.js.nft.json +1 -1
- package/.next/server/app/api/sessions/import/route.js +3 -3
- package/.next/server/app/api/sessions/import/route.js.nft.json +1 -1
- package/.next/server/app/api/sessions/route.js +1 -1
- package/.next/server/app/api/sessions/route.js.nft.json +1 -1
- package/.next/server/app/api/skills/route.js +2 -2
- package/.next/server/app/api/skills/route.js.nft.json +1 -1
- package/.next/server/app/api/update/route.js +2 -2
- package/.next/server/app/api/update/route.js.nft.json +1 -1
- package/.next/server/app/api/version/route.js +1 -1
- package/.next/server/app/api/version/route.js.nft.json +1 -1
- package/.next/server/app/api/web-auth/route.js +1 -1
- package/.next/server/app/api/workspaces/route.js +1 -1
- package/.next/server/app/api/workspaces/route.js.nft.json +1 -1
- package/.next/server/app/icon.svg/route.js +1 -1
- package/.next/server/app/index.html +1 -1
- package/.next/server/app/index.rsc +6 -9
- package/.next/server/app/index.segments/__PAGE__.segment.rsc +4 -7
- package/.next/server/app/index.segments/_full.segment.rsc +6 -9
- package/.next/server/app/index.segments/_tree.segment.rsc +2 -5
- package/.next/server/app/login/page.js +1 -1
- package/.next/server/app/login/page_client-reference-manifest.js +1 -1
- package/.next/server/app/login.html +1 -1
- package/.next/server/app/login.rsc +5 -8
- package/.next/server/app/login.segments/_full.segment.rsc +5 -8
- package/.next/server/app/login.segments/_tree.segment.rsc +2 -5
- package/.next/server/app/login.segments/login/__PAGE__.segment.rsc +3 -6
- package/.next/server/app/page.js +1 -1
- package/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/server/app-paths-manifest.json +8 -11
- package/.next/server/chunks/496.js +1 -1
- package/.next/server/chunks/532.js +1 -1
- package/.next/server/chunks/686.js +4 -8
- package/.next/server/middleware-build-manifest.js +1 -1
- package/.next/server/middleware-react-loadable-manifest.js +1 -1
- package/.next/server/pages/404.html +1 -1
- package/.next/server/pages/500.html +1 -1
- package/.next/server/server-reference-manifest.json +1 -1
- package/.next/static/chunks/2608.ec8ff1f5767c9056.js +1 -0
- package/.next/static/chunks/2716.93be8ad5c970c516.js +1 -0
- package/.next/static/chunks/4632.112d2e893d48d188.js +1 -0
- package/.next/static/chunks/4673.c1fb1580efc6ace7.js +1 -0
- package/.next/static/chunks/4686-f4a037d609cea3f2.js +1 -0
- package/.next/static/chunks/app/_global-error/page-f77e9ede9808b18e.js +1 -0
- package/.next/static/chunks/app/api/agent/[id]/events/route-f77e9ede9808b18e.js +1 -0
- package/.next/static/chunks/app/api/agent/[id]/route-f77e9ede9808b18e.js +1 -0
- package/.next/static/chunks/app/api/agent/[id]/tree/route-f77e9ede9808b18e.js +1 -0
- package/.next/static/chunks/app/api/agent/new/route-f77e9ede9808b18e.js +1 -0
- package/.next/static/chunks/app/api/config/route-f77e9ede9808b18e.js +1 -0
- package/.next/static/chunks/app/api/files/route-f77e9ede9808b18e.js +1 -0
- package/.next/static/chunks/app/api/git/route-f77e9ede9808b18e.js +1 -0
- package/.next/static/chunks/app/api/growth/route-f77e9ede9808b18e.js +1 -0
- package/.next/static/chunks/app/api/health/route-f77e9ede9808b18e.js +1 -0
- package/.next/static/chunks/app/api/models/route-f77e9ede9808b18e.js +1 -0
- package/.next/static/chunks/app/api/pi-settings/route-f77e9ede9808b18e.js +1 -0
- package/.next/static/chunks/app/api/plugins/route-f77e9ede9808b18e.js +1 -0
- package/.next/static/chunks/app/api/prompts/route-f77e9ede9808b18e.js +1 -0
- package/.next/static/chunks/app/api/security/route-f77e9ede9808b18e.js +1 -0
- package/.next/static/chunks/app/api/sessions/[id]/export/route-f77e9ede9808b18e.js +1 -0
- package/.next/static/chunks/app/api/sessions/[id]/route-f77e9ede9808b18e.js +1 -0
- package/.next/static/chunks/app/api/sessions/import/route-f77e9ede9808b18e.js +1 -0
- package/.next/static/chunks/app/api/sessions/route-f77e9ede9808b18e.js +1 -0
- package/.next/static/chunks/app/api/skills/route-f77e9ede9808b18e.js +1 -0
- package/.next/static/chunks/app/api/update/route-f77e9ede9808b18e.js +1 -0
- package/.next/static/chunks/app/api/version/route-f77e9ede9808b18e.js +1 -0
- package/.next/static/chunks/app/api/web-auth/route-f77e9ede9808b18e.js +1 -0
- package/.next/static/chunks/app/api/workspaces/route-f77e9ede9808b18e.js +1 -0
- package/.next/static/chunks/app/page-b51087831582ee37.js +1 -0
- package/.next/static/chunks/next/dist/client/components/builtin/app-error-f77e9ede9808b18e.js +1 -0
- package/.next/static/chunks/next/dist/client/components/builtin/forbidden-f77e9ede9808b18e.js +1 -0
- package/.next/static/chunks/next/dist/client/components/builtin/not-found-f77e9ede9808b18e.js +1 -0
- package/.next/static/chunks/next/dist/client/components/builtin/unauthorized-f77e9ede9808b18e.js +1 -0
- package/.next/static/chunks/webpack-c31263fd83d75ce6.js +1 -0
- package/.next/static/css/850c2fd130ffa2fc.css +1 -0
- package/.next/static/zsynuO_9Kx6nrXgkTUJ04/_buildManifest.js +1 -0
- package/.next/types/routes.d.ts +1 -4
- package/.next/types/validator.ts +0 -27
- package/README.md +17 -29
- package/assets/archive.jpg +0 -0
- package/assets/growth-1.jpg +0 -0
- package/assets/growth-2.jpg +0 -0
- package/assets/import.jpg +0 -0
- package/assets/main.jpg +0 -0
- package/assets/outline.jpg +0 -0
- package/assets/prompts-1.jpg +0 -0
- package/assets/prompts-2.jpg +0 -0
- package/assets/providers-1.jpg +0 -0
- package/assets/providers-2.jpg +0 -0
- package/assets/providers-3.jpg +0 -0
- package/assets/trace-1.jpg +0 -0
- package/assets/trace-2.png +0 -0
- package/assets/trace-3.png +0 -0
- package/assets/trace-4.png +0 -0
- package/assets/viewer-1.jpg +0 -0
- package/assets/viewer-2.jpg +0 -0
- package/bin/pi.js +50 -0
- package/next.config.ts +32 -7
- package/package.json +4 -9
- package/scripts/launcher.mjs +1 -1
- package/scripts/pick-folder.ps1 +2 -11
- package/scripts/release.mjs +31 -11
- package/scripts/verify-package.mjs +0 -53
- package/src/app/api/config/route.ts +1 -4
- package/src/app/api/growth/route.ts +14 -14
- package/src/app/api/models/route.ts +2 -1
- package/src/app/api/security/route.ts +3 -8
- package/src/app/api/update/route.ts +2 -8
- package/src/app/globals.css +90 -777
- package/src/app/layout.tsx +6 -13
- package/src/components/AppShell.tsx +229 -521
- package/src/components/ChatInput.tsx +26 -153
- package/src/components/ChatWindow.tsx +231 -51
- package/src/components/ContextMeter.tsx +17 -11
- package/src/components/FileViewer.tsx +11 -10
- package/src/components/GitPanel.tsx +11 -13
- package/src/components/ModelSelector.tsx +6 -19
- package/src/components/PiMark.tsx +38 -5
- package/src/components/ProjectPanel.tsx +111 -95
- package/src/components/PromptPanel.tsx +51 -132
- package/src/components/SessionImportSection.tsx +37 -53
- package/src/components/SessionSidebar.tsx +57 -159
- package/src/components/SettingsPanel.tsx +193 -330
- package/src/components/icons.tsx +0 -23
- package/src/hooks/useGrowth.ts +123 -156
- package/src/hooks/usePiWeb.ts +15 -122
- package/src/i18n.tsx +42 -98
- package/src/lib/agent-manager.ts +21 -187
- package/src/lib/command-validation.ts +1 -9
- package/src/lib/growth-service.ts +390 -166
- package/src/lib/growth-tracker.ts +64 -264
- package/src/lib/growth-turns.ts +31 -1
- package/src/lib/models/model-scope.ts +15 -0
- package/src/lib/models-service.ts +16 -0
- package/src/lib/pi-settings.ts +7 -0
- package/src/lib/pi.ts +2 -3
- package/src/lib/session-import/claude.ts +2 -13
- package/src/lib/session-import/codex.ts +8 -44
- package/src/lib/session-import/files.ts +0 -14
- package/src/lib/session-import/grok.ts +24 -54
- package/src/lib/session-import/index.ts +0 -5
- package/src/lib/session-import/types.ts +0 -12
- package/src/lib/session-import/writer.ts +3 -8
- package/src/lib/skills-service.ts +9 -16
- package/src/lib/theme.ts +1 -68
- package/src/lib/trajectory.ts +0 -1
- package/src/lib/types.ts +27 -36
- package/tsconfig.json +2 -1
- package/.next/server/app/api/swarm/[id]/route.js +0 -1
- package/.next/server/app/api/swarm/[id]/route.js.nft.json +0 -1
- package/.next/server/app/api/swarm/[id]/route_client-reference-manifest.js +0 -1
- package/.next/server/app/api/swarm/route.js +0 -1
- package/.next/server/app/api/swarm/route.js.nft.json +0 -1
- package/.next/server/app/api/swarm/route_client-reference-manifest.js +0 -1
- package/.next/server/app/api/tests/route.js +0 -2
- package/.next/server/app/api/tests/route.js.nft.json +0 -1
- package/.next/server/app/api/tests/route_client-reference-manifest.js +0 -1
- package/.next/server/chunks/757.js +0 -1
- package/.next/server/chunks/775.js +0 -3
- package/.next/static/0t1jBUAuVaTFTXxRyIo3q/_buildManifest.js +0 -1
- package/.next/static/chunks/2608.43c58236c9cce8e6.js +0 -1
- package/.next/static/chunks/2716.4ec42df1d51354c9.js +0 -1
- package/.next/static/chunks/4632.62139b565b657aac.js +0 -1
- package/.next/static/chunks/4673.7796104395deb8d7.js +0 -1
- package/.next/static/chunks/4686-17f21a006739a596.js +0 -1
- package/.next/static/chunks/7577.10af9bb661652da8.js +0 -1
- package/.next/static/chunks/8866.38ec4ac4ee5a1b74.js +0 -1
- package/.next/static/chunks/app/_global-error/page-5148173b3f55e415.js +0 -1
- package/.next/static/chunks/app/api/agent/[id]/events/route-5148173b3f55e415.js +0 -1
- package/.next/static/chunks/app/api/agent/[id]/route-5148173b3f55e415.js +0 -1
- package/.next/static/chunks/app/api/agent/[id]/tree/route-5148173b3f55e415.js +0 -1
- package/.next/static/chunks/app/api/agent/new/route-5148173b3f55e415.js +0 -1
- package/.next/static/chunks/app/api/config/route-5148173b3f55e415.js +0 -1
- package/.next/static/chunks/app/api/files/route-5148173b3f55e415.js +0 -1
- package/.next/static/chunks/app/api/git/route-5148173b3f55e415.js +0 -1
- package/.next/static/chunks/app/api/growth/route-5148173b3f55e415.js +0 -1
- package/.next/static/chunks/app/api/health/route-5148173b3f55e415.js +0 -1
- package/.next/static/chunks/app/api/models/route-5148173b3f55e415.js +0 -1
- package/.next/static/chunks/app/api/pi-settings/route-5148173b3f55e415.js +0 -1
- package/.next/static/chunks/app/api/plugins/route-5148173b3f55e415.js +0 -1
- package/.next/static/chunks/app/api/prompts/route-5148173b3f55e415.js +0 -1
- package/.next/static/chunks/app/api/security/route-5148173b3f55e415.js +0 -1
- package/.next/static/chunks/app/api/sessions/[id]/export/route-5148173b3f55e415.js +0 -1
- package/.next/static/chunks/app/api/sessions/[id]/route-5148173b3f55e415.js +0 -1
- package/.next/static/chunks/app/api/sessions/import/route-5148173b3f55e415.js +0 -1
- package/.next/static/chunks/app/api/sessions/route-5148173b3f55e415.js +0 -1
- package/.next/static/chunks/app/api/skills/route-5148173b3f55e415.js +0 -1
- package/.next/static/chunks/app/api/swarm/[id]/route-5148173b3f55e415.js +0 -1
- package/.next/static/chunks/app/api/swarm/route-5148173b3f55e415.js +0 -1
- package/.next/static/chunks/app/api/tests/route-5148173b3f55e415.js +0 -1
- package/.next/static/chunks/app/api/update/route-5148173b3f55e415.js +0 -1
- package/.next/static/chunks/app/api/version/route-5148173b3f55e415.js +0 -1
- package/.next/static/chunks/app/api/web-auth/route-5148173b3f55e415.js +0 -1
- package/.next/static/chunks/app/api/workspaces/route-5148173b3f55e415.js +0 -1
- package/.next/static/chunks/app/page-14379b8060d5cf07.js +0 -1
- package/.next/static/chunks/next/dist/client/components/builtin/app-error-5148173b3f55e415.js +0 -1
- package/.next/static/chunks/next/dist/client/components/builtin/forbidden-5148173b3f55e415.js +0 -1
- package/.next/static/chunks/next/dist/client/components/builtin/not-found-5148173b3f55e415.js +0 -1
- package/.next/static/chunks/next/dist/client/components/builtin/unauthorized-5148173b3f55e415.js +0 -1
- package/.next/static/chunks/webpack-d1a69ad9edb38b11.js +0 -1
- package/.next/static/css/d0a0162cb481abe8.css +0 -1
- package/.next/types/app/api/swarm/[id]/route.ts +0 -353
- package/.next/types/app/api/swarm/route.ts +0 -353
- package/.next/types/app/api/tests/route.ts +0 -353
- package/assets/archive.png +0 -0
- package/assets/growth-1.png +0 -0
- package/assets/growth-2.png +0 -0
- package/assets/import.png +0 -0
- package/assets/main.png +0 -0
- package/assets/outline.png +0 -0
- package/assets/prompts-1.png +0 -0
- package/assets/prompts-2.png +0 -0
- package/assets/providers-1.png +0 -0
- package/assets/providers-2.png +0 -0
- package/assets/providers-3.png +0 -0
- package/assets/trace-1.png +0 -0
- package/assets/viewer-1.png +0 -0
- package/assets/viewer-2.png +0 -0
- package/scripts/managed-niubash-assets.mjs +0 -146
- package/scripts/prepare-niubash-packages.mjs +0 -182
- package/scripts/run-test-file.mjs +0 -34
- package/scripts/smoke-managed-niubash.mjs +0 -62
- package/scripts/verify-release-config.mjs +0 -24
- package/src/app/api/swarm/[id]/route.ts +0 -28
- package/src/app/api/swarm/route.ts +0 -24
- package/src/app/api/tests/route.ts +0 -27
- package/src/components/SwarmCoordinatorPod.tsx +0 -1021
- package/src/components/TestInspectorPod.tsx +0 -549
- package/src/lib/delegation.ts +0 -93
- package/src/lib/growth-rounds.ts +0 -84
- package/src/lib/managed-niubash.ts +0 -231
- package/src/lib/runtime-activity.ts +0 -27
- package/src/lib/session-import/lock.ts +0 -25
- package/src/lib/swarm-service.ts +0 -371
- package/src/lib/test-run-service.ts +0 -122
- package/src/lib/workflow.ts +0 -55
- /package/.next/static/{0t1jBUAuVaTFTXxRyIo3q → zsynuO_9Kx6nrXgkTUJ04}/_ssgManifest.js +0 -0
|
@@ -95,80 +95,57 @@ export function SettingsPanel({
|
|
|
95
95
|
}}
|
|
96
96
|
>
|
|
97
97
|
<div
|
|
98
|
-
className="settings-dialog
|
|
98
|
+
className="settings-dialog glass-modal flex overflow-hidden"
|
|
99
99
|
style={{
|
|
100
|
-
width:
|
|
101
|
-
maxWidth: "calc(100vw -
|
|
102
|
-
height: "min(
|
|
103
|
-
borderRadius:
|
|
100
|
+
width: 800,
|
|
101
|
+
maxWidth: "calc(100vw - 48px)",
|
|
102
|
+
height: "min(800px, calc(100vh - 48px))",
|
|
103
|
+
borderRadius: 32,
|
|
104
|
+
boxShadow: "var(--dsw-elevation-prominent)",
|
|
104
105
|
}}
|
|
105
106
|
>
|
|
106
|
-
{/*
|
|
107
|
-
<div className="flex
|
|
108
|
-
<div className="
|
|
109
|
-
|
|
110
|
-
<IconSettingsOutline16 size={20} />
|
|
111
|
-
</div>
|
|
112
|
-
<div className="flex flex-col">
|
|
113
|
-
<div className="flex items-center gap-2">
|
|
114
|
-
<h1 className="text-base sm:text-lg font-extrabold text-slate-800 dark:text-slate-100 tracking-tight">全局设置中心</h1>
|
|
115
|
-
<span className="px-2.5 py-0.5 rounded-full clay-inset text-emerald-600 dark:text-emerald-400 font-mono text-[11px] font-semibold">config.json</span>
|
|
116
|
-
</div>
|
|
117
|
-
<p className="text-xs text-slate-400 hidden sm:block">管理工作台外观基调、语言偏好、模型调度与集成插件环境</p>
|
|
118
|
-
</div>
|
|
107
|
+
{/* 左导航(dsh trigger:h42 r12,active 填充) */}
|
|
108
|
+
<div className="settings-nav flex w-52 flex-none flex-col gap-0.5 py-4 pl-4 pr-3">
|
|
109
|
+
<div className="settings-title px-2 pb-3" style={{ fontSize: 16, fontWeight: 500 }}>
|
|
110
|
+
{t.settings}
|
|
119
111
|
</div>
|
|
120
|
-
|
|
112
|
+
{navItems.map((n) => (
|
|
121
113
|
<button
|
|
122
|
-
|
|
123
|
-
className="
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
114
|
+
key={n.id}
|
|
115
|
+
className="flex h-[42px] items-center gap-2 rounded-xl px-3 text-left transition-colors"
|
|
116
|
+
style={{
|
|
117
|
+
paddingLeft: 8,
|
|
118
|
+
paddingRight: 10,
|
|
119
|
+
fontSize: 14,
|
|
120
|
+
background: section === n.id ? "var(--dsw-active)" : "transparent",
|
|
121
|
+
color: section === n.id ? "var(--dsw-label-primary)" : "var(--dsw-label-secondary)",
|
|
122
|
+
}}
|
|
123
|
+
onMouseEnter={(e) => {
|
|
124
|
+
if (section !== n.id) e.currentTarget.style.background = "var(--dsw-hover)";
|
|
125
|
+
}}
|
|
126
|
+
onMouseLeave={(e) => {
|
|
127
|
+
if (section !== n.id) e.currentTarget.style.background = "transparent";
|
|
128
|
+
}}
|
|
129
|
+
onClick={() => setSection(n.id)}
|
|
134
130
|
>
|
|
135
|
-
<
|
|
131
|
+
{section === n.id ? <span style={{ color: "var(--dsw-accent)" }}>{n.icon}</span> : n.icon}
|
|
132
|
+
{n.label}
|
|
136
133
|
</button>
|
|
137
|
-
|
|
134
|
+
))}
|
|
138
135
|
</div>
|
|
139
136
|
|
|
140
|
-
{/*
|
|
141
|
-
<div className="
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
<
|
|
145
|
-
{
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
type="button"
|
|
151
|
-
className={`flex items-center justify-between px-3.5 py-2.5 rounded-xl text-xs font-semibold transition-all ${
|
|
152
|
-
isSelected
|
|
153
|
-
? "clay-card text-emerald-700 dark:text-emerald-300 font-bold bg-white dark:bg-slate-800 shadow-sm"
|
|
154
|
-
: "text-slate-600 dark:text-slate-300 hover:bg-white/60 dark:hover:bg-slate-800/60"
|
|
155
|
-
}`}
|
|
156
|
-
onClick={() => setSection(n.id)}
|
|
157
|
-
>
|
|
158
|
-
<div className="flex items-center gap-2.5">
|
|
159
|
-
<span className={isSelected ? "text-emerald-500" : "text-slate-400"}>{n.icon}</span>
|
|
160
|
-
<span>{n.label}</span>
|
|
161
|
-
</div>
|
|
162
|
-
{isSelected && <span className="w-1.5 h-1.5 rounded-full bg-emerald-500" />}
|
|
163
|
-
</button>
|
|
164
|
-
);
|
|
165
|
-
})}
|
|
166
|
-
</div>
|
|
167
|
-
|
|
137
|
+
{/* 右侧内容 */}
|
|
138
|
+
<div className="flex min-w-0 flex-1 flex-col">
|
|
139
|
+
<div className="flex items-center gap-2 px-6 py-3.5">
|
|
140
|
+
<div className="flex-1" />
|
|
141
|
+
<button className="btn-outline" style={{ height: 32, fontSize: 13 }} onClick={() => window.open("/api/config", "_blank")}>
|
|
142
|
+
<IconDownloadOutline16 size={14} /> {t.openConfigFile}
|
|
143
|
+
</button>
|
|
144
|
+
<button className="icon-btn" onClick={onClose}>
|
|
145
|
+
<IconCloseOutline14 size={15} />
|
|
146
|
+
</button>
|
|
168
147
|
</div>
|
|
169
|
-
|
|
170
|
-
{/* Right Configuration Content Area (9 cols) */}
|
|
171
|
-
<div className="col-span-12 md:col-span-9 min-h-0 flex-1 overflow-y-auto pr-2">
|
|
148
|
+
<div className="min-h-0 flex-1 overflow-y-auto px-6 py-5">
|
|
172
149
|
{section === "general" && <GeneralSection lang={lang} setLang={setLang} />}
|
|
173
150
|
{section === "models" && <ModelsSection />}
|
|
174
151
|
{section === "skills" && <SkillsSection cwd={cwd} onOpenFileContent={onOpenFileContent} />}
|
|
@@ -184,16 +161,6 @@ export function SettingsPanel({
|
|
|
184
161
|
|
|
185
162
|
// ---------- General ----------
|
|
186
163
|
|
|
187
|
-
const THEME_OPTIONS: Array<{ id: PebrelTheme; labelKey: string; nameZh: string; nameEn: string; light: string; dark: string; accent: string }> = [
|
|
188
|
-
{ id: "piweb", labelKey: "themePiWeb", nameZh: "PiWeb · 工作台", nameEn: "PiWeb · Workbench", light: "#34D399", dark: "#A7F3D0", accent: "#34D399" },
|
|
189
|
-
{ id: "ocean-ink", labelKey: "themeOceanInk", nameZh: "海与墨", nameEn: "Ocean Ink", light: "#38BDF8", dark: "#0284C7", accent: "#38BDF8" },
|
|
190
|
-
{ id: "silver-steel", labelKey: "themeSilverSteel", nameZh: "银钢", nameEn: "Silver Steel", light: "#E2E8F0", dark: "#475569", accent: "#94A3B8" },
|
|
191
|
-
{ id: "limestone-coal", labelKey: "themeLimestoneCoal", nameZh: "石灰与煤炭", nameEn: "Limestone & Coal", light: "#FB923C", dark: "#1E293B", accent: "#FB923C" },
|
|
192
|
-
{ id: "linen-moss", labelKey: "themeLinenMoss", nameZh: "亚麻与苔绿", nameEn: "Linen & Moss", light: "#FFDCC5", dark: "#006C4B", accent: "#006C4B" },
|
|
193
|
-
{ id: "nord-paper", labelKey: "themeNordPaper", nameZh: "北欧纸墨", nameEn: "Nordic Paper", light: "#F6FAFE", dark: "#00668A", accent: "#00668A" },
|
|
194
|
-
{ id: "pebrel", labelKey: "themePebrel", nameZh: "Pebrel", nameEn: "Pebrel", light: "#818CF8", dark: "#EEF2FF", accent: "#818CF8" },
|
|
195
|
-
];
|
|
196
|
-
|
|
197
164
|
function GeneralSection({
|
|
198
165
|
lang,
|
|
199
166
|
setLang,
|
|
@@ -202,7 +169,7 @@ function GeneralSection({
|
|
|
202
169
|
setLang: (l: "zh" | "en") => void;
|
|
203
170
|
}) {
|
|
204
171
|
const { t } = useI18n();
|
|
205
|
-
const [theme, setTheme] = useState<PebrelTheme>("
|
|
172
|
+
const [theme, setTheme] = useState<PebrelTheme>("dsh");
|
|
206
173
|
const [themeMode, setThemeMode] = useState<ThemeMode>("system");
|
|
207
174
|
const [fontSize, setFontSize] = useState(14);
|
|
208
175
|
const [chatFontSize, setChatFontSize] = useState(14);
|
|
@@ -212,8 +179,6 @@ function GeneralSection({
|
|
|
212
179
|
const [piSettings, setPiSettings] = useState<{ compaction: { enabled: boolean; reserveTokens: number; keepRecentTokens: number }; retry: { enabled: boolean; maxRetries: number; baseDelayMs: number } } | null>(null);
|
|
213
180
|
const [versions, setVersions] = useState<{ piWeb: string; piEngine: string } | null>(null);
|
|
214
181
|
const [webAuth, setWebAuth] = useState<{ enabled: boolean; authenticated: boolean } | null>(null);
|
|
215
|
-
const [logoutPending, setLogoutPending] = useState(false);
|
|
216
|
-
const [logoutError, setLogoutError] = useState(false);
|
|
217
182
|
const [settingsLoading, setSettingsLoading] = useState(true);
|
|
218
183
|
const [settingsSaving, setSettingsSaving] = useState(false);
|
|
219
184
|
const [settingsError, setSettingsError] = useState<"load" | "save" | null>(null);
|
|
@@ -368,17 +333,14 @@ function GeneralSection({
|
|
|
368
333
|
}
|
|
369
334
|
};
|
|
370
335
|
|
|
371
|
-
const themes =
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
})),
|
|
380
|
-
[lang, t],
|
|
381
|
-
);
|
|
336
|
+
const themes: { id: PebrelTheme; label: string; light: string; dark: string; accent: string }[] = [
|
|
337
|
+
{ id: "dsh", label: t.themeDsh, light: "#F9FAFB", dark: "#151517", accent: "#4176E6" },
|
|
338
|
+
{ id: "silver-steel", label: t.themeSilverSteel, light: "#F3F4F6", dark: "#1A1C24", accent: "#94A3B8" },
|
|
339
|
+
{ id: "limestone-coal", label: t.themeLimestoneCoal, light: "#F0EFEB", dark: "#171717", accent: "#CEB27E" },
|
|
340
|
+
{ id: "linen-moss", label: t.themeLinenMoss, light: "#F2F2EC", dark: "#1E211E", accent: "#A3B3A3" },
|
|
341
|
+
{ id: "nord-paper", label: t.themeNordPaper, light: "#FCFBF9", dark: "#2E3440", accent: "#88C0D0" },
|
|
342
|
+
{ id: "pebrel", label: t.themePebrel, light: "#F3F4F6", dark: "#0F111A", accent: "#52A8FF" },
|
|
343
|
+
];
|
|
382
344
|
|
|
383
345
|
return (
|
|
384
346
|
<div className="flex flex-col gap-6">
|
|
@@ -399,34 +361,38 @@ function GeneralSection({
|
|
|
399
361
|
/>
|
|
400
362
|
</Row>
|
|
401
363
|
<RowColumn title={t.appearance}>
|
|
402
|
-
<div className="
|
|
403
|
-
{themes.map((th) =>
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
</
|
|
424
|
-
|
|
425
|
-
|
|
364
|
+
<div className="flex w-full flex-wrap gap-2">
|
|
365
|
+
{themes.map((th) => (
|
|
366
|
+
<button
|
|
367
|
+
key={th.id}
|
|
368
|
+
className="flex flex-col items-center justify-center gap-1 transition-colors"
|
|
369
|
+
style={{
|
|
370
|
+
flex: "1 1 150px",
|
|
371
|
+
padding: "20px 32px",
|
|
372
|
+
borderRadius: 20,
|
|
373
|
+
border: `0.5px solid ${theme === th.id ? "var(--dsw-border-strong)" : "var(--dsw-border-l4)"}`,
|
|
374
|
+
background: theme === th.id ? "var(--dsw-module-platform)" : "transparent",
|
|
375
|
+
color: "var(--dsw-label-primary)",
|
|
376
|
+
fontSize: 14,
|
|
377
|
+
lineHeight: "22px",
|
|
378
|
+
}}
|
|
379
|
+
aria-pressed={theme === th.id}
|
|
380
|
+
onClick={() => pickTheme(th.id)}
|
|
381
|
+
>
|
|
382
|
+
<span className="flex items-center overflow-hidden rounded-md" style={{ border: "0.5px solid var(--dsw-border-l3)", width: 42, height: 20 }}>
|
|
383
|
+
<span style={{ background: th.light, flex: 1, height: "100%" }} />
|
|
384
|
+
<span style={{ background: th.dark, flex: 1, height: "100%" }} />
|
|
385
|
+
</span>
|
|
386
|
+
<span className="flex items-center gap-1.5">
|
|
387
|
+
<span className="inline-block rounded-full" style={{ width: 8, height: 8, background: th.accent }} />
|
|
388
|
+
{th.label}
|
|
389
|
+
</span>
|
|
390
|
+
</button>
|
|
391
|
+
))}
|
|
426
392
|
</div>
|
|
427
393
|
</RowColumn>
|
|
428
394
|
<RowColumn title={t.appearanceMode}>
|
|
429
|
-
<div className="
|
|
395
|
+
<div className="flex w-full flex-wrap gap-2">
|
|
430
396
|
{([
|
|
431
397
|
{ id: "light", label: t.themeLight },
|
|
432
398
|
{ id: "dark", label: t.themeDark },
|
|
@@ -434,12 +400,17 @@ function GeneralSection({
|
|
|
434
400
|
] as { id: ThemeMode; label: string }[]).map((mode) => (
|
|
435
401
|
<button
|
|
436
402
|
key={mode.id}
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
403
|
+
className="flex flex-col items-center justify-center gap-1 transition-colors"
|
|
404
|
+
style={{
|
|
405
|
+
flex: "1 1 120px",
|
|
406
|
+
padding: "16px 24px",
|
|
407
|
+
borderRadius: 20,
|
|
408
|
+
border: `0.5px solid ${themeMode === mode.id ? "var(--dsw-border-strong)" : "var(--dsw-border-l4)"}`,
|
|
409
|
+
background: themeMode === mode.id ? "var(--dsw-module-platform)" : "transparent",
|
|
410
|
+
color: "var(--dsw-label-primary)",
|
|
411
|
+
fontSize: 14,
|
|
412
|
+
lineHeight: "22px",
|
|
413
|
+
}}
|
|
443
414
|
aria-pressed={themeMode === mode.id}
|
|
444
415
|
onClick={() => pickThemeMode(mode.id)}
|
|
445
416
|
>
|
|
@@ -466,7 +437,7 @@ function GeneralSection({
|
|
|
466
437
|
</Row>
|
|
467
438
|
<Row title={t.autoCompact} desc={t.autoCompactDesc}>
|
|
468
439
|
<div className="flex items-center gap-2">
|
|
469
|
-
<button disabled={!piSettings || settingsSaving} className="relative h-5 w-9 flex-none rounded-full transition-colors" style={{ background: piSettings?.compaction.enabled ? "var(--dsw-accent)" : "var(--dsw-border-l3)" }} role="switch" aria-
|
|
440
|
+
<button disabled={!piSettings || settingsSaving} className="relative h-5 w-9 flex-none rounded-full transition-colors" style={{ background: piSettings?.compaction.enabled ? "var(--dsw-accent)" : "var(--dsw-border-l3)" }} role="switch" aria-checked={piSettings?.compaction.enabled ?? false} onClick={() => void patchPiSettings({ compaction: { enabled: !piSettings?.compaction.enabled } })}>
|
|
470
441
|
<span className="absolute top-0.5 h-4 w-4 rounded-full bg-white transition-all" style={{ left: piSettings?.compaction.enabled ? 18 : 2 }} />
|
|
471
442
|
</button>
|
|
472
443
|
{numberInput("compaction", "reserveTokens", t.reserveTokens)}
|
|
@@ -475,7 +446,7 @@ function GeneralSection({
|
|
|
475
446
|
</Row>
|
|
476
447
|
<Row title={t.autoRetry} desc={t.autoRetryDesc}>
|
|
477
448
|
<div className="flex items-center gap-2">
|
|
478
|
-
<button disabled={!piSettings || settingsSaving} className="relative h-5 w-9 flex-none rounded-full transition-colors" style={{ background: piSettings?.retry.enabled ? "var(--dsw-accent)" : "var(--dsw-border-l3)" }} role="switch" aria-
|
|
449
|
+
<button disabled={!piSettings || settingsSaving} className="relative h-5 w-9 flex-none rounded-full transition-colors" style={{ background: piSettings?.retry.enabled ? "var(--dsw-accent)" : "var(--dsw-border-l3)" }} role="switch" aria-checked={piSettings?.retry.enabled ?? false} onClick={() => void patchPiSettings({ retry: { enabled: !piSettings?.retry.enabled } })}>
|
|
479
450
|
<span className="absolute top-0.5 h-4 w-4 rounded-full bg-white transition-all" style={{ left: piSettings?.retry.enabled ? 18 : 2 }} />
|
|
480
451
|
</button>
|
|
481
452
|
{numberInput("retry", "maxRetries", t.maxRetries)}
|
|
@@ -508,24 +479,12 @@ function GeneralSection({
|
|
|
508
479
|
</Row>
|
|
509
480
|
{webAuth?.enabled ? (
|
|
510
481
|
<Row title={t.webAuth} desc={t.webAuthOn}>
|
|
511
|
-
{logoutError && <span role="alert" style={{ color: "var(--dsw-danger)", fontSize: 12 }}>{t.logoutFailed}</span>}
|
|
512
482
|
<button
|
|
513
483
|
className="btn-outline"
|
|
514
484
|
style={{ height: 30, padding: "0 14px", fontSize: 12.5 }}
|
|
515
|
-
disabled={logoutPending}
|
|
516
485
|
onClick={async () => {
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
setLogoutError(false);
|
|
520
|
-
try {
|
|
521
|
-
const response = await fetch("/api/web-auth", { method: "DELETE" });
|
|
522
|
-
const result = await response.json();
|
|
523
|
-
if (!response.ok || !result.success) throw new Error("logout failed");
|
|
524
|
-
window.location.href = "/login";
|
|
525
|
-
} catch {
|
|
526
|
-
setLogoutError(true);
|
|
527
|
-
setLogoutPending(false);
|
|
528
|
-
}
|
|
486
|
+
await fetch("/api/web-auth", { method: "DELETE" }).catch(() => {});
|
|
487
|
+
window.location.href = "/login";
|
|
529
488
|
}}
|
|
530
489
|
>
|
|
531
490
|
{t.logout}
|
|
@@ -736,7 +695,7 @@ function SelectOption({
|
|
|
736
695
|
|
|
737
696
|
function Row({ title, desc, children }: { title: string; desc?: string; children: React.ReactNode }) {
|
|
738
697
|
return (
|
|
739
|
-
<div className="
|
|
698
|
+
<div className="hairline-b flex items-center justify-between gap-6 pb-5">
|
|
740
699
|
<div className="min-w-0">
|
|
741
700
|
<div style={{ fontSize: 14, fontWeight: 400 }}>{title}</div>
|
|
742
701
|
{desc && (
|
|
@@ -745,7 +704,7 @@ function Row({ title, desc, children }: { title: string; desc?: string; children
|
|
|
745
704
|
</div>
|
|
746
705
|
)}
|
|
747
706
|
</div>
|
|
748
|
-
<div className="
|
|
707
|
+
<div className="flex-none">{children}</div>
|
|
749
708
|
</div>
|
|
750
709
|
);
|
|
751
710
|
}
|
|
@@ -770,10 +729,10 @@ function RowColumn({ title, desc, children }: { title: string; desc?: string; ch
|
|
|
770
729
|
/** 字号步进器:− 值 +(越界禁用;点中间数值回到 14 默认) */
|
|
771
730
|
function FontSizeStepper({ value, onChange, max }: { value: number; onChange: (v: number) => void; max: number }) {
|
|
772
731
|
return (
|
|
773
|
-
<div className="
|
|
732
|
+
<div className="flex items-center gap-2">
|
|
774
733
|
<button
|
|
775
|
-
|
|
776
|
-
|
|
734
|
+
className="icon-btn"
|
|
735
|
+
style={{ width: 26, height: 26, border: "0.5px solid var(--dsw-border-l3)", borderRadius: 8 }}
|
|
777
736
|
disabled={value <= 12}
|
|
778
737
|
onClick={() => onChange(value - 1)}
|
|
779
738
|
aria-label="decrease font size"
|
|
@@ -781,16 +740,15 @@ function FontSizeStepper({ value, onChange, max }: { value: number; onChange: (v
|
|
|
781
740
|
−
|
|
782
741
|
</button>
|
|
783
742
|
<button
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
title="reset to 14px"
|
|
743
|
+
style={{ minWidth: 48, textAlign: "center", fontSize: 13, color: value === 14 ? "var(--dsw-label-tertiary)" : "var(--dsw-label-primary)" }}
|
|
744
|
+
title="reset"
|
|
787
745
|
onClick={() => onChange(14)}
|
|
788
746
|
>
|
|
789
747
|
{value} px
|
|
790
748
|
</button>
|
|
791
749
|
<button
|
|
792
|
-
|
|
793
|
-
|
|
750
|
+
className="icon-btn"
|
|
751
|
+
style={{ width: 26, height: 26, border: "0.5px solid var(--dsw-border-l3)", borderRadius: 8 }}
|
|
794
752
|
disabled={value >= max}
|
|
795
753
|
onClick={() => onChange(value + 1)}
|
|
796
754
|
aria-label="increase font size"
|
|
@@ -1245,19 +1203,13 @@ function ModelsSection() {
|
|
|
1245
1203
|
|
|
1246
1204
|
return (
|
|
1247
1205
|
<div className="flex flex-col gap-5">
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
<div className="
|
|
1251
|
-
|
|
1206
|
+
<div>
|
|
1207
|
+
<div style={{ fontSize: 14, fontWeight: 400 }}>{t.modelsTitle}</div>
|
|
1208
|
+
<div className="mt-0.5" style={{ fontSize: 12.5, color: "var(--dsw-label-caption)" }}>
|
|
1209
|
+
{t.modelsDesc}
|
|
1252
1210
|
</div>
|
|
1253
|
-
<div className="
|
|
1254
|
-
|
|
1255
|
-
<p className="text-xs text-slate-600 dark:text-slate-300 leading-relaxed">
|
|
1256
|
-
{t.modelsDesc}
|
|
1257
|
-
</p>
|
|
1258
|
-
<p className="text-[11px] text-slate-400 dark:text-slate-400">
|
|
1259
|
-
{t.customModelIdentityNotice}
|
|
1260
|
-
</p>
|
|
1211
|
+
<div className="mt-1" style={{ fontSize: 12, color: "var(--dsw-label-caption)" }}>
|
|
1212
|
+
{t.customModelIdentityNotice}
|
|
1261
1213
|
</div>
|
|
1262
1214
|
</div>
|
|
1263
1215
|
|
|
@@ -1281,8 +1233,8 @@ function ModelsSection() {
|
|
|
1281
1233
|
const editingThis = editing === p.id;
|
|
1282
1234
|
return (
|
|
1283
1235
|
<div key={p.id}>
|
|
1284
|
-
{/*
|
|
1285
|
-
<div className="
|
|
1236
|
+
{/* 卡片:身份/操作行在上,额度显示放在同一个框内的第二段(分隔线区分) */}
|
|
1237
|
+
<div className="flex flex-col rounded-2xl px-4 py-3" style={{ border: "0.5px solid var(--dsw-border-l2)" }}>
|
|
1286
1238
|
<div className="flex items-center gap-3">
|
|
1287
1239
|
<ProviderBrand id={p.id} name={providerName(p.id)} size={34} />
|
|
1288
1240
|
<div className="min-w-0 flex-1">
|
|
@@ -1500,39 +1452,30 @@ function ModelsSection() {
|
|
|
1500
1452
|
})}
|
|
1501
1453
|
</div>
|
|
1502
1454
|
|
|
1503
|
-
{/*
|
|
1504
|
-
<div className="
|
|
1455
|
+
{/* 两个添加按钮 */}
|
|
1456
|
+
<div className="flex gap-3">
|
|
1505
1457
|
<button
|
|
1506
|
-
|
|
1507
|
-
|
|
1458
|
+
className="flex flex-1 items-center justify-center gap-2 rounded-2xl px-4 py-3.5 transition-colors"
|
|
1459
|
+
style={{ border: "0.5px solid var(--dsw-border-l3)", fontSize: 14 }}
|
|
1460
|
+
onMouseEnter={(e) => (e.currentTarget.style.background = "var(--dsw-hover)")}
|
|
1461
|
+
onMouseLeave={(e) => (e.currentTarget.style.background = "transparent")}
|
|
1508
1462
|
onClick={() => setAdding("builtin")}
|
|
1509
1463
|
>
|
|
1510
|
-
<
|
|
1511
|
-
|
|
1512
|
-
</div>
|
|
1513
|
-
<span>{t.addProvider}</span>
|
|
1464
|
+
<IconPlusOutline16 size={15} />
|
|
1465
|
+
{t.addProvider}
|
|
1514
1466
|
</button>
|
|
1515
1467
|
<button
|
|
1516
|
-
|
|
1517
|
-
|
|
1468
|
+
className="flex flex-1 items-center justify-center gap-2 rounded-2xl px-4 py-3.5 transition-colors"
|
|
1469
|
+
style={{ border: "0.5px solid var(--dsw-border-l3)", fontSize: 14 }}
|
|
1470
|
+
onMouseEnter={(e) => (e.currentTarget.style.background = "var(--dsw-hover)")}
|
|
1471
|
+
onMouseLeave={(e) => (e.currentTarget.style.background = "transparent")}
|
|
1518
1472
|
onClick={() => setAdding("custom")}
|
|
1519
1473
|
>
|
|
1520
|
-
<
|
|
1521
|
-
|
|
1522
|
-
</div>
|
|
1523
|
-
<span>{t.addCustomProvider}</span>
|
|
1474
|
+
<IconPlusOutline16 size={15} />
|
|
1475
|
+
{t.addCustomProvider}
|
|
1524
1476
|
</button>
|
|
1525
1477
|
</div>
|
|
1526
1478
|
|
|
1527
|
-
{/* 路由状态提示 */}
|
|
1528
|
-
<div className="clay-card p-4 rounded-2xl flex items-center gap-3 bg-gradient-to-r from-emerald-50/50 to-sky-50/50 dark:from-emerald-950/20 dark:to-sky-950/20 text-xs text-slate-600 dark:text-slate-300 border border-emerald-100/60 dark:border-emerald-950/40">
|
|
1529
|
-
<span className="text-emerald-500 font-bold flex items-center gap-1.5 flex-none">
|
|
1530
|
-
<span className="w-2 h-2 rounded-full bg-emerald-500 inline-block" />
|
|
1531
|
-
路由提示:
|
|
1532
|
-
</span>
|
|
1533
|
-
<span>请求使用当前选定的模型与供应商;此处未配置自动故障转移。</span>
|
|
1534
|
-
</div>
|
|
1535
|
-
|
|
1536
1479
|
{adding ? (
|
|
1537
1480
|
<ProviderSetupModal
|
|
1538
1481
|
mode={adding}
|
|
@@ -1743,115 +1686,59 @@ function ToolsSection({
|
|
|
1743
1686
|
return (
|
|
1744
1687
|
<div className="flex flex-col gap-5">
|
|
1745
1688
|
<div>
|
|
1746
|
-
<div style={{ fontSize:
|
|
1689
|
+
<div style={{ fontSize: 14, fontWeight: 400 }}>{t.toolsSection}</div>
|
|
1747
1690
|
<div className="mt-0.5" style={{ fontSize: 12.5, color: "var(--dsw-label-caption)" }}>
|
|
1748
|
-
|
|
1749
|
-
</div>
|
|
1750
|
-
</div>
|
|
1751
|
-
|
|
1752
|
-
{/* 3-tier permission modes (Screen 5) */}
|
|
1753
|
-
<div className="flex flex-col gap-2">
|
|
1754
|
-
<div className="text-xs font-medium text-slate-500">运行权限预设</div>
|
|
1755
|
-
<div className="grid grid-cols-1 sm:grid-cols-3 gap-2.5">
|
|
1756
|
-
<button
|
|
1757
|
-
type="button"
|
|
1758
|
-
className={`clay-card p-3 rounded-2xl text-left transition-all ${
|
|
1759
|
-
toolPreset === "readonly" ? "ring-2 ring-emerald-500 bg-white dark:bg-slate-800 shadow-md" : "bg-white/70 dark:bg-slate-900/60 opacity-85 hover:opacity-100"
|
|
1760
|
-
}`}
|
|
1761
|
-
onClick={() => onToolPresetChange("readonly")}
|
|
1762
|
-
>
|
|
1763
|
-
<div className="font-semibold text-xs text-slate-800 dark:text-slate-100 flex items-center justify-between">
|
|
1764
|
-
<span>{t.toolReadonly}</span>
|
|
1765
|
-
{toolPreset === "readonly" && <IconCheckOutline14 className="text-emerald-500" size={13} />}
|
|
1766
|
-
</div>
|
|
1767
|
-
<div className="text-[11px] text-slate-500 mt-1 leading-relaxed">仅读取文件与搜索,禁止修改系统或执行命令</div>
|
|
1768
|
-
</button>
|
|
1769
|
-
|
|
1770
|
-
<button
|
|
1771
|
-
type="button"
|
|
1772
|
-
className={`clay-card p-3 rounded-2xl text-left transition-all ${
|
|
1773
|
-
toolPreset === "standard" ? "ring-2 ring-emerald-500 bg-white dark:bg-slate-800 shadow-md" : "bg-white/70 dark:bg-slate-900/60 opacity-85 hover:opacity-100"
|
|
1774
|
-
}`}
|
|
1775
|
-
onClick={() => onToolPresetChange("standard")}
|
|
1776
|
-
>
|
|
1777
|
-
<div className="font-semibold text-xs text-slate-800 dark:text-slate-100 flex items-center justify-between">
|
|
1778
|
-
<span>{t.toolStandard}</span>
|
|
1779
|
-
{toolPreset === "standard" && <IconCheckOutline14 className="text-emerald-500" size={13} />}
|
|
1780
|
-
</div>
|
|
1781
|
-
<div className="text-[11px] text-slate-500 mt-1 leading-relaxed">常规开发工具集,终端执行与高危修改需确认</div>
|
|
1782
|
-
</button>
|
|
1783
|
-
|
|
1784
|
-
<button
|
|
1785
|
-
type="button"
|
|
1786
|
-
className={`clay-card p-3 rounded-2xl text-left transition-all ${
|
|
1787
|
-
toolPreset === "full" ? "ring-2 ring-emerald-500 bg-white dark:bg-slate-800 shadow-md" : "bg-white/70 dark:bg-slate-900/60 opacity-85 hover:opacity-100"
|
|
1788
|
-
}`}
|
|
1789
|
-
onClick={() => onToolPresetChange("full")}
|
|
1790
|
-
>
|
|
1791
|
-
<div className="font-semibold text-xs text-slate-800 dark:text-slate-100 flex items-center justify-between">
|
|
1792
|
-
<span>{t.toolFull}</span>
|
|
1793
|
-
{toolPreset === "full" && <IconCheckOutline14 className="text-emerald-500" size={13} />}
|
|
1794
|
-
</div>
|
|
1795
|
-
<div className="text-[11px] text-slate-500 mt-1 leading-relaxed">允许所有内置与扩展工具无障碍全自动调用</div>
|
|
1796
|
-
</button>
|
|
1691
|
+
{t.toolsSectionDesc}
|
|
1797
1692
|
</div>
|
|
1798
1693
|
</div>
|
|
1799
1694
|
|
|
1800
|
-
{
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
</
|
|
1695
|
+
<Row title={t.securityToolPreset} desc={t.securityToolPresetDesc}>
|
|
1696
|
+
<SelectOption
|
|
1697
|
+
value={toolPreset}
|
|
1698
|
+
options={[
|
|
1699
|
+
{ value: "readonly", label: t.toolReadonly },
|
|
1700
|
+
{ value: "standard", label: t.toolStandard },
|
|
1701
|
+
{ value: "full", label: t.toolFull },
|
|
1702
|
+
]}
|
|
1703
|
+
onChange={(value) => onToolPresetChange(value as ToolPreset)}
|
|
1704
|
+
/>
|
|
1705
|
+
</Row>
|
|
1811
1706
|
|
|
1812
1707
|
{all.length === 0 ? (
|
|
1813
|
-
<div className="
|
|
1708
|
+
<div className="rounded-2xl p-4" style={{ border: "0.5px dashed var(--dsw-border-l3)", fontSize: 12.5, color: "var(--dsw-label-caption)" }}>
|
|
1814
1709
|
{t.toolsNoSession}
|
|
1815
1710
|
</div>
|
|
1816
1711
|
) : (
|
|
1817
|
-
<div className="flex flex-col gap-2">
|
|
1818
|
-
<div className="flex items-center
|
|
1819
|
-
<span
|
|
1712
|
+
<div className="flex flex-col gap-2.5">
|
|
1713
|
+
<div className="flex items-center gap-2" style={{ fontSize: 12, color: "var(--dsw-label-caption)" }}>
|
|
1714
|
+
<span style={{ fontWeight: 600 }}>{t.toolsCurrentSession}</span>
|
|
1820
1715
|
<span>{t.toolsActiveCount.replace("{n}", String(active.size)).replace("{total}", String(all.length))}</span>
|
|
1821
1716
|
</div>
|
|
1822
1717
|
{all.map((tool) => {
|
|
1823
1718
|
const on = active.has(tool.name);
|
|
1824
1719
|
const desc = tool.description || describe(tool.name);
|
|
1825
1720
|
return (
|
|
1826
|
-
<div key={tool.name} className="
|
|
1827
|
-
<div className="min-w-0 flex-1
|
|
1828
|
-
<div
|
|
1829
|
-
<span className="clay-code-pill px-2.5 py-0.5 text-xs font-mono font-bold text-slate-800 dark:text-slate-200">
|
|
1830
|
-
{tool.name}
|
|
1831
|
-
</span>
|
|
1832
|
-
</div>
|
|
1721
|
+
<div key={tool.name} className="flex items-center gap-3 rounded-2xl px-4 py-2.5" style={{ border: "0.5px solid var(--dsw-border-l2)" }}>
|
|
1722
|
+
<div className="min-w-0 flex-1">
|
|
1723
|
+
<div style={{ fontSize: 13.5, fontWeight: 500, fontFamily: "var(--font-mono)" }}>{tool.name}</div>
|
|
1833
1724
|
{desc && (
|
|
1834
|
-
<div className="truncate
|
|
1725
|
+
<div className="truncate" style={{ fontSize: 11.5, color: "var(--dsw-label-caption)" }}>
|
|
1835
1726
|
{desc}
|
|
1836
1727
|
</div>
|
|
1837
1728
|
)}
|
|
1838
1729
|
</div>
|
|
1839
1730
|
<button
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
on ? "clay-btn-mint justify-end" : "bg-slate-200 dark:bg-slate-700 text-slate-400 justify-start"
|
|
1843
|
-
}`}
|
|
1731
|
+
className="relative h-5 w-9 flex-none rounded-full transition-colors"
|
|
1732
|
+
style={{ background: on ? "var(--dsw-accent)" : "var(--dsw-border-l3)" }}
|
|
1844
1733
|
role="switch"
|
|
1845
1734
|
aria-checked={on}
|
|
1846
1735
|
aria-label={tool.name}
|
|
1847
1736
|
disabled={!onSetTools || (on && active.size <= 1)}
|
|
1848
1737
|
onClick={() => toggle(tool.name)}
|
|
1849
1738
|
>
|
|
1850
|
-
<span className="
|
|
1851
|
-
{on ? "✓" : "−"}
|
|
1852
|
-
</span>
|
|
1739
|
+
<span className="absolute top-0.5 h-4 w-4 rounded-full bg-white transition-all" style={{ left: on ? 18 : 2 }} />
|
|
1853
1740
|
</button>
|
|
1854
|
-
|
|
1741
|
+
</div>
|
|
1855
1742
|
);
|
|
1856
1743
|
})}
|
|
1857
1744
|
</div>
|
|
@@ -1873,54 +1760,49 @@ interface SkillRow {
|
|
|
1873
1760
|
function SkillsSection({ cwd, onOpenFileContent }: { cwd: string; onOpenFileContent?: (path: string, content: string) => void }) {
|
|
1874
1761
|
const { t } = useI18n();
|
|
1875
1762
|
const [skills, setSkills] = useState<SkillRow[]>([]);
|
|
1876
|
-
const [error, setError] = useState<string | null>(null);
|
|
1877
|
-
const [loading, setLoading] = useState(true);
|
|
1878
|
-
const [busyFile, setBusyFile] = useState<string | null>(null);
|
|
1879
1763
|
|
|
1880
1764
|
const load = useCallback(async () => {
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
if (!r.ok || !j.success) throw new Error(j.error || t.toastError);
|
|
1886
|
-
setSkills(j.data.skills);
|
|
1887
|
-
setError(null);
|
|
1888
|
-
} catch (cause) {
|
|
1889
|
-
setError(cause instanceof Error ? cause.message : t.toastError);
|
|
1890
|
-
} finally {
|
|
1891
|
-
setLoading(false);
|
|
1892
|
-
}
|
|
1893
|
-
}, [cwd, t.toastError]);
|
|
1765
|
+
const r = await fetch(`/api/skills${cwd ? `?cwd=${encodeURIComponent(cwd)}` : ""}`);
|
|
1766
|
+
const j = await r.json();
|
|
1767
|
+
if (j.success) setSkills(j.data.skills);
|
|
1768
|
+
}, [cwd]);
|
|
1894
1769
|
|
|
1895
1770
|
useEffect(() => {
|
|
1896
1771
|
void load();
|
|
1897
1772
|
}, [load]);
|
|
1898
1773
|
|
|
1899
|
-
const
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
headers: { "Content-Type": "application/json" },
|
|
1907
|
-
body: JSON.stringify({ action: kind, filePath: s.filePath, ...(kind === "toggle" ? { disabled: !s.disabled } : {}), cwd }),
|
|
1908
|
-
});
|
|
1909
|
-
const j = await r.json();
|
|
1910
|
-
if (!r.ok || !j.success) throw new Error(j.error || t.toastError);
|
|
1911
|
-
if (kind === "read") onOpenFileContent?.(s.filePath, j.data.content);
|
|
1912
|
-
else await load();
|
|
1913
|
-
} catch (cause) {
|
|
1914
|
-
setError(cause instanceof Error ? cause.message : t.toastError);
|
|
1915
|
-
} finally {
|
|
1916
|
-
setBusyFile(null);
|
|
1917
|
-
}
|
|
1774
|
+
const toggle = async (s: SkillRow) => {
|
|
1775
|
+
const r = await fetch("/api/skills", {
|
|
1776
|
+
method: "POST",
|
|
1777
|
+
headers: { "Content-Type": "application/json" },
|
|
1778
|
+
body: JSON.stringify({ action: "toggle", filePath: s.filePath, disabled: !s.disabled, cwd }),
|
|
1779
|
+
});
|
|
1780
|
+
if ((await r.json()).success) void load();
|
|
1918
1781
|
};
|
|
1919
1782
|
|
|
1920
1783
|
// 查看技能文档:弹大窗口查看器(与项目文件查看器同款,Markdown 可切渲染/源码)
|
|
1784
|
+
const view = async (s: SkillRow) => {
|
|
1785
|
+
const r = await fetch("/api/skills", {
|
|
1786
|
+
method: "POST",
|
|
1787
|
+
headers: { "Content-Type": "application/json" },
|
|
1788
|
+
body: JSON.stringify({ action: "read", filePath: s.filePath, cwd }),
|
|
1789
|
+
});
|
|
1790
|
+
const j = await r.json();
|
|
1791
|
+
if (j.success) onOpenFileContent?.(s.filePath, j.data.content);
|
|
1792
|
+
};
|
|
1793
|
+
|
|
1794
|
+
const [deleteError, setDeleteError] = useState<string | null>(null);
|
|
1921
1795
|
const remove = async (s: SkillRow) => {
|
|
1922
1796
|
if (!window.confirm(t.confirmDeleteSkill.replace("{name}", s.name))) return;
|
|
1923
|
-
|
|
1797
|
+
setDeleteError(null);
|
|
1798
|
+
const r = await fetch("/api/skills", {
|
|
1799
|
+
method: "POST",
|
|
1800
|
+
headers: { "Content-Type": "application/json" },
|
|
1801
|
+
body: JSON.stringify({ action: "delete", filePath: s.filePath, cwd }),
|
|
1802
|
+
});
|
|
1803
|
+
const j = await r.json();
|
|
1804
|
+
if (j.success) void load();
|
|
1805
|
+
else setDeleteError(j.error || t.toastError);
|
|
1924
1806
|
};
|
|
1925
1807
|
|
|
1926
1808
|
const scopeLabel = (s: SkillRow["scope"]) => (s === "global" ? t.scopeGlobal : s === "project" ? t.scopeProject : t.scopePackage);
|
|
@@ -1928,15 +1810,13 @@ function SkillsSection({ cwd, onOpenFileContent }: { cwd: string; onOpenFileCont
|
|
|
1928
1810
|
return (
|
|
1929
1811
|
<div className="flex flex-col gap-4">
|
|
1930
1812
|
<div style={{ fontSize: 12, color: "var(--dsw-label-caption)" }}>{t.skillsDesc}</div>
|
|
1931
|
-
{
|
|
1813
|
+
{deleteError && (
|
|
1932
1814
|
<div role="alert" className="rounded-lg px-3 py-2" style={{ fontSize: 12.5, background: "var(--dsw-hover)", color: "var(--dsw-danger)" }}>
|
|
1933
|
-
{
|
|
1815
|
+
{deleteError}
|
|
1934
1816
|
</div>
|
|
1935
1817
|
)}
|
|
1936
|
-
{
|
|
1937
|
-
<div
|
|
1938
|
-
{loading ? t.skillsLoading : t.skillsEmpty}
|
|
1939
|
-
</div>
|
|
1818
|
+
{skills.length === 0 && (
|
|
1819
|
+
<div style={{ fontSize: 13, color: "var(--dsw-label-caption)" }}>—</div>
|
|
1940
1820
|
)}
|
|
1941
1821
|
<div className="flex flex-col gap-2">
|
|
1942
1822
|
{skills.map((s) => (
|
|
@@ -1959,8 +1839,7 @@ function SkillsSection({ cwd, onOpenFileContent }: { cwd: string; onOpenFileCont
|
|
|
1959
1839
|
<button
|
|
1960
1840
|
className="rounded-lg px-2.5 py-1"
|
|
1961
1841
|
style={{ fontSize: 11.5, color: "var(--dsw-label-tertiary)" }}
|
|
1962
|
-
|
|
1963
|
-
onClick={() => void action(s, "read")}
|
|
1842
|
+
onClick={() => view(s)}
|
|
1964
1843
|
>
|
|
1965
1844
|
{t.viewSkillFile}
|
|
1966
1845
|
</button>
|
|
@@ -1969,7 +1848,6 @@ function SkillsSection({ cwd, onOpenFileContent }: { cwd: string; onOpenFileCont
|
|
|
1969
1848
|
className="rounded-lg px-2.5 py-1"
|
|
1970
1849
|
style={{ fontSize: 11.5, color: "var(--dsw-danger)" }}
|
|
1971
1850
|
title={t.deleteSkillHint}
|
|
1972
|
-
disabled={busyFile !== null}
|
|
1973
1851
|
onClick={() => void remove(s)}
|
|
1974
1852
|
>
|
|
1975
1853
|
{t.delete}
|
|
@@ -1984,10 +1862,8 @@ function SkillsSection({ cwd, onOpenFileContent }: { cwd: string; onOpenFileCont
|
|
|
1984
1862
|
className="relative h-5 w-9 flex-none rounded-full transition-colors"
|
|
1985
1863
|
style={{ background: s.disabled ? "var(--dsw-border-l3)" : "var(--dsw-accent)" }}
|
|
1986
1864
|
role="switch"
|
|
1987
|
-
aria-label={s.name}
|
|
1988
1865
|
aria-checked={!s.disabled}
|
|
1989
|
-
|
|
1990
|
-
onClick={() => void action(s, "toggle")}
|
|
1866
|
+
onClick={() => toggle(s)}
|
|
1991
1867
|
>
|
|
1992
1868
|
<span
|
|
1993
1869
|
className="absolute top-0.5 h-4 w-4 rounded-full bg-white transition-all"
|
|
@@ -2017,7 +1893,6 @@ function PluginsSection({ cwd }: { cwd: string }) {
|
|
|
2017
1893
|
const [busy, setBusy] = useState(false);
|
|
2018
1894
|
const [busyKey, setBusyKey] = useState<string | null>(null);
|
|
2019
1895
|
const [toast, setToast] = useState<{ ok: boolean; msg: string; loading?: boolean } | null>(null);
|
|
2020
|
-
const [loadError, setLoadError] = useState<string | null>(null);
|
|
2021
1896
|
const [menuKey, setMenuKey] = useState<string | null>(null);
|
|
2022
1897
|
const menuRef = useRef<HTMLDivElement | null>(null);
|
|
2023
1898
|
|
|
@@ -2031,17 +1906,13 @@ function PluginsSection({ cwd }: { cwd: string }) {
|
|
|
2031
1906
|
}, []);
|
|
2032
1907
|
|
|
2033
1908
|
const load = useCallback(async () => {
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
if (!r.ok || !j.success) throw new Error(j.error || t.toastError);
|
|
1909
|
+
const r = await fetch(`/api/plugins${cwd ? `?cwd=${encodeURIComponent(cwd)}` : ""}`);
|
|
1910
|
+
const j = await r.json();
|
|
1911
|
+
if (j.success) {
|
|
2038
1912
|
setPackages(j.data.packages);
|
|
2039
1913
|
setExtensions(j.data.extensions);
|
|
2040
|
-
setLoadError(null);
|
|
2041
|
-
} catch (error) {
|
|
2042
|
-
setLoadError(error instanceof Error ? error.message : t.toastError);
|
|
2043
1914
|
}
|
|
2044
|
-
}, [cwd
|
|
1915
|
+
}, [cwd]);
|
|
2045
1916
|
|
|
2046
1917
|
useEffect(() => {
|
|
2047
1918
|
void load();
|
|
@@ -2141,11 +2012,6 @@ function PluginsSection({ cwd }: { cwd: string }) {
|
|
|
2141
2012
|
<div className="mt-1" style={{ fontSize: 13, color: "var(--dsw-label-tertiary)" }}>
|
|
2142
2013
|
{t.pluginPageDesc}
|
|
2143
2014
|
</div>
|
|
2144
|
-
{loadError && (
|
|
2145
|
-
<div role="alert" className="mt-3 rounded-xl px-3 py-2" style={{ fontSize: 12.5, color: "var(--dsw-danger)", background: "var(--dsw-hover)" }}>
|
|
2146
|
-
{loadError} <button type="button" className="underline" onClick={() => void load()}>{t.settingsRetry}</button>
|
|
2147
|
-
</div>
|
|
2148
|
-
)}
|
|
2149
2015
|
</div>
|
|
2150
2016
|
|
|
2151
2017
|
{toast && (
|
|
@@ -2226,11 +2092,8 @@ function PluginsSection({ cwd }: { cwd: string }) {
|
|
|
2226
2092
|
try {
|
|
2227
2093
|
const r = await fetch("/api/plugins", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ action: "reload", cwd }) });
|
|
2228
2094
|
const j = await r.json();
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
await load();
|
|
2232
|
-
} catch (error) {
|
|
2233
|
-
notify(false, error instanceof Error ? error.message : t.toastError);
|
|
2095
|
+
notify(j.success, j.success ? t.reloadedSessions.replace("{loaders}", String(j.data?.loaders ?? 0)).replace("{sessions}", String(j.data?.sessions ?? 0)) : j.error);
|
|
2096
|
+
if (j.success) await load();
|
|
2234
2097
|
} finally {
|
|
2235
2098
|
setBusy(false);
|
|
2236
2099
|
}
|