@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
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
8
8
|
import { PiMark } from "@/components/PiMark";
|
|
9
9
|
import { ChatInput, EMPTY_CHAT_DRAFT, type ChatDraft, type ChatDraftUpdate } from "@/components/ChatInput";
|
|
10
|
-
import { ChatWindow, SessionStatsBar } from "@/components/ChatWindow";
|
|
10
|
+
import { ChatWindow, SessionStatsBar, type RoundBadge } from "@/components/ChatWindow";
|
|
11
11
|
import { ExtensionDialogHost, ExtensionNotices } from "@/components/ExtensionUI";
|
|
12
12
|
import dynamic from "next/dynamic";
|
|
13
13
|
import { SessionSidebar } from "@/components/SessionSidebar";
|
|
@@ -23,21 +23,14 @@ const samePath = (a: string, b: string): boolean =>
|
|
|
23
23
|
const ProjectPanel = dynamic(() => import("@/components/ProjectPanel").then((m) => m.ProjectPanel), { ssr: false });
|
|
24
24
|
const FileViewer = dynamic(() => import("@/components/FileViewer").then((m) => m.FileViewer), { ssr: false });
|
|
25
25
|
const GitPanel = dynamic(() => import("@/components/GitPanel").then((m) => m.GitPanel), { ssr: false });
|
|
26
|
-
const TestInspectorPod = dynamic(() => import("@/components/TestInspectorPod").then((m) => m.TestInspectorPod), { ssr: false });
|
|
27
|
-
const SwarmCoordinatorPod = dynamic(() => import("@/components/SwarmCoordinatorPod").then((m) => m.SwarmCoordinatorPod), { ssr: false });
|
|
28
26
|
import {
|
|
29
27
|
IconAgentPresetOutline16,
|
|
30
|
-
IconArchiveOutline20,
|
|
31
|
-
IconBranchOutline16,
|
|
32
28
|
IconCheckOutline14,
|
|
33
29
|
IconChevronDown14,
|
|
34
30
|
IconFolderClose16,
|
|
35
31
|
IconFolderOpenOutline16,
|
|
36
32
|
IconPanelLeftOutline16,
|
|
37
33
|
IconProjectAddOutline16,
|
|
38
|
-
IconSettingsOutline16,
|
|
39
|
-
IconShieldOutline16,
|
|
40
|
-
IconWorkflowAgent16,
|
|
41
34
|
} from "@/components/icons";
|
|
42
35
|
import { useI18n } from "@/i18n";
|
|
43
36
|
import { usePiWeb } from "@/hooks/usePiWeb";
|
|
@@ -46,7 +39,7 @@ import { useFileViewer } from "@/hooks/useFileViewer";
|
|
|
46
39
|
import type { TreeNode } from "@/lib/growth-tree";
|
|
47
40
|
import { syncPebrelTheme } from "@/lib/theme";
|
|
48
41
|
import type { ModelChoice } from "@/components/ModelSelector";
|
|
49
|
-
import type { ImageAttachment, TrajEntry
|
|
42
|
+
import type { ImageAttachment, TrajEntry } from "@/lib/types";
|
|
50
43
|
|
|
51
44
|
// dsh ui-layout columns.ts 几何常量
|
|
52
45
|
const SIDEBAR_MIN = 264;
|
|
@@ -79,8 +72,6 @@ export function AppShell() {
|
|
|
79
72
|
currentPath,
|
|
80
73
|
state,
|
|
81
74
|
models,
|
|
82
|
-
modelLoading,
|
|
83
|
-
modelLoadError,
|
|
84
75
|
addedWorkspaces,
|
|
85
76
|
removedWorkspaces,
|
|
86
77
|
workspaceAliases,
|
|
@@ -95,11 +86,9 @@ export function AppShell() {
|
|
|
95
86
|
setGroupBy,
|
|
96
87
|
setOrderBy,
|
|
97
88
|
addWorkspaceByPicker,
|
|
98
|
-
addWorkspaceByPath,
|
|
99
89
|
removeWorkspace,
|
|
100
90
|
refreshModels,
|
|
101
91
|
sendCommand,
|
|
102
|
-
editAndResend,
|
|
103
92
|
newSession,
|
|
104
93
|
openSession,
|
|
105
94
|
closeSession,
|
|
@@ -155,8 +144,6 @@ export function AppShell() {
|
|
|
155
144
|
const [heroCwd, setHeroCwd] = useState("");
|
|
156
145
|
const [heroModel, setHeroModel] = useState<{ provider: string; id: string } | null>(null);
|
|
157
146
|
const [heroThinking, setHeroThinking] = useState("");
|
|
158
|
-
const [heroMode, setHeroMode] = useState<WorkflowMode>("agent");
|
|
159
|
-
const [activeTopView, setActiveTopView] = useState<"workbench" | "diff" | "tests" | "swarm" | "archive">("workbench");
|
|
160
147
|
const [isNarrow, setIsNarrow] = useState(false);
|
|
161
148
|
const [mobileSidebarOpen, setMobileSidebarOpen] = useState(false);
|
|
162
149
|
useEffect(() => {
|
|
@@ -370,9 +357,15 @@ export function AppShell() {
|
|
|
370
357
|
const doEditMessage = useCallback(
|
|
371
358
|
async (entryId: string, text: string) => {
|
|
372
359
|
if (!currentId) return;
|
|
373
|
-
await editAndResend
|
|
360
|
+
const done = await sendCommand({ cmd: "editAndResend", entryId, text });
|
|
361
|
+
if (!done?.success) {
|
|
362
|
+
setError(done?.error ?? t.editMessageFailed);
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
365
|
+
// 被编辑消息之后的尾部要从视图里消失(服务端已切断该分支)
|
|
366
|
+
resync();
|
|
374
367
|
},
|
|
375
|
-
[currentId,
|
|
368
|
+
[currentId, resync, sendCommand, setError, t.editMessageFailed],
|
|
376
369
|
);
|
|
377
370
|
|
|
378
371
|
const doArchiveSession = useCallback(
|
|
@@ -381,7 +374,6 @@ export function AppShell() {
|
|
|
381
374
|
await archiveSession(path);
|
|
382
375
|
} catch {
|
|
383
376
|
// usePiWeb exposes the request failure in the shared error banner.
|
|
384
|
-
return;
|
|
385
377
|
}
|
|
386
378
|
// 归档后会话立刻离开工作区列表(服务端同时让它收工)。正在看的会话被归档时
|
|
387
379
|
// 必须切走:先取同一工作区的另一个可见会话,没有就回到空态(工作区仍留在侧栏)。
|
|
@@ -440,7 +432,6 @@ export function AppShell() {
|
|
|
440
432
|
provider: effective?.provider,
|
|
441
433
|
modelId: effective?.id,
|
|
442
434
|
thinking: heroThinking || undefined,
|
|
443
|
-
mode: heroMode,
|
|
444
435
|
});
|
|
445
436
|
if (!p) return { success: false };
|
|
446
437
|
// Bind before the new-session render. In-flight ChatInput callbacks still own
|
|
@@ -530,8 +521,6 @@ export function AppShell() {
|
|
|
530
521
|
|
|
531
522
|
// 项目栏/文件查看器的工作区:当前会话的工作区优先,快照未到时回落到会话列表里的 cwd
|
|
532
523
|
const panelCwd = snapshot?.cwd || currentSession?.cwd || heroCwd || knownCwds[0] || "";
|
|
533
|
-
const activeSnapshot = currentPath && snapshot?.sessionPath && samePath(snapshot.sessionPath, currentPath) ? snapshot : null;
|
|
534
|
-
const activeWorkspaceCwd = currentPath ? (activeSnapshot?.cwd || currentSession?.cwd || "") : heroCwd;
|
|
535
524
|
|
|
536
525
|
// Git 面板自动刷新:pi 每完成一个会改动文件的工具,或一轮结束时,静默重拉
|
|
537
526
|
const mutatingDone = useMemo(
|
|
@@ -539,8 +528,6 @@ export function AppShell() {
|
|
|
539
528
|
[state.tools],
|
|
540
529
|
);
|
|
541
530
|
const [panelRefreshKey, setPanelRefreshKey] = useState(0);
|
|
542
|
-
const [gitSummary, setGitSummary] = useState<{ available: boolean; isRepo: boolean; branch: string | null; detached: boolean; files: unknown[]; statusError?: string } | null>(null);
|
|
543
|
-
const [gitSummaryError, setGitSummaryError] = useState(false);
|
|
544
531
|
const prevMutatingRef = useRef(mutatingDone);
|
|
545
532
|
const prevStreamingRef = useRef(isStreaming);
|
|
546
533
|
useEffect(() => {
|
|
@@ -553,31 +540,6 @@ export function AppShell() {
|
|
|
553
540
|
if (prevStreamingRef.current && !isStreaming) setPanelRefreshKey((k) => k + 1);
|
|
554
541
|
prevStreamingRef.current = isStreaming;
|
|
555
542
|
}, [isStreaming]);
|
|
556
|
-
useEffect(() => {
|
|
557
|
-
const controller = new AbortController();
|
|
558
|
-
setGitSummary(null);
|
|
559
|
-
setGitSummaryError(false);
|
|
560
|
-
if (!activeWorkspaceCwd) return () => controller.abort();
|
|
561
|
-
void (async () => {
|
|
562
|
-
try {
|
|
563
|
-
const response = await fetch(`/api/git?cwd=${encodeURIComponent(activeWorkspaceCwd)}`, { signal: controller.signal });
|
|
564
|
-
const body = await response.json();
|
|
565
|
-
if (!response.ok || !body.success) throw new Error(body.error || "Git 状态读取失败");
|
|
566
|
-
if (!controller.signal.aborted) setGitSummary(body.data);
|
|
567
|
-
} catch {
|
|
568
|
-
if (!controller.signal.aborted) setGitSummaryError(true);
|
|
569
|
-
}
|
|
570
|
-
})();
|
|
571
|
-
return () => controller.abort();
|
|
572
|
-
}, [activeWorkspaceCwd, panelRefreshKey]);
|
|
573
|
-
const gitBranchLabel = gitSummary?.isRepo ? (gitSummary.detached ? "HEAD" : gitSummary.branch || "—") : "—";
|
|
574
|
-
const gitStatusLabel = !activeWorkspaceCwd ? "未选择"
|
|
575
|
-
: gitSummaryError || gitSummary?.statusError ? "状态未知"
|
|
576
|
-
: !gitSummary ? "读取中"
|
|
577
|
-
: !gitSummary.available ? "Git 不可用"
|
|
578
|
-
: !gitSummary.isRepo ? "非仓库"
|
|
579
|
-
: gitSummary.files.length ? `${gitSummary.files.length} 改动` : "干净";
|
|
580
|
-
const gitCanCommit = Boolean(gitSummary?.isRepo && !gitSummary.statusError && gitSummary.files.length);
|
|
581
543
|
|
|
582
544
|
const insertIntoComposer = useCallback((text: string) => {
|
|
583
545
|
const textarea = document.querySelector<HTMLTextAreaElement>('[data-testid="composer"] textarea');
|
|
@@ -591,12 +553,12 @@ export function AppShell() {
|
|
|
591
553
|
requestAnimationFrame(() => textarea?.focus());
|
|
592
554
|
}, [draftKey, updateDraft]);
|
|
593
555
|
const openInEditor = useCallback(
|
|
594
|
-
async (filePath: string
|
|
556
|
+
async (filePath: string) => {
|
|
595
557
|
try {
|
|
596
558
|
const r = await fetch("/api/files", {
|
|
597
559
|
method: "POST",
|
|
598
560
|
headers: { "Content-Type": "application/json" },
|
|
599
|
-
body: JSON.stringify({ action: "open", cwd:
|
|
561
|
+
body: JSON.stringify({ action: "open", cwd: panelCwd, path: filePath }),
|
|
600
562
|
});
|
|
601
563
|
const j = await r.json();
|
|
602
564
|
if (!j.success) throw new Error(j.error || "failed to open file");
|
|
@@ -612,29 +574,39 @@ export function AppShell() {
|
|
|
612
574
|
useEffect(() => {
|
|
613
575
|
resetViewer();
|
|
614
576
|
}, [panelCwd, resetViewer]);
|
|
615
|
-
const growthTurnStarts = useMemo(
|
|
616
|
-
() => {
|
|
617
|
-
const persisted = state.snapshot?.userTurns ?? [];
|
|
618
|
-
const ids = new Set(persisted.map((turn) => turn.id));
|
|
619
|
-
const lastTs = persisted[persisted.length - 1]?.ts ?? -Infinity;
|
|
620
|
-
const live = state.messages
|
|
621
|
-
.filter((message) => message.role === "user" && (!message.id || !ids.has(message.id)) && (message.timestamp ?? Date.now()) >= lastTs - 1000)
|
|
622
|
-
.map((message) => message.timestamp ?? Date.now());
|
|
623
|
-
return [...persisted.map((turn) => turn.ts), ...live];
|
|
624
|
-
},
|
|
625
|
-
[state.snapshot?.userTurns, state.messages],
|
|
626
|
-
);
|
|
627
577
|
const growth = useGrowth({
|
|
628
578
|
cwd: panelCwd,
|
|
629
579
|
sessionPath: currentPath,
|
|
630
|
-
|
|
631
|
-
pending: state.growth.pending,
|
|
580
|
+
liveRounds: state.growth.rounds,
|
|
632
581
|
runtimeError: state.growth.error,
|
|
633
582
|
active: projectOpen || viewer.state.open,
|
|
583
|
+
watchRounds: Boolean(currentPath),
|
|
634
584
|
connected: state.connected,
|
|
635
|
-
turnStarts: growthTurnStarts,
|
|
636
585
|
});
|
|
637
586
|
const openFileNode = useCallback((node: TreeNode) => viewer.open(node.path, { from: node.from, lazy: node.lazy }), [viewer]);
|
|
587
|
+
const roundBadges = useMemo(() => {
|
|
588
|
+
const map = new Map<string, RoundBadge>();
|
|
589
|
+
for (const round of growth.rounds) {
|
|
590
|
+
if (round.kind !== "round" || !round.promptIds[0]) continue;
|
|
591
|
+
const st = round.stats;
|
|
592
|
+
map.set(round.promptIds[0], { commit: round.commit, files: st.added + st.modified + st.deleted + st.renamed, add: st.add, del: st.del });
|
|
593
|
+
}
|
|
594
|
+
return map;
|
|
595
|
+
}, [growth.rounds]);
|
|
596
|
+
const growthSelect = growth.select;
|
|
597
|
+
const showRound = useCallback((commit: string) => {
|
|
598
|
+
toggleProject(true);
|
|
599
|
+
growthSelect(commit);
|
|
600
|
+
}, [toggleProject, growthSelect]);
|
|
601
|
+
const jumpToChat = useCallback((messageId: string) => {
|
|
602
|
+
requestAnimationFrame(() => {
|
|
603
|
+
const node = document.querySelector<HTMLElement>(`[data-role="user"][data-message-id="${CSS.escape(messageId)}"]`);
|
|
604
|
+
if (!node) return;
|
|
605
|
+
node.scrollIntoView({ block: "center", behavior: "smooth" });
|
|
606
|
+
node.dataset.flash = "";
|
|
607
|
+
setTimeout(() => delete node.dataset.flash, 1600);
|
|
608
|
+
});
|
|
609
|
+
}, []);
|
|
638
610
|
|
|
639
611
|
useEffect(() => {
|
|
640
612
|
document.title = currentId && title && title !== "pi" ? `${title} · pi` : "pi";
|
|
@@ -660,6 +632,8 @@ export function AppShell() {
|
|
|
660
632
|
workspaceName={panelCwd ? getWorkspaceName(panelCwd) : ""}
|
|
661
633
|
cwd={panelCwd}
|
|
662
634
|
hasSession={Boolean(currentPath)}
|
|
635
|
+
running={isStreaming}
|
|
636
|
+
onJumpToChat={jumpToChat}
|
|
663
637
|
onOpenFile={openFileNode}
|
|
664
638
|
onReference={insertIntoComposer}
|
|
665
639
|
onOpenEditor={openInEditor}
|
|
@@ -673,159 +647,28 @@ export function AppShell() {
|
|
|
673
647
|
);
|
|
674
648
|
|
|
675
649
|
return (
|
|
676
|
-
<div
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
<span className="text-slate-900 dark:text-slate-100 max-w-xs truncate font-bold">
|
|
698
|
-
{currentId ? title : (heroCwd ? (getWorkspaceName ? getWorkspaceName(heroCwd) : basename(heroCwd)) : t.newChat)}
|
|
699
|
-
</span>
|
|
700
|
-
</div>
|
|
701
|
-
|
|
702
|
-
{/* Center/Nav: View Switcher Pill Tabs */}
|
|
703
|
-
<nav aria-label="视图切换" className="clay-inset flex min-w-0 items-center overflow-x-auto p-1 rounded-full bg-slate-100 dark:bg-slate-900 border border-slate-200 dark:border-slate-800 gap-1 text-xs font-semibold">
|
|
704
|
-
<button
|
|
705
|
-
type="button"
|
|
706
|
-
className={`px-3.5 py-1 rounded-full transition-all duration-150 ${
|
|
707
|
-
activeTopView === "workbench"
|
|
708
|
-
? "clay-btn-peach text-white font-bold shadow-sm"
|
|
709
|
-
: "text-slate-700 hover:text-slate-950 dark:text-slate-300 dark:hover:text-white"
|
|
710
|
-
}`}
|
|
711
|
-
onClick={() => setActiveTopView("workbench")}
|
|
712
|
-
>
|
|
713
|
-
工作台
|
|
714
|
-
</button>
|
|
715
|
-
<button
|
|
716
|
-
type="button"
|
|
717
|
-
className={`flex items-center gap-1.5 px-3.5 py-1 rounded-full transition-all duration-150 ${
|
|
718
|
-
activeTopView === "diff"
|
|
719
|
-
? "clay-btn-peach text-white font-bold shadow-sm"
|
|
720
|
-
: "text-slate-700 hover:text-slate-950 dark:text-slate-300 dark:hover:text-white"
|
|
721
|
-
}`}
|
|
722
|
-
onClick={() => { setPanelRefreshKey((key) => key + 1); setActiveTopView("diff"); }}
|
|
723
|
-
>
|
|
724
|
-
<span>差异对比</span>
|
|
725
|
-
<span className={`text-[10px] px-1.5 py-0.2 rounded-full font-bold ${
|
|
726
|
-
activeTopView === "diff" ? "bg-white/30 text-white" : "bg-emerald-100 text-emerald-800 dark:bg-emerald-950/70 dark:text-emerald-300"
|
|
727
|
-
}`}>
|
|
728
|
-
{gitSummary?.isRepo ? gitSummary.files.length : "—"}
|
|
729
|
-
</span>
|
|
730
|
-
</button>
|
|
731
|
-
<button
|
|
732
|
-
type="button"
|
|
733
|
-
className={`shrink-0 px-3.5 py-1 rounded-full transition-all duration-150 ${
|
|
734
|
-
activeTopView === "tests"
|
|
735
|
-
? "clay-btn-peach text-white font-bold shadow-sm"
|
|
736
|
-
: "text-slate-700 hover:text-slate-950 dark:text-slate-300 dark:hover:text-white"
|
|
737
|
-
}`}
|
|
738
|
-
onClick={() => setActiveTopView("tests")}
|
|
739
|
-
>
|
|
740
|
-
测试检查器
|
|
741
|
-
</button>
|
|
742
|
-
<button
|
|
743
|
-
type="button"
|
|
744
|
-
className={`flex items-center gap-1.5 px-3.5 py-1 rounded-full transition-all duration-150 ${
|
|
745
|
-
activeTopView === "swarm"
|
|
746
|
-
? "clay-btn-peach text-white font-bold shadow-sm"
|
|
747
|
-
: "text-slate-700 hover:text-slate-950 dark:text-slate-300 dark:hover:text-white"
|
|
748
|
-
}`}
|
|
749
|
-
onClick={() => setActiveTopView("swarm")}
|
|
750
|
-
>
|
|
751
|
-
<span>智能体群</span>
|
|
752
|
-
</button>
|
|
753
|
-
</nav>
|
|
754
|
-
|
|
755
|
-
{/* Right: Actions */}
|
|
756
|
-
<div className="flex items-center gap-2">
|
|
757
|
-
<button
|
|
758
|
-
type="button"
|
|
759
|
-
className="clay-badge hidden md:flex items-center gap-1.5 px-3 py-1 text-xs font-mono text-slate-800 dark:text-slate-200 bg-white dark:bg-slate-800 border border-slate-200 dark:border-slate-700 shadow-sm font-semibold hover:border-slate-300 transition-all cursor-pointer"
|
|
760
|
-
title="Git 工作区状态(点击进入差异对比)"
|
|
761
|
-
onClick={() => {
|
|
762
|
-
setPanelRefreshKey((key) => key + 1);
|
|
763
|
-
setActiveTopView("diff");
|
|
764
|
-
}}
|
|
765
|
-
>
|
|
766
|
-
<IconBranchOutline16 size={13} className="text-emerald-500" />
|
|
767
|
-
<span>{gitBranchLabel}</span>
|
|
768
|
-
<span className="text-slate-400">·</span>
|
|
769
|
-
<span className="text-amber-600 dark:text-amber-400 font-bold">{gitStatusLabel}</span>
|
|
770
|
-
</button>
|
|
771
|
-
<button
|
|
772
|
-
type="button"
|
|
773
|
-
className="w-8 h-8 clay-btn bg-white dark:bg-slate-800 text-slate-600 dark:text-slate-300 hover:text-slate-900 dark:hover:text-white transition-colors"
|
|
774
|
-
title={t.projectPanel}
|
|
775
|
-
aria-label={t.projectPanel}
|
|
776
|
-
data-testid="project-toggle"
|
|
777
|
-
onClick={() => toggleProject()}
|
|
778
|
-
>
|
|
779
|
-
<IconFolderOpenOutline16 size={15} />
|
|
780
|
-
</button>
|
|
781
|
-
<button
|
|
782
|
-
type="button"
|
|
783
|
-
className="w-8 h-8 clay-btn bg-white dark:bg-slate-800 text-slate-600 dark:text-slate-300 hover:text-slate-900 dark:hover:text-white transition-colors"
|
|
784
|
-
title={t.promptPanel}
|
|
785
|
-
aria-label={t.promptPanel}
|
|
786
|
-
data-testid="prompt-panel-toggle"
|
|
787
|
-
onClick={() => {
|
|
788
|
-
setPromptsOpen((open) => !open);
|
|
789
|
-
setSelected(null);
|
|
790
|
-
setGitDetailsOpen(false);
|
|
791
|
-
}}
|
|
792
|
-
>
|
|
793
|
-
<IconAgentPresetOutline16 size={15} />
|
|
794
|
-
</button>
|
|
795
|
-
<button
|
|
796
|
-
type="button"
|
|
797
|
-
className="w-8 h-8 clay-btn bg-white dark:bg-slate-800 text-slate-600 dark:text-slate-300 hover:text-slate-900 dark:hover:text-white transition-colors"
|
|
798
|
-
title={t.settings}
|
|
799
|
-
aria-label={t.settings}
|
|
800
|
-
data-testid="settings-toggle"
|
|
801
|
-
onClick={() => setSettingsOpen(true)}
|
|
802
|
-
>
|
|
803
|
-
<IconSettingsOutline16 size={15} />
|
|
804
|
-
</button>
|
|
805
|
-
</div>
|
|
806
|
-
</header>
|
|
807
|
-
|
|
808
|
-
{/* 三栏/四栏悬浮粘土卡片主体容器 */}
|
|
809
|
-
<main
|
|
810
|
-
className="pw-shell grid flex-1 min-h-0 overflow-hidden p-3 gap-3 bg-clay-bg"
|
|
811
|
-
style={{
|
|
812
|
-
gridTemplateColumns: gridCols,
|
|
813
|
-
transition: dragging ? "none" : "grid-template-columns var(--ds-duration-slow) var(--ds-ease-in-out)",
|
|
814
|
-
}}
|
|
650
|
+
<div
|
|
651
|
+
className="grid h-screen w-screen overflow-hidden"
|
|
652
|
+
style={{
|
|
653
|
+
gridTemplateColumns: gridCols,
|
|
654
|
+
transition: dragging ? "none" : "grid-template-columns var(--ds-duration-slow) var(--ds-ease-in-out)",
|
|
655
|
+
background: "var(--dsw-bg-base)",
|
|
656
|
+
}}
|
|
657
|
+
>
|
|
658
|
+
{/* 侧栏 */}
|
|
659
|
+
<div
|
|
660
|
+
className="min-h-0 overflow-hidden"
|
|
661
|
+
style={isNarrow ? {
|
|
662
|
+
position: "fixed",
|
|
663
|
+
inset: "0 auto 0 0",
|
|
664
|
+
width: "min(86vw, 340px)",
|
|
665
|
+
zIndex: 90,
|
|
666
|
+
background: "var(--dsw-sidebar-fill)",
|
|
667
|
+
transform: mobileSidebarOpen ? "translateX(0)" : "translateX(-105%)",
|
|
668
|
+
transition: "transform var(--ds-duration-normal) var(--ds-ease-in-out)",
|
|
669
|
+
boxShadow: mobileSidebarOpen ? "var(--dsw-elevation-prominent)" : "none",
|
|
670
|
+
} : { background: "var(--dsw-sidebar-fill)" }}
|
|
815
671
|
>
|
|
816
|
-
{/* 侧栏卡片 */}
|
|
817
|
-
<div
|
|
818
|
-
className="pw-sidebar-surface clay-surface rounded-3xl p-3 shrink-0 flex flex-col justify-between overflow-hidden shadow-md"
|
|
819
|
-
style={isNarrow ? {
|
|
820
|
-
position: "fixed",
|
|
821
|
-
inset: "0 auto 0 0",
|
|
822
|
-
width: "min(86vw, 340px)",
|
|
823
|
-
zIndex: 90,
|
|
824
|
-
transform: mobileSidebarOpen ? "translateX(0)" : "translateX(-105%)",
|
|
825
|
-
transition: "transform var(--ds-duration-normal) var(--ds-ease-in-out)",
|
|
826
|
-
boxShadow: mobileSidebarOpen ? "var(--dsw-elevation-prominent)" : "none",
|
|
827
|
-
} : undefined}
|
|
828
|
-
>
|
|
829
672
|
<SessionSidebar
|
|
830
673
|
sessions={sessions}
|
|
831
674
|
sessionListError={sessionListError}
|
|
@@ -870,104 +713,15 @@ export function AppShell() {
|
|
|
870
713
|
}}
|
|
871
714
|
onAddWorkspace={() => void addWorkspaceByPicker()}
|
|
872
715
|
draftCwd={!currentId ? heroCwd || null : null}
|
|
873
|
-
gitBranch={gitBranchLabel}
|
|
874
|
-
gitStatus={gitStatusLabel}
|
|
875
|
-
canAskCommit={gitCanCommit}
|
|
876
|
-
onAskCommit={() => { setActiveTopView("workbench"); insertIntoComposer(t.gitAskCommitPrompt); }}
|
|
877
|
-
activeTopView={activeTopView}
|
|
878
|
-
onSelectTopView={(v) => { setActiveTopView(v); setMobileSidebarOpen(false); }}
|
|
879
716
|
/>
|
|
880
717
|
</div>
|
|
881
718
|
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
<div className="clay-surface rounded-3xl p-3 shrink-0 flex flex-col overflow-hidden shadow-md">
|
|
885
|
-
{projectColumn}
|
|
886
|
-
</div>
|
|
887
|
-
) : <div aria-hidden="true" />)}
|
|
719
|
+
{/* 四列网格中始终保留项目列,否则关闭项目栏时会话区会落进 0px 列。 */}
|
|
720
|
+
{!isNarrow && (projectColumn || <div aria-hidden="true" />)}
|
|
888
721
|
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
{/* 视图内容切换 */}
|
|
894
|
-
{activeTopView === "diff" ? (
|
|
895
|
-
<div className="flex-1 min-h-0 overflow-hidden p-4 sm:p-6 bg-slate-50/50 dark:bg-slate-950/30">
|
|
896
|
-
<div className="clay-card mx-auto h-full max-w-6xl overflow-hidden rounded-3xl bg-white/85 dark:bg-slate-900/80">
|
|
897
|
-
<GitPanel cwd={activeWorkspaceCwd} refreshKey={panelRefreshKey} onClose={() => setActiveTopView("workbench")} onOpenFile={(filePath) => void openInEditor(filePath, activeWorkspaceCwd)} />
|
|
898
|
-
</div>
|
|
899
|
-
</div>
|
|
900
|
-
) : activeTopView === "tests" ? (
|
|
901
|
-
<div className="flex-1 min-h-0 overflow-y-auto p-4 sm:p-6 bg-slate-50/50 dark:bg-slate-950/30">
|
|
902
|
-
<TestInspectorPod
|
|
903
|
-
cwd={activeWorkspaceCwd}
|
|
904
|
-
onClose={() => setActiveTopView("workbench")}
|
|
905
|
-
onSwitchToWorkbench={() => setActiveTopView("workbench")}
|
|
906
|
-
onSwitchToDiff={() => setActiveTopView("diff")}
|
|
907
|
-
/>
|
|
908
|
-
</div>
|
|
909
|
-
) : activeTopView === "swarm" ? (
|
|
910
|
-
<div className="flex-1 min-h-0 overflow-hidden p-3 sm:p-4 bg-slate-50/50 dark:bg-slate-950/30">
|
|
911
|
-
<SwarmCoordinatorPod
|
|
912
|
-
cwd={activeWorkspaceCwd}
|
|
913
|
-
onClose={() => setActiveTopView("workbench")}
|
|
914
|
-
onSwitchToWorkbench={() => setActiveTopView("workbench")}
|
|
915
|
-
projectTrust={activeSnapshot?.projectTrust}
|
|
916
|
-
onWorkspaceChanged={() => setPanelRefreshKey((key) => key + 1)}
|
|
917
|
-
/>
|
|
918
|
-
</div>
|
|
919
|
-
) : activeTopView === "archive" ? (
|
|
920
|
-
<div className="flex-1 min-h-0 overflow-y-auto p-6 flex flex-col items-center bg-slate-50/40 dark:bg-slate-950/20">
|
|
921
|
-
<div className="clay-card max-w-2xl w-full p-6 rounded-3xl space-y-4">
|
|
922
|
-
<div className="flex items-center justify-between pb-3 border-b border-slate-200/60 dark:border-slate-800/60">
|
|
923
|
-
<h2 className="text-base font-bold text-slate-800 dark:text-slate-100 flex items-center gap-2">
|
|
924
|
-
<IconArchiveOutline20 size={18} className="text-slate-400" />
|
|
925
|
-
已归档会话 ({archivedSessions.length})
|
|
926
|
-
</h2>
|
|
927
|
-
<span className="text-xs text-slate-400">保留历史记录,不占用主工作台</span>
|
|
928
|
-
</div>
|
|
929
|
-
{archivedSessions.length === 0 ? (
|
|
930
|
-
<div className="text-center py-12 text-sm text-slate-400">暂无归档会话</div>
|
|
931
|
-
) : (
|
|
932
|
-
<div className="space-y-2">
|
|
933
|
-
{archivedSessions.map((session) => (
|
|
934
|
-
<div
|
|
935
|
-
key={session.path}
|
|
936
|
-
className="clay-card flex items-center justify-between p-3.5 rounded-2xl hover:scale-[1.01] transition-transform"
|
|
937
|
-
>
|
|
938
|
-
<div className="min-w-0 flex-1">
|
|
939
|
-
<div className="font-semibold text-sm text-slate-800 dark:text-slate-200 truncate">{session.name || session.firstMessage || basename(session.path)}</div>
|
|
940
|
-
<div className="text-xs text-slate-400 truncate mt-0.5">{session.cwd}</div>
|
|
941
|
-
</div>
|
|
942
|
-
<div className="flex items-center gap-2 ml-4">
|
|
943
|
-
<button
|
|
944
|
-
type="button"
|
|
945
|
-
className="clay-btn clay-btn-soft px-3 py-1.5 text-xs text-slate-700 dark:text-slate-200"
|
|
946
|
-
onClick={() => {
|
|
947
|
-
doUnarchiveSession(session.path);
|
|
948
|
-
setActiveTopView("workbench");
|
|
949
|
-
}}
|
|
950
|
-
>
|
|
951
|
-
恢复会话
|
|
952
|
-
</button>
|
|
953
|
-
<button
|
|
954
|
-
type="button"
|
|
955
|
-
className="clay-btn clay-btn-peach px-3 py-1.5 text-xs text-white"
|
|
956
|
-
onClick={() => {
|
|
957
|
-
openSession(session.path);
|
|
958
|
-
setActiveTopView("workbench");
|
|
959
|
-
}}
|
|
960
|
-
>
|
|
961
|
-
查看
|
|
962
|
-
</button>
|
|
963
|
-
</div>
|
|
964
|
-
</div>
|
|
965
|
-
))}
|
|
966
|
-
</div>
|
|
967
|
-
)}
|
|
968
|
-
</div>
|
|
969
|
-
</div>
|
|
970
|
-
) : !currentId ? (
|
|
722
|
+
{/* 会话区 */}
|
|
723
|
+
<div className="pi-main flex min-h-0 min-w-0 flex-col">
|
|
724
|
+
{!currentId ? (
|
|
971
725
|
<div className="flex min-h-0 flex-1 flex-col">
|
|
972
726
|
<Hero
|
|
973
727
|
draft={composerDrafts.current.get(heroDraftKey) ?? EMPTY_CHAT_DRAFT}
|
|
@@ -986,9 +740,6 @@ export function AppShell() {
|
|
|
986
740
|
getWorkspaceName={getWorkspaceName}
|
|
987
741
|
onSend={heroSend}
|
|
988
742
|
models={modelChoices}
|
|
989
|
-
modelLoading={modelLoading}
|
|
990
|
-
modelLoadError={modelLoadError}
|
|
991
|
-
onRetryModels={() => void refreshModels()}
|
|
992
743
|
providerNames={providerNames}
|
|
993
744
|
authByProvider={authByProvider}
|
|
994
745
|
addWorkspaceByPicker={addWorkspaceByPicker}
|
|
@@ -1000,11 +751,8 @@ export function AppShell() {
|
|
|
1000
751
|
}}
|
|
1001
752
|
defaultModel={defaultModel}
|
|
1002
753
|
heroModelLevels={heroModelLevels}
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
heroMode={heroMode}
|
|
1006
|
-
onSelectHeroMode={setHeroMode}
|
|
1007
|
-
onOpenSwarm={() => setActiveTopView("swarm")}
|
|
754
|
+
heroThinking={heroThinking}
|
|
755
|
+
onSelectHeroThinking={setHeroThinking}
|
|
1008
756
|
/>
|
|
1009
757
|
{/* 草稿阶段的错误行内显示(不再弹右下角) */}
|
|
1010
758
|
{state.error && (
|
|
@@ -1022,9 +770,43 @@ export function AppShell() {
|
|
|
1022
770
|
</div>
|
|
1023
771
|
) : (
|
|
1024
772
|
<>
|
|
1025
|
-
{/*
|
|
773
|
+
{/* 头部:标题行 + 页签行(dsh 两行式) */}
|
|
774
|
+
<div className="hairline-b px-5 pb-0 pt-3">
|
|
775
|
+
<div className="flex items-center gap-3">
|
|
776
|
+
<span className="min-w-0 flex-1 truncate" style={{ fontSize: 14.5, fontWeight: 600 }}>
|
|
777
|
+
{title}
|
|
778
|
+
</span>
|
|
779
|
+
{/* 项目生长入口;轨迹详情由对话内工具行点击唤起 */}
|
|
780
|
+
<button
|
|
781
|
+
type="button"
|
|
782
|
+
className="icon-btn"
|
|
783
|
+
style={{ width: 30, height: 30, background: projectOpen ? "var(--dsw-active)" : undefined }}
|
|
784
|
+
title={`${t.projectPanel} (Ctrl/⌘+Shift+E)`}
|
|
785
|
+
aria-label={t.projectPanel}
|
|
786
|
+
data-testid="project-toggle"
|
|
787
|
+
onClick={() => toggleProject()}
|
|
788
|
+
>
|
|
789
|
+
<IconFolderOpenOutline16 size={15} />
|
|
790
|
+
</button>
|
|
791
|
+
{/* 提示词来源:系统提示词 / 记忆 / 技能 / 模板 / 工具提示词集中列出,点开即看 */}
|
|
792
|
+
<button
|
|
793
|
+
type="button"
|
|
794
|
+
className="icon-btn"
|
|
795
|
+
style={{ width: 30, height: 30, background: promptsOpen ? "var(--dsw-active)" : undefined }}
|
|
796
|
+
title={t.promptPanel}
|
|
797
|
+
aria-label={t.promptPanel}
|
|
798
|
+
data-testid="prompt-panel-toggle"
|
|
799
|
+
onClick={() => {
|
|
800
|
+
setPromptsOpen((open) => !open);
|
|
801
|
+
setSelected(null);
|
|
802
|
+
setGitDetailsOpen(false);
|
|
803
|
+
}}
|
|
804
|
+
>
|
|
805
|
+
<IconAgentPresetOutline16 size={15} />
|
|
806
|
+
</button>
|
|
807
|
+
</div>
|
|
1026
808
|
{projectTrust?.required && !projectTrust.trusted && (
|
|
1027
|
-
<div className="
|
|
809
|
+
<div className="mt-2 flex flex-wrap items-center gap-2 rounded-xl px-3 py-2" style={{ fontSize: 12.5, background: "var(--dsw-hover)", border: "0.5px solid var(--dsw-border-l2)" }} role="status">
|
|
1028
810
|
<span style={{ color: "var(--dsw-warn)", fontWeight: 600 }}>⚠</span>
|
|
1029
811
|
<span className="min-w-0 flex-1">{projectTrust.source === "default-never" || projectTrust.source === "remembered" ? t.projectTrustBannerNever : t.projectTrustBanner}</span>
|
|
1030
812
|
<button className="btn-primary-white" style={{ height: 26, padding: "0 10px", fontSize: 12 }} onClick={() => void setProjectTrust(true)}>{t.projectTrustAllow}</button>
|
|
@@ -1033,6 +815,8 @@ export function AppShell() {
|
|
|
1033
815
|
)}
|
|
1034
816
|
</div>
|
|
1035
817
|
)}
|
|
818
|
+
{/* 轨迹页签已移除:轨迹按需查看(对话里的工具行 → 轨迹详情),不再占用主区域 */}
|
|
819
|
+
</div>
|
|
1036
820
|
|
|
1037
821
|
{/* 内容:只有对话区,轨迹按需在详情栏查看 */}
|
|
1038
822
|
{!snapshot ? (
|
|
@@ -1073,20 +857,22 @@ export function AppShell() {
|
|
|
1073
857
|
onOpenFile={openInEditor}
|
|
1074
858
|
onFork={handleFork}
|
|
1075
859
|
onEditMessage={doEditMessage}
|
|
860
|
+
roundBadges={roundBadges}
|
|
861
|
+
onShowRound={showRound}
|
|
1076
862
|
/>
|
|
1077
|
-
<div className="
|
|
863
|
+
<div className="px-4 pb-3 pt-2">
|
|
1078
864
|
<div className="mx-auto w-full" style={{ maxWidth: "var(--dsh-composer-card-max-width)" }}>
|
|
1079
865
|
{/* 运行状态指示由 ChatWindow 内的 WorkingIndicator 承担(含工具/输出 token 信息) */}
|
|
1080
866
|
<ChatInput
|
|
1081
867
|
key={draftKey}
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
868
|
+
draft={composerDrafts.current.get(draftKey) ?? EMPTY_CHAT_DRAFT}
|
|
869
|
+
onDraftChange={saveSessionDraft}
|
|
870
|
+
onUploadError={(message) => failUpload(draftKey, message)}
|
|
871
|
+
onUploadProgress={(delta) => trackUpload(draftKey, delta)}
|
|
872
|
+
uploadFailure={uploadFailures[draftKey]}
|
|
873
|
+
pendingUploadCount={uploadCounts[draftKey] ?? 0}
|
|
874
|
+
pendingSend={pendingSends[draftKey] ?? false}
|
|
875
|
+
onSendPendingChange={(pending) => trackSend(draftKey, pending)}
|
|
1090
876
|
isStreaming={isStreaming}
|
|
1091
877
|
contextPercent={snapshot?.contextUsage?.percent ?? null}
|
|
1092
878
|
contextTokens={snapshot?.contextUsage?.tokens ?? null}
|
|
@@ -1097,15 +883,9 @@ export function AppShell() {
|
|
|
1097
883
|
thinkingLevel={snapshot?.thinkingLevel}
|
|
1098
884
|
thinkingLevels={snapshot?.thinkingLevels ?? ["off", "minimal", "low", "medium", "high", "xhigh", "max"]}
|
|
1099
885
|
models={modelChoices}
|
|
1100
|
-
modelLoading={modelLoading}
|
|
1101
|
-
modelLoadError={modelLoadError}
|
|
1102
|
-
onRetryModels={() => void refreshModels()}
|
|
1103
886
|
providerNames={providerNames}
|
|
1104
887
|
authByProvider={authByProvider}
|
|
1105
888
|
queue={snapshot?.queue ?? { steering: [], followUp: [] }}
|
|
1106
|
-
workflow={snapshot?.workflow}
|
|
1107
|
-
onWorkflowModeChange={(mode) => sendCommand({ cmd: "setWorkflowMode", mode })}
|
|
1108
|
-
onApprovePlan={() => sendCommand({ cmd: "approvePlan" })}
|
|
1109
889
|
commands={slashCommands}
|
|
1110
890
|
onCommand={runSlashCommand}
|
|
1111
891
|
onSend={sendPrompt}
|
|
@@ -1127,46 +907,45 @@ export function AppShell() {
|
|
|
1127
907
|
{Object.entries(state.extensionStatuses).map(([k, v]) => <span key={k}>{v}</span>)}
|
|
1128
908
|
</div>
|
|
1129
909
|
)}
|
|
1130
|
-
|
|
910
|
+
</div>
|
|
1131
911
|
</div>
|
|
1132
912
|
</div>
|
|
1133
913
|
)
|
|
1134
914
|
}
|
|
1135
915
|
</>
|
|
1136
916
|
)}
|
|
1137
|
-
|
|
917
|
+
</div>
|
|
1138
918
|
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
</main>
|
|
919
|
+
{/* 提示词来源 / 轨迹 / Git 详情栏 */}
|
|
920
|
+
{(selected || gitDetailsOpen || promptsOpen) && (
|
|
921
|
+
<div
|
|
922
|
+
className="flex min-h-0 flex-col overflow-hidden"
|
|
923
|
+
style={isNarrow ? {
|
|
924
|
+
position: "fixed",
|
|
925
|
+
inset: "0 0 0 auto",
|
|
926
|
+
width: "min(92vw, 640px)",
|
|
927
|
+
zIndex: 90,
|
|
928
|
+
background: "var(--dsw-sidebar-fill)",
|
|
929
|
+
boxShadow: "var(--dsw-elevation-prominent)",
|
|
930
|
+
} : { background: "var(--dsw-sidebar-fill)", borderLeft: "0.5px solid var(--dsw-border-l2)" }}
|
|
931
|
+
>
|
|
932
|
+
<div className="min-h-0 flex-1">
|
|
933
|
+
{promptsOpen ? (
|
|
934
|
+
<PromptPanel
|
|
935
|
+
cwd={panelCwd}
|
|
936
|
+
sessionId={currentId}
|
|
937
|
+
refreshKey={currentId}
|
|
938
|
+
onOpenContent={(path, content) => viewer.openStatic(path, content)}
|
|
939
|
+
onClose={() => setPromptsOpen(false)}
|
|
940
|
+
/>
|
|
941
|
+
) : selected ? (
|
|
942
|
+
<TrajInspector entry={selected} onClose={() => setSelected(null)} />
|
|
943
|
+
) : (
|
|
944
|
+
<GitPanel cwd={panelCwd} refreshKey={panelRefreshKey} onClose={() => setGitDetailsOpen(false)} onAskCommit={() => insertIntoComposer(t.gitAskCommitPrompt)} onOpenFile={openInEditor} />
|
|
945
|
+
)}
|
|
946
|
+
</div>
|
|
947
|
+
</div>
|
|
948
|
+
)}
|
|
1170
949
|
|
|
1171
950
|
{/* 拖拽手柄 */}
|
|
1172
951
|
{!isNarrow && !sidebarCollapsed && (
|
|
@@ -1280,9 +1059,6 @@ function Hero({
|
|
|
1280
1059
|
getWorkspaceName,
|
|
1281
1060
|
onSend,
|
|
1282
1061
|
models,
|
|
1283
|
-
modelLoading,
|
|
1284
|
-
modelLoadError,
|
|
1285
|
-
onRetryModels,
|
|
1286
1062
|
providerNames,
|
|
1287
1063
|
authByProvider,
|
|
1288
1064
|
addWorkspaceByPicker,
|
|
@@ -1292,9 +1068,6 @@ function Hero({
|
|
|
1292
1068
|
heroModelLevels,
|
|
1293
1069
|
heroThinking,
|
|
1294
1070
|
onSelectHeroThinking,
|
|
1295
|
-
heroMode,
|
|
1296
|
-
onSelectHeroMode,
|
|
1297
|
-
onOpenSwarm,
|
|
1298
1071
|
}: {
|
|
1299
1072
|
draft: ChatDraft;
|
|
1300
1073
|
onDraftChange: (update: ChatDraftUpdate) => void;
|
|
@@ -1312,9 +1085,6 @@ function Hero({
|
|
|
1312
1085
|
getWorkspaceName?: (cwd: string) => string;
|
|
1313
1086
|
onSend: (text: string, images: ImageAttachment[]) => Promise<{ success: boolean }>;
|
|
1314
1087
|
models: ModelChoice[];
|
|
1315
|
-
modelLoading: boolean;
|
|
1316
|
-
modelLoadError: string | null;
|
|
1317
|
-
onRetryModels: () => void;
|
|
1318
1088
|
providerNames: Record<string, string>;
|
|
1319
1089
|
authByProvider: Record<string, boolean>;
|
|
1320
1090
|
addWorkspaceByPicker: () => Promise<string | null>;
|
|
@@ -1324,9 +1094,6 @@ function Hero({
|
|
|
1324
1094
|
heroModelLevels: string[];
|
|
1325
1095
|
heroThinking: string;
|
|
1326
1096
|
onSelectHeroThinking: (level: string) => void;
|
|
1327
|
-
heroMode: WorkflowMode;
|
|
1328
|
-
onSelectHeroMode: (mode: WorkflowMode) => void;
|
|
1329
|
-
onOpenSwarm?: () => void;
|
|
1330
1097
|
}) {
|
|
1331
1098
|
const { t } = useI18n();
|
|
1332
1099
|
const [wsMenu, setWsMenu] = useState(false);
|
|
@@ -1344,106 +1111,81 @@ function Hero({
|
|
|
1344
1111
|
}, [wsMenu]);
|
|
1345
1112
|
|
|
1346
1113
|
const pickWorkspace = async () => {
|
|
1347
|
-
setWsMenu(false);
|
|
1348
1114
|
const p = await addWorkspaceByPicker();
|
|
1349
1115
|
if (p) {
|
|
1350
1116
|
setCwd(p);
|
|
1117
|
+
setWsMenu(false);
|
|
1351
1118
|
}
|
|
1352
1119
|
};
|
|
1353
1120
|
|
|
1354
1121
|
const currentLabel = cwd ? (getWorkspaceName ? getWorkspaceName(cwd) : basename(cwd)) : t.startWith;
|
|
1355
1122
|
|
|
1356
1123
|
return (
|
|
1357
|
-
<div className="
|
|
1358
|
-
{/*
|
|
1359
|
-
<div className="
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
{/* 工作区芯片行 + 输入卡 同宽容器 */}
|
|
1364
|
-
<div className="pw-hero-content w-full flex flex-col items-stretch mx-auto my-auto z-10" style={{ maxWidth: "var(--dsh-composer-card-max-width)" }}>
|
|
1365
|
-
<div className="pw-hero-intro mb-4 text-center">
|
|
1366
|
-
<div className="flex items-center justify-center gap-4 px-4 py-2 rounded-full clay-card mb-3 mx-auto w-fit animate-bounce duration-1000 border border-slate-200 dark:border-slate-800">
|
|
1367
|
-
<div className="w-8 h-8 rounded-full bg-emerald-600 flex items-center justify-center shadow-[inset_1px_1px_2px_rgba(255,255,255,0.7)] text-white font-mono font-bold text-sm">
|
|
1368
|
-
π
|
|
1369
|
-
</div>
|
|
1370
|
-
<div className="flex items-center gap-2">
|
|
1371
|
-
<span className="font-extrabold tracking-wider text-slate-900 dark:text-slate-100 text-xs sm:text-sm">PIWEB</span>
|
|
1372
|
-
<span className="text-slate-400">/</span>
|
|
1373
|
-
<span className="text-[10px] tracking-widest text-slate-600 dark:text-slate-400 font-bold uppercase">WORKBENCH</span>
|
|
1374
|
-
</div>
|
|
1375
|
-
<span className="w-1.5 h-1.5 rounded-full bg-slate-300 dark:bg-slate-600" />
|
|
1376
|
-
<div className="flex items-center gap-1.5 px-2.5 py-0.5 rounded-full clay-inset border border-slate-200 dark:border-slate-800">
|
|
1377
|
-
<span className="w-2 h-2 rounded-full bg-emerald-500 animate-pulse" />
|
|
1378
|
-
<span className="font-mono text-[11px] text-emerald-700 dark:text-emerald-300 font-bold tracking-wider">01 / READY</span>
|
|
1379
|
-
</div>
|
|
1380
|
-
</div>
|
|
1381
|
-
<h1 className="text-3xl sm:text-4xl font-extrabold tracking-tight text-slate-900 dark:text-slate-50 mb-2 drop-shadow-sm">
|
|
1382
|
-
让想法落到代码。
|
|
1383
|
-
</h1>
|
|
1384
|
-
<p className="text-sm text-slate-600 dark:text-slate-300 max-w-lg mx-auto leading-relaxed font-medium">
|
|
1385
|
-
规划、执行、验证。一个专注于交付的轻量工作台。
|
|
1386
|
-
</p>
|
|
1124
|
+
<div className="flex h-full min-h-0 flex-col items-center justify-center px-6">
|
|
1125
|
+
{/* 品牌行:仅 π 标 */}
|
|
1126
|
+
<div className="mb-5 flex items-center justify-center gap-3">
|
|
1127
|
+
<PiMark size={40} />
|
|
1128
|
+
</div>
|
|
1387
1129
|
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1130
|
+
{/* 工作区芯片行 + 输入卡 同宽容器(芯片行与卡片左对齐) */}
|
|
1131
|
+
<div className="w-full flex flex-col items-stretch mx-auto" style={{ maxWidth: "var(--dsh-composer-card-max-width)" }}>
|
|
1132
|
+
<div ref={menuRef} className="mb-3 flex items-center gap-3" style={{ paddingLeft: 7 }}>
|
|
1133
|
+
<div className="relative">
|
|
1134
|
+
<button className="hero-chip" data-open={wsMenu} onClick={() => setWsMenu((v) => !v)}>
|
|
1135
|
+
<IconFolderClose16 className="hero-chip-icon" size={16} />
|
|
1136
|
+
<span className="max-w-[220px] truncate" suppressHydrationWarning>{currentLabel}</span>
|
|
1137
|
+
<span className="chevron">
|
|
1138
|
+
<IconChevronDown14 size={14} />
|
|
1139
|
+
</span>
|
|
1140
|
+
</button>
|
|
1141
|
+
{wsMenu && (
|
|
1142
|
+
<div className="popover absolute bottom-9 left-0 z-50 w-56 py-1">
|
|
1143
|
+
{knownCwds.map((c) => {
|
|
1144
|
+
const itemLabel = getWorkspaceName ? getWorkspaceName(c) : basename(c);
|
|
1145
|
+
return (
|
|
1146
|
+
<button
|
|
1147
|
+
key={c}
|
|
1148
|
+
className="flex w-full items-center gap-2.5 px-3.5 py-2 text-left transition-colors"
|
|
1149
|
+
style={{
|
|
1150
|
+
fontSize: 13,
|
|
1151
|
+
background: cwd === c ? "var(--dsw-accent-soft)" : "transparent",
|
|
1152
|
+
color: cwd === c ? "var(--dsw-accent)" : "inherit",
|
|
1153
|
+
}}
|
|
1154
|
+
onMouseEnter={(e) => {
|
|
1155
|
+
if (cwd !== c) e.currentTarget.style.background = "var(--dsw-hover)";
|
|
1156
|
+
}}
|
|
1157
|
+
onMouseLeave={(e) => {
|
|
1158
|
+
if (cwd !== c) e.currentTarget.style.background = "transparent";
|
|
1159
|
+
}}
|
|
1160
|
+
onClick={() => {
|
|
1161
|
+
setCwd(c);
|
|
1162
|
+
setWsMenu(false);
|
|
1163
|
+
}}
|
|
1164
|
+
>
|
|
1165
|
+
<IconFolderClose16 size={14} style={{ flex: "none", color: cwd === c ? "var(--dsw-accent)" : "var(--dsw-label-tertiary)" }} />
|
|
1166
|
+
<span className="min-w-0 flex-1 truncate">{itemLabel}</span>
|
|
1167
|
+
{cwd === c && <IconCheckOutline14 size={13} style={{ flex: "none" }} />}
|
|
1168
|
+
</button>
|
|
1169
|
+
);
|
|
1170
|
+
})}
|
|
1171
|
+
<div className="my-1" style={{ borderTop: "0.5px solid var(--dsw-border-l2)" }} />
|
|
1172
|
+
<button
|
|
1173
|
+
className="flex w-full items-center gap-2.5 px-3.5 py-2 text-left transition-colors"
|
|
1174
|
+
style={{ fontSize: 13 }}
|
|
1175
|
+
onMouseEnter={(e) => (e.currentTarget.style.background = "var(--dsw-hover)")}
|
|
1176
|
+
onMouseLeave={(e) => (e.currentTarget.style.background = "transparent")}
|
|
1177
|
+
onClick={pickWorkspace}
|
|
1178
|
+
>
|
|
1179
|
+
<IconProjectAddOutline16 size={14} style={{ flex: "none", color: "var(--dsw-label-tertiary)" }} />
|
|
1180
|
+
{t.addWorkspace}
|
|
1181
|
+
</button>
|
|
1182
|
+
</div>
|
|
1183
|
+
)}
|
|
1441
1184
|
</div>
|
|
1442
1185
|
</div>
|
|
1443
1186
|
|
|
1444
1187
|
{/* 输入卡 */}
|
|
1445
|
-
<
|
|
1446
|
-
<ChatInput
|
|
1188
|
+
<ChatInput
|
|
1447
1189
|
draft={draft}
|
|
1448
1190
|
onDraftChange={onDraftChange}
|
|
1449
1191
|
onUploadError={onUploadError}
|
|
@@ -1452,60 +1194,26 @@ function Hero({
|
|
|
1452
1194
|
pendingUploadCount={pendingUploadCount}
|
|
1453
1195
|
pendingSend={pendingSend}
|
|
1454
1196
|
onSendPendingChange={onSendPendingChange}
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
onSteer={() => ({ success: false })}
|
|
1476
|
-
onAbort={() => {}}
|
|
1477
|
-
onSelectModel={onSelectHeroModel}
|
|
1478
|
-
onSelectLevel={onSelectHeroThinking}
|
|
1479
|
-
/>
|
|
1480
|
-
<div className="flex items-center justify-between px-2 pt-2 text-slate-500 dark:text-slate-400 font-mono text-xs font-medium">
|
|
1481
|
-
<div className="flex items-center gap-3">
|
|
1482
|
-
<span className="flex items-center gap-1"><kbd className="px-1.5 py-0.5 rounded clay-inset font-bold text-slate-700 dark:text-slate-200 border border-slate-200 dark:border-slate-700">⏎</kbd> 发送</span>
|
|
1483
|
-
<span className="flex items-center gap-1"><kbd className="px-1.5 py-0.5 rounded clay-inset font-bold text-slate-700 dark:text-slate-200 border border-slate-200 dark:border-slate-700">⇧ ⏎</kbd> 换行</span>
|
|
1484
|
-
</div>
|
|
1485
|
-
<div className="flex items-center gap-1.5 text-emerald-700 dark:text-emerald-400 font-semibold">
|
|
1486
|
-
<IconShieldOutline16 size={13} />
|
|
1487
|
-
<span>主会话无系统级沙盒隔离</span>
|
|
1488
|
-
</div>
|
|
1489
|
-
</div>
|
|
1490
|
-
</div>
|
|
1491
|
-
|
|
1492
|
-
{/* Telemetry bottom bar */}
|
|
1493
|
-
<div className="flex items-center justify-center gap-4 text-slate-600 dark:text-slate-300 text-xs font-semibold tracking-wider">
|
|
1494
|
-
<div className="flex items-center gap-1.5">
|
|
1495
|
-
<span className="w-1.5 h-1.5 rounded-full bg-emerald-500" />
|
|
1496
|
-
<span>极速热加载</span>
|
|
1497
|
-
</div>
|
|
1498
|
-
<span>•</span>
|
|
1499
|
-
<div className="flex items-center gap-1.5">
|
|
1500
|
-
<span className="w-1.5 h-1.5 rounded-full bg-sky-500" />
|
|
1501
|
-
<span>MCP 扩展全兼容</span>
|
|
1502
|
-
</div>
|
|
1503
|
-
<span>•</span>
|
|
1504
|
-
<div className="flex items-center gap-1.5">
|
|
1505
|
-
<span className="w-1.5 h-1.5 rounded-full bg-orange-500" />
|
|
1506
|
-
<span>端侧安全加固</span>
|
|
1507
|
-
</div>
|
|
1508
|
-
</div>
|
|
1197
|
+
commands={commands}
|
|
1198
|
+
onCommand={onCommand}
|
|
1199
|
+
isStreaming={false}
|
|
1200
|
+
contextPercent={null}
|
|
1201
|
+
contextTokens={null}
|
|
1202
|
+
contextWindow={null}
|
|
1203
|
+
contextVisible={false}
|
|
1204
|
+
model={heroModel ? models.find((m) => m.provider === heroModel.provider && m.id === heroModel.id) ?? { provider: heroModel.provider, id: heroModel.id, name: heroModel.id, reasoning: false, contextWindow: 0 } : defaultModel}
|
|
1205
|
+
thinkingLevel={heroThinking || undefined}
|
|
1206
|
+
thinkingLevels={heroModelLevels}
|
|
1207
|
+
models={models}
|
|
1208
|
+
providerNames={providerNames}
|
|
1209
|
+
authByProvider={authByProvider}
|
|
1210
|
+
queue={{ steering: [], followUp: [] }}
|
|
1211
|
+
onSend={onSend}
|
|
1212
|
+
onSteer={() => ({ success: false })}
|
|
1213
|
+
onAbort={() => {}}
|
|
1214
|
+
onSelectModel={onSelectHeroModel}
|
|
1215
|
+
onSelectLevel={onSelectHeroThinking}
|
|
1216
|
+
/>
|
|
1509
1217
|
</div>
|
|
1510
1218
|
</div>
|
|
1511
1219
|
);
|