@rexvane/piweb 0.3.24 → 0.3.26
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 +11 -11
- package/.next/build-manifest.json +3 -3
- package/.next/prerender-manifest.json +5 -5
- package/.next/react-loadable-manifest.json +13 -20
- package/.next/required-server-files.js +2 -1
- package/.next/required-server-files.json +2 -1
- package/.next/routes-manifest.json +6 -8
- 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 +2 -2
- package/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
- package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
- package/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -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/agent/resources/route.js +1 -0
- package/.next/server/app/api/agent/resources/route.js.nft.json +1 -0
- package/.next/server/app/api/agent/resources/route_client-reference-manifest.js +1 -0
- 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/fs/route.js +1 -1
- package/.next/server/app/api/fs/route.js.nft.json +1 -1
- package/.next/server/app/api/git/route.js +4 -4
- package/.next/server/app/api/git/route.js.nft.json +1 -1
- package/.next/server/app/api/github/route.js +4 -4
- package/.next/server/app/api/github/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/defaults/route.js +1 -1
- package/.next/server/app/api/models/defaults/route.js.nft.json +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/remote/route.js +4 -3
- 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 +2 -2
- 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 +2 -2
- 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 -7
- package/.next/server/app/index.segments/__PAGE__.segment.rsc +6 -7
- package/.next/server/app/index.segments/_full.segment.rsc +6 -7
- package/.next/server/app/index.segments/_tree.segment.rsc +2 -3
- 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 +3 -3
- package/.next/server/app/login.segments/_full.segment.rsc +3 -3
- package/.next/server/app/login.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/login.segments/login/__PAGE__.segment.rsc +3 -3
- package/.next/server/app/page.js +1 -1
- package/.next/server/app/page.js.nft.json +1 -1
- package/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/server/app-paths-manifest.json +11 -11
- package/.next/server/chunks/3924.js +3 -3
- package/.next/server/chunks/538.js +1 -1
- package/.next/server/chunks/620.js +1 -0
- package/.next/server/chunks/6229.js +1 -1
- package/.next/server/chunks/7496.js +1 -1
- package/.next/server/chunks/7662.js +2 -2
- package/.next/server/chunks/8686.js +1 -1
- package/.next/server/functions-config-manifest.json +1 -2
- package/.next/server/instrumentation.js.nft.json +1 -1
- 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/preview-props.json +3 -3
- package/.next/server/server-reference-manifest.json +1 -1
- package/.next/static/QJCtqgDKzGS2BdKABd9ch/_buildManifest.js +1 -0
- package/.next/static/chunks/2577.86c8538fbf05a8b2.js +1 -0
- package/.next/static/chunks/2716.fa561519675a3ff1.js +1 -0
- package/.next/static/chunks/2906.8a0e09bb47605cf1.js +1 -0
- package/.next/static/chunks/4673.ea36e55005c0f82f.js +1 -0
- package/.next/static/chunks/4686-4dc565817a160351.js +1 -0
- package/.next/static/chunks/4778.0d2c5d75b791d352.js +1 -0
- package/.next/static/chunks/{618.949981da99be7182.js → 618.9a22656b863f242b.js} +2 -2
- package/.next/static/chunks/app/_global-error/page-f2d93969c0bcf406.js +1 -0
- package/.next/static/chunks/app/api/agent/[id]/events/route-f2d93969c0bcf406.js +1 -0
- package/.next/static/chunks/app/api/agent/[id]/route-f2d93969c0bcf406.js +1 -0
- package/.next/static/chunks/app/api/agent/[id]/tree/route-f2d93969c0bcf406.js +1 -0
- package/.next/static/chunks/app/api/agent/new/route-f2d93969c0bcf406.js +1 -0
- package/.next/static/chunks/app/api/agent/resources/route-f2d93969c0bcf406.js +1 -0
- package/.next/static/chunks/app/api/config/route-f2d93969c0bcf406.js +1 -0
- package/.next/static/chunks/app/api/files/route-f2d93969c0bcf406.js +1 -0
- package/.next/static/chunks/app/api/fs/route-f2d93969c0bcf406.js +1 -0
- package/.next/static/chunks/app/api/git/route-f2d93969c0bcf406.js +1 -0
- package/.next/static/chunks/app/api/github/route-f2d93969c0bcf406.js +1 -0
- package/.next/static/chunks/app/api/growth/route-f2d93969c0bcf406.js +1 -0
- package/.next/static/chunks/app/api/health/route-f2d93969c0bcf406.js +1 -0
- package/.next/static/chunks/app/api/models/defaults/route-f2d93969c0bcf406.js +1 -0
- package/.next/static/chunks/app/api/models/route-f2d93969c0bcf406.js +1 -0
- package/.next/static/chunks/app/api/pi-settings/route-f2d93969c0bcf406.js +1 -0
- package/.next/static/chunks/app/api/plugins/route-f2d93969c0bcf406.js +1 -0
- package/.next/static/chunks/app/api/prompts/route-f2d93969c0bcf406.js +1 -0
- package/.next/static/chunks/app/api/remote/route-f2d93969c0bcf406.js +1 -0
- package/.next/static/chunks/app/api/security/route-f2d93969c0bcf406.js +1 -0
- package/.next/static/chunks/app/api/sessions/[id]/export/route-f2d93969c0bcf406.js +1 -0
- package/.next/static/chunks/app/api/sessions/[id]/route-f2d93969c0bcf406.js +1 -0
- package/.next/static/chunks/app/api/sessions/import/route-f2d93969c0bcf406.js +1 -0
- package/.next/static/chunks/app/api/sessions/route-f2d93969c0bcf406.js +1 -0
- package/.next/static/chunks/app/api/skills/route-f2d93969c0bcf406.js +1 -0
- package/.next/static/chunks/app/api/update/route-f2d93969c0bcf406.js +1 -0
- package/.next/static/chunks/app/api/version/route-f2d93969c0bcf406.js +1 -0
- package/.next/static/chunks/app/api/web-auth/route-f2d93969c0bcf406.js +1 -0
- package/.next/static/chunks/app/api/workspaces/route-f2d93969c0bcf406.js +1 -0
- package/.next/static/chunks/app/page-71f2d3b3ed864b3d.js +1 -0
- package/.next/static/chunks/next/dist/client/components/builtin/app-error-f2d93969c0bcf406.js +1 -0
- package/.next/static/chunks/next/dist/client/components/builtin/forbidden-f2d93969c0bcf406.js +1 -0
- package/.next/static/chunks/next/dist/client/components/builtin/not-found-f2d93969c0bcf406.js +1 -0
- package/.next/static/chunks/next/dist/client/components/builtin/unauthorized-f2d93969c0bcf406.js +1 -0
- package/.next/static/chunks/webpack-ffc09b5fcc8aaa6d.js +1 -0
- package/.next/static/css/014eb1f21414e615.css +1 -0
- package/.next/static/css/56b129a13637fcfd.css +1 -0
- package/.next/static/css/7e3caf797e4cb19a.css +1 -0
- package/.next/types/app/api/agent/{[id]/side-chat → resources}/route.ts +3 -3
- package/.next/types/routes.d.ts +2 -2
- package/.next/types/validator.ts +9 -9
- package/README.md +1 -1
- package/next.config.ts +1 -1
- package/package.json +9 -9
- package/scripts/apply-global.mjs +87 -0
- package/scripts/launcher.mjs +21 -0
- package/scripts/testing/prepare-sdk-tools.mjs +19 -0
- package/src/app/api/agent/new/route.ts +2 -1
- package/src/app/api/agent/resources/route.ts +18 -0
- package/src/app/api/models/route.ts +22 -1
- package/src/app/api/plugins/route.ts +3 -2
- package/src/app/api/prompts/route.ts +3 -2
- package/src/app/api/remote/route.ts +22 -4
- package/src/app/api/sessions/[id]/export/route.ts +3 -1
- package/src/app/api/skills/route.ts +3 -2
- package/src/app/globals.css +1002 -1765
- package/src/components/AppShell.tsx +128 -93
- package/src/components/ChatInput.tsx +177 -98
- package/src/components/ChatWindow.tsx +189 -56
- package/src/components/ContextMeter.tsx +33 -9
- package/src/components/CustomSelect.module.css +142 -0
- package/src/components/CustomSelect.tsx +157 -0
- package/src/components/DrawerPanel.tsx +1 -1
- package/src/components/ExtensionUI.tsx +9 -3
- package/src/components/FileViewer.tsx +24 -21
- package/src/components/GitPanel.tsx +35 -13
- package/src/components/GrowthTree.tsx +6 -2
- package/src/components/ImageViewer.tsx +72 -0
- package/src/components/MarkdownBlock.tsx +67 -96
- package/src/components/ModelSelector.tsx +36 -39
- package/src/components/PiMark.tsx +76 -41
- package/src/components/PreviewImage.tsx +33 -0
- package/src/components/ProjectPanel.tsx +8 -5
- package/src/components/PromptPanel.tsx +85 -20
- package/src/components/ProviderSetupModal.module.css +12 -11
- package/src/components/ProviderSetupModal.tsx +107 -36
- package/src/components/RemoteSection.tsx +53 -2
- package/src/components/SessionImportSection.tsx +30 -15
- package/src/components/SessionSidebar.tsx +72 -62
- package/src/components/SettingsPanel.tsx +49 -151
- package/src/components/TrajectoryAnalyzer.module.css +26 -11
- package/src/components/TrajectoryView.tsx +11 -3
- package/src/components/chat/markdown-actions.tsx +23 -0
- package/src/components/composer/ComposerFiles.tsx +224 -0
- package/src/components/composer/ComposerImages.tsx +59 -127
- package/src/hooks/usePiWeb.ts +47 -2
- package/src/i18n.tsx +62 -80
- package/src/lib/agent-manager.ts +365 -33
- package/src/lib/chat/file-path.ts +54 -0
- package/src/lib/chat/skill-expansion.ts +51 -0
- package/src/lib/extension-ui.ts +25 -1
- package/src/lib/font.ts +37 -39
- package/src/lib/git-service.ts +4 -2
- package/src/lib/growth-turns.ts +3 -1
- package/src/lib/model-draft.ts +69 -2
- package/src/lib/models-service.ts +53 -3
- package/src/lib/pi.ts +2 -2
- package/src/lib/remote/assets.json +1 -1
- package/src/lib/remote/supervisor.ts +48 -5
- package/src/lib/security-service.ts +2 -1
- package/src/lib/session-reader.ts +25 -13
- package/src/lib/session-retention.ts +11 -8
- package/src/lib/skills-service.ts +2 -1
- package/src/lib/slash-command-token.ts +14 -0
- package/src/lib/trajectory.ts +111 -15
- package/src/lib/types.ts +28 -3
- package/src/lib/update-service.ts +29 -9
- package/src/lib/workspace-store.ts +36 -1
- package/.next/server/app/api/agent/[id]/side-chat/route.js +0 -5
- package/.next/server/app/api/agent/[id]/side-chat/route.js.nft.json +0 -1
- package/.next/server/app/api/agent/[id]/side-chat/route_client-reference-manifest.js +0 -1
- package/.next/server/chunks/3421.js +0 -1
- package/.next/static/chunks/2716.44fdf1b21454b961.js +0 -1
- package/.next/static/chunks/4673.e261707dce391be7.js +0 -1
- package/.next/static/chunks/4686-0cd87513f9ea3538.js +0 -1
- package/.next/static/chunks/4778.eddd3a5813f85824.js +0 -1
- package/.next/static/chunks/7411.5f79671208209b13.js +0 -1
- package/.next/static/chunks/8552.25c39ef4c995eba0.js +0 -1
- package/.next/static/chunks/app/_global-error/page-cbaaee0d7aacb85a.js +0 -1
- package/.next/static/chunks/app/api/agent/[id]/events/route-cbaaee0d7aacb85a.js +0 -1
- package/.next/static/chunks/app/api/agent/[id]/route-cbaaee0d7aacb85a.js +0 -1
- package/.next/static/chunks/app/api/agent/[id]/side-chat/route-cbaaee0d7aacb85a.js +0 -1
- package/.next/static/chunks/app/api/agent/[id]/tree/route-cbaaee0d7aacb85a.js +0 -1
- package/.next/static/chunks/app/api/agent/new/route-cbaaee0d7aacb85a.js +0 -1
- package/.next/static/chunks/app/api/config/route-cbaaee0d7aacb85a.js +0 -1
- package/.next/static/chunks/app/api/files/route-cbaaee0d7aacb85a.js +0 -1
- package/.next/static/chunks/app/api/fs/route-cbaaee0d7aacb85a.js +0 -1
- package/.next/static/chunks/app/api/git/route-cbaaee0d7aacb85a.js +0 -1
- package/.next/static/chunks/app/api/github/route-cbaaee0d7aacb85a.js +0 -1
- package/.next/static/chunks/app/api/growth/route-cbaaee0d7aacb85a.js +0 -1
- package/.next/static/chunks/app/api/health/route-cbaaee0d7aacb85a.js +0 -1
- package/.next/static/chunks/app/api/models/defaults/route-cbaaee0d7aacb85a.js +0 -1
- package/.next/static/chunks/app/api/models/route-cbaaee0d7aacb85a.js +0 -1
- package/.next/static/chunks/app/api/pi-settings/route-cbaaee0d7aacb85a.js +0 -1
- package/.next/static/chunks/app/api/plugins/route-cbaaee0d7aacb85a.js +0 -1
- package/.next/static/chunks/app/api/prompts/route-cbaaee0d7aacb85a.js +0 -1
- package/.next/static/chunks/app/api/remote/route-cbaaee0d7aacb85a.js +0 -1
- package/.next/static/chunks/app/api/security/route-cbaaee0d7aacb85a.js +0 -1
- package/.next/static/chunks/app/api/sessions/[id]/export/route-cbaaee0d7aacb85a.js +0 -1
- package/.next/static/chunks/app/api/sessions/[id]/route-cbaaee0d7aacb85a.js +0 -1
- package/.next/static/chunks/app/api/sessions/import/route-cbaaee0d7aacb85a.js +0 -1
- package/.next/static/chunks/app/api/sessions/route-cbaaee0d7aacb85a.js +0 -1
- package/.next/static/chunks/app/api/skills/route-cbaaee0d7aacb85a.js +0 -1
- package/.next/static/chunks/app/api/update/route-cbaaee0d7aacb85a.js +0 -1
- package/.next/static/chunks/app/api/version/route-cbaaee0d7aacb85a.js +0 -1
- package/.next/static/chunks/app/api/web-auth/route-cbaaee0d7aacb85a.js +0 -1
- package/.next/static/chunks/app/api/workspaces/route-cbaaee0d7aacb85a.js +0 -1
- package/.next/static/chunks/app/page-dc3cb41d91c7432e.js +0 -1
- package/.next/static/chunks/next/dist/client/components/builtin/app-error-cbaaee0d7aacb85a.js +0 -1
- package/.next/static/chunks/next/dist/client/components/builtin/forbidden-cbaaee0d7aacb85a.js +0 -1
- package/.next/static/chunks/next/dist/client/components/builtin/not-found-cbaaee0d7aacb85a.js +0 -1
- package/.next/static/chunks/next/dist/client/components/builtin/unauthorized-cbaaee0d7aacb85a.js +0 -1
- package/.next/static/chunks/webpack-2ac3d76b81895f5b.js +0 -1
- package/.next/static/css/08815507594e8b8d.css +0 -1
- package/.next/static/css/9fc559c2c4ad1d0f.css +0 -1
- package/.next/static/css/c6e2227c36ec2fd6.css +0 -1
- package/.next/static/css/f15a853af8b5127f.css +0 -1
- package/.next/static/lvKCJmjFj8LuqSojRVLRF/_buildManifest.js +0 -1
- package/src/app/api/agent/[id]/side-chat/route.ts +0 -77
- package/src/components/side-chat/SideChatPanel.module.css +0 -131
- package/src/components/side-chat/SideChatPanel.tsx +0 -189
- package/src/components/side-chat/icons.tsx +0 -8
- package/src/components/side-chat/request-history.ts +0 -29
- package/src/components/side-chat/store.ts +0 -248
- package/src/lib/chat/stream-settings.ts +0 -192
- package/src/lib/side-chat/context.ts +0 -123
- package/src/lib/side-chat/service.ts +0 -191
- package/src/lib/side-chat/types.ts +0 -61
- /package/.next/static/{lvKCJmjFj8LuqSojRVLRF → QJCtqgDKzGS2BdKABd9ch}/_ssgManifest.js +0 -0
|
@@ -13,8 +13,6 @@ import { UpdateNotice } from "@/components/update/UpdateNotice";
|
|
|
13
13
|
import dynamic from "next/dynamic";
|
|
14
14
|
import { SessionSidebar } from "@/components/SessionSidebar";
|
|
15
15
|
import type { DrawerSection } from "@/components/DrawerPanel";
|
|
16
|
-
import { SideChatPanel } from "@/components/side-chat/SideChatPanel";
|
|
17
|
-
import { SideChatIcon } from "@/components/side-chat/icons";
|
|
18
16
|
import { preserveChatReadingPosition } from "@/components/chat/reading-anchor";
|
|
19
17
|
import { DirectoryPicker } from "@/components/DirectoryPicker";
|
|
20
18
|
|
|
@@ -47,7 +45,11 @@ import type { ModelChoice } from "@/components/ModelSelector";
|
|
|
47
45
|
import type { NewSessionDefaults } from "@/lib/model-defaults";
|
|
48
46
|
import { readRememberedHeroModel, writeRememberedHeroModel } from "@/lib/hero-model-memory";
|
|
49
47
|
import { newSessionThinkingLevel } from "@/lib/thinking-level";
|
|
50
|
-
import
|
|
48
|
+
import { promptTitleText } from "@/lib/chat/skill-expansion";
|
|
49
|
+
import type { ImageAttachment, TrajEntry, WebSnapshot } from "@/lib/types";
|
|
50
|
+
|
|
51
|
+
/** 补全需要的资源清单;新会话页与冷会话都能从服务端单独发现,不必等快照 */
|
|
52
|
+
type WorkspaceCommands = Pick<WebSnapshot, "skills" | "promptTemplates" | "extensionCommands">;
|
|
51
53
|
|
|
52
54
|
// dsh ui-layout columns.ts 几何常量
|
|
53
55
|
const SIDEBAR_MIN = 264;
|
|
@@ -160,20 +162,14 @@ export function AppShell() {
|
|
|
160
162
|
const [settingsOpen, setSettingsOpen] = useState(false);
|
|
161
163
|
const [selected, setSelected] = useState<TrajEntry | null>(null);
|
|
162
164
|
/** 抽屉当前入口(由这里持有:轨迹详情关掉后要回到同一个入口) */
|
|
163
|
-
/**
|
|
165
|
+
/** 抽屉入口保存在浏览器 */
|
|
164
166
|
const [drawerSection, setDrawerSection] = useState<DrawerSection>("project");
|
|
165
167
|
const chooseDrawerSection = useCallback((section: DrawerSection) => {
|
|
166
168
|
setDrawerSection(section);
|
|
167
169
|
localStorage.setItem("piweb.drawerSection", section);
|
|
168
|
-
setSideChatOpen(false);
|
|
169
170
|
}, []);
|
|
170
171
|
/** 右侧抽屉(最右一列):入口列表 + 内容 */
|
|
171
172
|
const [drawerOpen, setDrawerOpen] = useState(false);
|
|
172
|
-
/**
|
|
173
|
-
* 右侧旁聊面板:和抽屉共用最右一列,所以两者互斥(开一个就关另一个)。
|
|
174
|
-
* 旁聊不再作为抽屉的一个入口 —— 它是独立面板,入口是会话头部那个图标按钮。
|
|
175
|
-
*/
|
|
176
|
-
const [sideChatOpen, setSideChatOpen] = useState(false);
|
|
177
173
|
/** 打开抽屉时「落到最新轮」只跳过这一次(点 badge 指定轮次时用) */
|
|
178
174
|
const skipLatestOnce = useRef(false);
|
|
179
175
|
/** useGrowth 的"落到最新轮":它在 AppShell 里、不随抽屉卸载,所以必须显式调用 */
|
|
@@ -182,6 +178,9 @@ export function AppShell() {
|
|
|
182
178
|
const [heroModel, setHeroModel] = useState<{ provider: string; id: string } | null>(null);
|
|
183
179
|
/** 当前工作区的新会话默认值(pi 的初始模型规则 + 设置里的思考档位),切换工作区时重新取 */
|
|
184
180
|
const [heroDefaults, setHeroDefaults] = useState<NewSessionDefaults | null>(null);
|
|
181
|
+
/** 按工作区缓存的命令清单:切会话时先用着,再后台刷新 */
|
|
182
|
+
const [workspaceResources, setWorkspaceResources] = useState<Record<string, WorkspaceCommands>>({});
|
|
183
|
+
const [resourcesVersion, setResourcesVersion] = useState(0);
|
|
185
184
|
const [heroThinking, setHeroThinking] = useState("");
|
|
186
185
|
const [isNarrow, setIsNarrow] = useState(false);
|
|
187
186
|
const [mobileSidebarOpen, setMobileSidebarOpen] = useState(false);
|
|
@@ -193,15 +192,25 @@ export function AppShell() {
|
|
|
193
192
|
const section = localStorage.getItem("piweb.drawerSection");
|
|
194
193
|
if (["project", "prompts", "git", "import"].includes(section ?? "")) setDrawerSection(section as DrawerSection);
|
|
195
194
|
}, []);
|
|
196
|
-
/**
|
|
195
|
+
/** 新会话时加载用户之前的选择;没有记住的,等 /api/models/defaults 回来用 pi 设置里的建议模型兜底 */
|
|
197
196
|
useEffect(() => {
|
|
198
197
|
if (!heroCwd) {
|
|
199
198
|
setHeroModel(null);
|
|
200
199
|
return;
|
|
201
200
|
}
|
|
202
|
-
//
|
|
201
|
+
// 优先这个工作区自己记住的模型;没有就先留空,交给下面的建议模型兜底
|
|
203
202
|
setHeroModel(readRememberedHeroModel(localStorage, heroCwd));
|
|
204
203
|
}, [heroCwd]);
|
|
204
|
+
/**
|
|
205
|
+
* 兜底:这个工作区没记住过选择时,用 pi 设置里的建议模型(defaults.model,服务端按
|
|
206
|
+
* enabledModels + 认证状态挑出来的)。否则点「新会话」永远是「当前模型不可用,请先选择模型」,
|
|
207
|
+
* 明明有可用模型却要先手动选一次。用户显式选过(记住的)就不覆盖。
|
|
208
|
+
*/
|
|
209
|
+
useEffect(() => {
|
|
210
|
+
const suggested = heroDefaults?.model;
|
|
211
|
+
if (!heroCwd || !suggested) return;
|
|
212
|
+
setHeroModel((current) => current ?? suggested);
|
|
213
|
+
}, [heroCwd, heroDefaults]);
|
|
205
214
|
useEffect(() => {
|
|
206
215
|
setHeroDefaults(null);
|
|
207
216
|
if (!heroCwd) return;
|
|
@@ -216,14 +225,36 @@ export function AppShell() {
|
|
|
216
225
|
stale = true;
|
|
217
226
|
};
|
|
218
227
|
}, [heroCwd]);
|
|
228
|
+
/** 补全数据归属的工作区:有会话看会话的 cwd,新会话看 Hero 选中的工作区 */
|
|
229
|
+
const resourceCwd = currentId
|
|
230
|
+
? sessions.find((session) => samePath(session.path, currentPath ?? ""))?.cwd ??
|
|
231
|
+
(state.snapshot?.sessionPath && samePath(state.snapshot.sessionPath, currentPath ?? "") ? state.snapshot.cwd : "")
|
|
232
|
+
: heroCwd;
|
|
233
|
+
// 补全不能等历史快照或 session_start/MCP:按工作区单独发现并缓存,切会话/改设置时后台刷新。
|
|
234
|
+
useEffect(() => {
|
|
235
|
+
if (!resourceCwd) return;
|
|
236
|
+
let stale = false;
|
|
237
|
+
fetch(`/api/agent/resources?cwd=${encodeURIComponent(resourceCwd)}`, { cache: "no-store" })
|
|
238
|
+
.then((r) => r.json() as Promise<{ success?: boolean; data?: WorkspaceCommands; error?: string }>)
|
|
239
|
+
.then((json) => {
|
|
240
|
+
if (stale) return;
|
|
241
|
+
if (!json.success || !json.data) throw new Error(json.error || "Failed to load workspace resources");
|
|
242
|
+
const data = json.data;
|
|
243
|
+
setWorkspaceResources((previous) => ({ ...previous, [resourceCwd]: data }));
|
|
244
|
+
})
|
|
245
|
+
.catch((error) => {
|
|
246
|
+
if (!stale) setError(error instanceof Error ? error.message : String(error));
|
|
247
|
+
});
|
|
248
|
+
return () => {
|
|
249
|
+
stale = true;
|
|
250
|
+
};
|
|
251
|
+
}, [currentId, resourceCwd, settingsOpen, resourcesVersion, setError]);
|
|
219
252
|
/** 右侧抽屉开关(项目栏降级成抽屉入口后,它是"最右一列是否打开"唯一的开关) */
|
|
220
253
|
const toggleDrawer = useCallback((next?: boolean) => {
|
|
221
254
|
preserveChatReadingPosition();
|
|
222
255
|
setDrawerOpen((cur) => {
|
|
223
256
|
const v = next ?? !cur;
|
|
224
257
|
localStorage.setItem("piweb.drawerOpen", v ? "1" : "0");
|
|
225
|
-
// 最右一列只有一个:开抽屉就收起旁聊
|
|
226
|
-
if (v) setSideChatOpen(false);
|
|
227
258
|
return v;
|
|
228
259
|
});
|
|
229
260
|
}, []);
|
|
@@ -248,7 +279,8 @@ export function AppShell() {
|
|
|
248
279
|
};
|
|
249
280
|
document.addEventListener("keydown", h);
|
|
250
281
|
return () => document.removeEventListener("keydown", h);
|
|
251
|
-
|
|
282
|
+
// selected 必须在依赖里:漏了它,打开轨迹详情后闭包里仍是旧的 null,第一下被当成「收起」,要按两次
|
|
283
|
+
}, [drawerOpen, drawerSection, selected, toggleDrawer, openDrawerSection]);
|
|
252
284
|
|
|
253
285
|
useEffect(() => {
|
|
254
286
|
const query = window.matchMedia("(max-width: 840px)");
|
|
@@ -391,12 +423,12 @@ export function AppShell() {
|
|
|
391
423
|
[sidebarWidth, rightWidth],
|
|
392
424
|
);
|
|
393
425
|
|
|
394
|
-
const toggleSidebar = () => {
|
|
426
|
+
const toggleSidebar = useCallback(() => {
|
|
395
427
|
preserveChatReadingPosition();
|
|
396
428
|
const next = !sidebarCollapsed;
|
|
397
429
|
setSidebarCollapsed(next);
|
|
398
430
|
localStorage.setItem("piweb.sidebarCollapsed", next ? "1" : "0");
|
|
399
|
-
};
|
|
431
|
+
}, [sidebarCollapsed]);
|
|
400
432
|
|
|
401
433
|
// ---------- 会话操作 ----------
|
|
402
434
|
const snapshot = state.snapshot;
|
|
@@ -412,6 +444,13 @@ export function AppShell() {
|
|
|
412
444
|
setSelected(entry);
|
|
413
445
|
}, []);
|
|
414
446
|
|
|
447
|
+
// 轨迹 context 条目的全文回查:快照只在轨迹里带资源头部(全文在 contextResources 一处)
|
|
448
|
+
const contextResourcesRef = useRef(snapshot?.contextResources ?? []);
|
|
449
|
+
useEffect(() => {
|
|
450
|
+
contextResourcesRef.current = snapshot?.contextResources ?? [];
|
|
451
|
+
}, [snapshot?.contextResources]);
|
|
452
|
+
const contextLookup = useCallback((path: string) => contextResourcesRef.current.find((r) => r.path === path)?.content, []);
|
|
453
|
+
|
|
415
454
|
// 上下文分段的数据源:系统提示词按注入资源字符数(很便宜);
|
|
416
455
|
// 消息字符统计交给 ContextMeter 弹窗打开时再算,避免每次 token 增量全量扫描
|
|
417
456
|
const contextSystemChars = useMemo(
|
|
@@ -426,20 +465,19 @@ export function AppShell() {
|
|
|
426
465
|
return () => clearTimeout(timer);
|
|
427
466
|
}, [state.error, clearError]);
|
|
428
467
|
|
|
429
|
-
const doRename = async (path: string, newName: string) => {
|
|
468
|
+
const doRename = useCallback(async (path: string, newName: string) => {
|
|
430
469
|
if (!newName.trim()) return;
|
|
431
|
-
|
|
470
|
+
// 冷会话:临时走命令路由(会按需打开,不启动 agent 的 rename 走 appendSessionInfo)
|
|
471
|
+
const result = currentPath && samePath(path, currentPath)
|
|
432
472
|
? await sendCommand({ cmd: "rename", text: newName.trim() })
|
|
433
473
|
: await sendCommand({ cmd: "rename", text: newName.trim() }, encodeURIComponent(b64url(path)));
|
|
434
474
|
if (!result?.success) {
|
|
435
475
|
throw new Error(result?.error || "failed to rename session");
|
|
436
476
|
}
|
|
477
|
+
// 侧栏就地改名即可。不要 resync:它重连的是「当前」会话的事件流,与被改名的冷会话无关,
|
|
478
|
+
// 正在流式输出时还会把眼前的对话整页重载一遍
|
|
437
479
|
patchSessionName(path, newName.trim());
|
|
438
|
-
|
|
439
|
-
// 冷会话:临时走命令路由(会按需打开,不启动 agent 的 rename 走 appendSessionInfo)
|
|
440
|
-
resync();
|
|
441
|
-
}
|
|
442
|
-
};
|
|
480
|
+
}, [currentPath, sendCommand, patchSessionName]);
|
|
443
481
|
|
|
444
482
|
const doForkSession = useCallback(
|
|
445
483
|
async (path: string, _cwd: string) => {
|
|
@@ -494,15 +532,42 @@ export function AppShell() {
|
|
|
494
532
|
const doUnarchiveSession = useCallback(
|
|
495
533
|
async (path: string) => {
|
|
496
534
|
try {
|
|
535
|
+
// 归档列表由 unarchiveSession 按服务端返回更新;不 resync 当前会话(同 doRename)
|
|
497
536
|
await unarchiveSession(path);
|
|
498
|
-
resync();
|
|
499
537
|
} catch {
|
|
500
538
|
// usePiWeb exposes the request failure in the shared error banner.
|
|
501
539
|
}
|
|
502
540
|
},
|
|
503
|
-
[unarchiveSession
|
|
541
|
+
[unarchiveSession],
|
|
504
542
|
);
|
|
505
543
|
|
|
544
|
+
// ---------- 侧栏回调:全部 useCallback,配合 SessionSidebar 的 memo ——
|
|
545
|
+
// 流式期间 state.messages 每 16ms 换引用,AppShell 整树重渲染时侧栏不再跟着空转
|
|
546
|
+
const openSessionAndCloseMobileNav = useCallback((path: string) => {
|
|
547
|
+
openSession(path);
|
|
548
|
+
setMobileSidebarOpen(false);
|
|
549
|
+
}, [openSession]);
|
|
550
|
+
const closeMobileNav = useCallback(() => setMobileSidebarOpen(false), []);
|
|
551
|
+
const startNewSessionInCurrentWorkspace = useCallback(() => {
|
|
552
|
+
// 对齐 dsh startSession:新会话默认落在当前会话的工作区(无则取最近工作区)
|
|
553
|
+
const cur = sessions.find((s) => s.path === currentPath)?.cwd;
|
|
554
|
+
setHeroCwd(cur || heroCwd || knownCwds[0] || "");
|
|
555
|
+
closeSession();
|
|
556
|
+
}, [sessions, currentPath, heroCwd, knownCwds, closeSession]);
|
|
557
|
+
const startNewSessionInWorkspace = useCallback((cwd: string) => {
|
|
558
|
+
// dsh 惰性新建:只预选工作区进草稿态,发送第一条消息才真正创建会话
|
|
559
|
+
setHeroCwd(cwd);
|
|
560
|
+
closeSession();
|
|
561
|
+
}, [closeSession]);
|
|
562
|
+
const exportSessionFile = useCallback((path: string) => {
|
|
563
|
+
window.open(`/api/sessions/${encodeURIComponent(b64url(path))}/export?format=jsonl`, "_blank");
|
|
564
|
+
}, []);
|
|
565
|
+
const openSettingsFromSidebar = useCallback(() => {
|
|
566
|
+
setSettingsOpen(true);
|
|
567
|
+
setMobileSidebarOpen(false);
|
|
568
|
+
}, []);
|
|
569
|
+
const addWorkspaceFromSidebar = useCallback(() => void addWorkspaceByPicker(), [addWorkspaceByPicker]);
|
|
570
|
+
|
|
506
571
|
const doRenameWorkspace = useCallback(
|
|
507
572
|
async (cwd: string, newName: string) => {
|
|
508
573
|
await renameWorkspace(cwd, newName);
|
|
@@ -512,13 +577,13 @@ export function AppShell() {
|
|
|
512
577
|
|
|
513
578
|
const doDeleteWorkspace = useCallback(
|
|
514
579
|
async (cwd: string) => {
|
|
580
|
+
// 工作区列表由 removeWorkspace 按服务端返回更新;不 resync 当前会话(同 doRename)
|
|
515
581
|
await removeWorkspace(cwd);
|
|
516
|
-
if (heroCwd
|
|
582
|
+
if (heroCwd && samePath(heroCwd, cwd)) {
|
|
517
583
|
setHeroCwd("");
|
|
518
584
|
}
|
|
519
|
-
resync();
|
|
520
585
|
},
|
|
521
|
-
[removeWorkspace, heroCwd
|
|
586
|
+
[removeWorkspace, heroCwd],
|
|
522
587
|
);
|
|
523
588
|
|
|
524
589
|
// Hero 发送:先完整应用新会话预设,再发第一条消息。
|
|
@@ -562,6 +627,9 @@ export function AppShell() {
|
|
|
562
627
|
[sendCommand, openSession, resync],
|
|
563
628
|
);
|
|
564
629
|
|
|
630
|
+
const commandResources = currentId && snapshot?.sessionPath && samePath(snapshot.sessionPath, currentPath ?? "")
|
|
631
|
+
? snapshot
|
|
632
|
+
: workspaceResources[resourceCwd];
|
|
565
633
|
// 斜杠命令:Web 内置 + pi 的技能 / 提示模板 / 扩展命令(后三类原样交给 SDK 展开或执行)
|
|
566
634
|
const slashCommands = useMemo(() => {
|
|
567
635
|
const list: { name: string; desc: string; kind: "builtin" | "skill" | "template" | "extension"; argumentHint?: string }[] = [
|
|
@@ -571,12 +639,12 @@ export function AppShell() {
|
|
|
571
639
|
{ name: "new", desc: t.cmdNew, kind: "builtin" },
|
|
572
640
|
{ name: "fork", desc: t.cmdFork, kind: "builtin" },
|
|
573
641
|
{ name: "reload", desc: t.cmdReload, kind: "builtin" },
|
|
574
|
-
...(
|
|
575
|
-
...(
|
|
576
|
-
...(
|
|
642
|
+
...(commandResources?.promptTemplates ?? []).map((p) => ({ name: p.name, desc: p.description, kind: "template" as const, argumentHint: p.argumentHint })),
|
|
643
|
+
...(commandResources?.extensionCommands ?? []).map((c) => ({ name: c.name, desc: c.description || c.source, kind: "extension" as const })),
|
|
644
|
+
...(commandResources?.skills ?? []).map((s) => ({ name: `skill:${s.name}`, desc: s.description, kind: "skill" as const })),
|
|
577
645
|
];
|
|
578
646
|
return list.sort((a, b) => a.name.localeCompare(b.name));
|
|
579
|
-
}, [
|
|
647
|
+
}, [commandResources, t]);
|
|
580
648
|
|
|
581
649
|
const runSlashCommand = useCallback(
|
|
582
650
|
(name: string, args: string) => {
|
|
@@ -588,10 +656,16 @@ export function AppShell() {
|
|
|
588
656
|
void sendCommand({ cmd: "fork" }).then((r) => {
|
|
589
657
|
if (r.success && r.data?.sessionPath) openSession(r.data.sessionPath);
|
|
590
658
|
});
|
|
591
|
-
} else if (name === "reload")
|
|
592
|
-
|
|
659
|
+
} else if (name === "reload") {
|
|
660
|
+
// 新会话页没有会话可重载:重新发现命令并刷模型目录即可
|
|
661
|
+
if (currentId) void sendCommand({ cmd: "reload" }).then(() => refreshModels());
|
|
662
|
+
else {
|
|
663
|
+
setResourcesVersion((version) => version + 1);
|
|
664
|
+
void refreshModels();
|
|
665
|
+
}
|
|
666
|
+
} else setError(t.cmdUnknown.replace("{name}", name));
|
|
593
667
|
},
|
|
594
|
-
[sendCommand, currentId, closeSession, openSession, setError, t],
|
|
668
|
+
[sendCommand, currentId, closeSession, openSession, refreshModels, setError, t],
|
|
595
669
|
);
|
|
596
670
|
|
|
597
671
|
const projectTrust = snapshot?.projectTrust;
|
|
@@ -612,11 +686,13 @@ export function AppShell() {
|
|
|
612
686
|
|
|
613
687
|
// 当前会话名 / 工作区标题
|
|
614
688
|
const currentSession = sessions.find((s) => s.path === currentPath);
|
|
615
|
-
const
|
|
689
|
+
const firstUserText = (
|
|
616
690
|
state.messages
|
|
617
691
|
.find((m) => m.role === "user")
|
|
618
692
|
?.content?.find((c: any) => c.type === "text" && c.text?.trim()) as any
|
|
619
693
|
)?.text?.trim();
|
|
694
|
+
// 技能展开的消息不能原样当标题(会露出 <skill name=…> 和 SKILL.md 正文)
|
|
695
|
+
const firstUserMsgText = firstUserText ? promptTitleText(firstUserText) : firstUserText;
|
|
620
696
|
const title =
|
|
621
697
|
snapshot?.name ||
|
|
622
698
|
currentSession?.name ||
|
|
@@ -696,6 +772,8 @@ export function AppShell() {
|
|
|
696
772
|
connected: state.connected,
|
|
697
773
|
});
|
|
698
774
|
const openFileNode = useCallback((node: TreeNode) => viewer.open(node.path, { from: node.from, lazy: node.lazy, kind: node.kind }), [viewer]);
|
|
775
|
+
// 正文行内代码里的路径走页内查看器;工具行上那个按钮仍然是本机编辑器(openInEditor)
|
|
776
|
+
const openPathInViewer = useCallback((filePath: string) => viewer.open(filePath), [viewer.open]);
|
|
699
777
|
// 打开面板时落在最新轮:useGrowth 活在 AppShell 里、不随抽屉卸载,state 不会自己回到最新,
|
|
700
778
|
// 所以每次从「关」变「开」(或换了工作区)都显式选一次最新轮。
|
|
701
779
|
// 新轮次出现时不再自动跳——用户打开面板后停在哪一轮就是哪一轮。
|
|
@@ -749,7 +827,7 @@ export function AppShell() {
|
|
|
749
827
|
// 抽屉里的一个入口,不再单独占一列,所以是 3 列而不是 4 列。
|
|
750
828
|
// JSX 里各列的渲染顺序必须与这里严格一致,否则网格会把内容摆错列。
|
|
751
829
|
// 最右一列只有一条宽度(rightWidth):详情栏与抽屉合并后不再各占一列。
|
|
752
|
-
const rightColumnVisible = Boolean(selected) || drawerOpen
|
|
830
|
+
const rightColumnVisible = Boolean(selected) || drawerOpen;
|
|
753
831
|
const gridCols = isNarrow ? "minmax(0,1fr)" : `${sidebarCollapsed ? SIDEBAR_COLLAPSED : sidebarWidth}px minmax(0,1fr) ${
|
|
754
832
|
rightColumnVisible ? `${rightWidth}px` : "0px"}`;
|
|
755
833
|
|
|
@@ -769,17 +847,7 @@ export function AppShell() {
|
|
|
769
847
|
} : { background: "var(--dsw-sidebar-fill)", borderLeft: "0.5px solid var(--dsw-border-l2)" }}
|
|
770
848
|
>
|
|
771
849
|
{selected ? (
|
|
772
|
-
<TrajInspector entry={selected} onClose={() => setSelected(null)} />
|
|
773
|
-
) : sideChatOpen ? (
|
|
774
|
-
<SideChatPanel
|
|
775
|
-
sessionId={currentId}
|
|
776
|
-
ready={Boolean(snapshot)}
|
|
777
|
-
mainModel={snapshot?.model}
|
|
778
|
-
models={modelChoices}
|
|
779
|
-
providerNames={providerNames}
|
|
780
|
-
authByProvider={authByProvider}
|
|
781
|
-
onClose={() => setSideChatOpen(false)}
|
|
782
|
-
/>
|
|
850
|
+
<TrajInspector entry={selected} onClose={() => setSelected(null)} contextLookup={contextLookup} />
|
|
783
851
|
) : (
|
|
784
852
|
<DrawerPanel
|
|
785
853
|
cwd={panelCwd}
|
|
@@ -845,35 +913,20 @@ export function AppShell() {
|
|
|
845
913
|
setGroupBy={setGroupBy}
|
|
846
914
|
setOrderBy={setOrderBy}
|
|
847
915
|
collapsed={isNarrow ? false : sidebarCollapsed}
|
|
848
|
-
onToggleCollapse={isNarrow ?
|
|
849
|
-
onOpen={
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
}}
|
|
853
|
-
onNew={() => {
|
|
854
|
-
// 对齐 dsh startSession:新会话默认落在当前会话的工作区(无则取最近工作区)
|
|
855
|
-
const cur = sessions.find((s) => s.path === currentPath)?.cwd;
|
|
856
|
-
setHeroCwd(cur || heroCwd || knownCwds[0] || "");
|
|
857
|
-
closeSession();
|
|
858
|
-
}}
|
|
859
|
-
onNewInWorkspace={(cwd) => {
|
|
860
|
-
// dsh 惰性新建:只预选工作区进草稿态,发送第一条消息才真正创建会话
|
|
861
|
-
setHeroCwd(cwd);
|
|
862
|
-
closeSession();
|
|
863
|
-
}}
|
|
916
|
+
onToggleCollapse={isNarrow ? closeMobileNav : toggleSidebar}
|
|
917
|
+
onOpen={openSessionAndCloseMobileNav}
|
|
918
|
+
onNew={startNewSessionInCurrentWorkspace}
|
|
919
|
+
onNewInWorkspace={startNewSessionInWorkspace}
|
|
864
920
|
onRename={doRename}
|
|
865
921
|
onFork={doForkSession}
|
|
866
922
|
onArchive={doArchiveSession}
|
|
867
923
|
onUnarchive={doUnarchiveSession}
|
|
868
|
-
onExport={
|
|
924
|
+
onExport={exportSessionFile}
|
|
869
925
|
getWorkspaceName={getWorkspaceName}
|
|
870
926
|
onRenameWorkspace={doRenameWorkspace}
|
|
871
927
|
onDeleteWorkspace={doDeleteWorkspace}
|
|
872
|
-
onOpenSettings={
|
|
873
|
-
|
|
874
|
-
setMobileSidebarOpen(false);
|
|
875
|
-
}}
|
|
876
|
-
onAddWorkspace={() => void addWorkspaceByPicker()}
|
|
928
|
+
onOpenSettings={openSettingsFromSidebar}
|
|
929
|
+
onAddWorkspace={addWorkspaceFromSidebar}
|
|
877
930
|
draftCwd={!currentId ? heroCwd || null : null}
|
|
878
931
|
/>
|
|
879
932
|
</div>
|
|
@@ -939,27 +992,6 @@ export function AppShell() {
|
|
|
939
992
|
<span className="min-w-0 flex-1 truncate" style={{ fontSize: 14.5, fontWeight: 600 }}>
|
|
940
993
|
{title}
|
|
941
994
|
</span>
|
|
942
|
-
{/* 旁聊直达按钮;旁聊与其他入口共用右侧抽屉。 */}
|
|
943
|
-
<button
|
|
944
|
-
type="button"
|
|
945
|
-
className="icon-btn"
|
|
946
|
-
style={{ width: 26, height: 26, color: sideChatOpen ? "var(--dsw-accent)" : "var(--dsw-label-secondary)" }}
|
|
947
|
-
aria-label={t.sideChat}
|
|
948
|
-
title={t.sideChat}
|
|
949
|
-
aria-pressed={sideChatOpen}
|
|
950
|
-
data-testid="side-chat-toggle"
|
|
951
|
-
onClick={() => {
|
|
952
|
-
preserveChatReadingPosition();
|
|
953
|
-
setSelected(null);
|
|
954
|
-
setSideChatOpen((open) => {
|
|
955
|
-
// 最右一列只有一个:开旁聊就收起抽屉
|
|
956
|
-
if (!open) toggleDrawer(false);
|
|
957
|
-
return !open;
|
|
958
|
-
});
|
|
959
|
-
}}
|
|
960
|
-
>
|
|
961
|
-
<SideChatIcon size={16} />
|
|
962
|
-
</button>
|
|
963
995
|
{/* 右侧抽屉的入口与开关 */}
|
|
964
996
|
<button
|
|
965
997
|
type="button"
|
|
@@ -1031,6 +1063,7 @@ export function AppShell() {
|
|
|
1031
1063
|
cwd={panelCwd}
|
|
1032
1064
|
onOpenTrajectory={openToolTrajectory}
|
|
1033
1065
|
onOpenFile={openInEditor}
|
|
1066
|
+
onOpenPath={openPathInViewer}
|
|
1034
1067
|
onFork={handleFork}
|
|
1035
1068
|
onEditMessage={doEditMessage}
|
|
1036
1069
|
roundBadges={roundBadges}
|
|
@@ -1115,7 +1148,7 @@ export function AppShell() {
|
|
|
1115
1148
|
{/* 窄屏最右一列:贴右边滑出(轨迹详情与抽屉共用,遮罩关闭回到对话) */}
|
|
1116
1149
|
{isNarrow && rightColumnVisible && (
|
|
1117
1150
|
<>
|
|
1118
|
-
<button className="fixed inset-0 z-[85]" style={{ background: "var(--dsw-mask)" }} onClick={() => (selected ? setSelected(null) :
|
|
1151
|
+
<button className="fixed inset-0 z-[85]" style={{ background: "var(--dsw-mask)" }} onClick={() => (selected ? setSelected(null) : toggleDrawer(false))} aria-label={t.close} />
|
|
1119
1152
|
{rightColumn}
|
|
1120
1153
|
</>
|
|
1121
1154
|
)}
|
|
@@ -1345,6 +1378,8 @@ function Hero({
|
|
|
1345
1378
|
pendingUploadCount={pendingUploadCount}
|
|
1346
1379
|
pendingSend={pendingSend}
|
|
1347
1380
|
onSendPendingChange={onSendPendingChange}
|
|
1381
|
+
// Hero 的附件占位,避免 @ 引用和图片盖住上方工作区选择器
|
|
1382
|
+
attachmentLayout="flow"
|
|
1348
1383
|
// 新会话也能 @ 引用:候选来自上面选中的工作区
|
|
1349
1384
|
cwd={cwd}
|
|
1350
1385
|
commands={commands}
|