@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.
- package/.next/BUILD_ID +1 -1
- package/.next/app-path-routes-manifest.json +9 -6
- package/.next/build-manifest.json +3 -3
- package/.next/prerender-manifest.json +6 -6
- package/.next/react-loadable-manifest.json +16 -4
- package/.next/routes-manifest.json +20 -0
- 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 +7 -4
- package/.next/server/app/_not-found.segments/_full.segment.rsc +7 -4
- package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +5 -2
- package/.next/server/app/_not-found.segments/_tree.segment.rsc +5 -2
- 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/swarm/[id]/route.js +1 -0
- package/.next/server/app/api/swarm/[id]/route.js.nft.json +1 -0
- package/.next/server/app/api/swarm/[id]/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/swarm/route.js +1 -0
- package/.next/server/app/api/swarm/route.js.nft.json +1 -0
- package/.next/server/app/api/swarm/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/tests/route.js +2 -0
- package/.next/server/app/api/tests/route.js.nft.json +1 -0
- package/.next/server/app/api/tests/route_client-reference-manifest.js +1 -0
- 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 +9 -6
- package/.next/server/app/index.segments/__PAGE__.segment.rsc +7 -4
- package/.next/server/app/index.segments/_full.segment.rsc +9 -6
- package/.next/server/app/index.segments/_tree.segment.rsc +5 -2
- 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 +8 -5
- package/.next/server/app/login.segments/_full.segment.rsc +8 -5
- package/.next/server/app/login.segments/_tree.segment.rsc +5 -2
- package/.next/server/app/login.segments/login/__PAGE__.segment.rsc +6 -3
- 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 +9 -6
- package/.next/server/chunks/496.js +1 -1
- package/.next/server/chunks/532.js +1 -1
- package/.next/server/chunks/686.js +6 -2
- package/.next/server/chunks/757.js +1 -0
- package/.next/server/chunks/775.js +3 -0
- 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/0t1jBUAuVaTFTXxRyIo3q/_buildManifest.js +1 -0
- package/.next/static/chunks/2608.43c58236c9cce8e6.js +1 -0
- package/.next/static/chunks/{2716.a9e808190a4e2424.js → 2716.4ec42df1d51354c9.js} +1 -1
- package/.next/static/chunks/4632.62139b565b657aac.js +1 -0
- package/.next/static/chunks/{4673.d3d0a7f2599080fb.js → 4673.7796104395deb8d7.js} +1 -1
- package/.next/static/chunks/4686-17f21a006739a596.js +1 -0
- package/.next/static/chunks/7577.10af9bb661652da8.js +1 -0
- package/.next/static/chunks/8866.38ec4ac4ee5a1b74.js +1 -0
- package/.next/static/chunks/app/_global-error/page-5148173b3f55e415.js +1 -0
- package/.next/static/chunks/app/api/agent/[id]/events/route-5148173b3f55e415.js +1 -0
- package/.next/static/chunks/app/api/agent/[id]/route-5148173b3f55e415.js +1 -0
- package/.next/static/chunks/app/api/agent/[id]/tree/route-5148173b3f55e415.js +1 -0
- package/.next/static/chunks/app/api/agent/new/route-5148173b3f55e415.js +1 -0
- package/.next/static/chunks/app/api/config/route-5148173b3f55e415.js +1 -0
- package/.next/static/chunks/app/api/files/route-5148173b3f55e415.js +1 -0
- package/.next/static/chunks/app/api/git/route-5148173b3f55e415.js +1 -0
- package/.next/static/chunks/app/api/growth/route-5148173b3f55e415.js +1 -0
- package/.next/static/chunks/app/api/health/route-5148173b3f55e415.js +1 -0
- package/.next/static/chunks/app/api/models/route-5148173b3f55e415.js +1 -0
- package/.next/static/chunks/app/api/pi-settings/route-5148173b3f55e415.js +1 -0
- package/.next/static/chunks/app/api/plugins/route-5148173b3f55e415.js +1 -0
- package/.next/static/chunks/app/api/prompts/route-5148173b3f55e415.js +1 -0
- package/.next/static/chunks/app/api/security/route-5148173b3f55e415.js +1 -0
- package/.next/static/chunks/app/api/sessions/[id]/export/route-5148173b3f55e415.js +1 -0
- package/.next/static/chunks/app/api/sessions/[id]/route-5148173b3f55e415.js +1 -0
- package/.next/static/chunks/app/api/sessions/import/route-5148173b3f55e415.js +1 -0
- package/.next/static/chunks/app/api/sessions/route-5148173b3f55e415.js +1 -0
- package/.next/static/chunks/app/api/skills/route-5148173b3f55e415.js +1 -0
- package/.next/static/chunks/app/api/swarm/[id]/route-5148173b3f55e415.js +1 -0
- package/.next/static/chunks/app/api/swarm/route-5148173b3f55e415.js +1 -0
- package/.next/static/chunks/app/api/tests/route-5148173b3f55e415.js +1 -0
- package/.next/static/chunks/app/api/update/route-5148173b3f55e415.js +1 -0
- package/.next/static/chunks/app/api/version/route-5148173b3f55e415.js +1 -0
- package/.next/static/chunks/app/api/web-auth/route-5148173b3f55e415.js +1 -0
- package/.next/static/chunks/app/api/workspaces/route-5148173b3f55e415.js +1 -0
- package/.next/static/chunks/app/page-14379b8060d5cf07.js +1 -0
- package/.next/static/chunks/next/dist/client/components/builtin/app-error-5148173b3f55e415.js +1 -0
- package/.next/static/chunks/next/dist/client/components/builtin/forbidden-5148173b3f55e415.js +1 -0
- package/.next/static/chunks/next/dist/client/components/builtin/not-found-5148173b3f55e415.js +1 -0
- package/.next/static/chunks/next/dist/client/components/builtin/unauthorized-5148173b3f55e415.js +1 -0
- package/.next/static/chunks/webpack-d1a69ad9edb38b11.js +1 -0
- package/.next/static/css/d0a0162cb481abe8.css +1 -0
- package/.next/types/app/api/swarm/[id]/route.ts +353 -0
- package/.next/types/app/api/swarm/route.ts +353 -0
- package/.next/types/app/api/tests/route.ts +353 -0
- package/.next/types/routes.d.ts +4 -1
- package/.next/types/validator.ts +27 -0
- package/README.md +10 -0
- package/package.json +4 -3
- package/scripts/pick-folder.ps1 +11 -2
- package/scripts/release.mjs +11 -31
- package/scripts/run-test-file.mjs +34 -0
- package/scripts/verify-release-config.mjs +24 -0
- package/src/app/api/config/route.ts +4 -1
- package/src/app/api/security/route.ts +8 -3
- package/src/app/api/swarm/[id]/route.ts +28 -0
- package/src/app/api/swarm/route.ts +24 -0
- package/src/app/api/tests/route.ts +27 -0
- package/src/app/api/update/route.ts +8 -2
- package/src/app/globals.css +777 -0
- package/src/app/layout.tsx +13 -6
- package/src/components/AppShell.tsx +505 -199
- package/src/components/ChatInput.tsx +153 -26
- package/src/components/ChatWindow.tsx +46 -40
- package/src/components/ContextMeter.tsx +11 -17
- package/src/components/FileViewer.tsx +1 -1
- package/src/components/GitPanel.tsx +13 -11
- package/src/components/ModelSelector.tsx +19 -6
- package/src/components/PiMark.tsx +5 -38
- package/src/components/ProjectPanel.tsx +1 -0
- package/src/components/PromptPanel.tsx +132 -51
- package/src/components/SessionImportSection.tsx +17 -17
- package/src/components/SessionSidebar.tsx +159 -57
- package/src/components/SettingsPanel.tsx +330 -193
- package/src/components/SwarmCoordinatorPod.tsx +1021 -0
- package/src/components/TestInspectorPod.tsx +549 -0
- package/src/components/icons.tsx +23 -0
- package/src/hooks/usePiWeb.ts +112 -8
- package/src/i18n.tsx +59 -3
- package/src/lib/agent-manager.ts +180 -12
- package/src/lib/command-validation.ts +9 -1
- package/src/lib/delegation.ts +93 -0
- package/src/lib/managed-niubash.ts +8 -4
- package/src/lib/pi.ts +3 -2
- package/src/lib/runtime-activity.ts +27 -0
- package/src/lib/session-import/index.ts +5 -0
- package/src/lib/session-import/lock.ts +25 -0
- package/src/lib/session-import/writer.ts +8 -3
- package/src/lib/skills-service.ts +16 -9
- package/src/lib/swarm-service.ts +371 -0
- package/src/lib/test-run-service.ts +122 -0
- package/src/lib/theme.ts +68 -1
- package/src/lib/trajectory.ts +1 -0
- package/src/lib/types.ts +14 -0
- package/src/lib/workflow.ts +55 -0
- package/.next/static/DdBwPYoHBKtaJ97QIr6Co/_buildManifest.js +0 -1
- package/.next/static/chunks/2608.ec8ff1f5767c9056.js +0 -1
- package/.next/static/chunks/4632.0ed92ae42905ce40.js +0 -1
- package/.next/static/chunks/4686-7d694def920ff093.js +0 -1
- package/.next/static/chunks/app/_global-error/page-f77e9ede9808b18e.js +0 -1
- package/.next/static/chunks/app/api/agent/[id]/events/route-f77e9ede9808b18e.js +0 -1
- package/.next/static/chunks/app/api/agent/[id]/route-f77e9ede9808b18e.js +0 -1
- package/.next/static/chunks/app/api/agent/[id]/tree/route-f77e9ede9808b18e.js +0 -1
- package/.next/static/chunks/app/api/agent/new/route-f77e9ede9808b18e.js +0 -1
- package/.next/static/chunks/app/api/config/route-f77e9ede9808b18e.js +0 -1
- package/.next/static/chunks/app/api/files/route-f77e9ede9808b18e.js +0 -1
- package/.next/static/chunks/app/api/git/route-f77e9ede9808b18e.js +0 -1
- package/.next/static/chunks/app/api/growth/route-f77e9ede9808b18e.js +0 -1
- package/.next/static/chunks/app/api/health/route-f77e9ede9808b18e.js +0 -1
- package/.next/static/chunks/app/api/models/route-f77e9ede9808b18e.js +0 -1
- package/.next/static/chunks/app/api/pi-settings/route-f77e9ede9808b18e.js +0 -1
- package/.next/static/chunks/app/api/plugins/route-f77e9ede9808b18e.js +0 -1
- package/.next/static/chunks/app/api/prompts/route-f77e9ede9808b18e.js +0 -1
- package/.next/static/chunks/app/api/security/route-f77e9ede9808b18e.js +0 -1
- package/.next/static/chunks/app/api/sessions/[id]/export/route-f77e9ede9808b18e.js +0 -1
- package/.next/static/chunks/app/api/sessions/[id]/route-f77e9ede9808b18e.js +0 -1
- package/.next/static/chunks/app/api/sessions/import/route-f77e9ede9808b18e.js +0 -1
- package/.next/static/chunks/app/api/sessions/route-f77e9ede9808b18e.js +0 -1
- package/.next/static/chunks/app/api/skills/route-f77e9ede9808b18e.js +0 -1
- package/.next/static/chunks/app/api/update/route-f77e9ede9808b18e.js +0 -1
- package/.next/static/chunks/app/api/version/route-f77e9ede9808b18e.js +0 -1
- package/.next/static/chunks/app/api/web-auth/route-f77e9ede9808b18e.js +0 -1
- package/.next/static/chunks/app/api/workspaces/route-f77e9ede9808b18e.js +0 -1
- package/.next/static/chunks/app/page-9a5440047c5a415c.js +0 -1
- package/.next/static/chunks/next/dist/client/components/builtin/app-error-f77e9ede9808b18e.js +0 -1
- package/.next/static/chunks/next/dist/client/components/builtin/forbidden-f77e9ede9808b18e.js +0 -1
- package/.next/static/chunks/next/dist/client/components/builtin/not-found-f77e9ede9808b18e.js +0 -1
- package/.next/static/chunks/next/dist/client/components/builtin/unauthorized-f77e9ede9808b18e.js +0 -1
- package/.next/static/chunks/webpack-42c5aae79b0e5d8c.js +0 -1
- package/.next/static/css/b2d35abc828d4713.css +0 -1
- /package/.next/static/{DdBwPYoHBKtaJ97QIr6Co → 0t1jBUAuVaTFTXxRyIo3q}/_ssgManifest.js +0 -0
|
@@ -34,46 +34,13 @@ export function PiMark({
|
|
|
34
34
|
/** 品牌簇:对齐 dsh 品牌模板 [Logo] [Name] [BADGE] */
|
|
35
35
|
export function BrandPi({ compact = false }: { compact?: boolean }) {
|
|
36
36
|
if (compact) {
|
|
37
|
-
return <PiMark size={18} />;
|
|
37
|
+
return <PiMark size={18} style={{ color: "var(--dsw-accent)" }} />;
|
|
38
38
|
}
|
|
39
39
|
return (
|
|
40
|
-
<div
|
|
41
|
-
className="
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
gap: 6,
|
|
45
|
-
}}
|
|
46
|
-
>
|
|
47
|
-
<PiMark size={17} />
|
|
48
|
-
<span
|
|
49
|
-
className="inline-flex items-center"
|
|
50
|
-
style={{
|
|
51
|
-
fontSize: 16.5,
|
|
52
|
-
fontWeight: 600,
|
|
53
|
-
letterSpacing: "-0.02em",
|
|
54
|
-
color: "var(--dsw-label-primary)",
|
|
55
|
-
lineHeight: 1,
|
|
56
|
-
}}
|
|
57
|
-
>
|
|
58
|
-
pi
|
|
59
|
-
</span>
|
|
60
|
-
<span
|
|
61
|
-
className="inline-flex items-center justify-center font-bold"
|
|
62
|
-
style={{
|
|
63
|
-
fontSize: 9.5,
|
|
64
|
-
letterSpacing: "0.06em",
|
|
65
|
-
height: 15,
|
|
66
|
-
paddingLeft: 4.5,
|
|
67
|
-
paddingRight: 4.5,
|
|
68
|
-
borderRadius: 4,
|
|
69
|
-
background: "var(--dsw-label-primary)",
|
|
70
|
-
color: "var(--dsw-bg-base)",
|
|
71
|
-
lineHeight: 1,
|
|
72
|
-
textTransform: "uppercase",
|
|
73
|
-
}}
|
|
74
|
-
>
|
|
75
|
-
HARNESS
|
|
76
|
-
</span>
|
|
40
|
+
<div className="pw-brand select-none">
|
|
41
|
+
<span className="pw-brand-symbol"><PiMark size={18} style={{ color: "var(--dsw-accent)" }} /></span>
|
|
42
|
+
<span className="pw-brand-name">piweb</span>
|
|
43
|
+
<span className="pw-brand-meta">CODE WORKBENCH</span>
|
|
77
44
|
</div>
|
|
78
45
|
);
|
|
79
46
|
}
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* 技能 / 提示模板,含扩展与 npm 包提供的那些);热会话额外给出"组装后的系统提示词"
|
|
7
7
|
* 与每个工具的定义,它们不是文件、没有路径,直接在本面板内展开查看。
|
|
8
8
|
*/
|
|
9
|
-
import { useCallback, useEffect, useState } from "react";
|
|
9
|
+
import { useCallback, useEffect, useRef, useState } from "react";
|
|
10
10
|
import { useI18n } from "@/i18n";
|
|
11
11
|
import { IconRefreshOutline14, IconCloseOutline14 } from "@/components/icons";
|
|
12
12
|
|
|
@@ -67,30 +67,46 @@ export function PromptPanel({
|
|
|
67
67
|
onClose: () => void;
|
|
68
68
|
}) {
|
|
69
69
|
const { t } = useI18n();
|
|
70
|
-
const [
|
|
70
|
+
const owner = JSON.stringify([cwd, sessionId]);
|
|
71
|
+
const [catalog, setCatalog] = useState<{ owner: string; data: PromptSourcesData } | null>(null);
|
|
72
|
+
const data = catalog?.owner === owner ? catalog.data : null;
|
|
71
73
|
const [error, setError] = useState<string | null>(null);
|
|
72
74
|
const [busy, setBusy] = useState(false);
|
|
73
75
|
const [opening, setOpening] = useState<string | null>(null);
|
|
76
|
+
const listRequest = useRef<AbortController | null>(null);
|
|
77
|
+
const readRequest = useRef<AbortController | null>(null);
|
|
74
78
|
|
|
75
79
|
const load = useCallback(async () => {
|
|
80
|
+
listRequest.current?.abort();
|
|
81
|
+
readRequest.current?.abort();
|
|
82
|
+
const controller = new AbortController();
|
|
83
|
+
listRequest.current = controller;
|
|
84
|
+
const current = () => listRequest.current === controller && !controller.signal.aborted;
|
|
85
|
+
setCatalog(null);
|
|
86
|
+
setOpening(null);
|
|
76
87
|
setBusy(true);
|
|
77
88
|
setError(null);
|
|
78
89
|
try {
|
|
79
90
|
const query = new URLSearchParams({ cwd });
|
|
80
91
|
if (sessionId) query.set("session", sessionId);
|
|
81
|
-
const response = await fetch(`/api/prompts?${query.toString()}
|
|
92
|
+
const response = await fetch(`/api/prompts?${query.toString()}`, { signal: controller.signal });
|
|
82
93
|
const body = await response.json();
|
|
94
|
+
if (!current()) return;
|
|
83
95
|
if (!response.ok || !body.success) throw new Error(body.error || `request failed (${response.status})`);
|
|
84
|
-
|
|
96
|
+
setCatalog({ owner, data: body.data as PromptSourcesData });
|
|
85
97
|
} catch (err) {
|
|
86
|
-
setError(err instanceof Error ? err.message : String(err));
|
|
98
|
+
if (current()) setError(err instanceof Error ? err.message : String(err));
|
|
87
99
|
} finally {
|
|
88
|
-
setBusy(false);
|
|
100
|
+
if (current()) setBusy(false);
|
|
89
101
|
}
|
|
90
|
-
}, [cwd, sessionId]);
|
|
102
|
+
}, [cwd, sessionId, owner]);
|
|
91
103
|
|
|
92
104
|
useEffect(() => {
|
|
93
105
|
void load();
|
|
106
|
+
return () => {
|
|
107
|
+
listRequest.current?.abort();
|
|
108
|
+
readRequest.current?.abort();
|
|
109
|
+
};
|
|
94
110
|
}, [load, refreshKey]);
|
|
95
111
|
|
|
96
112
|
const originLabel = (origin: PromptSourceOrigin): string => {
|
|
@@ -115,21 +131,28 @@ export function PromptPanel({
|
|
|
115
131
|
/** 文件类:按需读内容,交给外部查看器 */
|
|
116
132
|
const openFile = async (source: PromptSource) => {
|
|
117
133
|
if (!source.path || !onOpenContent) return;
|
|
134
|
+
readRequest.current?.abort();
|
|
135
|
+
const controller = new AbortController();
|
|
136
|
+
const catalogRequest = listRequest.current;
|
|
137
|
+
readRequest.current = controller;
|
|
138
|
+
const current = () => readRequest.current === controller && !controller.signal.aborted && listRequest.current === catalogRequest;
|
|
118
139
|
setOpening(source.path);
|
|
119
140
|
setError(null);
|
|
120
141
|
try {
|
|
121
142
|
const response = await fetch("/api/prompts", {
|
|
122
143
|
method: "POST",
|
|
144
|
+
signal: controller.signal,
|
|
123
145
|
headers: { "Content-Type": "application/json" },
|
|
124
146
|
body: JSON.stringify({ action: "read", filePath: source.path, cwd }),
|
|
125
147
|
});
|
|
126
148
|
const body = await response.json();
|
|
149
|
+
if (!current()) return;
|
|
127
150
|
if (!response.ok || !body.success) throw new Error(body.error || "read failed");
|
|
128
151
|
onOpenContent(source.path, body.data.content as string);
|
|
129
152
|
} catch (err) {
|
|
130
|
-
setError(err instanceof Error ? err.message : String(err));
|
|
153
|
+
if (current()) setError(err instanceof Error ? err.message : String(err));
|
|
131
154
|
} finally {
|
|
132
|
-
setOpening(null);
|
|
155
|
+
if (current()) setOpening(null);
|
|
133
156
|
}
|
|
134
157
|
};
|
|
135
158
|
|
|
@@ -138,50 +161,94 @@ export function PromptPanel({
|
|
|
138
161
|
);
|
|
139
162
|
const hasTools = (data?.tools?.length ?? 0) > 0;
|
|
140
163
|
|
|
164
|
+
const fileExtBadge = (name: string): string => {
|
|
165
|
+
const ext = name.split(".").pop()?.toUpperCase() ?? "DOC";
|
|
166
|
+
return ext.length <= 4 ? ext : "DOC";
|
|
167
|
+
};
|
|
168
|
+
|
|
141
169
|
return (
|
|
142
|
-
<div className="flex min-h-0 flex-1 flex-col">
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
<
|
|
146
|
-
<
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
170
|
+
<div className="flex min-h-0 flex-1 flex-col p-2 select-none">
|
|
171
|
+
{/* Right Header (Screen 2 Claymorphism) */}
|
|
172
|
+
<div className="flex items-center justify-between pb-3 mb-3 border-b border-slate-200 dark:border-slate-800 px-2">
|
|
173
|
+
<div className="flex items-center gap-2">
|
|
174
|
+
<div className="w-2.5 h-2.5 rounded-full bg-sky-500 animate-pulse" />
|
|
175
|
+
<h2 className="text-sm font-bold text-slate-900 dark:text-slate-100 tracking-tight">{t.promptPanel}</h2>
|
|
176
|
+
</div>
|
|
177
|
+
<div className="flex items-center gap-1.5 text-slate-500">
|
|
178
|
+
<button
|
|
179
|
+
type="button"
|
|
180
|
+
className="w-7 h-7 clay-btn bg-white dark:bg-slate-800 text-slate-700 hover:text-slate-950 dark:text-slate-300 dark:hover:text-white transition-colors shadow-sm"
|
|
181
|
+
title={t.promptRefresh}
|
|
182
|
+
aria-label={t.promptRefresh}
|
|
183
|
+
onClick={() => void load()}
|
|
184
|
+
>
|
|
185
|
+
<span className={busy ? "piweb-spin" : undefined} style={{ display: "inline-flex" }}>
|
|
186
|
+
<IconRefreshOutline14 size={13} />
|
|
187
|
+
</span>
|
|
188
|
+
</button>
|
|
189
|
+
<button
|
|
190
|
+
type="button"
|
|
191
|
+
className="w-7 h-7 clay-btn bg-white dark:bg-slate-800 text-slate-700 hover:text-rose-600 dark:text-slate-300 transition-colors shadow-sm"
|
|
192
|
+
title={t.close}
|
|
193
|
+
aria-label={t.close}
|
|
194
|
+
onClick={onClose}
|
|
195
|
+
>
|
|
196
|
+
<IconCloseOutline14 size={13} />
|
|
197
|
+
</button>
|
|
198
|
+
</div>
|
|
153
199
|
</div>
|
|
154
|
-
|
|
200
|
+
|
|
201
|
+
<div className="min-h-0 flex-1 overflow-y-auto px-1 space-y-4 text-xs">
|
|
155
202
|
{error && (
|
|
156
|
-
<div className="
|
|
203
|
+
<div className="rounded-xl px-3 py-2 bg-rose-50 text-rose-700 dark:bg-rose-950/40 dark:text-rose-300 font-semibold" role="alert">
|
|
157
204
|
{error}
|
|
158
205
|
</div>
|
|
159
206
|
)}
|
|
160
|
-
|
|
207
|
+
|
|
208
|
+
{!data && busy && <div className="text-slate-500 py-4 text-center">{t.promptLoading}</div>}
|
|
209
|
+
|
|
161
210
|
{data && !data.sessionReady && (
|
|
162
|
-
<div className="
|
|
211
|
+
<div className="rounded-xl px-3 py-2.5 clay-inset text-slate-600 dark:text-slate-300 leading-relaxed text-xs" role="status">
|
|
163
212
|
{t.promptColdHint}
|
|
164
213
|
</div>
|
|
165
214
|
)}
|
|
166
215
|
|
|
167
216
|
{groups.map((group) => (
|
|
168
|
-
<section key={group.kind} className="
|
|
169
|
-
<div className="
|
|
170
|
-
|
|
217
|
+
<section key={group.kind} className="space-y-1.5">
|
|
218
|
+
<div className="flex items-center justify-between text-xs font-bold text-slate-800 dark:text-slate-200 px-1 mb-1">
|
|
219
|
+
<span>{kindLabel(group.kind)}</span>
|
|
220
|
+
<span className="text-[10px] text-slate-500 dark:text-slate-400 font-mono font-semibold">{group.items.length} 项</span>
|
|
221
|
+
</div>
|
|
222
|
+
<div className="flex flex-col gap-1.5">
|
|
171
223
|
{group.items.map((source) => (
|
|
172
224
|
<button
|
|
173
225
|
key={`${source.kind}:${source.path ?? source.name}`}
|
|
174
226
|
type="button"
|
|
175
|
-
className=
|
|
176
|
-
|
|
227
|
+
className={`clay-card p-3 flex items-center justify-between hover:scale-[1.01] transition-all cursor-pointer rounded-2xl text-left bg-white dark:bg-slate-900 shadow-sm border border-slate-200 dark:border-slate-800 ${
|
|
228
|
+
opening === source.path ? "ring-2 ring-emerald-500" : ""
|
|
229
|
+
}`}
|
|
177
230
|
title={source.path ?? source.name}
|
|
178
231
|
onClick={() => void openFile(source)}
|
|
179
232
|
>
|
|
180
|
-
<
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
233
|
+
<div className="flex items-center gap-2.5 min-w-0 flex-1 mr-2">
|
|
234
|
+
<div className="w-7 h-7 clay-inset bg-emerald-50 text-emerald-700 dark:bg-emerald-950/70 dark:text-emerald-300 flex items-center justify-center rounded-xl font-mono font-bold text-[10px] flex-none">
|
|
235
|
+
{fileExtBadge(source.name)}
|
|
236
|
+
</div>
|
|
237
|
+
<div className="min-w-0 flex-1">
|
|
238
|
+
<div className="text-xs font-bold text-slate-900 dark:text-slate-100 truncate">{source.name}</div>
|
|
239
|
+
<div className="text-[11px] text-slate-600 dark:text-slate-400 truncate">{source.path ?? kindLabel(source.kind)}</div>
|
|
240
|
+
</div>
|
|
241
|
+
</div>
|
|
242
|
+
<div className="text-right flex-none flex flex-col items-end gap-0.5">
|
|
243
|
+
{source.bytes !== undefined && (
|
|
244
|
+
<span className="text-[11px] font-mono font-bold text-slate-800 dark:text-slate-200 block">
|
|
245
|
+
{formatBytes(source.bytes)}
|
|
246
|
+
</span>
|
|
247
|
+
)}
|
|
248
|
+
<span className="text-[10px] px-2 py-0.5 rounded-full bg-slate-100 dark:bg-slate-800 text-slate-700 dark:text-slate-300 font-semibold border border-slate-200 dark:border-slate-700">
|
|
249
|
+
{originLabel(source.origin)}
|
|
250
|
+
</span>
|
|
251
|
+
</div>
|
|
185
252
|
</button>
|
|
186
253
|
))}
|
|
187
254
|
</div>
|
|
@@ -189,15 +256,17 @@ export function PromptPanel({
|
|
|
189
256
|
))}
|
|
190
257
|
|
|
191
258
|
{hasTools && (
|
|
192
|
-
<section className="
|
|
193
|
-
<div className="
|
|
194
|
-
|
|
259
|
+
<section className="space-y-1.5">
|
|
260
|
+
<div className="flex items-center justify-between text-xs font-bold text-slate-800 dark:text-slate-200 px-1 mb-1">
|
|
261
|
+
<span>{t.promptKindTool}</span>
|
|
262
|
+
<span className="text-[10px] text-slate-500 dark:text-slate-400 font-mono font-semibold">{data!.tools!.length} 个工具</span>
|
|
263
|
+
</div>
|
|
264
|
+
<div className="flex flex-col gap-1.5">
|
|
195
265
|
{data!.tools!.map((tool) => (
|
|
196
266
|
<button
|
|
197
267
|
key={`tool:${tool.name}`}
|
|
198
268
|
type="button"
|
|
199
|
-
className="
|
|
200
|
-
style={{ border: "0.5px solid var(--dsw-border-l2)" }}
|
|
269
|
+
className="clay-card p-3 flex items-center justify-between hover:scale-[1.01] transition-all cursor-pointer rounded-2xl text-left bg-white dark:bg-slate-900 shadow-sm border border-slate-200 dark:border-slate-800"
|
|
201
270
|
title={tool.name}
|
|
202
271
|
onClick={() =>
|
|
203
272
|
onOpenContent?.(
|
|
@@ -214,8 +283,16 @@ export function PromptPanel({
|
|
|
214
283
|
)
|
|
215
284
|
}
|
|
216
285
|
>
|
|
217
|
-
<
|
|
218
|
-
|
|
286
|
+
<div className="flex items-center gap-2.5 min-w-0 flex-1 mr-2">
|
|
287
|
+
<div className="w-7 h-7 clay-inset bg-sky-50 text-sky-700 dark:bg-sky-950/70 dark:text-sky-300 flex items-center justify-center rounded-xl font-mono font-bold text-[10px] flex-none">
|
|
288
|
+
TOOL
|
|
289
|
+
</div>
|
|
290
|
+
<div className="min-w-0 flex-1">
|
|
291
|
+
<div className="text-xs font-bold text-slate-900 dark:text-slate-100 truncate">{tool.name}</div>
|
|
292
|
+
<div className="text-[11px] text-slate-600 dark:text-slate-400 truncate">{tool.description || t.promptKindTool}</div>
|
|
293
|
+
</div>
|
|
294
|
+
</div>
|
|
295
|
+
<span className="text-[10px] px-2 py-0.5 rounded-full bg-slate-100 dark:bg-slate-800 text-slate-700 dark:text-slate-300 font-semibold border border-slate-200 dark:border-slate-700 flex-none">
|
|
219
296
|
{tool.source === "builtin" ? t.promptToolBuiltin : t.promptToolExtension}
|
|
220
297
|
</span>
|
|
221
298
|
</button>
|
|
@@ -225,31 +302,35 @@ export function PromptPanel({
|
|
|
225
302
|
)}
|
|
226
303
|
|
|
227
304
|
{data?.assembledSystemPrompt && (
|
|
228
|
-
<section className="
|
|
229
|
-
<div className="
|
|
305
|
+
<section className="space-y-1.5">
|
|
306
|
+
<div className="text-xs font-bold text-slate-800 dark:text-slate-200 px-1 mb-1">{t.promptKindAssembled}</div>
|
|
230
307
|
<button
|
|
231
308
|
type="button"
|
|
232
|
-
className="w-full
|
|
233
|
-
style={{ border: "0.5px solid var(--dsw-border-l2)" }}
|
|
309
|
+
className="w-full clay-card p-3 flex items-center gap-2.5 hover:scale-[1.01] transition-all cursor-pointer rounded-2xl text-left bg-white dark:bg-slate-900 shadow-sm border border-slate-200 dark:border-slate-800"
|
|
234
310
|
title={t.promptAssembledKey}
|
|
235
311
|
onClick={() => onOpenContent?.("system-prompt.md", data.assembledSystemPrompt ?? "")}
|
|
236
312
|
>
|
|
237
|
-
<
|
|
313
|
+
<div className="w-7 h-7 clay-inset bg-emerald-50 text-emerald-700 dark:bg-emerald-950/70 dark:text-emerald-300 flex items-center justify-center rounded-xl font-bold text-sm flex-none">
|
|
314
|
+
π
|
|
315
|
+
</div>
|
|
316
|
+
<span className="truncate font-bold text-slate-900 dark:text-slate-100 text-xs">{t.promptAssembledKey}</span>
|
|
238
317
|
</button>
|
|
239
318
|
</section>
|
|
240
319
|
)}
|
|
241
320
|
|
|
242
321
|
{data?.compactedSummary && (
|
|
243
|
-
<section className="
|
|
244
|
-
<div className="
|
|
322
|
+
<section className="space-y-1.5">
|
|
323
|
+
<div className="text-xs font-bold text-slate-800 dark:text-slate-200 px-1 mb-1">{t.promptKindCompacted}</div>
|
|
245
324
|
<button
|
|
246
325
|
type="button"
|
|
247
|
-
className="w-full
|
|
248
|
-
style={{ border: "0.5px solid var(--dsw-border-l2)" }}
|
|
326
|
+
className="w-full clay-card p-3 flex items-center gap-2.5 hover:scale-[1.01] transition-all cursor-pointer rounded-2xl text-left bg-white dark:bg-slate-900 shadow-sm border border-slate-200 dark:border-slate-800"
|
|
249
327
|
title={t.promptCompactedKey}
|
|
250
328
|
onClick={() => onOpenContent?.("compacted-summary.md", data.compactedSummary ?? "")}
|
|
251
329
|
>
|
|
252
|
-
<
|
|
330
|
+
<div className="w-7 h-7 clay-inset bg-amber-50 text-amber-700 dark:bg-amber-950/70 dark:text-amber-300 flex items-center justify-center rounded-xl font-bold text-xs flex-none">
|
|
331
|
+
SUM
|
|
332
|
+
</div>
|
|
333
|
+
<span className="truncate font-bold text-slate-900 dark:text-slate-100 text-xs">{t.promptCompactedKey}</span>
|
|
253
334
|
</button>
|
|
254
335
|
</section>
|
|
255
336
|
)}
|
|
@@ -305,7 +305,7 @@ export function SessionImportSection({ cwd }: { cwd: string }) {
|
|
|
305
305
|
|
|
306
306
|
{/* 来源选择器:选中哪个就只列哪个工具的会话 */}
|
|
307
307
|
{groups.length > 0 && (
|
|
308
|
-
<div className="flex flex-wrap items-center gap-1.5" role="tablist" aria-label={t.importPickSource} data-testid="import-source-tabs">
|
|
308
|
+
<div className="flex flex-wrap items-center gap-2 p-1.5 rounded-2xl bg-slate-100/80 dark:bg-slate-900/60" role="tablist" aria-label={t.importPickSource} data-testid="import-source-tabs">
|
|
309
309
|
{groups.map((group) => {
|
|
310
310
|
const active = group.source === visibleSource;
|
|
311
311
|
return (
|
|
@@ -314,18 +314,16 @@ export function SessionImportSection({ cwd }: { cwd: string }) {
|
|
|
314
314
|
type="button"
|
|
315
315
|
role="tab"
|
|
316
316
|
aria-selected={active}
|
|
317
|
-
className=
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
color: active ? "#fff" : "var(--dsw-label-tertiary)",
|
|
323
|
-
}}
|
|
317
|
+
className={`clay-btn rounded-xl px-3.5 py-1.5 transition-all text-xs font-medium flex items-center gap-1.5 ${
|
|
318
|
+
active
|
|
319
|
+
? "clay-btn-peach text-white shadow-sm font-semibold"
|
|
320
|
+
: "clay-btn-soft text-slate-600 dark:text-slate-300"
|
|
321
|
+
}`}
|
|
324
322
|
onClick={() => setActiveSource(group.source)}
|
|
325
323
|
data-testid={`import-source-${group.source}`}
|
|
326
324
|
>
|
|
327
|
-
{sourceLabel(group.source)}
|
|
328
|
-
<span
|
|
325
|
+
<span>{sourceLabel(group.source)}</span>
|
|
326
|
+
<span className={`text-[10px] px-1.5 py-0.2 rounded-full ${active ? "bg-white/30 text-white" : "bg-slate-200 dark:bg-slate-700 text-slate-500"}`}>{group.rows.length}</span>
|
|
329
327
|
</button>
|
|
330
328
|
);
|
|
331
329
|
})}
|
|
@@ -339,10 +337,10 @@ export function SessionImportSection({ cwd }: { cwd: string }) {
|
|
|
339
337
|
{t.importGroupCount.replace("{n}", String(activeGroup.rows.length)).replace("{imported}", String(activeGroup.imported))}
|
|
340
338
|
</span>
|
|
341
339
|
<div className="flex-1" />
|
|
342
|
-
<button className="rounded-lg px-2 py-
|
|
340
|
+
<button className="clay-btn clay-btn-soft rounded-lg px-2.5 py-1 text-xs" onClick={() => selectGroup(activeGroup.rows, true)}>
|
|
343
341
|
{t.importSelectAll}
|
|
344
342
|
</button>
|
|
345
|
-
<button className="rounded-lg px-2 py-
|
|
343
|
+
<button className="clay-btn clay-btn-soft rounded-lg px-2.5 py-1 text-xs text-slate-400" onClick={() => selectGroup(activeGroup.rows, false)}>
|
|
346
344
|
{t.importSelectNone}
|
|
347
345
|
</button>
|
|
348
346
|
</div>
|
|
@@ -354,20 +352,22 @@ export function SessionImportSection({ cwd }: { cwd: string }) {
|
|
|
354
352
|
return (
|
|
355
353
|
<label
|
|
356
354
|
key={rowKey}
|
|
357
|
-
className=
|
|
358
|
-
|
|
355
|
+
className={`clay-card flex cursor-pointer items-center gap-3 rounded-2xl px-3.5 py-2.5 transition-all hover:translate-x-0.5 ${
|
|
356
|
+
checked ? "ring-2 ring-emerald-500 bg-white dark:bg-slate-800" : "bg-white/70 dark:bg-slate-900/60"
|
|
357
|
+
}`}
|
|
358
|
+
style={{ opacity: done ? 0.55 : 1 }}
|
|
359
359
|
>
|
|
360
360
|
<input type="checkbox" checked={checked} disabled={done} onChange={() => toggle(row)} />
|
|
361
361
|
<div className="min-w-0 flex-1">
|
|
362
|
-
<div className="truncate
|
|
362
|
+
<div className="truncate text-xs font-medium text-slate-800 dark:text-slate-100" title={row.title}>
|
|
363
363
|
{row.title || t.importUntitled}
|
|
364
364
|
</div>
|
|
365
|
-
<div className="truncate
|
|
365
|
+
<div className="truncate text-[11px] text-slate-400" title={row.projectPath}>
|
|
366
366
|
{row.projectPath || t.importNoProject}
|
|
367
367
|
</div>
|
|
368
368
|
</div>
|
|
369
369
|
{done && (
|
|
370
|
-
<span className="rounded-full px-2 py-0.5
|
|
370
|
+
<span className="clay-badge rounded-full px-2 py-0.5 text-[10px] bg-slate-100 text-slate-400">
|
|
371
371
|
{t.importAlready}
|
|
372
372
|
</span>
|
|
373
373
|
)}
|