@rexvane/piweb 0.3.20 → 0.3.22
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 +6 -4
- 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 +12 -12
- package/.next/required-server-files.js +18 -2
- package/.next/required-server-files.json +18 -2
- package/.next/routes-manifest.json +12 -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 +3 -3
- 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/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 +3 -3
- 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 +1 -1
- 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 +19 -13
- package/.next/server/app/index.segments/__PAGE__.segment.rsc +25 -19
- package/.next/server/app/index.segments/_full.segment.rsc +19 -13
- package/.next/server/app/index.segments/_tree.segment.rsc +3 -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 +6 -4
- package/.next/server/chunks/229.js +1 -1
- package/.next/server/chunks/421.js +1 -0
- package/.next/server/chunks/430.js +215 -31
- package/.next/server/chunks/496.js +1 -1
- package/.next/server/chunks/538.js +1 -1
- package/.next/server/chunks/662.js +2 -0
- package/.next/server/chunks/686.js +1 -1
- package/.next/server/chunks/807.js +1 -1
- package/.next/server/chunks/813.js +23 -1
- package/.next/server/chunks/920.js +2 -2
- package/.next/server/chunks/924.js +1 -1
- package/.next/server/chunks/970.js +305 -0
- 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/static/WMazYvD-gbeLsCUfUxvvM/_buildManifest.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.4cf66cbdd8364c57.js +1 -0
- package/.next/static/chunks/4686-09fcd0028079006c.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.67366dc5032a141d.js +1 -0
- package/.next/static/chunks/8552.25c39ef4c995eba0.js +1 -0
- package/.next/static/chunks/app/_global-error/page-f977af7f858990a2.js +1 -0
- package/.next/static/chunks/app/api/agent/[id]/events/route-f977af7f858990a2.js +1 -0
- package/.next/static/chunks/app/api/agent/[id]/route-f977af7f858990a2.js +1 -0
- package/.next/static/chunks/app/api/agent/[id]/tree/route-f977af7f858990a2.js +1 -0
- package/.next/static/chunks/app/api/agent/new/route-f977af7f858990a2.js +1 -0
- package/.next/static/chunks/app/api/config/route-f977af7f858990a2.js +1 -0
- package/.next/static/chunks/app/api/files/route-f977af7f858990a2.js +1 -0
- package/.next/static/chunks/app/api/fs/route-f977af7f858990a2.js +1 -0
- package/.next/static/chunks/app/api/git/route-f977af7f858990a2.js +1 -0
- package/.next/static/chunks/app/api/github/route-f977af7f858990a2.js +1 -0
- package/.next/static/chunks/app/api/growth/route-f977af7f858990a2.js +1 -0
- package/.next/static/chunks/app/api/health/route-f977af7f858990a2.js +1 -0
- package/.next/static/chunks/app/api/models/defaults/route-f977af7f858990a2.js +1 -0
- package/.next/static/chunks/app/api/models/route-f977af7f858990a2.js +1 -0
- package/.next/static/chunks/app/api/pi-settings/route-f977af7f858990a2.js +1 -0
- package/.next/static/chunks/app/api/plugins/route-f977af7f858990a2.js +1 -0
- package/.next/static/chunks/app/api/prompts/route-f977af7f858990a2.js +1 -0
- package/.next/static/chunks/app/api/remote/route-f977af7f858990a2.js +1 -0
- package/.next/static/chunks/app/api/security/route-f977af7f858990a2.js +1 -0
- package/.next/static/chunks/app/api/sessions/[id]/export/route-f977af7f858990a2.js +1 -0
- package/.next/static/chunks/app/api/sessions/[id]/route-f977af7f858990a2.js +1 -0
- package/.next/static/chunks/app/api/sessions/import/route-f977af7f858990a2.js +1 -0
- package/.next/static/chunks/app/api/sessions/route-f977af7f858990a2.js +1 -0
- package/.next/static/chunks/app/api/skills/route-f977af7f858990a2.js +1 -0
- package/.next/static/chunks/app/api/update/route-f977af7f858990a2.js +1 -0
- package/.next/static/chunks/app/api/version/route-f977af7f858990a2.js +1 -0
- package/.next/static/chunks/app/api/web-auth/route-f977af7f858990a2.js +1 -0
- package/.next/static/chunks/app/api/workspaces/route-f977af7f858990a2.js +1 -0
- package/.next/static/chunks/app/page-fc4ed2ff29b6235e.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-f977af7f858990a2.js +1 -0
- package/.next/static/chunks/next/dist/client/components/builtin/forbidden-f977af7f858990a2.js +1 -0
- package/.next/static/chunks/next/dist/client/components/builtin/not-found-f977af7f858990a2.js +1 -0
- package/.next/static/chunks/next/dist/client/components/builtin/unauthorized-f977af7f858990a2.js +1 -0
- package/.next/static/chunks/webpack-22ebffc20adb44ae.js +1 -0
- package/.next/static/css/6671d0d1589fdc0e.css +1 -0
- package/.next/static/css/{2959bd6b626b7ffe.css → f15a853af8b5127f.css} +1 -1
- 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]/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 +3 -1
- package/.next/types/validator.ts +76 -2
- package/README.md +14 -5
- package/next.config.ts +4 -2
- package/package.json +2 -2
- package/scripts/release.mjs +1 -1
- package/src/app/api/agent/[id]/events/route.ts +42 -13
- 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/api/update/route.ts +18 -2
- package/src/app/globals.css +275 -0
- package/src/components/AppShell.tsx +89 -33
- package/src/components/ChatInput.tsx +245 -63
- package/src/components/ChatWindow.tsx +123 -108
- package/src/components/ContextMeter.tsx +204 -123
- package/src/components/DrawerPanel.tsx +19 -26
- package/src/components/ExtensionUI.tsx +8 -2
- package/src/components/GitPanel.tsx +110 -40
- package/src/components/MarkdownBlock.tsx +333 -37
- 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 +365 -80
- package/src/components/TrajectoryView.tsx +4 -6
- package/src/components/chat/chat-scroll.ts +18 -0
- package/src/components/composer/ComposerImages.tsx +306 -0
- package/src/components/update/CopyCommand.tsx +34 -0
- package/src/components/update/UpdateNotice.tsx +130 -0
- package/src/components/update/update-store.ts +155 -0
- package/src/hooks/useGrowth.ts +3 -0
- package/src/hooks/usePiWeb.ts +54 -27
- package/src/hooks/useWorkspaceFiles.ts +93 -0
- package/src/i18n.tsx +182 -6
- package/src/instrumentation-node.ts +3 -0
- package/src/lib/agent-manager.ts +193 -119
- package/src/lib/chat/event-stream.ts +113 -0
- package/src/lib/chat/stream-settings.ts +192 -0
- package/src/lib/files-service.ts +100 -1
- 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/pi.ts +27 -2
- package/src/lib/process-format.ts +3 -2
- package/src/lib/session/metrics.ts +31 -0
- package/src/lib/thinking-level.ts +39 -0
- package/src/lib/trajectory.ts +17 -3
- package/src/lib/types.ts +19 -1
- package/src/lib/update/startup-check.ts +117 -0
- package/src/lib/update-service.ts +23 -3
- package/src/lib/workspace-index.ts +270 -0
- package/.next/server/chunks/131.js +0 -1
- package/.next/server/chunks/243.js +0 -1
- package/.next/server/chunks/741.js +0 -165
- package/.next/static/_Y3Lq0lI5RJ1tFSOnDi0L/_buildManifest.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-b1e9fa876d0ea671.js +0 -1
- package/.next/static/chunks/4778.e4c9ed093a413bc4.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.d3fe337a9117c325.js +0 -1
- package/.next/static/chunks/8552.6bfe3e101250b20f.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-2e425ef83fba0782.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-3614a8337843456b.js +0 -1
- package/.next/static/css/7391c42e1ae617f9.css +0 -1
- /package/.next/static/{_Y3Lq0lI5RJ1tFSOnDi0L → WMazYvD-gbeLsCUfUxvvM}/_ssgManifest.js +0 -0
|
@@ -28,7 +28,9 @@ import { DiffView, type DiffLine, parseUnifiedDiff } from "@/components/DiffView
|
|
|
28
28
|
import { languageForPath } from "@/lib/highlight";
|
|
29
29
|
import { copyText } from "@/lib/clipboard";
|
|
30
30
|
import { OutlineRail } from "@/components/OutlineRail";
|
|
31
|
+
import { ChatScrollContext, type ChatScroll } from "@/components/chat/chat-scroll";
|
|
31
32
|
import { useI18n } from "@/i18n";
|
|
33
|
+
import { fmtDuration, fmtPercent, fmtThroughput, fmtTokens } from "@/lib/format";
|
|
32
34
|
import type { ToolCardState } from "@/hooks/usePiWeb";
|
|
33
35
|
import type { ContextResource, TrajEntry, TrajTokens, WebContent, WebMessage, WebStats } from "@/lib/types";
|
|
34
36
|
import {
|
|
@@ -127,16 +129,16 @@ function TurnMetaPill({ durationMs, usage, t }: { durationMs: number; usage?: Tr
|
|
|
127
129
|
<>
|
|
128
130
|
<div className="flex items-center justify-between gap-2">
|
|
129
131
|
<span style={{ color: "var(--dsw-label-caption)" }}>{t.inputTokens}</span>
|
|
130
|
-
<span>{
|
|
132
|
+
<span>{fmtTokens(billed)}</span>
|
|
131
133
|
</div>
|
|
132
134
|
<div className="flex items-center justify-between gap-2">
|
|
133
135
|
<span style={{ color: "var(--dsw-label-caption)" }}>{t.outputTokens}</span>
|
|
134
|
-
<span>{
|
|
136
|
+
<span>{fmtTokens(usage.output ?? 0)}</span>
|
|
135
137
|
</div>
|
|
136
138
|
{(usage.cacheRead ?? 0) + (usage.cacheWrite ?? 0) > 0 && (
|
|
137
139
|
<div className="flex items-center justify-between gap-2">
|
|
138
140
|
<span style={{ color: "var(--dsw-label-caption)" }}>{t.cacheHits}</span>
|
|
139
|
-
<span>{
|
|
141
|
+
<span>{fmtPercent(((usage.cacheRead ?? 0) / Math.max(billed, 1)) * 100)}</span>
|
|
140
142
|
</div>
|
|
141
143
|
)}
|
|
142
144
|
</>
|
|
@@ -614,9 +616,13 @@ const ThinkStep = memo(function ThinkStep({ text, live, startedAt }: { text: str
|
|
|
614
616
|
// ---------- 叙述:模型在工具之间说的话,作为阶段标题(基线对齐,主色) ----------
|
|
615
617
|
|
|
616
618
|
const NarrationBlock = memo(function NarrationBlock({ text, live, error }: { text: string; live?: boolean; error?: boolean }) {
|
|
619
|
+
// 流过的这一段一直交给流式组件:live 结束后它先把还没放出来的字放完,再自己换成整段渲染。
|
|
620
|
+
// 一结束就换成 <Markdown>,打字机这类慢样式积压的字会一口吐出来。
|
|
621
|
+
const [streamed, setStreamed] = useState(Boolean(live));
|
|
622
|
+
if (live && !streamed) setStreamed(true);
|
|
617
623
|
return (
|
|
618
624
|
<div className="pw-narr" data-error={error || undefined}>
|
|
619
|
-
{
|
|
625
|
+
{streamed ? <StreamingMarkdown text={text} live={Boolean(live)} /> : <Markdown text={text} />}
|
|
620
626
|
</div>
|
|
621
627
|
);
|
|
622
628
|
});
|
|
@@ -705,7 +711,7 @@ function WorkingStep({ startedAt, outputTokens, workingMessage, hasLiveStep }: {
|
|
|
705
711
|
<div className="pw-line">
|
|
706
712
|
{label ? <span className="pw-shimmer">{label}</span> : <span className="pw-verb">{fillBold(WORKING_FOR, { d: fmtSpan(elapsed) })}</span>}
|
|
707
713
|
{label && <span className="pw-meta">{fmtSpan(elapsed)}</span>}
|
|
708
|
-
{outputTokens ? <span className="pw-meta">↓ {
|
|
714
|
+
{outputTokens ? <span className="pw-meta">↓ {fmtTokens(outputTokens)} tokens</span> : null}
|
|
709
715
|
</div>
|
|
710
716
|
</Step>
|
|
711
717
|
);
|
|
@@ -1238,22 +1244,6 @@ function TurnLite({ turn, messages }: { turn: Turn; messages: WebMessage[] }) {
|
|
|
1238
1244
|
);
|
|
1239
1245
|
}
|
|
1240
1246
|
|
|
1241
|
-
function fmtTok(n: number): string {
|
|
1242
|
-
const scaled = (value: number) => value >= 100 ? String(Math.round(value)) : String(Math.round(value * 10) / 10);
|
|
1243
|
-
return n >= 1_000_000 ? `${scaled(n / 1_000_000)}M` : n >= 1000 ? `${scaled(n / 1000)}K` : String(n);
|
|
1244
|
-
}
|
|
1245
|
-
|
|
1246
|
-
function fmtDuration(ms: number): string {
|
|
1247
|
-
const seconds = ms / 1000;
|
|
1248
|
-
if (seconds < 60) return `${Math.round(seconds * 10) / 10}s`;
|
|
1249
|
-
const whole = Math.round(seconds);
|
|
1250
|
-
return `${Math.floor(whole / 60)}m${whole % 60}s`;
|
|
1251
|
-
}
|
|
1252
|
-
|
|
1253
|
-
function fmtThroughput(tokensPerSecond: number): string {
|
|
1254
|
-
return tokensPerSecond >= 10 ? String(Math.round(tokensPerSecond)) : String(Math.round(tokensPerSecond * 10) / 10);
|
|
1255
|
-
}
|
|
1256
|
-
|
|
1257
1247
|
export function SessionStatsBar({
|
|
1258
1248
|
stats,
|
|
1259
1249
|
}: {
|
|
@@ -1267,24 +1257,30 @@ export function SessionStatsBar({
|
|
|
1267
1257
|
.replace("{steps}", String(stats.assistantMessages)),
|
|
1268
1258
|
];
|
|
1269
1259
|
const durations: string[] = [];
|
|
1270
|
-
|
|
1271
|
-
|
|
1260
|
+
const coverage = (n: number | undefined, total: number) => n !== undefined && n < total
|
|
1261
|
+
? ` (${t.statsTimedCoverage.replace("{n}", String(n)).replace("{total}", String(total))})` : "";
|
|
1262
|
+
if (stats.llmMs > 0) durations.push(`LLM ${fmtDuration(stats.llmMs)}${coverage(stats.llmTimedSteps, stats.assistantMessages)}`);
|
|
1263
|
+
if (stats.toolMs > 0) durations.push(`${t.toolCalls} ${fmtDuration(stats.toolMs)}${coverage(stats.toolTimedCalls, stats.toolCalls)}`);
|
|
1272
1264
|
if (durations.length) groups.push(durations.join(" · "));
|
|
1273
1265
|
const speeds: string[] = [];
|
|
1274
|
-
|
|
1275
|
-
if (stats.
|
|
1266
|
+
const timedLabel = (steps: number) => ` (${t.statsMeasuredSteps.replace("{n}", String(steps))})`;
|
|
1267
|
+
if (stats.ttftSteps > 0) speeds.push(`${t.ttftAverage} ${fmtDuration(stats.ttftMs / stats.ttftSteps)}${timedLabel(stats.ttftSteps)}`);
|
|
1268
|
+
if (stats.decodeMs > 0) speeds.push(`${fmtThroughput(stats.decodeTokens / (stats.decodeMs / 1000))} tok/s${timedLabel(stats.decodeSteps ?? stats.ttftSteps)}`);
|
|
1276
1269
|
if (speeds.length) groups.push(speeds.join(" · "));
|
|
1277
1270
|
const billedInput = stats.tokens.input + stats.tokens.cacheRead + stats.tokens.cacheWrite;
|
|
1278
1271
|
if (billedInput > 0 || stats.tokens.output > 0) {
|
|
1279
|
-
if (billedInput > 0) groups.push(`${t.cacheHits} ${
|
|
1280
|
-
groups.push(`${t.inputTokens} ${
|
|
1272
|
+
if (billedInput > 0) groups.push(`${t.cacheHits} ${fmtPercent((stats.tokens.cacheRead / billedInput) * 100)}`);
|
|
1273
|
+
groups.push(`${t.inputTokens} ${fmtTokens(billedInput)} tok · ${t.outputTokens} ${fmtTokens(stats.tokens.output)} tok`);
|
|
1281
1274
|
}
|
|
1282
1275
|
const line = groups.join(" | ");
|
|
1283
1276
|
return (
|
|
1284
1277
|
// 悬停显示完整统计:自定义浮层固定在统计条上方(原生 title 位置不可控)
|
|
1278
|
+
// 容器只有 544px(内容窄轴下限)而整行实测 959px,以前一律省略号截断:
|
|
1279
|
+
// 提高精度后更长,「输入 / 输出 tokens」这类用户最关心的反而看不到。
|
|
1280
|
+
// 改成允许换行:窄屏折成两行完整显示,代价是纵向多占 20px。
|
|
1285
1281
|
<div className="group relative mx-auto mt-1 block w-full px-4 pt-1 text-center">
|
|
1286
1282
|
<div
|
|
1287
|
-
className="overflow-
|
|
1283
|
+
className="[overflow-wrap:anywhere]"
|
|
1288
1284
|
style={{ fontSize: "var(--dsh-content-font-size-secondary, 13px)", lineHeight: "20px", color: "var(--dsw-label-caption)" }}
|
|
1289
1285
|
>
|
|
1290
1286
|
{line}
|
|
@@ -1425,6 +1421,16 @@ export function ChatWindow({
|
|
|
1425
1421
|
const widthPreference = useRef<number | null>(null);
|
|
1426
1422
|
const widthDrag = useRef<{ side: "left" | "right"; origin: number; width: number; pointerId: number } | null>(null);
|
|
1427
1423
|
const stickToBottom = useRef(true);
|
|
1424
|
+
/** 上一次滚动事件时的 scrollTop:比它小就是在往上走(用户翻看、大纲 / 搜索跳转) */
|
|
1425
|
+
const lastScrollTop = useRef(0);
|
|
1426
|
+
// 流式正文逐帧变高时据此决定要不要跟着贴底(见 chat/chat-scroll)
|
|
1427
|
+
const chatScroll = useMemo<ChatScroll>(() => ({
|
|
1428
|
+
shouldPin: () => stickToBottom.current,
|
|
1429
|
+
pin: () => {
|
|
1430
|
+
const node = scrollRef.current;
|
|
1431
|
+
if (node) node.scrollTop = node.scrollHeight;
|
|
1432
|
+
},
|
|
1433
|
+
}), []);
|
|
1428
1434
|
const { t } = useI18n();
|
|
1429
1435
|
useEffect(() => {
|
|
1430
1436
|
const root = rootRef.current;
|
|
@@ -1635,89 +1641,98 @@ export function ChatWindow({
|
|
|
1635
1641
|
}}
|
|
1636
1642
|
/>
|
|
1637
1643
|
))}
|
|
1638
|
-
<
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
{/* 重连提示:跟随消息流显示在最后一条输出下面 */}
|
|
1691
|
-
{!connected && (
|
|
1692
|
-
<div className="mt-3 flex justify-center">
|
|
1693
|
-
<div
|
|
1694
|
-
className="rounded-full px-3 py-1"
|
|
1695
|
-
style={{ fontSize: "var(--piweb-chat-font-t)", color: "var(--dsw-label-caption)", border: "0.5px dashed var(--dsw-border-l3)" }}
|
|
1696
|
-
>
|
|
1697
|
-
{t.reconnecting}
|
|
1644
|
+
<ChatScrollContext.Provider value={chatScroll}>
|
|
1645
|
+
<div
|
|
1646
|
+
ref={scrollRef}
|
|
1647
|
+
className="min-h-0 flex-1 overflow-y-auto"
|
|
1648
|
+
role="log"
|
|
1649
|
+
aria-live="polite"
|
|
1650
|
+
aria-relevant="additions"
|
|
1651
|
+
onScroll={(event) => {
|
|
1652
|
+
const node = event.currentTarget;
|
|
1653
|
+
const distance = node.scrollHeight - node.scrollTop - node.clientHeight;
|
|
1654
|
+
const atBottom = distance < 96;
|
|
1655
|
+
// 往上走就立刻松开贴底,不等离底 96px:流式正文每帧都会贴一次底,
|
|
1656
|
+
// 大纲 / 搜索的平滑跳转刚起步、还在 96px 以内时就会被拽回去。
|
|
1657
|
+
// 已经在最底下(内容变短被浏览器夹回来)不算往上翻。
|
|
1658
|
+
const movedUp = node.scrollTop < lastScrollTop.current - 1 && distance > 2;
|
|
1659
|
+
lastScrollTop.current = node.scrollTop;
|
|
1660
|
+
if (movedUp) stickToBottom.current = false;
|
|
1661
|
+
else stickToBottom.current = atBottom;
|
|
1662
|
+
setAwayFromBottom(!atBottom);
|
|
1663
|
+
if (atBottom) setUnseen(0);
|
|
1664
|
+
scheduleUpgradeCheck();
|
|
1665
|
+
}}
|
|
1666
|
+
>
|
|
1667
|
+
<div className="mx-auto flex w-full flex-col px-4 py-6" style={{ maxWidth: "var(--dsh-chat-content-width)" }}>
|
|
1668
|
+
{turns.map((turn, ti) => (
|
|
1669
|
+
// 回合间 28px 的分隔挂在槽上:用户消息现在总是槽里的第一个子元素,它自己的 mt-7 会被 first:mt-0 吃掉
|
|
1670
|
+
<div key={turn.key} data-turn-slot={ti} ref={slotRef(ti)} className="mt-7 first:mt-0">
|
|
1671
|
+
{isNear(ti) ? (
|
|
1672
|
+
<TurnBlock
|
|
1673
|
+
turn={turn}
|
|
1674
|
+
messages={messages}
|
|
1675
|
+
tools={tools}
|
|
1676
|
+
cwd={cwd}
|
|
1677
|
+
isStreaming={isStreaming}
|
|
1678
|
+
isLast={ti === turns.length - 1}
|
|
1679
|
+
lastAssistantIndex={lastAssistantIndex}
|
|
1680
|
+
contextFiles={turn.userIndex >= 0 && turn.userIndex === firstUser ? contextFiles : undefined}
|
|
1681
|
+
retryNotice={retryNotice}
|
|
1682
|
+
streamStartedAt={streamStartedAt}
|
|
1683
|
+
runningTool={runningTool}
|
|
1684
|
+
outputTokens={stats?.tokens.output}
|
|
1685
|
+
workingMessage={workingMessage}
|
|
1686
|
+
onFork={onFork}
|
|
1687
|
+
onEditMessage={onEditMessage}
|
|
1688
|
+
roundBadge={turn.userIndex >= 0 ? roundBadges?.get(messages[turn.userIndex].id ?? "") : undefined}
|
|
1689
|
+
onShowRound={onShowRound}
|
|
1690
|
+
onInspectTool={onOpenTrajectory}
|
|
1691
|
+
onOpenFile={onOpenFile}
|
|
1692
|
+
/>
|
|
1693
|
+
) : (
|
|
1694
|
+
<TurnLite turn={turn} messages={messages} />
|
|
1695
|
+
)}
|
|
1698
1696
|
</div>
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
<span>{compaction.phase === "start" ? t.compacting : compaction.errorMessage ? `${t.compactionFailed}: ${compaction.errorMessage}` : t.compactionComplete}</span>
|
|
1705
|
-
{compaction.phase === "end" && <button type="button" onClick={onClearCompaction} aria-label={t.close} className="icon-btn">×</button>}
|
|
1697
|
+
))}
|
|
1698
|
+
{/* 还没有任何消息就已在流式(极少见):单独给一条工作指示 */}
|
|
1699
|
+
{isStreaming && streamStartedAt && turns.length === 0 && (
|
|
1700
|
+
<div className="pw-turn">
|
|
1701
|
+
<WorkingStep startedAt={streamStartedAt} outputTokens={stats?.tokens.output} workingMessage={workingMessage} hasLiveStep={Boolean(runningTool)} />
|
|
1706
1702
|
</div>
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1703
|
+
)}
|
|
1704
|
+
{/* 重连提示:跟随消息流显示在最后一条输出下面 */}
|
|
1705
|
+
{!connected && (
|
|
1706
|
+
<div className="mt-3 flex justify-center">
|
|
1707
|
+
<div
|
|
1708
|
+
className="rounded-full px-3 py-1"
|
|
1709
|
+
style={{ fontSize: "var(--piweb-chat-font-t)", color: "var(--dsw-label-caption)", border: "0.5px dashed var(--dsw-border-l3)" }}
|
|
1710
|
+
>
|
|
1711
|
+
{t.reconnecting}
|
|
1712
|
+
</div>
|
|
1713
|
+
</div>
|
|
1714
|
+
)}
|
|
1715
|
+
{compaction && (
|
|
1716
|
+
<div className="mt-3 flex justify-center" role="status">
|
|
1717
|
+
<div className="flex items-center gap-2 rounded-xl px-3 py-2" style={{ fontSize: "var(--piweb-chat-font-s)", background: "var(--dsw-hover)", color: "var(--dsw-label-secondary)" }}>
|
|
1718
|
+
<span>{compaction.phase === "start" ? t.compacting : compaction.errorMessage ? `${t.compactionFailed}: ${compaction.errorMessage}` : t.compactionComplete}</span>
|
|
1719
|
+
{compaction.phase === "end" && <button type="button" onClick={onClearCompaction} aria-label={t.close} className="icon-btn">×</button>}
|
|
1720
|
+
</div>
|
|
1721
|
+
</div>
|
|
1722
|
+
)}
|
|
1723
|
+
{error && (
|
|
1724
|
+
<button
|
|
1725
|
+
className="mt-3 rounded-xl px-3 py-2 text-left"
|
|
1726
|
+
style={{ fontSize: "var(--piweb-chat-font-s)", background: "var(--dsw-danger)", color: "white" }}
|
|
1727
|
+
onClick={onClearError}
|
|
1728
|
+
role="alert"
|
|
1729
|
+
>
|
|
1730
|
+
{error}
|
|
1731
|
+
</button>
|
|
1732
|
+
)}
|
|
1733
|
+
</div>
|
|
1719
1734
|
</div>
|
|
1720
|
-
</
|
|
1735
|
+
</ChatScrollContext.Provider>
|
|
1721
1736
|
{/* 右侧大纲导航:从助手回答的标题和用户消息生成,随滚动高亮,悬停展开 */}
|
|
1722
1737
|
<OutlineRail scrollRef={scrollRef} revision={`${messages.length}:${nearTurns.size}:${isStreaming ? 1 : 0}`} />
|
|
1723
1738
|
{/* 脱离底部时的“回到底部”浮钮:与输入框卡片同轴,挂在右上角顶部,图标带新内容角标 */}
|