@rexvane/piweb 0.3.21 → 0.3.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.next/BUILD_ID +1 -1
- package/.next/app-path-routes-manifest.json +9 -6
- package/.next/build-manifest.json +5 -5
- package/.next/next-minimal-server.js.nft.json +1 -1
- package/.next/next-server.js.nft.json +1 -1
- package/.next/prerender-manifest.json +7 -7
- package/.next/react-loadable-manifest.json +18 -11
- package/.next/required-server-files.js +16 -1
- package/.next/required-server-files.json +16 -1
- package/.next/routes-manifest.json +20 -0
- package/.next/server/app/_global-error/page.js +2 -2
- package/.next/server/app/_global-error/page.js.nft.json +1 -1
- 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.meta +8 -0
- package/.next/server/app/_global-error.rsc +15 -11
- package/.next/server/app/_global-error.segments/_full.segment.rsc +15 -11
- package/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +20 -16
- package/.next/server/app/_global-error.segments/_tree.segment.rsc +3 -1
- package/.next/server/app/_not-found/page.js +1 -1
- package/.next/server/app/_not-found/page.js.nft.json +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.meta +8 -0
- package/.next/server/app/_not-found.rsc +17 -13
- package/.next/server/app/_not-found.segments/_full.segment.rsc +17 -13
- package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +24 -20
- package/.next/server/app/_not-found.segments/_tree.segment.rsc +4 -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]/side-chat/route.js +5 -0
- package/.next/server/app/api/agent/[id]/side-chat/route.js.nft.json +1 -0
- package/.next/server/app/api/agent/[id]/side-chat/route_client-reference-manifest.js +1 -0
- 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/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 +5 -0
- package/.next/server/app/api/github/route.js.nft.json +1 -0
- package/.next/server/app/api/github/route_client-reference-manifest.js +1 -0
- 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/health/route.js.nft.json +1 -1
- package/.next/server/app/api/models/defaults/route.js +1 -0
- package/.next/server/app/api/models/defaults/route.js.nft.json +1 -0
- package/.next/server/app/api/models/defaults/route_client-reference-manifest.js +1 -0
- 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 -4
- package/.next/server/app/api/remote/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 +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/web-auth/route.js.nft.json +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/icon.svg/route.js.nft.json +1 -1
- package/.next/server/app/icon.svg.meta +1 -1
- package/.next/server/app/index.html +1 -1
- package/.next/server/app/index.meta +8 -0
- package/.next/server/app/index.rsc +20 -13
- package/.next/server/app/index.segments/__PAGE__.segment.rsc +26 -19
- package/.next/server/app/index.segments/_full.segment.rsc +20 -13
- package/.next/server/app/index.segments/_tree.segment.rsc +4 -2
- package/.next/server/app/login/page.js +1 -1
- package/.next/server/app/login/page.js.nft.json +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.meta +8 -0
- package/.next/server/app/login.rsc +23 -17
- package/.next/server/app/login.segments/_full.segment.rsc +23 -17
- package/.next/server/app/login.segments/_tree.segment.rsc +4 -2
- package/.next/server/app/login.segments/login/__PAGE__.segment.rsc +30 -24
- 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 +9 -6
- package/.next/server/chunks/1813.js +23 -0
- package/.next/server/chunks/2430.js +377 -0
- package/.next/server/chunks/2970.js +305 -0
- package/.next/server/chunks/3421.js +1 -0
- package/.next/server/chunks/3924.js +6 -0
- package/.next/server/chunks/4807.js +1 -0
- package/.next/server/chunks/538.js +1 -1
- package/.next/server/chunks/5393.js +1 -0
- package/.next/server/chunks/5802.js +2 -0
- package/.next/server/chunks/6229.js +1 -0
- package/.next/server/chunks/7496.js +1 -0
- package/.next/server/chunks/7662.js +2 -0
- package/.next/server/chunks/8686.js +1 -0
- package/.next/server/chunks/{593.js → 974.js} +1 -1
- package/.next/server/chunks/{920.js → 9920.js} +2 -2
- package/.next/server/functions-config-manifest.json +2 -1
- package/.next/server/instrumentation.js +1 -1
- 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/middleware.js +185 -23
- package/.next/server/middleware.js.nft.json +1 -1
- package/.next/server/pages/404.html +1 -1
- package/.next/server/pages/500.html +1 -1
- package/.next/server/preview-props.json +5 -0
- package/.next/server/server-reference-manifest.json +1 -1
- package/.next/server/webpack-runtime.js +1 -1
- package/.next/static/chunks/2716.44fdf1b21454b961.js +1 -0
- package/.next/static/chunks/316-698d10eb465bd8ef.js +1 -0
- package/.next/static/chunks/3794-0978865cda773008.js +52 -0
- package/.next/static/chunks/4673.e261707dce391be7.js +1 -0
- package/.next/static/chunks/4686-0cd87513f9ea3538.js +1 -0
- package/.next/static/chunks/4778.eddd3a5813f85824.js +1 -0
- package/.next/static/chunks/4bd1b696-e199e788e58acf79.js +1 -0
- package/.next/static/chunks/{5173.91ddb803785009f8.js → 5173.dfd1cbb795b9a21c.js} +1 -1
- package/.next/static/chunks/618.949981da99be7182.js +4 -0
- package/.next/static/chunks/7411.5f79671208209b13.js +1 -0
- package/.next/static/chunks/app/_global-error/page-cbaaee0d7aacb85a.js +1 -0
- package/.next/static/chunks/app/api/agent/[id]/events/route-cbaaee0d7aacb85a.js +1 -0
- package/.next/static/chunks/app/api/agent/[id]/route-cbaaee0d7aacb85a.js +1 -0
- package/.next/static/chunks/app/api/agent/[id]/side-chat/route-cbaaee0d7aacb85a.js +1 -0
- package/.next/static/chunks/app/api/agent/[id]/tree/route-cbaaee0d7aacb85a.js +1 -0
- package/.next/static/chunks/app/api/agent/new/route-cbaaee0d7aacb85a.js +1 -0
- package/.next/static/chunks/app/api/config/route-cbaaee0d7aacb85a.js +1 -0
- package/.next/static/chunks/app/api/files/route-cbaaee0d7aacb85a.js +1 -0
- package/.next/static/chunks/app/api/fs/route-cbaaee0d7aacb85a.js +1 -0
- package/.next/static/chunks/app/api/git/route-cbaaee0d7aacb85a.js +1 -0
- package/.next/static/chunks/app/api/github/route-cbaaee0d7aacb85a.js +1 -0
- package/.next/static/chunks/app/api/growth/route-cbaaee0d7aacb85a.js +1 -0
- package/.next/static/chunks/app/api/health/route-cbaaee0d7aacb85a.js +1 -0
- package/.next/static/chunks/app/api/models/defaults/route-cbaaee0d7aacb85a.js +1 -0
- package/.next/static/chunks/app/api/models/route-cbaaee0d7aacb85a.js +1 -0
- package/.next/static/chunks/app/api/pi-settings/route-cbaaee0d7aacb85a.js +1 -0
- package/.next/static/chunks/app/api/plugins/route-cbaaee0d7aacb85a.js +1 -0
- package/.next/static/chunks/app/api/prompts/route-cbaaee0d7aacb85a.js +1 -0
- package/.next/static/chunks/app/api/remote/route-cbaaee0d7aacb85a.js +1 -0
- package/.next/static/chunks/app/api/security/route-cbaaee0d7aacb85a.js +1 -0
- package/.next/static/chunks/app/api/sessions/[id]/export/route-cbaaee0d7aacb85a.js +1 -0
- package/.next/static/chunks/app/api/sessions/[id]/route-cbaaee0d7aacb85a.js +1 -0
- package/.next/static/chunks/app/api/sessions/import/route-cbaaee0d7aacb85a.js +1 -0
- package/.next/static/chunks/app/api/sessions/route-cbaaee0d7aacb85a.js +1 -0
- package/.next/static/chunks/app/api/skills/route-cbaaee0d7aacb85a.js +1 -0
- package/.next/static/chunks/app/api/update/route-cbaaee0d7aacb85a.js +1 -0
- package/.next/static/chunks/app/api/version/route-cbaaee0d7aacb85a.js +1 -0
- package/.next/static/chunks/app/api/web-auth/route-cbaaee0d7aacb85a.js +1 -0
- package/.next/static/chunks/app/api/workspaces/route-cbaaee0d7aacb85a.js +1 -0
- package/.next/static/chunks/app/page-dc3cb41d91c7432e.js +1 -0
- package/.next/static/chunks/framework-47b723d27f4a4963.js +1 -0
- package/.next/static/chunks/main-5403f0764579e5fe.js +5 -0
- package/.next/static/chunks/next/dist/client/components/builtin/app-error-cbaaee0d7aacb85a.js +1 -0
- package/.next/static/chunks/next/dist/client/components/builtin/forbidden-cbaaee0d7aacb85a.js +1 -0
- package/.next/static/chunks/next/dist/client/components/builtin/not-found-cbaaee0d7aacb85a.js +1 -0
- package/.next/static/chunks/next/dist/client/components/builtin/unauthorized-cbaaee0d7aacb85a.js +1 -0
- package/.next/static/chunks/webpack-2ac3d76b81895f5b.js +1 -0
- package/.next/static/css/9fc559c2c4ad1d0f.css +1 -0
- package/.next/static/css/c6e2227c36ec2fd6.css +1 -0
- package/.next/static/css/{2959bd6b626b7ffe.css → f15a853af8b5127f.css} +1 -1
- package/.next/static/hYPL1-dmhKFjLS3f5yoqN/_buildManifest.js +1 -0
- package/.next/types/app/api/agent/[id]/events/route.ts +3 -1
- package/.next/types/app/api/agent/[id]/route.ts +3 -1
- package/.next/types/app/api/agent/[id]/side-chat/route.ts +355 -0
- package/.next/types/app/api/agent/[id]/tree/route.ts +3 -1
- package/.next/types/app/api/agent/new/route.ts +3 -1
- package/.next/types/app/api/config/route.ts +3 -1
- package/.next/types/app/api/files/route.ts +3 -1
- package/.next/types/app/api/fs/route.ts +3 -1
- package/.next/types/app/api/git/route.ts +3 -1
- package/.next/types/app/api/github/route.ts +355 -0
- package/.next/types/app/api/growth/route.ts +3 -1
- package/.next/types/app/api/health/route.ts +3 -1
- package/.next/types/app/api/models/defaults/route.ts +355 -0
- package/.next/types/app/api/models/route.ts +3 -1
- package/.next/types/app/api/pi-settings/route.ts +3 -1
- package/.next/types/app/api/plugins/route.ts +3 -1
- package/.next/types/app/api/prompts/route.ts +3 -1
- package/.next/types/app/api/remote/route.ts +3 -1
- package/.next/types/app/api/security/route.ts +3 -1
- package/.next/types/app/api/sessions/[id]/export/route.ts +3 -1
- package/.next/types/app/api/sessions/[id]/route.ts +3 -1
- package/.next/types/app/api/sessions/import/route.ts +3 -1
- package/.next/types/app/api/sessions/route.ts +3 -1
- package/.next/types/app/api/skills/route.ts +3 -1
- package/.next/types/app/api/update/route.ts +3 -1
- package/.next/types/app/api/version/route.ts +3 -1
- package/.next/types/app/api/web-auth/route.ts +3 -1
- package/.next/types/app/api/workspaces/route.ts +3 -1
- package/.next/types/app/layout.ts +4 -1
- package/.next/types/app/login/page.ts +4 -1
- package/.next/types/app/page.ts +4 -1
- package/.next/types/cache-life.d.ts +11 -43
- package/.next/types/routes.d.ts +4 -1
- package/.next/types/validator.ts +86 -2
- package/next.config.ts +2 -0
- package/package.json +8 -2
- package/scripts/release.mjs +3 -7
- package/scripts/runtime/check-installed.mjs +66 -0
- package/scripts/runtime/pi-packages.mjs +51 -0
- package/scripts/verify-package.mjs +29 -7
- package/src/app/api/agent/[id]/side-chat/route.ts +77 -0
- package/src/app/api/files/route.ts +5 -2
- package/src/app/api/github/route.ts +26 -0
- package/src/app/api/models/defaults/route.ts +18 -0
- package/src/app/api/models/route.ts +24 -1
- package/src/app/globals.css +1748 -0
- package/src/components/AppShell.tsx +145 -43
- package/src/components/ChatInput.tsx +264 -79
- package/src/components/ChatWindow.tsx +13 -23
- package/src/components/ContextMeter.tsx +213 -144
- package/src/components/DrawerPanel.tsx +1 -1
- package/src/components/FileViewer.tsx +89 -8
- package/src/components/GitPanel.tsx +110 -40
- package/src/components/GrowthTree.tsx +30 -5
- package/src/components/ModelSelector.tsx +21 -5
- package/src/components/ProjectPanel.tsx +55 -30
- package/src/components/PromptPanel.tsx +2 -4
- package/src/components/ProviderSetupModal.module.css +20 -0
- package/src/components/ProviderSetupModal.tsx +134 -29
- package/src/components/SettingsPanel.tsx +252 -27
- package/src/components/TrajectoryView.tsx +4 -6
- package/src/components/chat/reading-anchor.ts +81 -0
- package/src/components/composer/ComposerImages.tsx +326 -0
- package/src/components/side-chat/SideChatPanel.module.css +131 -0
- package/src/components/side-chat/SideChatPanel.tsx +189 -0
- package/src/components/side-chat/icons.tsx +8 -0
- package/src/components/side-chat/request-history.ts +29 -0
- package/src/components/side-chat/store.ts +248 -0
- package/src/hooks/useFileViewer.ts +6 -4
- package/src/hooks/useGrowth.ts +3 -0
- package/src/hooks/usePiWeb.ts +12 -2
- package/src/hooks/useWorkspaceFiles.ts +93 -0
- package/src/i18n.tsx +150 -2
- package/src/lib/agent-manager.ts +135 -104
- package/src/lib/files-service.ts +166 -3
- package/src/lib/format.ts +56 -0
- package/src/lib/git-service.ts +45 -13
- package/src/lib/github-identity.ts +245 -0
- package/src/lib/growth-service.ts +6 -2
- package/src/lib/hero-model-memory.ts +28 -0
- package/src/lib/model-defaults.ts +105 -0
- package/src/lib/model-draft.ts +257 -9
- package/src/lib/model-endpoint.ts +23 -0
- package/src/lib/model-files.ts +33 -0
- package/src/lib/models-service.ts +240 -78
- package/src/lib/path-security.ts +24 -32
- package/src/lib/pi.ts +27 -2
- package/src/lib/process-format.ts +3 -2
- package/src/lib/session/metrics.ts +31 -0
- package/src/lib/session-reader.ts +1 -1
- package/src/lib/side-chat/context.ts +123 -0
- package/src/lib/side-chat/service.ts +191 -0
- package/src/lib/side-chat/types.ts +61 -0
- package/src/lib/thinking-level.ts +39 -0
- package/src/lib/trajectory.ts +17 -3
- package/src/lib/types.ts +12 -1
- package/src/lib/update-service.ts +4 -6
- package/src/lib/workspace-index.ts +270 -0
- package/.next/server/chunks/229.js +0 -1
- package/.next/server/chunks/393.js +0 -1
- package/.next/server/chunks/421.js +0 -1
- package/.next/server/chunks/430.js +0 -193
- package/.next/server/chunks/496.js +0 -1
- package/.next/server/chunks/662.js +0 -2
- package/.next/server/chunks/686.js +0 -1
- package/.next/server/chunks/741.js +0 -165
- package/.next/server/chunks/802.js +0 -2
- package/.next/server/chunks/807.js +0 -1
- package/.next/server/chunks/813.js +0 -1
- package/.next/server/chunks/924.js +0 -6
- package/.next/static/chunks/2716.7ad073038e94a802.js +0 -1
- package/.next/static/chunks/316-5ea443ee6b512aa9.js +0 -1
- package/.next/static/chunks/3794-3679f1c05160977c.js +0 -52
- package/.next/static/chunks/4673.8a5a9e6855350e34.js +0 -1
- package/.next/static/chunks/4686-882531aef1d2e250.js +0 -1
- package/.next/static/chunks/4778.305dc6abb9889627.js +0 -1
- package/.next/static/chunks/4bd1b696-93bf506b236d6248.js +0 -1
- package/.next/static/chunks/618.ee5d5bf1c647e483.js +0 -4
- package/.next/static/chunks/839.bfd2db36483faa3e.js +0 -1
- package/.next/static/chunks/app/_global-error/page-b179d9f7f6f2daf9.js +0 -1
- package/.next/static/chunks/app/api/agent/[id]/events/route-b179d9f7f6f2daf9.js +0 -1
- package/.next/static/chunks/app/api/agent/[id]/route-b179d9f7f6f2daf9.js +0 -1
- package/.next/static/chunks/app/api/agent/[id]/tree/route-b179d9f7f6f2daf9.js +0 -1
- package/.next/static/chunks/app/api/agent/new/route-b179d9f7f6f2daf9.js +0 -1
- package/.next/static/chunks/app/api/config/route-b179d9f7f6f2daf9.js +0 -1
- package/.next/static/chunks/app/api/files/route-b179d9f7f6f2daf9.js +0 -1
- package/.next/static/chunks/app/api/fs/route-b179d9f7f6f2daf9.js +0 -1
- package/.next/static/chunks/app/api/git/route-b179d9f7f6f2daf9.js +0 -1
- package/.next/static/chunks/app/api/growth/route-b179d9f7f6f2daf9.js +0 -1
- package/.next/static/chunks/app/api/health/route-b179d9f7f6f2daf9.js +0 -1
- package/.next/static/chunks/app/api/models/route-b179d9f7f6f2daf9.js +0 -1
- package/.next/static/chunks/app/api/pi-settings/route-b179d9f7f6f2daf9.js +0 -1
- package/.next/static/chunks/app/api/plugins/route-b179d9f7f6f2daf9.js +0 -1
- package/.next/static/chunks/app/api/prompts/route-b179d9f7f6f2daf9.js +0 -1
- package/.next/static/chunks/app/api/remote/route-b179d9f7f6f2daf9.js +0 -1
- package/.next/static/chunks/app/api/security/route-b179d9f7f6f2daf9.js +0 -1
- package/.next/static/chunks/app/api/sessions/[id]/export/route-b179d9f7f6f2daf9.js +0 -1
- package/.next/static/chunks/app/api/sessions/[id]/route-b179d9f7f6f2daf9.js +0 -1
- package/.next/static/chunks/app/api/sessions/import/route-b179d9f7f6f2daf9.js +0 -1
- package/.next/static/chunks/app/api/sessions/route-b179d9f7f6f2daf9.js +0 -1
- package/.next/static/chunks/app/api/skills/route-b179d9f7f6f2daf9.js +0 -1
- package/.next/static/chunks/app/api/update/route-b179d9f7f6f2daf9.js +0 -1
- package/.next/static/chunks/app/api/version/route-b179d9f7f6f2daf9.js +0 -1
- package/.next/static/chunks/app/api/web-auth/route-b179d9f7f6f2daf9.js +0 -1
- package/.next/static/chunks/app/api/workspaces/route-b179d9f7f6f2daf9.js +0 -1
- package/.next/static/chunks/app/page-c04af72e549695bd.js +0 -1
- package/.next/static/chunks/framework-c4e651fe89edfa91.js +0 -1
- package/.next/static/chunks/main-6159b22a8cb0b7c5.js +0 -5
- package/.next/static/chunks/next/dist/client/components/builtin/app-error-b179d9f7f6f2daf9.js +0 -1
- package/.next/static/chunks/next/dist/client/components/builtin/forbidden-b179d9f7f6f2daf9.js +0 -1
- package/.next/static/chunks/next/dist/client/components/builtin/not-found-b179d9f7f6f2daf9.js +0 -1
- package/.next/static/chunks/next/dist/client/components/builtin/unauthorized-b179d9f7f6f2daf9.js +0 -1
- package/.next/static/chunks/webpack-9b4bb574e5817654.js +0 -1
- package/.next/static/css/ea27b9e30f76303f.css +0 -1
- package/.next/static/sNX5GQkOWuK5YEID1hT-C/_buildManifest.js +0 -1
- /package/.next/static/{sNX5GQkOWuK5YEID1hT-C → hYPL1-dmhKFjLS3f5yoqN}/_ssgManifest.js +0 -0
|
@@ -39,10 +39,19 @@ interface GitInfo {
|
|
|
39
39
|
statusError?: string;
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
+
interface GithubIdentity {
|
|
43
|
+
login: string;
|
|
44
|
+
name?: string;
|
|
45
|
+
avatar?: string;
|
|
46
|
+
url?: string;
|
|
47
|
+
repo?: string;
|
|
48
|
+
}
|
|
49
|
+
|
|
42
50
|
interface DiffResult {
|
|
43
51
|
patch: string;
|
|
44
52
|
truncated: boolean;
|
|
45
53
|
binary: boolean;
|
|
54
|
+
stats?: { add: number; del: number };
|
|
46
55
|
title?: string;
|
|
47
56
|
}
|
|
48
57
|
|
|
@@ -75,6 +84,7 @@ export function GitPanel({
|
|
|
75
84
|
}) {
|
|
76
85
|
const { t } = useI18n();
|
|
77
86
|
const [info, setInfo] = useState<GitInfo | null>(null);
|
|
87
|
+
const [identity, setIdentity] = useState<GithubIdentity | null>(null);
|
|
78
88
|
const [loading, setLoading] = useState(false);
|
|
79
89
|
const [statusError, setStatusError] = useState("");
|
|
80
90
|
const [diffError, setDiffError] = useState("");
|
|
@@ -105,6 +115,23 @@ export function GitPanel({
|
|
|
105
115
|
}
|
|
106
116
|
}, [cwd]);
|
|
107
117
|
|
|
118
|
+
// GitHub 身份单独拉:要走网络(gh / 头像),不能拖慢上面那条纯本地的状态读取
|
|
119
|
+
useEffect(() => {
|
|
120
|
+
setIdentity(null);
|
|
121
|
+
if (!cwd) return;
|
|
122
|
+
const controller = new AbortController();
|
|
123
|
+
void (async () => {
|
|
124
|
+
try {
|
|
125
|
+
const r = await fetch(`/api/github?cwd=${encodeURIComponent(cwd)}`, { signal: controller.signal });
|
|
126
|
+
const j = await r.json();
|
|
127
|
+
if (j?.success && j.data?.identity) setIdentity(j.data.identity as GithubIdentity);
|
|
128
|
+
} catch {
|
|
129
|
+
/* 取不到就只显示分支,不报错 */
|
|
130
|
+
}
|
|
131
|
+
})();
|
|
132
|
+
return () => controller.abort();
|
|
133
|
+
}, [cwd, refreshKey]);
|
|
134
|
+
|
|
108
135
|
useEffect(() => {
|
|
109
136
|
setInfo(null);
|
|
110
137
|
setView(null);
|
|
@@ -165,13 +192,31 @@ export function GitPanel({
|
|
|
165
192
|
const error = view ? diffError : statusError || info?.statusError || "";
|
|
166
193
|
|
|
167
194
|
const parsed = useMemo(() => (diff?.patch ? parseUnifiedDiff(diff.patch) : null), [diff]);
|
|
168
|
-
|
|
195
|
+
// 新服务端给完整 Git 统计;旧服务端只在 patch 完整时才允许本地计数。
|
|
196
|
+
const stats = diff?.binary ? null : diff?.stats ?? (parsed && !diff?.truncated ? diffStats(parsed) : null);
|
|
169
197
|
|
|
170
198
|
const conflicts = info?.files.filter((f) => f.kind === "conflict") ?? [];
|
|
171
199
|
const staged = info?.files.filter((f) => f.kind !== "conflict" && f.kind !== "untracked" && f.indexStatus !== " ") ?? [];
|
|
172
200
|
const unstaged = info?.files.filter((f) => f.kind !== "conflict" && f.kind !== "untracked" && f.workStatus !== " ") ?? [];
|
|
173
201
|
const untracked = info?.files.filter((f) => f.kind === "untracked") ?? [];
|
|
174
202
|
|
|
203
|
+
/**
|
|
204
|
+
* 按 Git 的三个区呈现(分支 / 上游那些是「版本库」侧,在别的行):
|
|
205
|
+
* - 暂存区:`git add` 过的,下次 commit 会带上;
|
|
206
|
+
* - 工作区:改了还没 add 的 + 未跟踪的新文件(都属于「还没进暂存区」);
|
|
207
|
+
* - 冲突单独置顶(不先解决,提交走不下去)。
|
|
208
|
+
* 未跟踪文件要整份当新增行看,模式逐文件给,不能按组统一。
|
|
209
|
+
*/
|
|
210
|
+
const areas: Array<{ key: string; label: string; entries: Array<{ file: GitFileEntry; mode: "staged" | "worktree" | "untracked" }>; danger?: boolean }> = [
|
|
211
|
+
{ key: "conflict", label: t.gitConflict, entries: conflicts.map((file) => ({ file, mode: "worktree" as const })), danger: true },
|
|
212
|
+
{ key: "staged", label: t.gitStagingArea, entries: staged.map((file) => ({ file, mode: "staged" as const })) },
|
|
213
|
+
{
|
|
214
|
+
key: "worktree",
|
|
215
|
+
label: t.gitWorkingTree,
|
|
216
|
+
entries: [...unstaged.map((file) => ({ file, mode: "worktree" as const })), ...untracked.map((file) => ({ file, mode: "untracked" as const }))],
|
|
217
|
+
},
|
|
218
|
+
].filter((area) => area.entries.length > 0);
|
|
219
|
+
|
|
175
220
|
const openFile = (f: GitFileEntry, mode: "staged" | "worktree" | "untracked", label: string) => changeView({ kind: "file", path: f.path, mode, label });
|
|
176
221
|
|
|
177
222
|
const headerTitle = view
|
|
@@ -247,6 +292,39 @@ export function GitPanel({
|
|
|
247
292
|
)}
|
|
248
293
|
{!view && info?.isRepo && (
|
|
249
294
|
<div className="flex flex-col gap-4">
|
|
295
|
+
{/* GitHub 身份:头像 + 账号(取不到就整行不出现) */}
|
|
296
|
+
{identity && (
|
|
297
|
+
<div className="flex items-center gap-2">
|
|
298
|
+
{identity.avatar ? (
|
|
299
|
+
// eslint-disable-next-line @next/next/no-img-element
|
|
300
|
+
<img src={identity.avatar} alt="" width={22} height={22} className="flex-none rounded-full" />
|
|
301
|
+
) : (
|
|
302
|
+
<span
|
|
303
|
+
className="grid flex-none place-items-center rounded-full"
|
|
304
|
+
style={{ width: 22, height: 22, background: "var(--dsw-selector)", fontSize: 11, fontWeight: 600, color: "var(--dsw-label-secondary)" }}
|
|
305
|
+
aria-hidden
|
|
306
|
+
>
|
|
307
|
+
{identity.login.slice(0, 1).toUpperCase()}
|
|
308
|
+
</span>
|
|
309
|
+
)}
|
|
310
|
+
<a
|
|
311
|
+
href={identity.url ?? `https://github.com/${identity.login}`}
|
|
312
|
+
target="_blank"
|
|
313
|
+
rel="noreferrer"
|
|
314
|
+
className="min-w-0 truncate"
|
|
315
|
+
style={{ fontSize: 12.5, color: "var(--dsw-label-secondary)", textDecoration: "none" }}
|
|
316
|
+
title={identity.name ? `${identity.name} (@${identity.login})` : `@${identity.login}`}
|
|
317
|
+
>
|
|
318
|
+
@{identity.login}
|
|
319
|
+
</a>
|
|
320
|
+
{identity.repo && (
|
|
321
|
+
<span className="min-w-0 truncate" style={{ fontSize: 11.5, color: "var(--dsw-label-caption)" }} title={identity.repo}>
|
|
322
|
+
· {identity.repo}
|
|
323
|
+
</span>
|
|
324
|
+
)}
|
|
325
|
+
</div>
|
|
326
|
+
)}
|
|
327
|
+
|
|
250
328
|
{/* 分支行 */}
|
|
251
329
|
<div className="flex flex-wrap items-center gap-2" style={{ fontSize: 12.5 }}>
|
|
252
330
|
<span
|
|
@@ -280,45 +358,37 @@ export function GitPanel({
|
|
|
280
358
|
{error ? null : info.files.length === 0 ? (
|
|
281
359
|
<div style={{ fontSize: 12.5, color: "var(--dsw-label-caption)" }}>{t.gitNoChanges}</div>
|
|
282
360
|
) : (
|
|
283
|
-
|
|
284
|
-
{
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
{
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
{f.path}
|
|
315
|
-
</span>
|
|
316
|
-
</button>
|
|
317
|
-
))}
|
|
318
|
-
</div>
|
|
319
|
-
</div>
|
|
320
|
-
),
|
|
321
|
-
)
|
|
361
|
+
areas.map((area) => (
|
|
362
|
+
<div key={area.key}>
|
|
363
|
+
<div className="mb-1 flex items-center gap-2" style={{ fontSize: 11.5, color: area.danger ? "var(--dsw-danger)" : "var(--dsw-label-caption)" }}>
|
|
364
|
+
<span style={{ fontWeight: 600 }}>{area.label}</span>
|
|
365
|
+
<span>{area.entries.length}</span>
|
|
366
|
+
</div>
|
|
367
|
+
<div className="flex flex-col">
|
|
368
|
+
{area.entries.map(({ file: f, mode }) => (
|
|
369
|
+
<button
|
|
370
|
+
key={`${area.key}:${f.path}`}
|
|
371
|
+
type="button"
|
|
372
|
+
className="flex w-full items-center gap-2 rounded-lg px-2 py-1 text-left transition-colors"
|
|
373
|
+
title={f.path}
|
|
374
|
+
onMouseEnter={(e) => (e.currentTarget.style.background = "var(--dsw-hover)")}
|
|
375
|
+
onMouseLeave={(e) => (e.currentTarget.style.background = "transparent")}
|
|
376
|
+
onClick={() => openFile(f, mode, mode === "staged" ? t.gitStagedDiff : mode === "untracked" ? t.gitUntracked : t.gitWorktreeDiff)}
|
|
377
|
+
>
|
|
378
|
+
<span
|
|
379
|
+
className="flex-none rounded px-1 text-center"
|
|
380
|
+
style={{ minWidth: 16, fontSize: 10.5, fontFamily: "var(--font-mono)", fontWeight: 600, color: KIND_COLOR[f.kind], background: "var(--dsw-hover)" }}
|
|
381
|
+
>
|
|
382
|
+
{f.kind === "untracked" ? "?" : f.kind === "conflict" ? "!" : mode === "staged" ? f.indexStatus : f.workStatus !== " " ? f.workStatus : f.indexStatus}
|
|
383
|
+
</span>
|
|
384
|
+
<span className="min-w-0 flex-1 truncate" style={{ fontSize: 12, fontFamily: "var(--font-mono)", color: "var(--dsw-label-secondary)" }}>
|
|
385
|
+
{f.path}
|
|
386
|
+
</span>
|
|
387
|
+
</button>
|
|
388
|
+
))}
|
|
389
|
+
</div>
|
|
390
|
+
</div>
|
|
391
|
+
))
|
|
322
392
|
)}
|
|
323
393
|
|
|
324
394
|
{/* 最近提交 */}
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* 生成中呼吸态、新步入场动画、窗口化渲染、键盘导航(↑↓ 移动,←→ 收展,Enter 打开)。
|
|
7
7
|
*/
|
|
8
8
|
import { memo, useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
9
|
-
import { IconAtOutline16, IconFileOutline16, IconTriangleRightFill14 } from "@/components/icons";
|
|
9
|
+
import { IconAtOutline16, IconFileOutline16, IconFolderClose16, IconTriangleRightFill14 } from "@/components/icons";
|
|
10
10
|
import { useI18n } from "@/i18n";
|
|
11
11
|
import { fileIconSrc, folderIconSrc } from "@/lib/file-icons";
|
|
12
12
|
import { flattenTree, treeNodeKey, type FlatRow, type TreeNode } from "@/lib/growth-tree";
|
|
@@ -182,14 +182,16 @@ export const GrowthTree = memo(function GrowthTree({
|
|
|
182
182
|
selected={row.node.kind === "file" && selectedPath === row.node.path}
|
|
183
183
|
focused={focused === treeNodeKey(row.node)}
|
|
184
184
|
onActivate={activate}
|
|
185
|
+
onOpenFolder={onOpenFile}
|
|
185
186
|
onFocusRow={setFocused}
|
|
186
187
|
onReference={onReference}
|
|
187
188
|
onOpenEditor={onOpenEditor}
|
|
188
189
|
pendingLabel={t.growthPending}
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
190
|
+
lazyLabel={t.growthLazyDir}
|
|
191
|
+
deletedLabel={t.growthStatD}
|
|
192
|
+
referenceLabel={t.fileReference}
|
|
192
193
|
editorLabel={t.fileOpenEditor}
|
|
194
|
+
openFolderLabel={t.viewerOpenFolder}
|
|
193
195
|
/>
|
|
194
196
|
))}
|
|
195
197
|
</div>
|
|
@@ -205,6 +207,7 @@ const TreeRow = memo(function TreeRow({
|
|
|
205
207
|
selected,
|
|
206
208
|
focused,
|
|
207
209
|
onActivate,
|
|
210
|
+
onOpenFolder,
|
|
208
211
|
onFocusRow,
|
|
209
212
|
onReference,
|
|
210
213
|
onOpenEditor,
|
|
@@ -213,6 +216,7 @@ const TreeRow = memo(function TreeRow({
|
|
|
213
216
|
deletedLabel,
|
|
214
217
|
referenceLabel,
|
|
215
218
|
editorLabel,
|
|
219
|
+
openFolderLabel,
|
|
216
220
|
}: {
|
|
217
221
|
row: FlatRow;
|
|
218
222
|
top: number;
|
|
@@ -220,6 +224,7 @@ const TreeRow = memo(function TreeRow({
|
|
|
220
224
|
selected: boolean;
|
|
221
225
|
focused: boolean;
|
|
222
226
|
onActivate: (row: FlatRow) => void;
|
|
227
|
+
onOpenFolder: (node: TreeNode) => void;
|
|
223
228
|
onFocusRow: (path: string) => void;
|
|
224
229
|
onReference?: (path: string) => void;
|
|
225
230
|
onOpenEditor?: (path: string) => void;
|
|
@@ -228,6 +233,7 @@ const TreeRow = memo(function TreeRow({
|
|
|
228
233
|
deletedLabel: string;
|
|
229
234
|
referenceLabel: string;
|
|
230
235
|
editorLabel: string;
|
|
236
|
+
openFolderLabel: string;
|
|
231
237
|
}) {
|
|
232
238
|
const { node, depth, expanded } = row;
|
|
233
239
|
const isDir = node.kind === "dir";
|
|
@@ -253,6 +259,10 @@ const TreeRow = memo(function TreeRow({
|
|
|
253
259
|
onFocusRow(treeNodeKey(node));
|
|
254
260
|
onActivate(row);
|
|
255
261
|
}}
|
|
262
|
+
onDoubleClick={() => {
|
|
263
|
+
// 目录双击 = 打开文件夹预览(单击仍是展开/收起,不动既有习惯)
|
|
264
|
+
if (isDir) onOpenFolder(node);
|
|
265
|
+
}}
|
|
256
266
|
>
|
|
257
267
|
<span className="pw-tree-chevron" aria-hidden>
|
|
258
268
|
{isDir && <IconTriangleRightFill14 size={10} style={{ transform: expanded ? "rotate(90deg)" : undefined, transition: "transform var(--ds-duration-fast)" }} />}
|
|
@@ -264,8 +274,23 @@ const TreeRow = memo(function TreeRow({
|
|
|
264
274
|
{node.status === "R" && node.from && <span className="pw-tree-from">← {node.from.split("/").pop()}</span>}
|
|
265
275
|
</span>
|
|
266
276
|
{node.pending && <span className="pw-tree-pulse" aria-label={pendingLabel} />}
|
|
267
|
-
{!node.lazy && (onReference || (!isDir && onOpenEditor)) && (
|
|
277
|
+
{!node.lazy && (onReference || (!isDir && onOpenEditor) || isDir) && (
|
|
268
278
|
<span className="pw-tree-actions">
|
|
279
|
+
{isDir && (
|
|
280
|
+
<button
|
|
281
|
+
type="button"
|
|
282
|
+
className="icon-btn"
|
|
283
|
+
style={{ width: 18, height: 18, borderRadius: 5 }}
|
|
284
|
+
title={openFolderLabel}
|
|
285
|
+
aria-label={`${openFolderLabel}: ${node.path}`}
|
|
286
|
+
onClick={(e) => {
|
|
287
|
+
e.stopPropagation();
|
|
288
|
+
onOpenFolder(node);
|
|
289
|
+
}}
|
|
290
|
+
>
|
|
291
|
+
<IconFolderClose16 size={11} />
|
|
292
|
+
</button>
|
|
293
|
+
)}
|
|
269
294
|
{onReference && (
|
|
270
295
|
<button
|
|
271
296
|
type="button"
|
|
@@ -27,6 +27,9 @@ export function ModelSelector({
|
|
|
27
27
|
onSelectLevel,
|
|
28
28
|
open: openProp,
|
|
29
29
|
onOpenChange,
|
|
30
|
+
modelOnly = false,
|
|
31
|
+
disabled = false,
|
|
32
|
+
defaultOption,
|
|
30
33
|
}: {
|
|
31
34
|
model?: { provider: string; id: string; name: string };
|
|
32
35
|
thinkingLevel?: string;
|
|
@@ -38,6 +41,10 @@ export function ModelSelector({
|
|
|
38
41
|
onSelectLevel: (level: string) => void;
|
|
39
42
|
open?: boolean;
|
|
40
43
|
onOpenChange?: (open: boolean) => void;
|
|
44
|
+
/** Compact consumers can show just the model list. */
|
|
45
|
+
modelOnly?: boolean;
|
|
46
|
+
disabled?: boolean;
|
|
47
|
+
defaultOption?: { label: string; selected: boolean; onSelect: () => void };
|
|
41
48
|
}) {
|
|
42
49
|
const [openState, setOpenState] = useState(false);
|
|
43
50
|
const open = openProp ?? openState;
|
|
@@ -104,10 +111,10 @@ export function ModelSelector({
|
|
|
104
111
|
|
|
105
112
|
const hasModel = Boolean(model?.id);
|
|
106
113
|
const modelName = hasModel ? (model?.name || model?.id || "") : "";
|
|
107
|
-
const effortLabel = hasModel && thinkingLevel && thinkingLevel !== "off" ? thinkingLevel : undefined;
|
|
108
|
-
const triggerTitle = hasModel
|
|
114
|
+
const effortLabel = !modelOnly && hasModel && thinkingLevel && thinkingLevel !== "off" ? thinkingLevel : undefined;
|
|
115
|
+
const triggerTitle = (defaultOption?.selected ? `${defaultOption.label} · ` : "") + (hasModel
|
|
109
116
|
? `${providerNames[model?.provider ?? ""] ?? model?.provider}/${modelName}${effortLabel ? ` · ${effortLabel}` : ""}`
|
|
110
|
-
: t.selectModel;
|
|
117
|
+
: t.selectModel);
|
|
111
118
|
|
|
112
119
|
return (
|
|
113
120
|
<div ref={ref} className="relative">
|
|
@@ -119,6 +126,7 @@ export function ModelSelector({
|
|
|
119
126
|
aria-label={triggerTitle}
|
|
120
127
|
aria-haspopup="menu"
|
|
121
128
|
aria-expanded={open}
|
|
129
|
+
disabled={disabled}
|
|
122
130
|
onClick={() => {
|
|
123
131
|
setOpen(!open);
|
|
124
132
|
setPane("model");
|
|
@@ -198,6 +206,14 @@ export function ModelSelector({
|
|
|
198
206
|
{pane === "model" && (
|
|
199
207
|
<div className="flex min-h-0 flex-col">
|
|
200
208
|
<div ref={listRef} className="max-h-[340px] overflow-y-auto py-1">
|
|
209
|
+
{defaultOption && (
|
|
210
|
+
<button type="button" role="menuitemradio" aria-checked={defaultOption.selected} data-selected={defaultOption.selected}
|
|
211
|
+
className="flex min-h-10 w-full items-center justify-between gap-3 rounded-[10px] px-3 py-2 text-left"
|
|
212
|
+
style={{ fontSize: 13, background: defaultOption.selected ? "var(--dsw-active)" : undefined }}
|
|
213
|
+
onClick={() => { defaultOption.onSelect(); setOpen(false); }}>
|
|
214
|
+
{defaultOption.label}{defaultOption.selected && <IconCheckOutline14 size={14} />}
|
|
215
|
+
</button>
|
|
216
|
+
)}
|
|
201
217
|
{groups.length === 0 && (
|
|
202
218
|
<div className="px-3 py-3" style={{ fontSize: 13, color: "var(--dsw-label-caption)" }}>
|
|
203
219
|
{t.emptyModels}
|
|
@@ -213,7 +229,7 @@ export function ModelSelector({
|
|
|
213
229
|
{g.label}
|
|
214
230
|
</div>
|
|
215
231
|
{g.models.map((m) => {
|
|
216
|
-
const selected = model?.provider === m.provider && model?.id === m.id;
|
|
232
|
+
const selected = !defaultOption?.selected && model?.provider === m.provider && model?.id === m.id;
|
|
217
233
|
return (
|
|
218
234
|
<button
|
|
219
235
|
key={`${m.provider}/${m.id}`}
|
|
@@ -250,7 +266,7 @@ export function ModelSelector({
|
|
|
250
266
|
</div>
|
|
251
267
|
{/* 思考档位入口:模型只有一档(非推理模型 / models.json 没写 reasoning: true)时也显示,
|
|
252
268
|
但置灰并说明是模型不支持,而不是让入口凭空消失 */}
|
|
253
|
-
{hasModel && (thinkingLevels.length > 1 ? (
|
|
269
|
+
{hasModel && !modelOnly && (thinkingLevels.length > 1 ? (
|
|
254
270
|
<button
|
|
255
271
|
type="button"
|
|
256
272
|
className="mt-1 flex w-full items-center justify-between gap-3 border-t px-3 pb-1 pt-2 text-left transition-colors"
|
|
@@ -84,6 +84,12 @@ function roundColor(round: GrowthEntry): string {
|
|
|
84
84
|
return "var(--dsw-warn)";
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
+
/** 这一轮有没有实际改动(轮次列表据此把没改动的轮降成次要色,改过的才显示 +/− 行数) */
|
|
88
|
+
function hasChanges(round: GrowthEntry): boolean {
|
|
89
|
+
const st = round.stats;
|
|
90
|
+
return Boolean(st.added || st.modified || st.deleted || st.renamed || st.add || st.del);
|
|
91
|
+
}
|
|
92
|
+
|
|
87
93
|
function changedLines(round: GrowthEntry): number {
|
|
88
94
|
return round.stats.add + round.stats.del;
|
|
89
95
|
}
|
|
@@ -175,7 +181,13 @@ export function ProjectPanel({
|
|
|
175
181
|
};
|
|
176
182
|
}, [cwd, gitRefreshKey, latestCommit]);
|
|
177
183
|
|
|
178
|
-
const totals = useMemo(() =>
|
|
184
|
+
const totals = useMemo(() => {
|
|
185
|
+
if (selected && growth.scope === "round") {
|
|
186
|
+
const st = selected.stats;
|
|
187
|
+
return { files: st.added + st.modified + st.deleted + st.renamed, add: st.add, del: st.del };
|
|
188
|
+
}
|
|
189
|
+
return growth.changesReady === false ? null : sumChanges(growth.changes);
|
|
190
|
+
}, [selected, growth.scope, growth.changes, growth.changesReady]);
|
|
179
191
|
const piRounds = useMemo(() => rounds.filter((round) => round.kind === "round").length, [rounds]);
|
|
180
192
|
const maxLines = useMemo(() => Math.max(1, ...rounds.map(changedLines)), [rounds]);
|
|
181
193
|
const railRef = useRef<HTMLDivElement>(null);
|
|
@@ -304,9 +316,12 @@ export function ProjectPanel({
|
|
|
304
316
|
<div className="mt-0.5 flex items-center gap-2" style={{ fontSize: 11.5, color: "var(--dsw-label-caption)" }}>
|
|
305
317
|
<span>{fmtTime(selected.ts)}</span>
|
|
306
318
|
<span>·</span>
|
|
307
|
-
<span>{t.
|
|
308
|
-
|
|
309
|
-
|
|
319
|
+
{growth.scope === "session" && <span>{t.growthScopeSession}</span>}
|
|
320
|
+
{totals ? <>
|
|
321
|
+
<span>{t.growthRoundFiles.replace("{n}", String(totals.files))}</span>
|
|
322
|
+
<span style={{ color: "var(--dsw-success)" }}>+{totals.add}</span>
|
|
323
|
+
<span style={{ color: "var(--dsw-danger)" }}>−{totals.del}</span>
|
|
324
|
+
</> : <span>—</span>}
|
|
310
325
|
{statusText && <span style={{ color: "var(--dsw-warn)" }}>{statusText}</span>}
|
|
311
326
|
<span className="flex-1" />
|
|
312
327
|
{onJumpToChat && selected.promptIds[0] && (
|
|
@@ -397,7 +412,7 @@ export function ProjectPanel({
|
|
|
397
412
|
data-kind={round.kind}
|
|
398
413
|
data-active={selected?.commit === round.commit || undefined}
|
|
399
414
|
style={{ height: h, background: roundColor(round) }}
|
|
400
|
-
title={`${roundTitle(round, tt)} · ${fmtTime(round.ts)} · ${t.growthRoundFiles.replace("{n}", String(round.
|
|
415
|
+
title={`${roundTitle(round, tt)} · ${fmtTime(round.ts)} · ${t.growthRoundFiles.replace("{n}", String(round.stats.added + round.stats.modified + round.stats.deleted + round.stats.renamed))} +${round.stats.add} −${round.stats.del}`}
|
|
401
416
|
onClick={() => growth.select(round.commit)}
|
|
402
417
|
/>
|
|
403
418
|
);
|
|
@@ -478,31 +493,41 @@ export function ProjectPanel({
|
|
|
478
493
|
)}
|
|
479
494
|
{/* 轮次列表:当前轮高亮,打开时自动滚到它 */}
|
|
480
495
|
<div ref={roundListRef} className="min-h-0 max-h-[260px] overflow-y-auto border-t pt-1" style={{ borderColor: "var(--dsw-border-l1)" }}>
|
|
481
|
-
{rounds.map((round) =>
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
496
|
+
{rounds.map((round) => {
|
|
497
|
+
const changed = hasChanges(round);
|
|
498
|
+
// 没改动的轮整行降对比度,扫视时自然被跳过;选中的那行不灰(否则像被禁用)
|
|
499
|
+
const dimmed = !changed && selected?.commit !== round.commit;
|
|
500
|
+
return (
|
|
501
|
+
<button
|
|
502
|
+
key={round.commit}
|
|
503
|
+
type="button"
|
|
504
|
+
role="option"
|
|
505
|
+
aria-selected={selected?.commit === round.commit}
|
|
506
|
+
data-selected={selected?.commit === round.commit || undefined}
|
|
507
|
+
className="flex w-full items-center gap-2 rounded-lg px-2.5 py-1.5 text-left transition-colors"
|
|
508
|
+
style={{ background: selected?.commit === round.commit ? "var(--dsw-active)" : "transparent", ...(dimmed ? { opacity: 0.45 } : {}) }}
|
|
509
|
+
onMouseEnter={(e) => {
|
|
510
|
+
if (selected?.commit !== round.commit) e.currentTarget.style.background = "var(--dsw-hover)";
|
|
511
|
+
}}
|
|
512
|
+
onMouseLeave={(e) => {
|
|
513
|
+
if (selected?.commit !== round.commit) e.currentTarget.style.background = "transparent";
|
|
514
|
+
}}
|
|
515
|
+
title={`${roundTitle(round, tt)} · ${fmtTime(round.ts)}`}
|
|
516
|
+
onClick={() => {
|
|
517
|
+
growth.select(round.commit);
|
|
518
|
+
setRoundPickerOpen(false);
|
|
519
|
+
}}
|
|
520
|
+
>
|
|
521
|
+
<span className="min-w-0 flex-1 truncate" style={{ fontSize: 12.5 }}>{roundTitle(round, tt)}</span>
|
|
522
|
+
{changed && (round.stats.add > 0 || round.stats.del > 0) ? (
|
|
523
|
+
<span className="flex-none" style={{ fontSize: 11, fontFamily: "var(--font-mono)" }}>
|
|
524
|
+
<span style={{ color: "var(--dsw-success)" }}>+{round.stats.add}</span> <span style={{ color: "var(--dsw-danger)" }}>−{round.stats.del}</span>
|
|
525
|
+
</span>
|
|
526
|
+
) : null}
|
|
527
|
+
<span className="flex-none" style={{ fontSize: 11, color: "var(--dsw-label-caption)" }}>{fmtTime(round.ts)}</span>
|
|
528
|
+
</button>
|
|
529
|
+
);
|
|
530
|
+
})}
|
|
506
531
|
</div>
|
|
507
532
|
</div>
|
|
508
533
|
)}
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import { useCallback, useEffect, useState } from "react";
|
|
10
10
|
import { useI18n } from "@/i18n";
|
|
11
|
+
import { fmtBytes } from "@/lib/format";
|
|
11
12
|
import { IconRefreshOutline14, IconCloseOutline14 } from "@/components/icons";
|
|
12
13
|
|
|
13
14
|
export type PromptSourceKind = "system" | "append" | "agents" | "skill" | "template" | "tool" | "assembled";
|
|
@@ -45,10 +46,7 @@ export interface PromptSourcesData {
|
|
|
45
46
|
const GROUP_KINDS: PromptSourceKind[] = ["system", "append", "agents", "skill", "template", "tool", "assembled"];
|
|
46
47
|
|
|
47
48
|
function formatBytes(bytes: number | undefined): string {
|
|
48
|
-
|
|
49
|
-
if (bytes < 1024) return `${bytes} B`;
|
|
50
|
-
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
51
|
-
return `${(bytes / 1024 / 1024).toFixed(1)} MB`;
|
|
49
|
+
return bytes ? fmtBytes(bytes) : "";
|
|
52
50
|
}
|
|
53
51
|
|
|
54
52
|
export function PromptPanel({
|
|
@@ -489,6 +489,12 @@
|
|
|
489
489
|
color: var(--dsw-danger);
|
|
490
490
|
padding: 4px 0;
|
|
491
491
|
}
|
|
492
|
+
.catalogHint {
|
|
493
|
+
margin-top: 8px;
|
|
494
|
+
font-size: 11.5px;
|
|
495
|
+
line-height: 1.5;
|
|
496
|
+
color: var(--dsw-label-caption);
|
|
497
|
+
}
|
|
492
498
|
|
|
493
499
|
/* ---- 候选选择对话框 ---- */
|
|
494
500
|
.candidateMask {
|
|
@@ -542,6 +548,20 @@
|
|
|
542
548
|
.candidateRow:hover {
|
|
543
549
|
background: var(--dsw-hover);
|
|
544
550
|
}
|
|
551
|
+
/* 已在内置目录里的候选:不可勾选 */
|
|
552
|
+
.candidateRow[data-disabled] {
|
|
553
|
+
cursor: default;
|
|
554
|
+
color: var(--dsw-label-tertiary);
|
|
555
|
+
}
|
|
556
|
+
.candidateRow[data-disabled]:hover {
|
|
557
|
+
background: transparent;
|
|
558
|
+
}
|
|
559
|
+
.candidateTag {
|
|
560
|
+
flex: none;
|
|
561
|
+
margin-left: auto;
|
|
562
|
+
font-size: 11px;
|
|
563
|
+
color: var(--dsw-label-caption);
|
|
564
|
+
}
|
|
545
565
|
.candidateId {
|
|
546
566
|
font-family: var(--font-mono);
|
|
547
567
|
font-size: 12.5px;
|