@herbertgao/pi-extensions 2026.9.19 → 2026.10.0
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/README.md +14 -16
- package/THIRD_PARTY_NOTICES.md +29 -3
- package/node_modules/@herbertgao/pi-bark/README.md +39 -3
- package/node_modules/@herbertgao/pi-bark/package.json +1 -1
- package/node_modules/@herbertgao/pi-bark/src/index.ts +275 -15
- package/node_modules/@herbertgao/pi-cc-extensions/README.en.md +85 -68
- package/node_modules/@herbertgao/pi-cc-extensions/README.md +79 -62
- package/node_modules/@herbertgao/pi-cc-extensions/assets/readme/preview.webp +0 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/config.ts +116 -34
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/panel.ts +370 -118
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/agent-summary/core.ts +6 -36
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/agent-summary/index.ts +2 -2
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/compact-thinking.ts +31 -33
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/context.ts +330 -360
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/reference/index.ts +56 -108
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/reference/session.ts +97 -15
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/reference/subagent.ts +80 -45
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/shell/flush-docked-bash.ts +36 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/shell/footer-layout.ts +229 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/shell/footer.ts +521 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/shell/mcp-chip.ts +136 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/shell/startup-header.ts +92 -61
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/shell/working-message.ts +58 -38
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/index.ts +19 -3
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/compact-mode.ts +1127 -231
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/default-mode.ts +267 -262
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/index.ts +38 -48
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/markdown-enhance.ts +8 -2
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/hover.ts +2 -7
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/interaction.ts +287 -114
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/layout.ts +15 -6
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/packets.ts +18 -72
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/scroll.ts +61 -26
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/codemode.ts +303 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/ansi-utils.ts +25 -57
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-component.ts +125 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-edit-render.ts +227 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-header.ts +136 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-highlight.ts +151 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-inline.ts +324 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-layout.ts +762 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-limits.ts +152 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-palette.ts +410 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-parse.ts +535 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-presentation.ts +4 -4
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-renderer.ts +21 -3219
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-text.ts +44 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-write-render.ts +568 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/index.ts +103 -48
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/line-width-safety.ts +8 -7
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/write-execution.ts +2 -1
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/grouping.ts +171 -196
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/mcp-title.ts +39 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{message-display.ts → tool/message-display.ts} +27 -93
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/names.ts +429 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/result.ts +259 -164
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{show-more-hint.ts → tool/show-more-hint.ts} +10 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/transcript-refresh.ts +8 -26
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/utils/ansi-color.ts +44 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/utils/ansi-text.ts +77 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/utils/component-tree.ts +34 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/utils/format.ts +28 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/utils/fullscreen-detect.ts +51 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/utils/patch-keys.ts +124 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/utils/sgr-mouse.ts +70 -0
- package/node_modules/@herbertgao/pi-cc-extensions/package.json +10 -8
- package/node_modules/@herbertgao/pi-subagents/CHANGELOG.md +6 -0
- package/node_modules/@herbertgao/pi-subagents/package.json +1 -1
- package/node_modules/@herbertgao/pi-subagents/src/mention-clone.ts +3 -2
- package/node_modules/@herbertgao/sol-pi/CONTRIBUTING.md +22 -0
- package/node_modules/@herbertgao/sol-pi/README.md +36 -17
- package/node_modules/@herbertgao/sol-pi/THIRD_PARTY_NOTICES.md +10 -6
- package/node_modules/@herbertgao/sol-pi/agents-install.md +15 -12
- package/node_modules/@herbertgao/sol-pi/docs/compatibility.md +24 -8
- package/node_modules/@herbertgao/sol-pi/docs/configuration.md +2 -2
- package/node_modules/@herbertgao/sol-pi/docs/star-history.md +18 -0
- package/node_modules/@herbertgao/sol-pi/package.json +4 -2
- package/node_modules/@herbertgao/sol-pi/scripts/check-sol-pi-config.mjs +1 -1
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/config.ts +1 -1
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/action-fusion/file-queue.ts +23 -4
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/action-fusion/index.ts +32 -1
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/action-fusion/then-run.ts +3 -2
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/observation-pack/observation.ts +1 -1
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/online-context-compact/economics.ts +27 -7
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/online-context-compact/extension.ts +137 -41
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/online-context-compact/index.ts +1 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/online-context-compact/plan.ts +2 -1
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/online-context-compact/projection.ts +58 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/online-context-compact/state.ts +28 -6
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/online-context-compact/tools.ts +2 -1
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/runtime-paths.ts +15 -2
- package/node_modules/@juicesharp/rpiv-ask-user-question/package.json +2 -2
- package/node_modules/@pi-plugins/fast-mode/README.md +7 -1
- package/node_modules/@pi-plugins/fast-mode/dist/index.d.mts.map +1 -1
- package/node_modules/@pi-plugins/fast-mode/dist/index.mjs +7 -1
- package/node_modules/@pi-plugins/fast-mode/dist/index.mjs.map +1 -1
- package/node_modules/@pi-plugins/fast-mode/package.json +5 -5
- package/node_modules/pi-antigravity/README.md +3 -0
- package/node_modules/pi-antigravity/package.json +8 -8
- package/node_modules/pi-antigravity/src/auth/oauth.ts +6 -3
- package/node_modules/pi-antigravity/src/index.ts +114 -110
- package/node_modules/pi-antigravity/src/stream/stream.ts +1 -1
- package/node_modules/pi-antigravity/src/utils/util.ts +18 -1
- package/node_modules/pi-multi-account/CHANGELOG.md +20 -0
- package/node_modules/pi-multi-account/README.md +46 -8
- package/node_modules/pi-multi-account/config-file-transaction.ts +42 -0
- package/node_modules/pi-multi-account/index.ts +255 -269
- package/node_modules/pi-multi-account/model-pins.ts +36 -0
- package/node_modules/pi-multi-account/package.json +9 -7
- package/node_modules/pi-multi-account/slot-proxy-auth.ts +12 -2
- package/node_modules/pi-multi-account/slot-proxy.ts +27 -5
- package/node_modules/pi-web-access/CHANGELOG.md +17 -0
- package/node_modules/pi-web-access/README.md +6 -3
- package/node_modules/pi-web-access/brave-rate-limit.ts +170 -0
- package/node_modules/pi-web-access/brave.ts +28 -11
- package/node_modules/pi-web-access/dist/index.js +202 -33
- package/node_modules/pi-web-access/gemini-search.ts +2 -5
- package/node_modules/pi-web-access/index.ts +13 -7
- package/node_modules/pi-web-access/openai-search.ts +24 -10
- package/node_modules/pi-web-access/package.json +1 -1
- package/node_modules/pi-web-access/summary-review.ts +8 -2
- package/node_modules/{@herbertgao/resume-from → resume-from}/README.md +16 -12
- package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/claude-code/adapter.d.ts +0 -5
- package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/claude-code/adapter.js +2 -3
- package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/claude-code/contract.d.ts +2 -2
- package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/claude-code/reader.d.ts +3 -0
- package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/claude-code/reader.js +27 -15
- package/node_modules/{@herbertgao/resume-from/dist/adapters/codex → resume-from/dist/adapters/claude-code}/redaction.js +16 -3
- package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/claude-code/writer.d.ts +2 -4
- package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/claude-code/writer.js +1 -1
- package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/codex/contract.d.ts +2 -4
- package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/codex/index.js +1 -2
- package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/codex/read.d.ts +16 -0
- package/node_modules/resume-from/dist/adapters/codex/read.js +723 -0
- package/node_modules/{@herbertgao/resume-from/dist/adapters/pi → resume-from/dist/adapters/codex}/redaction.js +16 -3
- package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/codex/rollout.d.ts +44 -4
- package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/codex/rollout.js +88 -4
- package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/codex/write.d.ts +2 -2
- package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/codex/write.js +2 -2
- package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/contract.d.ts +5 -1
- package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/pi/adapter.js +10 -6
- package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/pi/contract.d.ts +2 -2
- package/node_modules/{@herbertgao/resume-from/dist/adapters/claude-code → resume-from/dist/adapters/pi}/redaction.js +16 -3
- package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/pi/serialize.d.ts +2 -4
- package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/pi/serialize.js +2 -2
- package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/cli/render.d.ts +1 -1
- package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/cli/render.js +4 -4
- package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/cli/runner.js +2 -2
- package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/contract.d.ts +2 -2
- package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/pi-extension/command.js +31 -8
- package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/wiring.d.ts +1 -1
- package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/wiring.js +1 -2
- package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/confirmation.d.ts +4 -1
- package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/confirmation.js +2 -2
- package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/contract.d.ts +8 -6
- package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/discovery/contract.d.ts +10 -5
- package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/discovery/finder.d.ts +2 -1
- package/node_modules/resume-from/dist/import/discovery/finder.js +269 -0
- package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/landing/contract.d.ts +3 -3
- package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/landing/lander.js +3 -3
- package/node_modules/resume-from/dist/import/pipeline.d.ts +28 -0
- package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/pipeline.js +31 -11
- package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/preview/builder.d.ts +2 -2
- package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/preview/builder.js +9 -5
- package/node_modules/resume-from/dist/import/preview/warnings.d.ts +14 -0
- package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/preview/warnings.js +26 -20
- package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/wiring.js +12 -2
- package/node_modules/{@herbertgao/resume-from → resume-from}/dist/platform/repo/contract.d.ts +9 -3
- package/node_modules/{@herbertgao/resume-from → resume-from}/dist/platform/repo/git.d.ts +4 -4
- package/node_modules/{@herbertgao/resume-from → resume-from}/dist/platform/repo/git.js +7 -4
- package/node_modules/resume-from/dist/platform/repo/reader.d.ts +3 -0
- package/node_modules/{@herbertgao/resume-from → resume-from}/dist/platform/repo/reader.js +60 -20
- package/node_modules/{@herbertgao/resume-from → resume-from}/dist/session/contract.d.ts +8 -1
- package/node_modules/{@herbertgao/resume-from → resume-from}/package.json +19 -32
- package/node_modules/{@herbertgao/resume-from → resume-from}/shims/pi/extensions/resume-from.js +2 -2
- package/package.json +18 -22
- package/node_modules/@herbertgao/pi-cc-extensions/assets/readme/hero.en.svg +0 -101
- package/node_modules/@herbertgao/pi-cc-extensions/assets/readme/hero.svg +0 -101
- package/node_modules/@herbertgao/pi-cc-extensions/assets/readme/welcome.webp +0 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/render-utils.ts +0 -5
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/types.ts +0 -30
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/write-display-utils.ts +0 -12
- package/node_modules/@herbertgao/resume-from/dist/adapters/codex/read.js +0 -270
- package/node_modules/@herbertgao/resume-from/dist/import/discovery/finder.js +0 -129
- package/node_modules/@herbertgao/resume-from/dist/import/pipeline.d.ts +0 -17
- package/node_modules/@herbertgao/resume-from/dist/import/preview/warnings.d.ts +0 -11
- package/node_modules/@herbertgao/resume-from/dist/platform/repo/reader.d.ts +0 -8
- package/node_modules/pi-footer/CHANGELOG.md +0 -145
- package/node_modules/pi-footer/LICENSE +0 -21
- package/node_modules/pi-footer/README.md +0 -354
- package/node_modules/pi-footer/package.json +0 -68
- package/node_modules/pi-footer/src/cache.ts +0 -106
- package/node_modules/pi-footer/src/colors.ts +0 -237
- package/node_modules/pi-footer/src/config.ts +0 -202
- package/node_modules/pi-footer/src/event-widgets.ts +0 -41
- package/node_modules/pi-footer/src/extension-statuses.ts +0 -102
- package/node_modules/pi-footer/src/git.ts +0 -135
- package/node_modules/pi-footer/src/index.ts +0 -268
- package/node_modules/pi-footer/src/metrics.ts +0 -141
- package/node_modules/pi-footer/src/presets.ts +0 -383
- package/node_modules/pi-footer/src/render.ts +0 -114
- package/node_modules/pi-footer/src/separators.ts +0 -52
- package/node_modules/pi-footer/src/types.ts +0 -122
- package/node_modules/pi-footer/src/ui/color-level-confirm.ts +0 -22
- package/node_modules/pi-footer/src/ui/color-options.ts +0 -195
- package/node_modules/pi-footer/src/ui/config-lifecycle.ts +0 -61
- package/node_modules/pi-footer/src/ui/edit-colors.ts +0 -13
- package/node_modules/pi-footer/src/ui/events.ts +0 -35
- package/node_modules/pi-footer/src/ui/extension-status-picker.ts +0 -60
- package/node_modules/pi-footer/src/ui/extension-statuses.ts +0 -73
- package/node_modules/pi-footer/src/ui/fields.ts +0 -252
- package/node_modules/pi-footer/src/ui/global-menu.ts +0 -126
- package/node_modules/pi-footer/src/ui/helpers.ts +0 -44
- package/node_modules/pi-footer/src/ui/layout.ts +0 -17
- package/node_modules/pi-footer/src/ui/line-list.ts +0 -19
- package/node_modules/pi-footer/src/ui/model.ts +0 -64
- package/node_modules/pi-footer/src/ui/navigation.ts +0 -29
- package/node_modules/pi-footer/src/ui/option-edit.ts +0 -93
- package/node_modules/pi-footer/src/ui/overlay-render.ts +0 -103
- package/node_modules/pi-footer/src/ui/screen-context.ts +0 -18
- package/node_modules/pi-footer/src/ui/screen-controller.ts +0 -27
- package/node_modules/pi-footer/src/ui/screen-render.ts +0 -32
- package/node_modules/pi-footer/src/ui/screen-state.ts +0 -20
- package/node_modules/pi-footer/src/ui/screen.ts +0 -243
- package/node_modules/pi-footer/src/ui/screens/add-widget.ts +0 -92
- package/node_modules/pi-footer/src/ui/screens/confirm-exit.ts +0 -52
- package/node_modules/pi-footer/src/ui/screens/controller.ts +0 -12
- package/node_modules/pi-footer/src/ui/screens/edit-colors.ts +0 -68
- package/node_modules/pi-footer/src/ui/screens/edit-widget.ts +0 -115
- package/node_modules/pi-footer/src/ui/screens/extension-status-row.ts +0 -61
- package/node_modules/pi-footer/src/ui/screens/global.ts +0 -75
- package/node_modules/pi-footer/src/ui/screens/line-list.ts +0 -108
- package/node_modules/pi-footer/src/ui/screens/main.ts +0 -99
- package/node_modules/pi-footer/src/ui/screens/terminal.ts +0 -121
- package/node_modules/pi-footer/src/ui/screens/widget-list.ts +0 -154
- package/node_modules/pi-footer/src/ui/terminal-menu.ts +0 -32
- package/node_modules/pi-footer/src/ui/theme.ts +0 -48
- package/node_modules/pi-footer/src/ui/title-bar.ts +0 -100
- package/node_modules/pi-footer/src/ui/widget-actions.ts +0 -76
- package/node_modules/pi-footer/src/ui.ts +0 -66
- package/node_modules/pi-footer/src/widgets/context.ts +0 -28
- package/node_modules/pi-footer/src/widgets/core/active-tools.ts +0 -17
- package/node_modules/pi-footer/src/widgets/core/context-window.ts +0 -24
- package/node_modules/pi-footer/src/widgets/core/cwd-basename.ts +0 -19
- package/node_modules/pi-footer/src/widgets/core/cwd.ts +0 -108
- package/node_modules/pi-footer/src/widgets/core/event.ts +0 -34
- package/node_modules/pi-footer/src/widgets/core/external-status.ts +0 -102
- package/node_modules/pi-footer/src/widgets/core/model-provider.ts +0 -20
- package/node_modules/pi-footer/src/widgets/core/model.ts +0 -35
- package/node_modules/pi-footer/src/widgets/core/provider.ts +0 -17
- package/node_modules/pi-footer/src/widgets/core/session-name.ts +0 -17
- package/node_modules/pi-footer/src/widgets/core/text-verbosity.ts +0 -17
- package/node_modules/pi-footer/src/widgets/core/thinking-level.ts +0 -17
- package/node_modules/pi-footer/src/widgets/git/ahead-behind.ts +0 -17
- package/node_modules/pi-footer/src/widgets/git/branch.ts +0 -81
- package/node_modules/pi-footer/src/widgets/git/clean.ts +0 -19
- package/node_modules/pi-footer/src/widgets/git/deletions.ts +0 -17
- package/node_modules/pi-footer/src/widgets/git/diff.ts +0 -38
- package/node_modules/pi-footer/src/widgets/git/insertions.ts +0 -17
- package/node_modules/pi-footer/src/widgets/git/remote.ts +0 -17
- package/node_modules/pi-footer/src/widgets/git/root.ts +0 -17
- package/node_modules/pi-footer/src/widgets/git/sha.ts +0 -17
- package/node_modules/pi-footer/src/widgets/git/staged.ts +0 -17
- package/node_modules/pi-footer/src/widgets/git/status.ts +0 -19
- package/node_modules/pi-footer/src/widgets/git/unstaged.ts +0 -17
- package/node_modules/pi-footer/src/widgets/git/untracked.ts +0 -17
- package/node_modules/pi-footer/src/widgets/instance.ts +0 -118
- package/node_modules/pi-footer/src/widgets/layout/custom-text.ts +0 -18
- package/node_modules/pi-footer/src/widgets/layout/flex-separator.ts +0 -32
- package/node_modules/pi-footer/src/widgets/layout/separator.ts +0 -57
- package/node_modules/pi-footer/src/widgets/layout/spacer.ts +0 -31
- package/node_modules/pi-footer/src/widgets/options.ts +0 -122
- package/node_modules/pi-footer/src/widgets/project/runtime.ts +0 -361
- package/node_modules/pi-footer/src/widgets/registry.ts +0 -223
- package/node_modules/pi-footer/src/widgets/session/assistant-messages.ts +0 -17
- package/node_modules/pi-footer/src/widgets/session/compactions.ts +0 -17
- package/node_modules/pi-footer/src/widgets/session/elapsed.ts +0 -18
- package/node_modules/pi-footer/src/widgets/session/last-activity.ts +0 -18
- package/node_modules/pi-footer/src/widgets/session/messages.ts +0 -19
- package/node_modules/pi-footer/src/widgets/session/session-id.ts +0 -17
- package/node_modules/pi-footer/src/widgets/session/session-start.ts +0 -18
- package/node_modules/pi-footer/src/widgets/session/tool-results.ts +0 -17
- package/node_modules/pi-footer/src/widgets/session/total-messages.ts +0 -19
- package/node_modules/pi-footer/src/widgets/session/total-time.ts +0 -18
- package/node_modules/pi-footer/src/widgets/session/user-messages.ts +0 -17
- package/node_modules/pi-footer/src/widgets/store.ts +0 -26
- package/node_modules/pi-footer/src/widgets/tokens/cache-hit-rate.ts +0 -55
- package/node_modules/pi-footer/src/widgets/tokens/cache-read.ts +0 -18
- package/node_modules/pi-footer/src/widgets/tokens/cache-write.ts +0 -18
- package/node_modules/pi-footer/src/widgets/tokens/context-bar.ts +0 -108
- package/node_modules/pi-footer/src/widgets/tokens/context-length.ts +0 -26
- package/node_modules/pi-footer/src/widgets/tokens/context-remaining.ts +0 -29
- package/node_modules/pi-footer/src/widgets/tokens/context.ts +0 -29
- package/node_modules/pi-footer/src/widgets/tokens/cost.ts +0 -51
- package/node_modules/pi-footer/src/widgets/tokens/input-speed.ts +0 -25
- package/node_modules/pi-footer/src/widgets/tokens/input-tokens.ts +0 -18
- package/node_modules/pi-footer/src/widgets/tokens/output-speed.ts +0 -25
- package/node_modules/pi-footer/src/widgets/tokens/output-tokens.ts +0 -18
- package/node_modules/pi-footer/src/widgets/tokens/tokens.ts +0 -20
- package/node_modules/pi-footer/src/widgets/tokens/total-speed.ts +0 -25
- package/node_modules/pi-footer/src/widgets/tokens/total-tokens.ts +0 -18
- package/node_modules/pi-footer/src/widgets/types.ts +0 -181
- package/node_modules/pi-footer/src/widgets/utils/colors.ts +0 -11
- package/node_modules/pi-footer/src/widgets/utils/context.ts +0 -112
- package/node_modules/pi-footer/src/widgets/utils/session.ts +0 -19
- package/node_modules/pi-footer/src/widgets/utils/token-format.ts +0 -78
- /package/node_modules/{@herbertgao/resume-from → resume-from}/LICENSE +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/claude-code/contract.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/claude-code/entries.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/claude-code/entries.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/claude-code/index.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/claude-code/index.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/claude-code/layout.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/claude-code/layout.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/claude-code/readback.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/claude-code/readback.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/claude-code/redaction.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/claude-code/validation.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/claude-code/validation.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/codex/contract.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/codex/index.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/codex/readback.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/codex/readback.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/codex/redaction.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/codex/validation.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/codex/validation.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/contract.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/pi/adapter.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/pi/contract.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/pi/format.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/pi/format.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/pi/index.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/pi/index.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/pi/parse.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/pi/parse.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/pi/redaction.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/pi/validate.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/pi/validate.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/bin.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/bin.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/contract.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/contract.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/agents.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/agents.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/cli/args.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/cli/args.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/cli/contract.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/cli/contract.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/cli/index.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/cli/index.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/cli/presentation.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/cli/presentation.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/cli/runner.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/contract.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/entry.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/entry.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/index.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/index.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/pi-extension/command.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/pi-extension/contract.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/pi-extension/contract.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/pi-extension/index.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/pi-extension/index.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/pi-extension/picker.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/pi-extension/picker.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/pi-extension/presentation.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/pi-extension/presentation.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/pi-extension/register.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/pi-extension/register.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/pi-extension/ui.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/pi-extension/ui.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/profile.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/profile.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/registry.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/registry.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/contract.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/discovery/contract.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/discovery/errors.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/discovery/errors.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/discovery/homes.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/discovery/homes.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/discovery/index.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/discovery/index.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/discovery/ordering.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/discovery/ordering.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/errors.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/errors.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/index.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/index.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/landing/contract.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/landing/errors.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/landing/errors.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/landing/handover.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/landing/handover.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/landing/index.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/landing/index.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/landing/lander.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/landing/marker.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/landing/marker.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/preview/contract.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/preview/contract.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/preview/format.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/preview/format.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/preview/index.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/preview/index.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/transfer/contract.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/transfer/contract.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/transfer/index.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/transfer/index.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/transfer/rules.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/transfer/rules.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/wiring.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/index.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/index.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/platform/config/contract.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/platform/config/contract.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/platform/config/defaults.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/platform/config/defaults.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/platform/config/errors.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/platform/config/errors.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/platform/config/index.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/platform/config/index.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/platform/config/loader.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/platform/config/loader.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/platform/config/paths.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/platform/config/paths.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/platform/config/validate.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/platform/config/validate.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/platform/repo/contract.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/platform/repo/index.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/platform/repo/index.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/platform/store/contract.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/platform/store/contract.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/platform/store/file-committer.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/platform/store/file-committer.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/platform/store/index.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/platform/store/index.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/platform/tokens/contract.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/platform/tokens/contract.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/platform/tokens/estimator.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/platform/tokens/estimator.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/platform/tokens/index.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/platform/tokens/index.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/session/contract.js +0 -0
|
@@ -1,11 +1,21 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Compact mode:每条含 toolCall 的 assistant message 折叠为一条逐步累加的摘要行
|
|
3
|
-
* (`Ran for 8s, bash×2, read×2`),edit/write
|
|
4
|
-
* 普通工具折叠时不显示独立行;展开(Ctrl+O / fullscreen
|
|
5
|
-
* compact-thinking/Pi 原生或专用
|
|
3
|
+
* (`Ran for 8s, bash×2, read×2`),edit/write 独立标题行(`✓ write <path> (+25 -0)`),
|
|
4
|
+
* 普通工具折叠时不显示独立行;展开(Ctrl+O / fullscreen 点击)时助手文本按原生渲染,
|
|
5
|
+
* thinking 与工具卡装进 userMessageBg 面板(卡内恢复 compact-thinking/Pi 原生或专用
|
|
6
|
+
* renderer)。edit/write 复用 mode=on 的 rich diff
|
|
7
|
+
* 与 `editDiffCollapsedLines` / `writeDiffCollapsedLines` / `expandedPreviewMaxLines`。
|
|
6
8
|
* Agent/Task 族:调用只进摘要计数,tool 卡始终折叠(避免 pending→完成高度闪动)。
|
|
7
9
|
* 底部 Agents/Tasks 面板由 pi-subagents/pi-tasks 独立 widget 负责,不经 tool 卡外置。
|
|
8
10
|
*
|
|
11
|
+
* live 围观态(回合进行中 / 收尾静默期):正文跟折叠态同形;摘要行与单一槽位卡
|
|
12
|
+
* (`↳` + 最新的一个块:仍在增长的思考预览,或本回合最近的工具,运行中优先)挂在
|
|
13
|
+
* transcript 容器的回合末尾(尾行组件,anchor 与工具卡之后、下一消息之前)。
|
|
14
|
+
* 思考原地增长不轮换,只有新的思考块或工具调用进来才换槽位内容。
|
|
15
|
+
* 摘要行挂末尾是刻意的:运行中它恒在可写视口底缘,收尾时原地从 Running... 翻成
|
|
16
|
+
* Ran for 真实落进 scrollback;放在 anchor 内部则会被工具/diff 顶出视口后定格。
|
|
17
|
+
* (anchor 未挂载进容器的独立渲染场景退回组件内旧行为。)
|
|
18
|
+
*
|
|
9
19
|
* 工具计数:read 按非空路径去重、其余按调用计数(首次出现顺序);edit/write 不进摘要。
|
|
10
20
|
* 时长 = 回合流逝挂钟;进行中 Running...,结束 Ran for。
|
|
11
21
|
* abort/error/length 状态行挂在摘要外层,避免被折叠吞掉。
|
|
@@ -19,13 +29,13 @@ import {
|
|
|
19
29
|
ToolExecutionComponent,
|
|
20
30
|
type Theme,
|
|
21
31
|
} from "@earendil-works/pi-coding-agent";
|
|
22
|
-
import { truncateToWidth, visibleWidth, Box } from "@earendil-works/pi-tui";
|
|
32
|
+
import { truncateToWidth, visibleWidth, Box, Spacer } from "@earendil-works/pi-tui";
|
|
23
33
|
import { config, getToolDisplayConfig } from "../config/config.ts";
|
|
24
34
|
import { toolLoadingIcon } from "../utils/tool-loading-icon.ts";
|
|
25
35
|
import { sanitizeToolResultText } from "../utils/tool-result-sanitize.ts";
|
|
26
36
|
import { refreshTranscriptComponent } from "./transcript-refresh.ts";
|
|
27
|
-
import { getMessageDisplayTheme } from "./message-display.ts";
|
|
28
|
-
import { showMoreHintText } from "./show-more-hint.ts";
|
|
37
|
+
import { getMessageDisplayTheme } from "./tool/message-display.ts";
|
|
38
|
+
import { showMoreHintText } from "./tool/show-more-hint.ts";
|
|
29
39
|
import {
|
|
30
40
|
countEditDiffStats,
|
|
31
41
|
countWriteDiffStats,
|
|
@@ -33,13 +43,33 @@ import {
|
|
|
33
43
|
} from "./tool/diff/diff-renderer.ts";
|
|
34
44
|
import { renderRichToolResult } from "./tool/diff/index.ts";
|
|
35
45
|
import type { WriteExecutionMetadataStore } from "./tool/diff/write-execution.ts";
|
|
46
|
+
import { isToolCallHovered } from "./mouse/hover.ts";
|
|
47
|
+
import { getToolMouseTui } from "./mouse/scroll.ts";
|
|
48
|
+
import { insetComponent, renderExpandedToolResult, scheduleAnimation } from "./tool/result.ts";
|
|
49
|
+
import { paddedBackgroundRow } from "./tool/grouping.ts";
|
|
50
|
+
import { formatDisplayPath } from "./tool/names.ts";
|
|
51
|
+
import {
|
|
52
|
+
hasVisibleText,
|
|
53
|
+
removeStyledRange,
|
|
54
|
+
stripBackgroundAnsi,
|
|
55
|
+
stripTerminalSequencesPreservingLayout,
|
|
56
|
+
} from "../utils/ansi-text.ts";
|
|
57
|
+
import { walkComponentTree } from "../utils/component-tree.ts";
|
|
36
58
|
import {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
59
|
+
ASSISTANT_REENTRY_KEY,
|
|
60
|
+
ASSISTANT_SET_EXPANDED_KEY,
|
|
61
|
+
ASSISTANT_TOGGLE_ROUND_KEY,
|
|
62
|
+
COMPACT_MODE_PATCH_KEY,
|
|
63
|
+
COMPACT_THINKING_PATCH_KEY,
|
|
64
|
+
patchRegistry,
|
|
65
|
+
PROTOTYPE_ORIGINAL_KEY,
|
|
66
|
+
TOOL_GROUPING_PARENT_KEY,
|
|
67
|
+
} from "../utils/patch-keys.ts";
|
|
68
|
+
|
|
69
|
+
/** Pi 的 native 组件与扩展可能各持一份 pi-tui,Spacer 不能只靠 instanceof 判。 */
|
|
70
|
+
function isSpacerComponent(value: any): boolean {
|
|
71
|
+
return value instanceof Spacer || value?.constructor?.name === "Spacer";
|
|
72
|
+
}
|
|
43
73
|
|
|
44
74
|
/** compact 渲染层对 compact-thinking 的只读查询面(不建第二套计时器)。 */
|
|
45
75
|
export type CompactThinkingQuery = {
|
|
@@ -49,19 +79,14 @@ export type CompactThinkingQuery = {
|
|
|
49
79
|
setCompactSummaryActive?(active: boolean): void;
|
|
50
80
|
};
|
|
51
81
|
|
|
52
|
-
const COMPACT_MODE_PATCH_KEY = Symbol.for("pi.ccstyle.compact-mode-patch");
|
|
53
|
-
const COMPACT_THINKING_OWNER_KEY = Symbol.for("pi.ccstyle.compact-thinking-owner");
|
|
54
|
-
const COMPACT_THINKING_PATCH_KEY = Symbol.for("pi.ccstyle.compact-thinking-update");
|
|
55
|
-
const PROTOTYPE_ORIGINAL_KEY = Symbol.for("pi.ccstyle.prototype-original");
|
|
56
|
-
const ASSISTANT_SET_EXPANDED_DESCRIPTION = "pi.ccstyle.compact-assistant-set-expanded";
|
|
57
|
-
const ASSISTANT_TOGGLE_ROUND_DESCRIPTION = "pi.ccstyle.compact-assistant-toggle-round";
|
|
58
|
-
const ASSISTANT_REENTRY_DESCRIPTION = "pi.ccstyle.compact-assistant-reentry";
|
|
59
|
-
const ASSISTANT_SET_EXPANDED_KEY = Symbol.for(ASSISTANT_SET_EXPANDED_DESCRIPTION);
|
|
60
|
-
const ASSISTANT_TOGGLE_ROUND_KEY = Symbol.for(ASSISTANT_TOGGLE_ROUND_DESCRIPTION);
|
|
61
|
-
const ASSISTANT_REENTRY_KEY = Symbol.for(ASSISTANT_REENTRY_DESCRIPTION);
|
|
62
|
-
|
|
63
82
|
const EDIT_WRITE_TOOLS = new Set(["edit", "write"]);
|
|
64
83
|
|
|
84
|
+
/**
|
|
85
|
+
* 回合收尾后延迟收拢的静默期(ms)。正文 token 往往先于 toolCall 到达,
|
|
86
|
+
* 立刻收拢会得到「先收再开」两次转场;静默期内被新回合接替则一并收拢。
|
|
87
|
+
*/
|
|
88
|
+
const FOLD_SETTLE_MS = 250;
|
|
89
|
+
|
|
65
90
|
type CompactThinkingTheme = Pick<Theme, "fg" | "italic" | "bold">;
|
|
66
91
|
|
|
67
92
|
/** 与 compact-thinking 主渲染器共用的静态文字样式。 */
|
|
@@ -290,16 +315,6 @@ function darkenBgAnsi(theme: any, slot: string): string {
|
|
|
290
315
|
return `\x1b[48;2;${r};${g};${b}m`;
|
|
291
316
|
}
|
|
292
317
|
|
|
293
|
-
/** 去掉行内所有 CSI/OSC 序列后是否有可见文本(判断工具卡首尾内容行)。 */
|
|
294
|
-
function hasVisibleText(line: string): boolean {
|
|
295
|
-
return (
|
|
296
|
-
line
|
|
297
|
-
.replace(/\x1b\[[0-?]*[ -/]*[@-~]/g, "")
|
|
298
|
-
.replace(/\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)/g, "")
|
|
299
|
-
.trim().length > 0
|
|
300
|
-
);
|
|
301
|
-
}
|
|
302
|
-
|
|
303
318
|
/** 工具卡深色行:左右内缩(左 2 右 3 格,含工具卡自身 padding),背景只到内容区,
|
|
304
319
|
* 修复行尾 reset 截断。 */
|
|
305
320
|
function toolCardBgRow(
|
|
@@ -320,47 +335,80 @@ function toolCardBgRow(
|
|
|
320
335
|
const body = ` ${clipped}${" ".repeat(pad)} `;
|
|
321
336
|
const stable = body.replace(/\x1b\[(?:0)?m/g, (reset) => reset + bgAnsi);
|
|
322
337
|
const outerBg = typeof theme?.getBgAnsi === "function" ? String(theme.getBgAnsi(slot)) : "";
|
|
323
|
-
const inset = (n) => (outerBg ? `${outerBg}${" ".repeat(n)}\x1b[49m` : " ".repeat(n));
|
|
338
|
+
const inset = (n: number) => (outerBg ? `${outerBg}${" ".repeat(n)}\x1b[49m` : " ".repeat(n));
|
|
324
339
|
return `${inset(leftInset)}${bgAnsi}${stable}\x1b[49m${inset(rightInset)}`;
|
|
325
340
|
}
|
|
326
|
-
/** edit/write
|
|
341
|
+
/** edit/write 展开卡:保持原样式(展开卡背景 Box),不应用工具卡深色/间距改动。 */
|
|
327
342
|
function editWriteExpandedCard(theme: any): any {
|
|
328
343
|
return new Box(
|
|
329
344
|
1,
|
|
330
345
|
1,
|
|
331
|
-
typeof theme.bg === "function"
|
|
346
|
+
typeof theme.bg === "function"
|
|
347
|
+
? (text: string) => theme.bg(config.expandedCardBackground, text)
|
|
348
|
+
: undefined,
|
|
332
349
|
);
|
|
333
350
|
}
|
|
334
351
|
|
|
335
|
-
/** compact
|
|
352
|
+
/** compact 展开面板:外卡片保持展开卡背景;展开的 thinking 另包一层更深的
|
|
353
|
+
* 内卡(自身 Box 背景与外卡同色,不区分会糊在一起),工具卡直接铺在外卡背景上。
|
|
354
|
+
* 每个子卡前面补 1 行分隔(上一条已经是空行时不再补),子卡自带的首尾空行会被裁掉,
|
|
355
|
+
* 避免与外卡/内卡 padding 叠出双空行。
|
|
356
|
+
* hits:子卡的行区间与命中目标(thinking 命中自身,工具命中 tool 组件),
|
|
357
|
+
* 供展开后点击 hint。未开启 toolHits 时不映射工具,点击仍归外层面板。
|
|
358
|
+
* live=true:live 槽位用,不铺底色也不留上下空行,首行紧跟 `↳`、其余行缩进对齐
|
|
359
|
+
* (Claude Code 的 ⎿ 口径)。行的产出仍是一行对一行,hits 行号无需平移。
|
|
360
|
+
*/
|
|
336
361
|
function layoutExpandedToolCard(
|
|
337
362
|
theme: any,
|
|
338
363
|
children: any[],
|
|
339
364
|
width: number,
|
|
365
|
+
paints?: string[][],
|
|
366
|
+
toolHits = false,
|
|
367
|
+
live = false,
|
|
340
368
|
): { lines: string[]; hits: Array<{ child: any; start: number; end: number }> } {
|
|
341
|
-
const slot =
|
|
369
|
+
const slot = config.expandedCardBackground;
|
|
342
370
|
const toolBgAnsi = darkenBgAnsi(theme, slot);
|
|
343
|
-
|
|
371
|
+
/** live 每行前面的标记宽度(首行 `↳ `,其余对齐空格)。 */
|
|
372
|
+
const markerWidth = live ? 2 : 0;
|
|
373
|
+
const innerWidth = Math.max(0, width - 2 - markerWidth);
|
|
344
374
|
const lines: string[] = [];
|
|
345
375
|
const hits: Array<{ child: any; start: number; end: number }> = [];
|
|
346
|
-
|
|
347
|
-
let
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
skipLeadingBlank = false;
|
|
355
|
-
}
|
|
356
|
-
const rangeStart = lines.length;
|
|
357
|
-
for (let i = start; i < childLines.length; i++) {
|
|
358
|
-
lines.push(paddedBackgroundRow(theme, slot, childLines[i], width));
|
|
359
|
-
}
|
|
360
|
-
if (lines.length > rangeStart) hits.push({ child, start: rangeStart, end: lines.length });
|
|
361
|
-
continue;
|
|
376
|
+
const isThinkingPreview = (child: any) => typeof child?.setHintHovered === "function";
|
|
377
|
+
let lastBlank = false;
|
|
378
|
+
/** live 首行被吃掉的前导缩进:续行同步左移同样格数,保持块内相对对齐。 */
|
|
379
|
+
let liveLead = 0;
|
|
380
|
+
const pushRow = (raw: string) => {
|
|
381
|
+
if (!live) {
|
|
382
|
+
lines.push(paddedBackgroundRow(theme, slot, raw, width));
|
|
383
|
+
return;
|
|
362
384
|
}
|
|
363
|
-
|
|
385
|
+
// 槽位里不重复展开入口(见 SLOT_HINT_PATTERNS)。
|
|
386
|
+
const line = stripSlotHint(raw);
|
|
387
|
+
// 工具卡行首带 SGR:ANSI 之后才是缩进空格。
|
|
388
|
+
const head = line.match(/^((?:\x1b\[[0-9;]*m)*)( *)/);
|
|
389
|
+
const ansi = head?.[1] ?? "";
|
|
390
|
+
const spaces = head?.[2]?.length ?? 0;
|
|
391
|
+
const first = lines.length === 0;
|
|
392
|
+
if (first) liveLead = spaces;
|
|
393
|
+
// 首行挂 muted 的 `↳`(与 default-mode 工具结果行同色);续行按 liveLead 去缩进。
|
|
394
|
+
// 两侧都保留行首 SGR:丢掉它整行会掉回终端默认前景色。
|
|
395
|
+
const marker = first ? `${theme.fg("muted", "↳")} ` : " ";
|
|
396
|
+
const lead = first ? spaces : Math.min(spaces, liveLead);
|
|
397
|
+
lines.push(
|
|
398
|
+
`${marker}${ansi}${truncateToWidth(line.slice(ansi.length + lead), innerWidth, "")}`,
|
|
399
|
+
);
|
|
400
|
+
};
|
|
401
|
+
const pushBlank = () => {
|
|
402
|
+
if (live || lastBlank) return;
|
|
403
|
+
lines.push(paddedBackgroundRow(theme, slot, "", width));
|
|
404
|
+
lastBlank = true;
|
|
405
|
+
};
|
|
406
|
+
for (let childIndex = 0; childIndex < children.length; childIndex++) {
|
|
407
|
+
const child = children[childIndex];
|
|
408
|
+
const painted = paints?.[childIndex];
|
|
409
|
+
const childLines = Array.isArray(painted) ? painted : child.render(innerWidth);
|
|
410
|
+
// live 不留空行、也不叠深色内卡:槽位没有边框。
|
|
411
|
+
const innerCard = !live && isThinkingPreview(child) && child.expanded === true;
|
|
364
412
|
let first = -1;
|
|
365
413
|
let last = -1;
|
|
366
414
|
for (let i = 0; i < childLines.length; i++) {
|
|
@@ -370,25 +418,41 @@ function layoutExpandedToolCard(
|
|
|
370
418
|
}
|
|
371
419
|
}
|
|
372
420
|
if (first < 0) {
|
|
421
|
+
if (live) continue;
|
|
373
422
|
for (const line of childLines) {
|
|
374
423
|
lines.push(paddedBackgroundRow(theme, slot, line, width));
|
|
424
|
+
lastBlank = !hasVisibleText(line);
|
|
375
425
|
}
|
|
376
426
|
continue;
|
|
377
427
|
}
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
428
|
+
if (!innerCard) pushBlank();
|
|
429
|
+
const rangeStart = lines.length;
|
|
430
|
+
// 展开的 thinking 走深色内卡(上下各 1 行 padding),工具卡直接铺在外卡上。
|
|
431
|
+
const paintRow = innerCard
|
|
432
|
+
? (line: string) => lines.push(toolCardBgRow(theme, slot, toolBgAnsi, line, width))
|
|
433
|
+
: pushRow;
|
|
434
|
+
if (innerCard) paintRow("");
|
|
435
|
+
for (let i = first; i <= last; i++) paintRow(childLines[i]);
|
|
436
|
+
if (innerCard) paintRow("");
|
|
437
|
+
// 内卡的下 padding 不能当分隔:后面的子卡要再留 1 行外卡空行。
|
|
438
|
+
lastBlank = false;
|
|
439
|
+
// thinking 命中自身;工具在展开卡内需要能二次展开时才映射回 tool 组件。
|
|
440
|
+
if (isThinkingPreview(child)) hits.push({ child, start: rangeStart, end: lines.length });
|
|
441
|
+
else if (toolHits && child.__ccTool) {
|
|
442
|
+
hits.push({ child: child.__ccTool, start: rangeStart, end: lines.length });
|
|
382
443
|
}
|
|
383
|
-
lines.push(toolCardBgRow(theme, slot, toolBgAnsi, "", width));
|
|
384
444
|
}
|
|
385
|
-
|
|
445
|
+
pushBlank();
|
|
386
446
|
return { lines, hits };
|
|
387
447
|
}
|
|
388
448
|
|
|
389
449
|
function compactRoundCard(
|
|
390
450
|
cardItems: Array<{ child?: any; tool?: any }>,
|
|
391
451
|
toolRender: (tool: any, width: number) => string[],
|
|
452
|
+
/** true 时卡内工具行归 tool 组件,可点击二次展开(整回合展开卡)。 */
|
|
453
|
+
toolHits = false,
|
|
454
|
+
/** true 时走 live 槽位排布:不留空行、首行紧跟 `↳`。 */
|
|
455
|
+
live = false,
|
|
392
456
|
): any {
|
|
393
457
|
const children: any[] = [];
|
|
394
458
|
for (const item of cardItems) {
|
|
@@ -397,26 +461,67 @@ function compactRoundCard(
|
|
|
397
461
|
const tool = item.tool;
|
|
398
462
|
children.push({
|
|
399
463
|
__ccToolCard: true,
|
|
464
|
+
__ccTool: tool,
|
|
400
465
|
render: (innerWidth: number) => toolRender(tool, innerWidth),
|
|
401
466
|
invalidate: () => tool.invalidate?.(),
|
|
402
467
|
});
|
|
403
468
|
}
|
|
404
469
|
}
|
|
470
|
+
/** 子块可用宽度:live 还要让出首行 `↳ ` 的标记位。 */
|
|
471
|
+
const contentWidth = (width: number) => Math.max(0, width - 2 - (live ? 2 : 0));
|
|
472
|
+
let paint:
|
|
473
|
+
| {
|
|
474
|
+
width: number;
|
|
475
|
+
theme: unknown;
|
|
476
|
+
/** 背景槽位也是面板行的输入:改配置后不能复用旧底色的整卡。 */
|
|
477
|
+
bgSlot: string;
|
|
478
|
+
paints: unknown[];
|
|
479
|
+
lines: string[];
|
|
480
|
+
hits: Array<{ child: any; start: number; end: number }>;
|
|
481
|
+
}
|
|
482
|
+
| undefined;
|
|
483
|
+
const layout = (width: number) => {
|
|
484
|
+
const theme = themeOf();
|
|
485
|
+
const bgSlot = config.expandedCardBackground;
|
|
486
|
+
const innerWidth = contentWidth(width);
|
|
487
|
+
const paints = children.map((child) => {
|
|
488
|
+
const lines = child.render?.(innerWidth);
|
|
489
|
+
return Array.isArray(lines) ? lines : [];
|
|
490
|
+
});
|
|
491
|
+
if (
|
|
492
|
+
paint &&
|
|
493
|
+
paint.width === width &&
|
|
494
|
+
paint.theme === theme &&
|
|
495
|
+
paint.bgSlot === bgSlot &&
|
|
496
|
+
paint.paints.length === paints.length &&
|
|
497
|
+
paint.paints.every((item, index) => item === paints[index])
|
|
498
|
+
) {
|
|
499
|
+
return paint;
|
|
500
|
+
}
|
|
501
|
+
const laid = layoutExpandedToolCard(theme, children, width, paints, toolHits, live);
|
|
502
|
+
paint = {
|
|
503
|
+
width,
|
|
504
|
+
theme,
|
|
505
|
+
bgSlot,
|
|
506
|
+
paints,
|
|
507
|
+
lines: laid.lines,
|
|
508
|
+
hits: laid.hits,
|
|
509
|
+
};
|
|
510
|
+
return paint;
|
|
511
|
+
};
|
|
405
512
|
return {
|
|
406
513
|
children,
|
|
407
514
|
render(width: number): string[] {
|
|
408
|
-
return
|
|
515
|
+
return layout(width).lines;
|
|
409
516
|
},
|
|
410
517
|
childAtRow(localRow: number, width: number) {
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
const { hits } = layoutExpandedToolCard(themeOf(), children, width);
|
|
414
|
-
for (const hit of hits) {
|
|
415
|
-
if (row >= hit.start && row < hit.end) return hit.child;
|
|
518
|
+
for (const hit of layout(width).hits) {
|
|
519
|
+
if (localRow >= hit.start && localRow < hit.end) return hit.child;
|
|
416
520
|
}
|
|
417
521
|
return null;
|
|
418
522
|
},
|
|
419
523
|
invalidate() {
|
|
524
|
+
paint = undefined;
|
|
420
525
|
for (const child of children) child.invalidate?.();
|
|
421
526
|
},
|
|
422
527
|
};
|
|
@@ -431,28 +536,16 @@ function isToolComponent(value: any): boolean {
|
|
|
431
536
|
}
|
|
432
537
|
|
|
433
538
|
function detachAssistantExpansion(component: any): void {
|
|
434
|
-
|
|
435
|
-
typeof component?.[ASSISTANT_SET_EXPANDED_KEY]
|
|
436
|
-
component.setExpanded
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
owned = Object.getOwnPropertySymbols(component).some(
|
|
440
|
-
(symbol) =>
|
|
441
|
-
symbol.description === ASSISTANT_SET_EXPANDED_DESCRIPTION && component[symbol] === true,
|
|
442
|
-
);
|
|
539
|
+
if (
|
|
540
|
+
typeof component?.[ASSISTANT_SET_EXPANDED_KEY] !== "function" ||
|
|
541
|
+
component.setExpanded !== component[ASSISTANT_SET_EXPANDED_KEY]
|
|
542
|
+
) {
|
|
543
|
+
return;
|
|
443
544
|
}
|
|
444
|
-
if (!owned) return;
|
|
445
|
-
|
|
446
545
|
delete component.setExpanded;
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
symbol.description === ASSISTANT_TOGGLE_ROUND_DESCRIPTION ||
|
|
451
|
-
symbol.description === ASSISTANT_REENTRY_DESCRIPTION
|
|
452
|
-
) {
|
|
453
|
-
delete component[symbol];
|
|
454
|
-
}
|
|
455
|
-
}
|
|
546
|
+
delete component[ASSISTANT_SET_EXPANDED_KEY];
|
|
547
|
+
delete component[ASSISTANT_TOGGLE_ROUND_KEY];
|
|
548
|
+
delete component[ASSISTANT_REENTRY_KEY];
|
|
456
549
|
}
|
|
457
550
|
|
|
458
551
|
/** 供 mouse-interaction 识别可点击的 compact assistant 行(仅 compact 模式下生效)。 */
|
|
@@ -500,27 +593,91 @@ const trackedAssistantComponents = new Set<any>();
|
|
|
500
593
|
const trackedToolComponents = new Set<any>();
|
|
501
594
|
let hoveredAssistantComponent: any;
|
|
502
595
|
|
|
596
|
+
/**
|
|
597
|
+
* 活动回合摘要 getter:常驻状态行(pi working row)镜像用。
|
|
598
|
+
* installCompactMode 注册、卸载时清空,直接读 activeRound,不持有回合闭包。
|
|
599
|
+
*/
|
|
600
|
+
let compactRunStatusGetter: (() => string | undefined) | undefined;
|
|
601
|
+
|
|
602
|
+
/** 活动回合的摘要文本(`Running... · 9s, bash×1`);无活动回合返回 undefined。 */
|
|
603
|
+
export function getCompactRunStatusText(): string | undefined {
|
|
604
|
+
return compactRunStatusGetter?.();
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
/**
|
|
608
|
+
* 回合尾行记录:摘要行(live 时连槽位卡)挂成 transcript 容器里回合末尾的
|
|
609
|
+
* 兄弟组件,而不是 anchor 内部。anchor 内部的可变内容一旦被工具/diff 顶出视口
|
|
610
|
+
* 就再也写不进 scrollback(regular 主屏只能差分可视区),运行态 Running 会永久
|
|
611
|
+
* 定格在挤出前最后一帧;尾行恒在可写底缘,收尾的 Ran for 才真实落进历史。
|
|
612
|
+
* 模块级持有(与 trackedAssistantComponents 同理):跨 /reload 去重与清理。
|
|
613
|
+
*/
|
|
614
|
+
type MountedRoundTail = {
|
|
615
|
+
host: any;
|
|
616
|
+
container: any;
|
|
617
|
+
parts: any[];
|
|
618
|
+
round: any;
|
|
619
|
+
};
|
|
620
|
+
const tailByAnchor = new WeakMap<object, MountedRoundTail>();
|
|
621
|
+
const mountedTails = new Set<MountedRoundTail>();
|
|
622
|
+
|
|
503
623
|
export function setHoveredCompactAssistant(component: any): boolean {
|
|
504
624
|
if (hoveredAssistantComponent === component) return false;
|
|
505
625
|
hoveredAssistantComponent = component;
|
|
506
626
|
return true;
|
|
507
627
|
}
|
|
508
628
|
|
|
629
|
+
/**
|
|
630
|
+
* 整回合展开卡内的工具被点击展开时调用:让 compact 的强制折叠放行这一个,
|
|
631
|
+
* 并把同一回合里其他展开的工具收回去(保持单开)。非 round 内工具是空操作。
|
|
632
|
+
*/
|
|
633
|
+
let roundToolExpansion: { markUserExpanded(tool: any): void } | undefined;
|
|
634
|
+
|
|
635
|
+
export function markCompactRoundToolExpanded(tool: any): void {
|
|
636
|
+
roundToolExpansion?.markUserExpanded(tool);
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
/** 面板展开前的视口位置:收起时还原,避免面板撑高后收起导致视口错位。 */
|
|
640
|
+
const panelViewportBeforeExpand = new WeakMap<
|
|
641
|
+
object,
|
|
642
|
+
{ scrollTop: number; followingEnd: boolean }
|
|
643
|
+
>();
|
|
644
|
+
|
|
645
|
+
function capturePanelViewport(card: any): void {
|
|
646
|
+
if (!card || panelViewportBeforeExpand.has(card)) return;
|
|
647
|
+
const view = getToolMouseTui()?.getPrimaryScrollView?.();
|
|
648
|
+
if (!view || typeof view.scrollTop !== "number" || typeof view.scrollTo !== "function") return;
|
|
649
|
+
panelViewportBeforeExpand.set(card, {
|
|
650
|
+
scrollTop: view.scrollTop,
|
|
651
|
+
followingEnd: view.isFollowingEnd !== false,
|
|
652
|
+
});
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
function restorePanelViewport(card: any): void {
|
|
656
|
+
const snapshot = panelViewportBeforeExpand.get(card);
|
|
657
|
+
if (!snapshot) return;
|
|
658
|
+
panelViewportBeforeExpand.delete(card);
|
|
659
|
+
const view = getToolMouseTui()?.getPrimaryScrollView?.();
|
|
660
|
+
if (!view) return;
|
|
661
|
+
// 展开前就在底部:交回官方 follow;否则回到展开前的滚动偏移。
|
|
662
|
+
if (snapshot.followingEnd && typeof view.scrollToEnd === "function") view.scrollToEnd();
|
|
663
|
+
else if (typeof view.scrollTo === "function") view.scrollTo(snapshot.scrollTop);
|
|
664
|
+
}
|
|
665
|
+
|
|
509
666
|
function compactEditWriteLine(
|
|
510
667
|
component: any,
|
|
511
668
|
width: number,
|
|
512
669
|
writeMetadata?: WriteExecutionMetadataStore,
|
|
670
|
+
options: { hint?: boolean; flushLeft?: boolean } = {},
|
|
513
671
|
): string[] {
|
|
514
672
|
const theme = themeOf();
|
|
515
673
|
const name = String(component.toolName ?? "tool");
|
|
516
674
|
const args = component.args ?? {};
|
|
517
|
-
const path =
|
|
675
|
+
const path =
|
|
518
676
|
typeof args.path === "string" && args.path
|
|
519
677
|
? args.path
|
|
520
678
|
: typeof args.file_path === "string" && args.file_path
|
|
521
679
|
? args.file_path
|
|
522
|
-
: ""
|
|
523
|
-
);
|
|
680
|
+
: "";
|
|
524
681
|
const isError = component.result?.isError === true;
|
|
525
682
|
const isPending = !component.result || component.isPartial === true;
|
|
526
683
|
const icon = isError ? "✗" : isPending ? toolLoadingIcon() : "✓";
|
|
@@ -543,9 +700,11 @@ function compactEditWriteLine(
|
|
|
543
700
|
statsStyled = ` ${theme.fg("dim", "(")}${theme.fg("success", `+${stats.added}`)} ${theme.fg("error", `-${stats.removed}`)}${theme.fg("dim", ")")}`;
|
|
544
701
|
}
|
|
545
702
|
}
|
|
546
|
-
|
|
703
|
+
// 展开卡 Box(1,1) 已 pad;折叠行自己留 1 格前导空格
|
|
704
|
+
const iconPart = `${options.flushLeft ? "" : " "}${theme.fg(iconColor, icon)} `;
|
|
547
705
|
const namePart = theme.fg("toolTitle", name);
|
|
548
|
-
const hintText =
|
|
706
|
+
const hintText =
|
|
707
|
+
options.hint !== false && component.expanded !== true ? ` · ${showMoreHintText()}` : "";
|
|
549
708
|
const fixedWidth =
|
|
550
709
|
visibleWidth(iconPart) +
|
|
551
710
|
visibleWidth(namePart) +
|
|
@@ -558,34 +717,133 @@ function compactEditWriteLine(
|
|
|
558
717
|
return ["", truncateToWidth(line, width, "")];
|
|
559
718
|
}
|
|
560
719
|
|
|
561
|
-
/**
|
|
562
|
-
|
|
720
|
+
/** expanded 写进 rich diff 闭包,折叠/展开分槽缓存,避免每帧 parseDiff。 */
|
|
721
|
+
const compactRichDiffCache = new WeakMap<
|
|
722
|
+
object,
|
|
723
|
+
{ result: unknown; collapsed?: unknown; expanded?: unknown }
|
|
724
|
+
>();
|
|
725
|
+
|
|
726
|
+
type CompactEditPaintHit = {
|
|
727
|
+
width: number;
|
|
728
|
+
theme: unknown;
|
|
729
|
+
/** 展开卡的背景槽位同样参与命中判断。 */
|
|
730
|
+
bgSlot: string;
|
|
731
|
+
expanded: boolean;
|
|
732
|
+
result: unknown;
|
|
733
|
+
isPartial: boolean;
|
|
734
|
+
args: unknown;
|
|
735
|
+
hover: boolean;
|
|
736
|
+
ioHover: unknown;
|
|
737
|
+
lines: string[];
|
|
738
|
+
};
|
|
739
|
+
const compactEditPaintCache = new WeakMap<object, CompactEditPaintHit>();
|
|
740
|
+
|
|
741
|
+
function compactEditIoHover(component: any): unknown {
|
|
742
|
+
const view = component?.resultRendererComponent;
|
|
743
|
+
return typeof view?.getHoveredSection === "function" ? view.getHoveredSection() : null;
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
/**
|
|
747
|
+
* compact edit/write:标题行 + mode=on 同一套 rich diff。
|
|
748
|
+
* 折叠/展开都走 `renderRichToolResult`,limits 不另开一套。
|
|
749
|
+
* 已完成的结果跨帧复用行;pending 不缓存,避免 loader 动画冻住。
|
|
750
|
+
*/
|
|
751
|
+
function compactEditWriteLines(
|
|
752
|
+
component: any,
|
|
753
|
+
width: number,
|
|
754
|
+
writeMetadata?: WriteExecutionMetadataStore,
|
|
755
|
+
): string[] {
|
|
756
|
+
const expanded = component.expanded === true;
|
|
757
|
+
const isPartial = component.isPartial === true;
|
|
758
|
+
const pending = !component.result || isPartial;
|
|
759
|
+
const hover = isToolCallHovered(component.toolCallId);
|
|
760
|
+
const ioHover = compactEditIoHover(component);
|
|
761
|
+
const theme = themeOf();
|
|
762
|
+
if (!pending) {
|
|
763
|
+
const hit = compactEditPaintCache.get(component);
|
|
764
|
+
if (
|
|
765
|
+
hit &&
|
|
766
|
+
hit.width === width &&
|
|
767
|
+
hit.theme === theme &&
|
|
768
|
+
hit.bgSlot === config.expandedCardBackground &&
|
|
769
|
+
hit.expanded === expanded &&
|
|
770
|
+
hit.result === component.result &&
|
|
771
|
+
hit.isPartial === isPartial &&
|
|
772
|
+
hit.args === component.args &&
|
|
773
|
+
hit.hover === hover &&
|
|
774
|
+
hit.ioHover === ioHover
|
|
775
|
+
) {
|
|
776
|
+
return hit.lines;
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
const lines = paintCompactEditWrite(component, width, writeMetadata);
|
|
780
|
+
if (!pending) {
|
|
781
|
+
compactEditPaintCache.set(component, {
|
|
782
|
+
width,
|
|
783
|
+
theme,
|
|
784
|
+
bgSlot: config.expandedCardBackground,
|
|
785
|
+
expanded,
|
|
786
|
+
result: component.result,
|
|
787
|
+
isPartial,
|
|
788
|
+
args: component.args,
|
|
789
|
+
hover,
|
|
790
|
+
ioHover: compactEditIoHover(component),
|
|
791
|
+
lines,
|
|
792
|
+
});
|
|
793
|
+
}
|
|
794
|
+
return lines;
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
function paintCompactEditWrite(
|
|
563
798
|
component: any,
|
|
564
799
|
width: number,
|
|
565
800
|
writeMetadata?: WriteExecutionMetadataStore,
|
|
566
801
|
): string[] {
|
|
567
802
|
const theme = themeOf();
|
|
568
803
|
const result = component.result;
|
|
804
|
+
const expanded = component.expanded === true;
|
|
569
805
|
const isError = result?.isError === true;
|
|
570
|
-
const
|
|
571
|
-
|
|
806
|
+
const isPending = !result || component.isPartial === true;
|
|
807
|
+
let candidate: unknown;
|
|
808
|
+
if (!isPending && writeMetadata) {
|
|
809
|
+
let entry = compactRichDiffCache.get(component);
|
|
810
|
+
if (!entry || entry.result !== result) {
|
|
811
|
+
entry = { result };
|
|
812
|
+
compactRichDiffCache.set(component, entry);
|
|
813
|
+
}
|
|
814
|
+
const slot = expanded ? "expanded" : "collapsed";
|
|
815
|
+
candidate = entry[slot];
|
|
816
|
+
if (!isRichDiffComponent(candidate)) {
|
|
817
|
+
candidate = renderRichToolResult(
|
|
572
818
|
String(component.toolName ?? ""),
|
|
573
819
|
result,
|
|
574
820
|
{
|
|
575
|
-
expanded
|
|
821
|
+
expanded,
|
|
576
822
|
isPartial: component.isPartial === true,
|
|
577
823
|
isError,
|
|
824
|
+
isHovered: () => isToolCallHovered(component.toolCallId),
|
|
578
825
|
},
|
|
579
826
|
theme,
|
|
580
827
|
component,
|
|
581
828
|
writeMetadata,
|
|
582
829
|
getToolDisplayConfig,
|
|
583
|
-
)
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
830
|
+
);
|
|
831
|
+
if (isRichDiffComponent(candidate)) entry[slot] = candidate;
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
const hasRich = isRichDiffComponent(candidate);
|
|
835
|
+
if (hasRich) {
|
|
587
836
|
component.resultRendererComponent = candidate;
|
|
588
|
-
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
const title = compactEditWriteLine(component, width, writeMetadata, { hint: !hasRich });
|
|
840
|
+
if (!hasRich && !expanded) {
|
|
841
|
+
return title;
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
let detail: any;
|
|
845
|
+
if (hasRich) {
|
|
846
|
+
detail = expanded ? candidate : insetComponent(candidate as any);
|
|
589
847
|
} else {
|
|
590
848
|
const outputText = sanitizeToolResultText(
|
|
591
849
|
Array.isArray(result?.content)
|
|
@@ -603,19 +861,28 @@ function compactEditWriteExpandedLines(
|
|
|
603
861
|
state.ccstyleIoView,
|
|
604
862
|
component.args,
|
|
605
863
|
component,
|
|
864
|
+
true, // 外层 Box(1,1) 已 pad
|
|
606
865
|
);
|
|
607
866
|
component.resultRendererComponent = detail;
|
|
608
867
|
}
|
|
609
868
|
|
|
869
|
+
if (!expanded) {
|
|
870
|
+
return [...title, ...detail.render(width)];
|
|
871
|
+
}
|
|
872
|
+
|
|
610
873
|
const box = editWriteExpandedCard(theme);
|
|
611
874
|
box.addChild({
|
|
612
875
|
render(innerWidth: number): string[] {
|
|
613
|
-
return compactEditWriteLine(component, innerWidth, writeMetadata
|
|
876
|
+
return compactEditWriteLine(component, innerWidth, writeMetadata, {
|
|
877
|
+
hint: false,
|
|
878
|
+
flushLeft: true,
|
|
879
|
+
}).slice(1);
|
|
614
880
|
},
|
|
615
881
|
invalidate() {},
|
|
616
882
|
});
|
|
617
883
|
box.addChild(detail);
|
|
618
|
-
|
|
884
|
+
// 与原生 ToolExecutionComponent 一样:Spacer(1) + Box,否则贴上一条 tool 少 1 行间距
|
|
885
|
+
return ["", ...box.render(width)];
|
|
619
886
|
}
|
|
620
887
|
|
|
621
888
|
function compactAssistantLineComponent(
|
|
@@ -626,17 +893,49 @@ function compactAssistantLineComponent(
|
|
|
626
893
|
options: { hint?: boolean; leadingBlank?: boolean; pad?: number } = {},
|
|
627
894
|
): any {
|
|
628
895
|
const self = component as any;
|
|
896
|
+
let paint:
|
|
897
|
+
| {
|
|
898
|
+
width: number;
|
|
899
|
+
theme: unknown;
|
|
900
|
+
resolved: string;
|
|
901
|
+
hover: boolean;
|
|
902
|
+
hint: boolean;
|
|
903
|
+
pad: number;
|
|
904
|
+
frame: number;
|
|
905
|
+
leadingBlank: boolean;
|
|
906
|
+
lines: string[];
|
|
907
|
+
}
|
|
908
|
+
| undefined;
|
|
629
909
|
return {
|
|
630
910
|
render(width: number): string[] {
|
|
631
911
|
const theme = themeOf();
|
|
632
912
|
const pad = Math.max(0, options.pad ?? (Number(self.outputPad) || 0));
|
|
633
913
|
const available = Math.max(0, width - pad);
|
|
634
|
-
const
|
|
914
|
+
const hint = options.hint !== false;
|
|
915
|
+
const hintText = hint ? ` · ${showMoreHintText()}` : "";
|
|
635
916
|
const summaryWidth = Math.max(0, available - visibleWidth(hintText));
|
|
636
917
|
const resolved = typeof summary === "function" ? summary() : summary;
|
|
637
918
|
const runningActive = resolved.startsWith("Running...");
|
|
919
|
+
const hover = hoveredAssistantComponent === component;
|
|
920
|
+
const frame = runningActive ? (query?.getThinkingAnimationFrame?.() ?? 0) : 0;
|
|
921
|
+
const leadingBlank = options.leadingBlank !== false;
|
|
922
|
+
if (
|
|
923
|
+
paint &&
|
|
924
|
+
paint.width === width &&
|
|
925
|
+
paint.theme === theme &&
|
|
926
|
+
paint.resolved === resolved &&
|
|
927
|
+
paint.hover === hover &&
|
|
928
|
+
paint.hint === hint &&
|
|
929
|
+
paint.pad === pad &&
|
|
930
|
+
paint.frame === frame &&
|
|
931
|
+
paint.leadingBlank === leadingBlank
|
|
932
|
+
) {
|
|
933
|
+
return paint.lines;
|
|
934
|
+
}
|
|
638
935
|
const plainText = truncateToWidth(resolved, summaryWidth, "…");
|
|
639
|
-
|
|
936
|
+
// Dim thinking text 开启时整行统一 dim,否则工具计数保持 muted。
|
|
937
|
+
const plain = (value: string) => theme.fg(config.dimThinkingText ? "dim" : "muted", value);
|
|
938
|
+
let text = plain(plainText);
|
|
640
939
|
if (runningActive || plainText.startsWith("Ran for ")) {
|
|
641
940
|
const separator = plainText.indexOf(", ");
|
|
642
941
|
const heading = separator < 0 ? plainText : plainText.slice(0, separator);
|
|
@@ -645,21 +944,31 @@ function compactAssistantLineComponent(
|
|
|
645
944
|
const durationSeparator = heading.indexOf(" · ");
|
|
646
945
|
const label = durationSeparator < 0 ? heading : heading.slice(0, durationSeparator);
|
|
647
946
|
const duration = durationSeparator < 0 ? "" : heading.slice(durationSeparator);
|
|
648
|
-
text = `${animateCompactThinkingText(
|
|
649
|
-
label,
|
|
650
|
-
theme,
|
|
651
|
-
query?.getThinkingAnimationFrame?.() ?? 0,
|
|
652
|
-
)}${styleCompactThinkingText(duration, theme)}${theme.fg("muted", tools)}`;
|
|
947
|
+
text = `${animateCompactThinkingText(label, theme, query?.getThinkingAnimationFrame?.() ?? 0)}${styleCompactThinkingText(duration, theme)}${plain(tools)}`;
|
|
653
948
|
} else {
|
|
654
|
-
text = `${styleCompactThinkingText(heading, theme)}${
|
|
949
|
+
text = `${styleCompactThinkingText(heading, theme)}${plain(tools)}`;
|
|
655
950
|
}
|
|
656
951
|
}
|
|
657
|
-
const hintColor =
|
|
952
|
+
const hintColor = hover ? "text" : "dim";
|
|
658
953
|
const line = `${text}${hintText ? theme.fg(hintColor, hintText) : ""}`;
|
|
659
954
|
const rendered = `${" ".repeat(pad)}${truncateToWidth(line, available, "")}`;
|
|
660
|
-
|
|
955
|
+
const lines = leadingBlank ? ["", rendered] : [rendered];
|
|
956
|
+
paint = {
|
|
957
|
+
width,
|
|
958
|
+
theme,
|
|
959
|
+
resolved,
|
|
960
|
+
hover,
|
|
961
|
+
hint,
|
|
962
|
+
pad,
|
|
963
|
+
frame,
|
|
964
|
+
leadingBlank,
|
|
965
|
+
lines,
|
|
966
|
+
};
|
|
967
|
+
return lines;
|
|
968
|
+
},
|
|
969
|
+
invalidate() {
|
|
970
|
+
paint = undefined;
|
|
661
971
|
},
|
|
662
|
-
invalidate() {},
|
|
663
972
|
};
|
|
664
973
|
}
|
|
665
974
|
|
|
@@ -675,14 +984,50 @@ function compactStopStatusLine(status: string, pad = 0): any {
|
|
|
675
984
|
};
|
|
676
985
|
}
|
|
677
986
|
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
)
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
987
|
+
/**
|
|
988
|
+
* 槽位卡内的展开入口:卡里是只读围观,展开走摘要行自己的 hint,卡内不再重复提示。
|
|
989
|
+
* 提示有两处来源——我们的 showMoreHintText(`… +N more lines · click to show more`)与
|
|
990
|
+
* pi 原生工具卡(`… (N earlier lines, ctrl+o to expand)`),所以按渲染结果收尾裁剪。
|
|
991
|
+
* 只去掉动作词,`(N more lines)` 这类计数保留。
|
|
992
|
+
*/
|
|
993
|
+
/** 展开入口的动作词:我们的 `click to show more`、pi 的 `<key> to expand`(键名可能取不到)。 */
|
|
994
|
+
const EXPAND_ACTION = String.raw`(?:click to show more|(?:[\w/+]+\s+)?to (?:show more|expand))`;
|
|
995
|
+
|
|
996
|
+
const SLOT_HINT_PATTERNS: RegExp[] = [
|
|
997
|
+
// 卡在括号里:`(9 more lines, click to show more)` / `(195 earlier lines, ctrl+o to expand)`,
|
|
998
|
+
// 只去掉动作词,收尾括号与计数保留。
|
|
999
|
+
new RegExp(String.raw`,\s*[^()]*?${EXPAND_ACTION}(?=\))`),
|
|
1000
|
+
// 直接挂在行尾:整段去掉。
|
|
1001
|
+
// 尾部分隔点:ccstyle 统一 `·`,同时兼容旧帧与 pi 原生的 `•`
|
|
1002
|
+
new RegExp(String.raw`\s*(?:[•·]\s*)?${EXPAND_ACTION}\s*$`),
|
|
1003
|
+
];
|
|
1004
|
+
|
|
1005
|
+
function stripSlotHint(line: string): string {
|
|
1006
|
+
const plain = stripTerminalSequencesPreservingLayout(line);
|
|
1007
|
+
for (const re of SLOT_HINT_PATTERNS) {
|
|
1008
|
+
const match = re.exec(plain);
|
|
1009
|
+
if (!match) continue;
|
|
1010
|
+
// 删除区间而非截断后拼纯文本:收尾括号留在原样式里(dim),不会掉回默认前景色。
|
|
1011
|
+
return removeStyledRange(line, match.index, match.index + match[0].length);
|
|
1012
|
+
}
|
|
1013
|
+
return line;
|
|
1014
|
+
}
|
|
1015
|
+
|
|
1016
|
+
/** live 槽位卡:按 pad 缩进;首行 `↳` 由卡自身的 live 排布加,行不增不减。 */
|
|
1017
|
+
function compactLiveSlot(card: any, pad = 0): any {
|
|
1018
|
+
const innerOf = (width: number) => Math.max(0, width - Math.max(0, pad));
|
|
1019
|
+
return {
|
|
1020
|
+
render(width: number): string[] {
|
|
1021
|
+
const indent = " ".repeat(Math.max(0, pad));
|
|
1022
|
+
return card.render(innerOf(width)).map((line: string) => `${indent}${line}`);
|
|
1023
|
+
},
|
|
1024
|
+
childAtRow(localRow: number, width: number) {
|
|
1025
|
+
return card.childAtRow?.(localRow, innerOf(width)) ?? null;
|
|
1026
|
+
},
|
|
1027
|
+
invalidate() {
|
|
1028
|
+
card.invalidate();
|
|
1029
|
+
},
|
|
1030
|
+
};
|
|
686
1031
|
}
|
|
687
1032
|
|
|
688
1033
|
function appendStopStatus(component: any, status: string | undefined): void {
|
|
@@ -724,16 +1069,9 @@ function ensureAssistantSetExpanded(component: any): void {
|
|
|
724
1069
|
|
|
725
1070
|
function collectMountedComponents(root: any): void {
|
|
726
1071
|
if (!root || typeof root !== "object") return;
|
|
727
|
-
const seen = new Set<any>();
|
|
728
1072
|
const assistants = new Set<any>();
|
|
729
1073
|
const tools = new Set<any>();
|
|
730
|
-
|
|
731
|
-
if (!value || typeof value !== "object" || seen.has(value)) return;
|
|
732
|
-
seen.add(value);
|
|
733
|
-
if (Array.isArray(value)) {
|
|
734
|
-
for (const child of value) visit(child);
|
|
735
|
-
return;
|
|
736
|
-
}
|
|
1074
|
+
walkComponentTree(root, (value: any) => {
|
|
737
1075
|
if (isAssistantComponent(value)) {
|
|
738
1076
|
assistants.add(value);
|
|
739
1077
|
// 仅在 compact 模式给实例装 setExpanded,避免 on/off 下 Ctrl+O/mouse 回归。
|
|
@@ -741,20 +1079,7 @@ function collectMountedComponents(root: any): void {
|
|
|
741
1079
|
} else if (isToolComponent(value)) {
|
|
742
1080
|
tools.add(value);
|
|
743
1081
|
}
|
|
744
|
-
|
|
745
|
-
if (Array.isArray(children)) {
|
|
746
|
-
for (const child of children) visit(child);
|
|
747
|
-
}
|
|
748
|
-
try {
|
|
749
|
-
const mounted = value.getMountedRoots?.();
|
|
750
|
-
if (Array.isArray(mounted)) {
|
|
751
|
-
for (const root2 of mounted) visit(root2);
|
|
752
|
-
}
|
|
753
|
-
} catch {
|
|
754
|
-
// renderer 切换中的惰性 Proxy 可能暂时没有 mounted roots
|
|
755
|
-
}
|
|
756
|
-
};
|
|
757
|
-
visit(root);
|
|
1082
|
+
});
|
|
758
1083
|
// 扫到组件才替换跟踪表。面板/custom UI 打开时 root 往往扫不到 transcript,
|
|
759
1084
|
// 若此时清空会丢掉 live updateContent/updateDisplay 已登记的实例,
|
|
760
1085
|
// /ccstyle 切换就只能靠 /reload 重建。
|
|
@@ -769,14 +1094,8 @@ function collectMountedComponents(root: any): void {
|
|
|
769
1094
|
}
|
|
770
1095
|
|
|
771
1096
|
export function installCompactMode(deps: CompactModeInstallDeps): CompactModeHooks {
|
|
772
|
-
const
|
|
773
|
-
|
|
774
|
-
if (previous) {
|
|
775
|
-
// Replacement reload:先释放旧 compact 外层,再清理其 thinking owner,
|
|
776
|
-
// 否则下面会把旧 thinking wrapper 误采样为 native prototype。
|
|
777
|
-
previous.dispose();
|
|
778
|
-
host[COMPACT_THINKING_OWNER_KEY]?.stop?.();
|
|
779
|
-
}
|
|
1097
|
+
const previous = patchRegistry.get<CompactModePatch>(COMPACT_MODE_PATCH_KEY);
|
|
1098
|
+
if (previous) previous.dispose();
|
|
780
1099
|
|
|
781
1100
|
const assistantPrototype = AssistantMessageComponent.prototype as any;
|
|
782
1101
|
const toolPrototype = ToolExecutionComponent.prototype as any;
|
|
@@ -812,13 +1131,24 @@ export function installCompactMode(deps: CompactModeInstallDeps): CompactModeHoo
|
|
|
812
1131
|
detachedMessages: any[];
|
|
813
1132
|
active: boolean;
|
|
814
1133
|
suppressedToolIds: Set<string>;
|
|
1134
|
+
/** live 槽位内联渲染的工具 id;回合结束时随槽位一起交还外层。 */
|
|
1135
|
+
liveSlotToolIds: Set<string>;
|
|
1136
|
+
/** 收尾后的静默期截止时间戳;期间仍按围观态渲染,到点才收拢。 */
|
|
1137
|
+
settleUntil?: number;
|
|
1138
|
+
foldTimer?: ReturnType<typeof setTimeout>;
|
|
815
1139
|
/** 回合挂钟起点,保证 Running 时长连续递增。 */
|
|
816
1140
|
startedAt: number;
|
|
817
1141
|
endedAt?: number;
|
|
1142
|
+
/** 回合尾行(摘要 + live 槽位),挂在 transcript 容器的回合末尾。 */
|
|
1143
|
+
tail?: MountedRoundTail;
|
|
818
1144
|
};
|
|
819
1145
|
let activeRound: CompactRound | undefined;
|
|
820
1146
|
let roundByComponent = new WeakMap<object, CompactRound>();
|
|
821
1147
|
const expandedRoundToolIds = new Set<string>();
|
|
1148
|
+
/** 用户显式点开的 round 内工具;强制折叠不压回这些 id。 */
|
|
1149
|
+
const explicitRoundToolIds = new Set<string>();
|
|
1150
|
+
const liveSlotToolIds = new Set<string>();
|
|
1151
|
+
const pendingFoldRounds = new Set<CompactRound>();
|
|
822
1152
|
let uiRef: { requestRender?: (force?: boolean) => void } | undefined;
|
|
823
1153
|
let roundTickTimer: ReturnType<typeof setInterval> | undefined;
|
|
824
1154
|
|
|
@@ -849,17 +1179,32 @@ export function installCompactMode(deps: CompactModeInstallDeps): CompactModeHoo
|
|
|
849
1179
|
stopRoundTick();
|
|
850
1180
|
return;
|
|
851
1181
|
}
|
|
1182
|
+
settleStrandedRounds();
|
|
852
1183
|
try {
|
|
853
|
-
|
|
1184
|
+
// 非 force:保留 fullscreen 布局缓存和差分绘制。
|
|
1185
|
+
uiRef?.requestRender?.();
|
|
854
1186
|
} catch {
|
|
855
1187
|
/* 无 UI */
|
|
856
1188
|
}
|
|
857
1189
|
}, 250);
|
|
858
|
-
roundTickTimer.unref?.();
|
|
859
1190
|
};
|
|
860
1191
|
|
|
861
|
-
/**
|
|
862
|
-
const
|
|
1192
|
+
/** 围观中:活动回合,或收尾静默期还没走完的回合(槽位卡仍挂在屏幕上)。 */
|
|
1193
|
+
const roundLive = (round: CompactRound): boolean =>
|
|
1194
|
+
round.active || (round.settleUntil !== undefined && Date.now() < round.settleUntil);
|
|
1195
|
+
|
|
1196
|
+
const clearFoldTimer = (round: CompactRound): void => {
|
|
1197
|
+
if (round.foldTimer !== undefined) {
|
|
1198
|
+
clearTimeout(round.foldTimer);
|
|
1199
|
+
round.foldTimer = undefined;
|
|
1200
|
+
}
|
|
1201
|
+
round.settleUntil = undefined;
|
|
1202
|
+
pendingFoldRounds.delete(round);
|
|
1203
|
+
};
|
|
1204
|
+
|
|
1205
|
+
/** 结束回合活动态;不做任何重绘。 */
|
|
1206
|
+
const finalizeRound = (round: CompactRound): void => {
|
|
1207
|
+
clearFoldTimer(round);
|
|
863
1208
|
round.active = false;
|
|
864
1209
|
if (round.endedAt === undefined) round.endedAt = Date.now();
|
|
865
1210
|
if (activeRound === round) {
|
|
@@ -867,7 +1212,36 @@ export function installCompactMode(deps: CompactModeInstallDeps): CompactModeHoo
|
|
|
867
1212
|
deps.query?.setCompactSummaryActive?.(false);
|
|
868
1213
|
stopRoundTick();
|
|
869
1214
|
}
|
|
870
|
-
|
|
1215
|
+
};
|
|
1216
|
+
|
|
1217
|
+
/**
|
|
1218
|
+
* 结束回合活动态;render=true 时走静默期,延迟 FOLD_SETTLE_MS 再收拢。
|
|
1219
|
+
* 分组语义不受影响:activeRound 立即让位,新消息不会被并进已收尾的回合。
|
|
1220
|
+
*/
|
|
1221
|
+
const endRound = (round: CompactRound, render = false): void => {
|
|
1222
|
+
if (!render || !round.active) {
|
|
1223
|
+
finalizeRound(round);
|
|
1224
|
+
if (render) renderRound(round);
|
|
1225
|
+
return;
|
|
1226
|
+
}
|
|
1227
|
+
finalizeRound(round);
|
|
1228
|
+
if (round.foldTimer !== undefined) return;
|
|
1229
|
+
round.settleUntil = Date.now() + FOLD_SETTLE_MS;
|
|
1230
|
+
pendingFoldRounds.add(round);
|
|
1231
|
+
round.foldTimer = setTimeout(() => {
|
|
1232
|
+
round.foldTimer = undefined;
|
|
1233
|
+
round.settleUntil = undefined;
|
|
1234
|
+
pendingFoldRounds.delete(round);
|
|
1235
|
+
renderRound(round);
|
|
1236
|
+
}, FOLD_SETTLE_MS);
|
|
1237
|
+
};
|
|
1238
|
+
|
|
1239
|
+
/** 新回合接替时立即收掉还在静默期的回合,把两次转场并成一次。 */
|
|
1240
|
+
const flushSettle = (): void => {
|
|
1241
|
+
for (const round of [...pendingFoldRounds]) {
|
|
1242
|
+
clearFoldTimer(round);
|
|
1243
|
+
renderRound(round);
|
|
1244
|
+
}
|
|
871
1245
|
};
|
|
872
1246
|
|
|
873
1247
|
const renderAssistantWithoutThinking = (
|
|
@@ -888,11 +1262,46 @@ export function installCompactMode(deps: CompactModeInstallDeps): CompactModeHoo
|
|
|
888
1262
|
return result;
|
|
889
1263
|
};
|
|
890
1264
|
|
|
1265
|
+
/** compact 只允许单开:收回一张工具卡的展开态。 */
|
|
1266
|
+
const collapseTool = (tool: any): void => {
|
|
1267
|
+
if (typeof tool?.setExpanded === "function") tool.setExpanded(false);
|
|
1268
|
+
else {
|
|
1269
|
+
tool.expanded = false;
|
|
1270
|
+
tool.updateDisplay?.();
|
|
1271
|
+
}
|
|
1272
|
+
};
|
|
1273
|
+
|
|
1274
|
+
/** 按条件收回已展开的工具卡。 */
|
|
1275
|
+
const collapseTools = (shouldCollapse: (tool: any) => boolean): void => {
|
|
1276
|
+
for (const tool of trackedToolComponents) {
|
|
1277
|
+
if (tool.expanded === true && shouldCollapse(tool)) collapseTool(tool);
|
|
1278
|
+
}
|
|
1279
|
+
};
|
|
1280
|
+
|
|
1281
|
+
const markRoundToolUserExpanded = (tool: any): void => {
|
|
1282
|
+
const id = String(tool?.toolCallId ?? "");
|
|
1283
|
+
if (!id || !expandedRoundToolIds.has(id)) return;
|
|
1284
|
+
for (const other of trackedToolComponents) {
|
|
1285
|
+
const otherId = String(other?.toolCallId ?? "");
|
|
1286
|
+
if (other === tool || !expandedRoundToolIds.has(otherId) || other.expanded !== true) continue;
|
|
1287
|
+
explicitRoundToolIds.delete(otherId);
|
|
1288
|
+
collapseTool(other);
|
|
1289
|
+
}
|
|
1290
|
+
explicitRoundToolIds.add(id);
|
|
1291
|
+
};
|
|
1292
|
+
roundToolExpansion = { markUserExpanded: markRoundToolUserExpanded };
|
|
1293
|
+
|
|
891
1294
|
const roundMessages = (round: CompactRound): any[] => [
|
|
892
1295
|
...round.messages.values(),
|
|
893
1296
|
...round.detachedMessages,
|
|
894
1297
|
];
|
|
895
1298
|
|
|
1299
|
+
/** 镜像给常驻状态行的文案:只认当前活动回合(摘要行被顶出视口时由 working-message 取用)。 */
|
|
1300
|
+
compactRunStatusGetter = () => {
|
|
1301
|
+
const round = activeRound;
|
|
1302
|
+
return round?.active ? summarize(roundMessages(round), true, round) || undefined : undefined;
|
|
1303
|
+
};
|
|
1304
|
+
|
|
896
1305
|
const roundToolCallIds = (round: CompactRound): Set<string> => {
|
|
897
1306
|
const ids = new Set<string>();
|
|
898
1307
|
for (const message of roundMessages(round)) {
|
|
@@ -903,83 +1312,552 @@ export function installCompactMode(deps: CompactModeInstallDeps): CompactModeHoo
|
|
|
903
1312
|
return ids;
|
|
904
1313
|
};
|
|
905
1314
|
|
|
1315
|
+
/**
|
|
1316
|
+
* anchor 所在的 transcript 容器(pi chatContainer)。
|
|
1317
|
+
* 优先走分组补丁在 addChild 时挂的父指针;没有则沿 tui 组件树找直属父容器。
|
|
1318
|
+
*/
|
|
1319
|
+
const tailContainerOf = (anchor: any): any => {
|
|
1320
|
+
const keyed = anchor?.[TOOL_GROUPING_PARENT_KEY];
|
|
1321
|
+
if (keyed && Array.isArray(keyed.children) && keyed.children.includes(anchor)) {
|
|
1322
|
+
return keyed;
|
|
1323
|
+
}
|
|
1324
|
+
let found: any;
|
|
1325
|
+
walkComponentTree(getToolMouseTui(), (value: any) => {
|
|
1326
|
+
if (found) return false;
|
|
1327
|
+
if (Array.isArray(value?.children) && value.children.includes(anchor)) {
|
|
1328
|
+
found = value;
|
|
1329
|
+
return false;
|
|
1330
|
+
}
|
|
1331
|
+
return undefined;
|
|
1332
|
+
});
|
|
1333
|
+
return found;
|
|
1334
|
+
};
|
|
1335
|
+
|
|
1336
|
+
/** 组件在树里的直属父容器。TUI 切换/reparent 后缓存的 container 会过期。 */
|
|
1337
|
+
const containerOfHost = (host: any): any => {
|
|
1338
|
+
let found: any;
|
|
1339
|
+
walkComponentTree(getToolMouseTui(), (value: any) => {
|
|
1340
|
+
if (found) return false;
|
|
1341
|
+
if (Array.isArray(value?.children) && value.children.includes(host)) {
|
|
1342
|
+
found = value;
|
|
1343
|
+
return false;
|
|
1344
|
+
}
|
|
1345
|
+
return undefined;
|
|
1346
|
+
});
|
|
1347
|
+
return found;
|
|
1348
|
+
};
|
|
1349
|
+
|
|
1350
|
+
/** 尾行实际所在容器;缓存的 container 过期时按树找回来。 */
|
|
1351
|
+
const tailHostContainer = (tail: MountedRoundTail): any =>
|
|
1352
|
+
containerOfHost(tail.host) ??
|
|
1353
|
+
(tail.container?.children?.includes?.(tail.host) === true ? tail.container : undefined);
|
|
1354
|
+
|
|
1355
|
+
/** 摘尾行:按实际所在容器摘,避免缓存过期时摘了个空、行留在树上。 */
|
|
1356
|
+
const removeTailHost = (tail: MountedRoundTail): void => {
|
|
1357
|
+
tailHostContainer(tail)?.removeChild?.(tail.host);
|
|
1358
|
+
};
|
|
1359
|
+
|
|
1360
|
+
const ensureTailHost = (round: CompactRound): MountedRoundTail => {
|
|
1361
|
+
if (round.tail) return round.tail;
|
|
1362
|
+
// 同一 anchor 上残留的旧回合尾行(reset/重组后建新回合)先摘除,避免双摘要。
|
|
1363
|
+
const stale = tailByAnchor.get(round.anchor);
|
|
1364
|
+
if (stale) {
|
|
1365
|
+
removeTailHost(stale);
|
|
1366
|
+
mountedTails.delete(stale);
|
|
1367
|
+
tailByAnchor.delete(round.anchor);
|
|
1368
|
+
}
|
|
1369
|
+
const tail: MountedRoundTail = { host: undefined, container: undefined, parts: [], round };
|
|
1370
|
+
const host: any = {
|
|
1371
|
+
outputPad: Number(round.anchor?.outputPad) || 0,
|
|
1372
|
+
children: [],
|
|
1373
|
+
render: (width: number) => tail.parts.flatMap((part: any) => part?.render?.(width) ?? []),
|
|
1374
|
+
childAtRow(localRow: number, width: number) {
|
|
1375
|
+
let offset = 0;
|
|
1376
|
+
for (const part of tail.parts) {
|
|
1377
|
+
const lines = part?.render?.(width);
|
|
1378
|
+
const count = Array.isArray(lines) ? lines.length : 0;
|
|
1379
|
+
if (localRow < offset + count) {
|
|
1380
|
+
return part?.childAtRow?.(localRow - offset, width) ?? null;
|
|
1381
|
+
}
|
|
1382
|
+
offset += count;
|
|
1383
|
+
}
|
|
1384
|
+
return null;
|
|
1385
|
+
},
|
|
1386
|
+
invalidate: () => {
|
|
1387
|
+
for (const part of tail.parts) part?.invalidate?.();
|
|
1388
|
+
},
|
|
1389
|
+
};
|
|
1390
|
+
Object.defineProperty(host, "expanded", {
|
|
1391
|
+
configurable: true,
|
|
1392
|
+
get: () => tail.round?.anchor?.expanded === true,
|
|
1393
|
+
set: (value: boolean) => {
|
|
1394
|
+
const anchor = tail.round?.anchor;
|
|
1395
|
+
if (anchor) anchor.expanded = value;
|
|
1396
|
+
},
|
|
1397
|
+
});
|
|
1398
|
+
// 点击/悬停归属与 anchor 同一展开入口;toggle 每次 renderRound 重建,按值转发。
|
|
1399
|
+
// 经 tail.round 动态解引用:重建过户时 anchor 可能换成别的组件。
|
|
1400
|
+
ensureAssistantSetExpanded(host);
|
|
1401
|
+
Object.defineProperty(host, "roundAnchor", {
|
|
1402
|
+
configurable: true,
|
|
1403
|
+
get: () => tail.round?.anchor,
|
|
1404
|
+
});
|
|
1405
|
+
host[ASSISTANT_TOGGLE_ROUND_KEY] = (expanded: boolean) =>
|
|
1406
|
+
tail.round?.anchor?.[ASSISTANT_TOGGLE_ROUND_KEY]?.(expanded);
|
|
1407
|
+
tail.host = host;
|
|
1408
|
+
tailByAnchor.set(round.anchor, tail);
|
|
1409
|
+
mountedTails.add(tail);
|
|
1410
|
+
round.tail = tail;
|
|
1411
|
+
return tail;
|
|
1412
|
+
};
|
|
1413
|
+
|
|
1414
|
+
/**
|
|
1415
|
+
* 尾行归位。运行中(含收尾静默期)尾行恒为容器最末一行:计数行钉在
|
|
1416
|
+
* transcript 底缘,运行中到达的提示行/custom 条目只能落在它上方,计数
|
|
1417
|
+
* 不会被顶离底部截成两行。回合收尾后摘要行回落到回合最后一个成员之后,
|
|
1418
|
+
* 外来行统一换到摘要行之下,通知行永远落在回合边界外。
|
|
1419
|
+
*/
|
|
1420
|
+
const syncTailPosition = (round: CompactRound): void => {
|
|
1421
|
+
const tail = round.tail;
|
|
1422
|
+
if (!tail?.host) return;
|
|
1423
|
+
const container =
|
|
1424
|
+
tail.container?.children?.includes?.(round.anchor) === true
|
|
1425
|
+
? tail.container
|
|
1426
|
+
: tailContainerOf(round.anchor);
|
|
1427
|
+
if (!container || !Array.isArray(container.children)) return;
|
|
1428
|
+
tail.container = container;
|
|
1429
|
+
const children = container.children as any[];
|
|
1430
|
+
const current = children.indexOf(tail.host);
|
|
1431
|
+
if (current >= 0) children.splice(current, 1);
|
|
1432
|
+
const ids = roundToolCallIds(round);
|
|
1433
|
+
const matchesTool = (value: any): boolean =>
|
|
1434
|
+
typeof value?.toolCallId === "string" && ids.has(value.toolCallId);
|
|
1435
|
+
const isMember = (value: any): boolean =>
|
|
1436
|
+
round.messages.has(value) ||
|
|
1437
|
+
matchesTool(value) ||
|
|
1438
|
+
(Array.isArray(value?.children) && value.children.some(matchesTool));
|
|
1439
|
+
let first = -1;
|
|
1440
|
+
let last = -1;
|
|
1441
|
+
for (let i = 0; i < children.length; i++) {
|
|
1442
|
+
if (!isMember(children[i])) continue;
|
|
1443
|
+
if (first < 0) first = i;
|
|
1444
|
+
last = i;
|
|
1445
|
+
}
|
|
1446
|
+
const lastMember = last >= 0 ? children[last] : undefined;
|
|
1447
|
+
const intruders: any[] = [];
|
|
1448
|
+
for (let i = last - 1; i > first; i--) {
|
|
1449
|
+
if (!isMember(children[i])) intruders.unshift(children.splice(i, 1)[0]);
|
|
1450
|
+
}
|
|
1451
|
+
if (lastMember) last = children.indexOf(lastMember);
|
|
1452
|
+
const insertAt = last < 0 ? children.length : last + 1;
|
|
1453
|
+
if (roundLive(round)) {
|
|
1454
|
+
// 活回合:外来行压到最后成员之后,尾行占容器最末。
|
|
1455
|
+
children.splice(insertAt, 0, ...intruders);
|
|
1456
|
+
children.push(tail.host);
|
|
1457
|
+
} else {
|
|
1458
|
+
children.splice(insertAt, 0, tail.host, ...intruders);
|
|
1459
|
+
}
|
|
1460
|
+
tail.host[TOOL_GROUPING_PARENT_KEY] = container;
|
|
1461
|
+
};
|
|
1462
|
+
|
|
1463
|
+
const unmountRoundTail = (round: CompactRound): void => {
|
|
1464
|
+
const tail = round.tail;
|
|
1465
|
+
if (!tail) return;
|
|
1466
|
+
round.tail = undefined;
|
|
1467
|
+
removeTailHost(tail);
|
|
1468
|
+
mountedTails.delete(tail);
|
|
1469
|
+
if (tailByAnchor.get(round.anchor) === tail) tailByAnchor.delete(round.anchor);
|
|
1470
|
+
};
|
|
1471
|
+
|
|
1472
|
+
/**
|
|
1473
|
+
* pi 把新工具卡 append 在容器末尾(尾行之后):updateDisplay 时把所属回合的
|
|
1474
|
+
* 尾行压回末尾。只有 live/收尾静默期的回合还会长工具。
|
|
1475
|
+
*/
|
|
1476
|
+
const syncTailForTool = (toolCallId: string): void => {
|
|
1477
|
+
if (!toolCallId) return;
|
|
1478
|
+
for (const round of [activeRound, ...pendingFoldRounds]) {
|
|
1479
|
+
if (round?.tail && roundToolCallIds(round).has(toolCallId)) {
|
|
1480
|
+
syncTailPosition(round);
|
|
1481
|
+
return;
|
|
1482
|
+
}
|
|
1483
|
+
}
|
|
1484
|
+
};
|
|
1485
|
+
|
|
1486
|
+
/**
|
|
1487
|
+
* refresh 重建时继承旧回合:resetRounds 只清索引不摘 compact 尾行,
|
|
1488
|
+
* 残留尾行带着旧 round。同一组件重放同一消息(lastMessage 回放)
|
|
1489
|
+
* 才算重建——保留挂钟起点、已脱离的工具历史与尾行组件本身;
|
|
1490
|
+
* 旧回合已收尾的直接落成折叠态(不回摆 Running、不重演静默期),
|
|
1491
|
+
* 仍活动的只接回挂钟,Running 时长继续走。
|
|
1492
|
+
*/
|
|
1493
|
+
const adoptPriorRound = (round: CompactRound, message: any): void => {
|
|
1494
|
+
let prior: CompactRound | undefined;
|
|
1495
|
+
for (const tail of mountedTails) {
|
|
1496
|
+
const stale = tail.round as CompactRound | undefined;
|
|
1497
|
+
if (
|
|
1498
|
+
stale &&
|
|
1499
|
+
// 先认组件:重放时消息对象未必是同一个(会话重读/重建),
|
|
1500
|
+
// 只认消息身份会漏掉本该接回的回合,摘要行就此掉队。
|
|
1501
|
+
(stale.anchor === round.anchor ||
|
|
1502
|
+
stale.messages.has(round.anchor) ||
|
|
1503
|
+
stale.detachedMessages.includes(message))
|
|
1504
|
+
) {
|
|
1505
|
+
prior = stale;
|
|
1506
|
+
break;
|
|
1507
|
+
}
|
|
1508
|
+
}
|
|
1509
|
+
if (!prior) return;
|
|
1510
|
+
round.startedAt = prior.startedAt;
|
|
1511
|
+
if (prior.detachedMessages.length) round.detachedMessages.push(...prior.detachedMessages);
|
|
1512
|
+
if (!prior.active) {
|
|
1513
|
+
round.active = false;
|
|
1514
|
+
round.endedAt = prior.endedAt ?? round.startedAt;
|
|
1515
|
+
}
|
|
1516
|
+
const tail = prior.tail;
|
|
1517
|
+
if (tail) {
|
|
1518
|
+
prior.tail = undefined;
|
|
1519
|
+
if (tailByAnchor.get(prior.anchor) === tail) tailByAnchor.delete(prior.anchor);
|
|
1520
|
+
tail.round = round;
|
|
1521
|
+
tailByAnchor.set(round.anchor, tail);
|
|
1522
|
+
round.tail = tail;
|
|
1523
|
+
}
|
|
1524
|
+
};
|
|
1525
|
+
|
|
1526
|
+
/** 新建活回合:literal + 激活 + refresh 重建时的旧回合收编。 */
|
|
1527
|
+
const createRound = (anchor: any, message: any): CompactRound => {
|
|
1528
|
+
const round: CompactRound = {
|
|
1529
|
+
anchor,
|
|
1530
|
+
messages: new Map(),
|
|
1531
|
+
detachedMessages: [],
|
|
1532
|
+
active: true,
|
|
1533
|
+
suppressedToolIds: new Set(),
|
|
1534
|
+
liveSlotToolIds: new Set(),
|
|
1535
|
+
startedAt: Date.now(),
|
|
1536
|
+
};
|
|
1537
|
+
activateRound(round);
|
|
1538
|
+
adoptPriorRound(round, message);
|
|
1539
|
+
return round;
|
|
1540
|
+
};
|
|
1541
|
+
|
|
1542
|
+
/**
|
|
1543
|
+
* 尾行挂载入口:makeParts 按渲染态产出内容(折叠态 [摘要行],live 态
|
|
1544
|
+
* [摘要行, 槽位卡]),identity 决定行内 hover/点击归属组件(尾行模式下是
|
|
1545
|
+
* host,hit-test 直达它)。anchor 未挂进任何容器时(独立渲染)退回
|
|
1546
|
+
* contentContainer——与旧行为一致。
|
|
1547
|
+
*/
|
|
1548
|
+
const mountRoundTail = (round: CompactRound, makeParts: (identity: any) => any[]): void => {
|
|
1549
|
+
if (!tailContainerOf(round.anchor)) {
|
|
1550
|
+
// anchor 不在任何容器里(独立渲染/未挂载):退回 anchor 内部,与旧行为一致。
|
|
1551
|
+
unmountRoundTail(round);
|
|
1552
|
+
for (const part of makeParts(round.anchor)) {
|
|
1553
|
+
round.anchor?.contentContainer?.addChild?.(part);
|
|
1554
|
+
}
|
|
1555
|
+
return;
|
|
1556
|
+
}
|
|
1557
|
+
const tail = ensureTailHost(round);
|
|
1558
|
+
tail.parts = makeParts(tail.host);
|
|
1559
|
+
tail.host.children = tail.parts;
|
|
1560
|
+
syncTailPosition(round);
|
|
1561
|
+
};
|
|
1562
|
+
|
|
1563
|
+
/**
|
|
1564
|
+
* 末块仍是 thinking = 思考还在长;后面跟了正文或工具调用就算结束。
|
|
1565
|
+
* 围观态只预览这一种,已完成的思考直接回收进摘要行,不在屏幕上留 Thought 行。
|
|
1566
|
+
*/
|
|
1567
|
+
const hasTrailingThinking = (message: any): boolean => {
|
|
1568
|
+
const content = Array.isArray(message?.content) ? message.content : [];
|
|
1569
|
+
for (let i = content.length - 1; i >= 0; i--) {
|
|
1570
|
+
const item = content[i];
|
|
1571
|
+
if (item?.type === "thinking") return true;
|
|
1572
|
+
if (item?.type === "toolCall") return false;
|
|
1573
|
+
if (item?.type === "text" && String(item.text ?? "").trim()) return false;
|
|
1574
|
+
}
|
|
1575
|
+
return false;
|
|
1576
|
+
};
|
|
1577
|
+
|
|
1578
|
+
/** 运行中的工具:已开始且尚无最终结果。 */
|
|
1579
|
+
const isRunningTool = (tool: any): boolean =>
|
|
1580
|
+
tool?.executionStarted === true && (!tool.result || tool.isPartial === true);
|
|
1581
|
+
|
|
1582
|
+
/**
|
|
1583
|
+
* live 围观态:正文 + 摘要行照折叠态渲染,只在摘要行下多一张槽位卡。
|
|
1584
|
+
* - 思考:只预览最后一条仍在思考的消息,它的块是当前最新内容;
|
|
1585
|
+
* - 工具:没有活动思考时才占槽位,运行中优先,否则取本回合最近的,完成也留着;
|
|
1586
|
+
* - 槽位只放一个块(思考或工具),新的思考块/工具调用进来才轮换;
|
|
1587
|
+
* - 本回合所有工具都由槽位卡接管,外层不再单独成行。
|
|
1588
|
+
* 可见性判断只在这里发生;回合结束(renderRound 非 active)只剩槽位卡消失。
|
|
1589
|
+
*/
|
|
1590
|
+
const renderRoundLive = (
|
|
1591
|
+
round: CompactRound,
|
|
1592
|
+
stopStatus: string | undefined,
|
|
1593
|
+
getSummary: () => string,
|
|
1594
|
+
): void => {
|
|
1595
|
+
// 仍在思考的消息 = 当前最新内容,优先占槽位。
|
|
1596
|
+
let previewComponent: any;
|
|
1597
|
+
for (const [component, message] of round.messages) {
|
|
1598
|
+
if (hasTrailingThinking(message)) previewComponent = component;
|
|
1599
|
+
}
|
|
1600
|
+
|
|
1601
|
+
const thinkingKids: any[] = [];
|
|
1602
|
+
for (const [component, message] of round.messages) {
|
|
1603
|
+
if (component !== previewComponent) {
|
|
1604
|
+
component.contentContainer?.clear?.();
|
|
1605
|
+
continue;
|
|
1606
|
+
}
|
|
1607
|
+
passThroughAssistant(component, message);
|
|
1608
|
+
const kids = Array.isArray(component.contentContainer?.children)
|
|
1609
|
+
? [...component.contentContainer.children]
|
|
1610
|
+
: [];
|
|
1611
|
+
component.contentContainer?.clear?.();
|
|
1612
|
+
// 只摘思考预览块进槽位;已收尾的思考回收进摘要行。
|
|
1613
|
+
for (const kid of kids) {
|
|
1614
|
+
if (typeof kid?.setHintHovered === "function") thinkingKids.push(kid);
|
|
1615
|
+
}
|
|
1616
|
+
}
|
|
1617
|
+
// 正文照折叠态渲染(不含 thinking):live 与收尾后同形,收尾只剩槽位卡消失。
|
|
1618
|
+
renderAssistantWithoutThinking(round.anchor, round.messages.get(round.anchor));
|
|
1619
|
+
|
|
1620
|
+
const toolsById = new Map<string, any>();
|
|
1621
|
+
for (const tool of trackedToolComponents) {
|
|
1622
|
+
if (typeof tool?.toolCallId === "string") toolsById.set(tool.toolCallId, tool);
|
|
1623
|
+
}
|
|
1624
|
+
let runningTool: any;
|
|
1625
|
+
let lastTool: any;
|
|
1626
|
+
for (const [, message] of round.messages) {
|
|
1627
|
+
for (const item of Array.isArray(message?.content) ? message.content : []) {
|
|
1628
|
+
if (item?.type !== "toolCall" || typeof item.id !== "string") continue;
|
|
1629
|
+
const tool = toolsById.get(item.id);
|
|
1630
|
+
// edit/write 保留自己的 compact 行,不进槽位。
|
|
1631
|
+
if (EDIT_WRITE_TOOLS.has(String(tool?.toolName ?? item.name ?? ""))) continue;
|
|
1632
|
+
round.liveSlotToolIds.add(item.id);
|
|
1633
|
+
liveSlotToolIds.add(item.id);
|
|
1634
|
+
if (!tool) continue;
|
|
1635
|
+
lastTool = tool;
|
|
1636
|
+
if (isRunningTool(tool)) runningTool = tool;
|
|
1637
|
+
}
|
|
1638
|
+
}
|
|
1639
|
+
// 槽位单块:活动思考最新;否则本回合最近的工具(运行中原位增长,完成也留着)。
|
|
1640
|
+
const slotTool = thinkingKids.length > 0 ? undefined : (runningTool ?? lastTool);
|
|
1641
|
+
const cardItems: Array<{ child?: any; tool?: any }> = slotTool
|
|
1642
|
+
? [{ tool: slotTool }]
|
|
1643
|
+
: thinkingKids.map((child) => ({ child }));
|
|
1644
|
+
|
|
1645
|
+
const anchor = round.anchor;
|
|
1646
|
+
const pad = Number(anchor.outputPad) || 0;
|
|
1647
|
+
// 摘要行与槽位卡挂回合尾行(transcript 容器内回合末尾,而非 anchor 内部):
|
|
1648
|
+
// 运行中恒在可写视口底缘,收尾的 Ran for 原地落进 scrollback,不再留下
|
|
1649
|
+
// 被挤出视口后定格的 Running 帧。
|
|
1650
|
+
mountRoundTail(round, (identity) => {
|
|
1651
|
+
const parts: any[] = [compactAssistantLineComponent(identity, getSummary, deps.query)];
|
|
1652
|
+
if (cardItems.length > 0) {
|
|
1653
|
+
parts.push(
|
|
1654
|
+
compactLiveSlot(
|
|
1655
|
+
compactRoundCard(
|
|
1656
|
+
cardItems,
|
|
1657
|
+
(tool, innerWidth) => patch.toolOriginalRender.call(tool, innerWidth),
|
|
1658
|
+
false,
|
|
1659
|
+
true,
|
|
1660
|
+
),
|
|
1661
|
+
pad,
|
|
1662
|
+
),
|
|
1663
|
+
);
|
|
1664
|
+
}
|
|
1665
|
+
return parts;
|
|
1666
|
+
});
|
|
1667
|
+
appendStopStatus(anchor, stopStatus);
|
|
1668
|
+
};
|
|
1669
|
+
|
|
906
1670
|
const renderRound = (round: CompactRound): void => {
|
|
907
1671
|
const messages = roundMessages(round);
|
|
908
1672
|
const stopStatus = roundStopStatus(messages);
|
|
909
1673
|
if (stopStatus) endRound(round);
|
|
910
1674
|
// Running 时每次 render 重算时长(含挂钟下限);结束后固定。
|
|
1675
|
+
// 静默期内回合已收尾,摘要行直接给 Ran for,只有槽位卡多留一拍。
|
|
911
1676
|
const getSummary = () => summarize(roundMessages(round), round.active, round);
|
|
912
1677
|
const summary = getSummary();
|
|
913
1678
|
for (const id of round.suppressedToolIds) expandedRoundToolIds.delete(id);
|
|
914
1679
|
round.suppressedToolIds.clear();
|
|
1680
|
+
// 回合收起(或本回合不在展开态)时忘掉用户的单开选择,并把面板接管的工具一起收回:
|
|
1681
|
+
// 少了后一步,被点开的工具会带着自身的 expanded 落到 toolInstalledRender 的放行分支,
|
|
1682
|
+
// 面板一收就单独渲染成一张卡。
|
|
1683
|
+
if (round.anchor.expanded !== true) {
|
|
1684
|
+
const ids = roundToolCallIds(round);
|
|
1685
|
+
for (const id of ids) explicitRoundToolIds.delete(id);
|
|
1686
|
+
collapseTools(
|
|
1687
|
+
(tool) =>
|
|
1688
|
+
ids.has(String(tool.toolCallId ?? "")) &&
|
|
1689
|
+
!EDIT_WRITE_TOOLS.has(String(tool.toolName ?? "")),
|
|
1690
|
+
);
|
|
1691
|
+
}
|
|
1692
|
+
for (const id of round.liveSlotToolIds) liveSlotToolIds.delete(id);
|
|
1693
|
+
round.liveSlotToolIds.clear();
|
|
915
1694
|
|
|
916
1695
|
for (const [component, message] of round.messages) {
|
|
917
1696
|
component.lastMessage = message;
|
|
918
1697
|
ensureAssistantSetExpanded(component);
|
|
919
1698
|
component[ASSISTANT_TOGGLE_ROUND_KEY] = (expanded: boolean) => {
|
|
1699
|
+
const wasExpanded = round.anchor.expanded === true;
|
|
1700
|
+
if (expanded && !wasExpanded) capturePanelViewport(round.anchor);
|
|
920
1701
|
for (const member of round.messages.keys()) member.expanded = expanded;
|
|
921
1702
|
if (!expanded) {
|
|
922
1703
|
for (const id of round.suppressedToolIds) expandedRoundToolIds.delete(id);
|
|
923
1704
|
round.suppressedToolIds.clear();
|
|
924
1705
|
}
|
|
925
1706
|
renderRound(round);
|
|
1707
|
+
if (!expanded && wasExpanded) restorePanelViewport(round.anchor);
|
|
926
1708
|
};
|
|
927
1709
|
}
|
|
928
1710
|
|
|
929
1711
|
if (round.anchor.expanded === true) {
|
|
930
|
-
|
|
1712
|
+
// 展开内容留在摘要行那个位置:回合最后一个成员(含 write/edit)之后。
|
|
1713
|
+
// 放进 anchor 内部会跳到回合起点,盖到中间的 write/edit 上面。
|
|
1714
|
+
// anchor 未挂进 transcript 时没有这个位置,退回 anchor 内部。
|
|
1715
|
+
const mounted = Boolean(tailContainerOf(round.anchor));
|
|
1716
|
+
const toolsById = new Map<string, any>();
|
|
1717
|
+
for (const tool of trackedToolComponents) {
|
|
1718
|
+
if (typeof tool?.toolCallId === "string") toolsById.set(tool.toolCallId, tool);
|
|
1719
|
+
}
|
|
1720
|
+
// 助手文本按原生渲染、不进面板(面板只留 thinking 与工具卡)。
|
|
1721
|
+
// thinking/工具卡按连续段各包一张面板;先出文本再出面板,
|
|
1722
|
+
// 否则消息内容里的 thinking([thinking, text, toolCall])会盖到文本上面。
|
|
1723
|
+
const anchorChildren: any[] = [];
|
|
1724
|
+
let cardItems: Array<{ child?: any; tool?: any }> = [];
|
|
1725
|
+
const flushPanel = () => {
|
|
1726
|
+
if (cardItems.length === 0) return;
|
|
1727
|
+
if (cardItems.every((item) => !item.tool && isSpacerComponent(item.child))) {
|
|
1728
|
+
for (const item of cardItems) anchorChildren.push(item.child);
|
|
1729
|
+
} else {
|
|
1730
|
+
// 面板上沿留 1 行卡外空行:面板底色与 userMessage 同色,
|
|
1731
|
+
// 贴在一起会看成一个块;卡内顶部再加 1 行 padding 保持上下对称。
|
|
1732
|
+
anchorChildren.push(new Spacer(1));
|
|
1733
|
+
anchorChildren.push(
|
|
1734
|
+
compactRoundCard(
|
|
1735
|
+
cardItems,
|
|
1736
|
+
(tool, innerWidth) => patch.toolOriginalRender.call(tool, innerWidth),
|
|
1737
|
+
// 整回合展开卡:卡内工具行归 tool 组件,可单击二次展开。
|
|
1738
|
+
true,
|
|
1739
|
+
),
|
|
1740
|
+
);
|
|
1741
|
+
}
|
|
1742
|
+
cardItems = [];
|
|
1743
|
+
};
|
|
1744
|
+
const takeChild = (child: any, outside: boolean) => {
|
|
1745
|
+
if (!outside) {
|
|
1746
|
+
cardItems.push({ child });
|
|
1747
|
+
return;
|
|
1748
|
+
}
|
|
1749
|
+
flushPanel();
|
|
1750
|
+
anchorChildren.push(child);
|
|
1751
|
+
};
|
|
1752
|
+
const placedToolIds = new Set<string>();
|
|
1753
|
+
const placeTool = (id: string, fallbackName?: string) => {
|
|
1754
|
+
const tool = toolsById.get(id);
|
|
1755
|
+
if (!tool || EDIT_WRITE_TOOLS.has(String(tool.toolName ?? fallbackName ?? ""))) return;
|
|
1756
|
+
if (placedToolIds.has(id)) return;
|
|
1757
|
+
placedToolIds.add(id);
|
|
1758
|
+
cardItems.push({ tool });
|
|
1759
|
+
};
|
|
1760
|
+
type LocalEntry = { outside: boolean; child?: any; toolId?: string; toolName?: string };
|
|
931
1761
|
for (const [component, message] of round.messages) {
|
|
932
1762
|
passThroughAssistant(component, message);
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
1763
|
+
const kids = Array.isArray(component.contentContainer?.children)
|
|
1764
|
+
? [...component.contentContainer.children]
|
|
1765
|
+
: [];
|
|
936
1766
|
component.contentContainer?.clear?.();
|
|
1767
|
+
const cursor = { i: 0 };
|
|
1768
|
+
let inThinkingRun = false;
|
|
1769
|
+
// 本消息产出先收集再发射:文本(含其前导 Spacer)统一排在面板前面。
|
|
1770
|
+
const local: LocalEntry[] = [];
|
|
1771
|
+
const takeRun = (outside: boolean) => {
|
|
1772
|
+
while (cursor.i < kids.length && isSpacerComponent(kids[cursor.i])) {
|
|
1773
|
+
local.push({ outside, child: kids[cursor.i++] });
|
|
1774
|
+
}
|
|
1775
|
+
if (cursor.i < kids.length) local.push({ outside, child: kids[cursor.i++] });
|
|
1776
|
+
};
|
|
1777
|
+
for (const item of Array.isArray(message?.content) ? message.content : []) {
|
|
1778
|
+
if (item?.type === "thinking") {
|
|
1779
|
+
if (!String(item.thinking ?? "").trim()) continue;
|
|
1780
|
+
if (!inThinkingRun) {
|
|
1781
|
+
takeRun(false);
|
|
1782
|
+
inThinkingRun = true;
|
|
1783
|
+
}
|
|
1784
|
+
continue;
|
|
1785
|
+
}
|
|
1786
|
+
inThinkingRun = false;
|
|
1787
|
+
if (item?.type === "text" && String(item.text ?? "").trim()) {
|
|
1788
|
+
takeRun(true);
|
|
1789
|
+
continue;
|
|
1790
|
+
}
|
|
1791
|
+
if (item?.type === "toolCall" && typeof item.id === "string") {
|
|
1792
|
+
local.push({ outside: false, toolId: item.id, toolName: item.name });
|
|
1793
|
+
}
|
|
1794
|
+
}
|
|
1795
|
+
while (cursor.i < kids.length) local.push({ outside: false, child: kids[cursor.i++] });
|
|
1796
|
+
for (const entry of local) if (entry.outside) takeChild(entry.child, true);
|
|
1797
|
+
for (const entry of local) {
|
|
1798
|
+
if (entry.outside) continue;
|
|
1799
|
+
if (entry.toolId) placeTool(entry.toolId, entry.toolName);
|
|
1800
|
+
else takeChild(entry.child, false);
|
|
1801
|
+
}
|
|
1802
|
+
}
|
|
1803
|
+
for (const message of round.detachedMessages) {
|
|
1804
|
+
for (const item of Array.isArray(message?.content) ? message.content : []) {
|
|
1805
|
+
if (item?.type === "toolCall" && typeof item.id === "string") {
|
|
1806
|
+
placeTool(item.id, item.name);
|
|
1807
|
+
}
|
|
1808
|
+
}
|
|
937
1809
|
}
|
|
938
1810
|
const ids = roundToolCallIds(round);
|
|
939
1811
|
for (const id of ids) {
|
|
940
1812
|
round.suppressedToolIds.add(id);
|
|
941
1813
|
expandedRoundToolIds.add(id);
|
|
1814
|
+
if (!placedToolIds.has(id)) placeTool(id);
|
|
942
1815
|
}
|
|
943
|
-
// Round
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
tool.
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
const cardItems: Array<{ child?: any; tool?: any }> = assistantChildren.map((child) => ({
|
|
959
|
-
child,
|
|
960
|
-
}));
|
|
961
|
-
const roundIds = roundToolCallIds(round);
|
|
962
|
-
for (const tool of trackedToolComponents) {
|
|
963
|
-
if (!roundIds.has(tool.toolCallId) || EDIT_WRITE_TOOLS.has(String(tool.toolName ?? ""))) {
|
|
964
|
-
continue;
|
|
1816
|
+
// Round 展开只打开外层卡片。普通工具保持折叠,避免长输出递归撑满屏幕;
|
|
1817
|
+
// 用户单独点开的(explicitRoundToolIds)不压回去。
|
|
1818
|
+
collapseTools(
|
|
1819
|
+
(tool) =>
|
|
1820
|
+
ids.has(String(tool.toolCallId ?? "")) &&
|
|
1821
|
+
!EDIT_WRITE_TOOLS.has(String(tool.toolName ?? "")) &&
|
|
1822
|
+
!explicitRoundToolIds.has(String(tool.toolCallId ?? "")),
|
|
1823
|
+
);
|
|
1824
|
+
flushPanel();
|
|
1825
|
+
if (mounted) {
|
|
1826
|
+
// 正文留在各自的 assistant 消息上,面板挂回摘要行原位。
|
|
1827
|
+
const outside: any[] = [];
|
|
1828
|
+
const panel: any[] = [];
|
|
1829
|
+
for (const child of anchorChildren) {
|
|
1830
|
+
(typeof child?.childAtRow === "function" ? panel : outside).push(child);
|
|
965
1831
|
}
|
|
966
|
-
|
|
1832
|
+
for (const child of outside) round.anchor.contentContainer.addChild(child);
|
|
1833
|
+
if (panel.length > 0) mountRoundTail(round, () => panel);
|
|
1834
|
+
else unmountRoundTail(round);
|
|
1835
|
+
} else {
|
|
1836
|
+
unmountRoundTail(round);
|
|
1837
|
+
for (const child of anchorChildren) round.anchor.contentContainer.addChild(child);
|
|
967
1838
|
}
|
|
968
|
-
round.anchor.contentContainer.addChild(
|
|
969
|
-
compactRoundCard(cardItems, (tool, innerWidth) =>
|
|
970
|
-
patch.toolOriginalRender.call(tool, innerWidth),
|
|
971
|
-
),
|
|
972
|
-
);
|
|
973
1839
|
// 展开卡内工具会显示 error,外层仍挂 abort/length,避免只藏在折叠工具里。
|
|
974
1840
|
appendStopStatus(round.anchor, stopStatus);
|
|
975
1841
|
return;
|
|
976
1842
|
}
|
|
977
1843
|
|
|
1844
|
+
if (roundLive(round)) {
|
|
1845
|
+
// 围观态:单一槽位卡(活动思考预览 + 当前工具),回合结束自动收回。
|
|
1846
|
+
renderRoundLive(round, stopStatus, getSummary);
|
|
1847
|
+
return;
|
|
1848
|
+
}
|
|
1849
|
+
|
|
978
1850
|
for (const [component, message] of round.messages) {
|
|
979
1851
|
if (component === round.anchor) {
|
|
980
1852
|
renderAssistantWithoutThinking(component, message);
|
|
981
1853
|
// 空摘要不挂行(getter 在 Running 启动瞬间也可能短暂为空)
|
|
982
|
-
if (summary || round.active)
|
|
1854
|
+
if (summary || round.active) {
|
|
1855
|
+
mountRoundTail(round, (identity) => [
|
|
1856
|
+
compactAssistantLineComponent(identity, getSummary, deps.query),
|
|
1857
|
+
]);
|
|
1858
|
+
} else {
|
|
1859
|
+
unmountRoundTail(round);
|
|
1860
|
+
}
|
|
983
1861
|
// 折叠时工具行被隐藏:abort/error/length 必须挂在摘要外层。
|
|
984
1862
|
appendStopStatus(component, stopStatus);
|
|
985
1863
|
} else {
|
|
@@ -989,6 +1867,7 @@ export function installCompactMode(deps: CompactModeInstallDeps): CompactModeHoo
|
|
|
989
1867
|
};
|
|
990
1868
|
|
|
991
1869
|
const activateRound = (round: CompactRound): void => {
|
|
1870
|
+
flushSettle();
|
|
992
1871
|
round.active = true;
|
|
993
1872
|
if (!round.startedAt) round.startedAt = Date.now();
|
|
994
1873
|
delete round.endedAt;
|
|
@@ -1000,15 +1879,50 @@ export function installCompactMode(deps: CompactModeInstallDeps): CompactModeHoo
|
|
|
1000
1879
|
const finishRound = (round: CompactRound): void => endRound(round, true);
|
|
1001
1880
|
|
|
1002
1881
|
const resetRounds = (): void => {
|
|
1882
|
+
for (const round of [...pendingFoldRounds]) clearFoldTimer(round);
|
|
1003
1883
|
activeRound = undefined;
|
|
1004
1884
|
roundByComponent = new WeakMap();
|
|
1005
1885
|
expandedRoundToolIds.clear();
|
|
1886
|
+
explicitRoundToolIds.clear();
|
|
1887
|
+
liveSlotToolIds.clear();
|
|
1888
|
+
// 尾行是 transcript 兄弟组件:compact 下保留为静态行(与原 anchor 内摘要
|
|
1889
|
+
// 一致;同 anchor 重建回合时 ensureTailHost 去重摘除),非 compact 摘掉,
|
|
1890
|
+
// 别把紧凑摘要带进 default 渲染。
|
|
1891
|
+
for (const tail of [...mountedTails]) {
|
|
1892
|
+
const container = tailHostContainer(tail);
|
|
1893
|
+
if (!container) {
|
|
1894
|
+
mountedTails.delete(tail);
|
|
1895
|
+
continue;
|
|
1896
|
+
}
|
|
1897
|
+
tail.container = container;
|
|
1898
|
+
if (config.mode === "compact") continue;
|
|
1899
|
+
container.removeChild(tail.host);
|
|
1900
|
+
mountedTails.delete(tail);
|
|
1901
|
+
if (tail.round?.tail === tail) tail.round.tail = undefined;
|
|
1902
|
+
if (tail.round?.anchor && tailByAnchor.get(tail.round.anchor) === tail) {
|
|
1903
|
+
tailByAnchor.delete(tail.round.anchor);
|
|
1904
|
+
}
|
|
1905
|
+
}
|
|
1006
1906
|
deps.query?.setCompactSummaryActive?.(false);
|
|
1007
1907
|
stopRoundTick();
|
|
1008
1908
|
};
|
|
1009
1909
|
|
|
1910
|
+
/**
|
|
1911
|
+
* 兜底:resetRounds 会丢掉回合索引但保留已挂的尾行,重放没能接回的回合会一直
|
|
1912
|
+
* active——尾行永远显示 Running...(时长还在涨)却不再计入新工具。既不是活动
|
|
1913
|
+
* 回合也不在收尾静默期的活回合就地收尾,不让它永远挂在屏幕上。
|
|
1914
|
+
*/
|
|
1915
|
+
const settleStrandedRounds = (): void => {
|
|
1916
|
+
for (const tail of [...mountedTails]) {
|
|
1917
|
+
const round = tail.round as CompactRound | undefined;
|
|
1918
|
+
if (!round?.active || round === activeRound || pendingFoldRounds.has(round)) continue;
|
|
1919
|
+
endRound(round, true);
|
|
1920
|
+
}
|
|
1921
|
+
};
|
|
1922
|
+
|
|
1010
1923
|
patch.assistantInstalled = function (this: any, message: any, isStreaming?: boolean) {
|
|
1011
1924
|
const self = this as any;
|
|
1925
|
+
// 同 compact-thinking:isStreaming 丢失 → mermaid 流式误渲染来回闪。
|
|
1012
1926
|
if (isStreaming !== undefined) self.isStreaming = isStreaming;
|
|
1013
1927
|
if (self[ASSISTANT_REENTRY_KEY] === patch) {
|
|
1014
1928
|
return patch.assistantNative.call(this, message, isStreaming);
|
|
@@ -1034,27 +1948,11 @@ export function installCompactMode(deps: CompactModeInstallDeps): CompactModeHoo
|
|
|
1034
1948
|
if (hasText && (!round || round.anchor !== this)) {
|
|
1035
1949
|
if (round) round.messages.delete(this);
|
|
1036
1950
|
if (activeRound) finishRound(activeRound);
|
|
1037
|
-
round =
|
|
1038
|
-
anchor: this,
|
|
1039
|
-
messages: new Map(),
|
|
1040
|
-
detachedMessages: [],
|
|
1041
|
-
active: true,
|
|
1042
|
-
suppressedToolIds: new Set(),
|
|
1043
|
-
startedAt: Date.now(),
|
|
1044
|
-
};
|
|
1951
|
+
round = createRound(this, message);
|
|
1045
1952
|
roundByComponent.set(this, round);
|
|
1046
|
-
activateRound(round);
|
|
1047
1953
|
} else if (!round) {
|
|
1048
|
-
round = activeRound ??
|
|
1049
|
-
anchor: this,
|
|
1050
|
-
messages: new Map(),
|
|
1051
|
-
detachedMessages: [],
|
|
1052
|
-
active: true,
|
|
1053
|
-
suppressedToolIds: new Set(),
|
|
1054
|
-
startedAt: Date.now(),
|
|
1055
|
-
};
|
|
1954
|
+
round = activeRound ?? createRound(this, message);
|
|
1056
1955
|
roundByComponent.set(this, round);
|
|
1057
|
-
if (!activeRound) activateRound(round);
|
|
1058
1956
|
}
|
|
1059
1957
|
round.messages.set(this, message);
|
|
1060
1958
|
renderRound(round);
|
|
@@ -1074,7 +1972,8 @@ export function installCompactMode(deps: CompactModeInstallDeps): CompactModeHoo
|
|
|
1074
1972
|
return renderAssistantWithoutThinking(this, message);
|
|
1075
1973
|
}
|
|
1076
1974
|
if (round) {
|
|
1077
|
-
|
|
1975
|
+
// 围观态下 anchor 还挂着槽位卡,必须重绘才能收回。
|
|
1976
|
+
endRound(round, true);
|
|
1078
1977
|
roundByComponent.delete(this);
|
|
1079
1978
|
return passThroughAssistant(this, message);
|
|
1080
1979
|
}
|
|
@@ -1086,16 +1985,8 @@ export function installCompactMode(deps: CompactModeInstallDeps): CompactModeHoo
|
|
|
1086
1985
|
if (hasThinking) {
|
|
1087
1986
|
let round = roundByComponent.get(this);
|
|
1088
1987
|
if (!round) {
|
|
1089
|
-
round = activeRound ??
|
|
1090
|
-
anchor: this,
|
|
1091
|
-
messages: new Map(),
|
|
1092
|
-
detachedMessages: [],
|
|
1093
|
-
active: true,
|
|
1094
|
-
suppressedToolIds: new Set(),
|
|
1095
|
-
startedAt: Date.now(),
|
|
1096
|
-
};
|
|
1988
|
+
round = activeRound ?? createRound(this, message);
|
|
1097
1989
|
roundByComponent.set(this, round);
|
|
1098
|
-
if (!activeRound) activateRound(round);
|
|
1099
1990
|
}
|
|
1100
1991
|
round.messages.set(this, message);
|
|
1101
1992
|
renderRound(round);
|
|
@@ -1123,12 +2014,13 @@ export function installCompactMode(deps: CompactModeInstallDeps): CompactModeHoo
|
|
|
1123
2014
|
if (EDIT_WRITE_TOOLS.has(name)) {
|
|
1124
2015
|
if (this.executionStarted && (!this.result || this.isPartial === true))
|
|
1125
2016
|
scheduleAnimation(this);
|
|
1126
|
-
|
|
1127
|
-
if (this.expanded !== true) return lines;
|
|
1128
|
-
return compactEditWriteExpandedLines(this, width, deps.writeMetadata);
|
|
2017
|
+
return compactEditWriteLines(this, width, deps.writeMetadata);
|
|
1129
2018
|
}
|
|
1130
2019
|
// Agent/Task 等同普通工具:折叠不外置(live 面板走独立 widget)。
|
|
1131
2020
|
if (expandedRoundToolIds.has(String(this.toolCallId ?? ""))) return [];
|
|
2021
|
+
// live 围观态:本回合工具交由槽位卡内联渲染,外层不再单独成行,
|
|
2022
|
+
// 卡片因此不会随工具完成反复挂载/卸载。
|
|
2023
|
+
if (liveSlotToolIds.has(String(this.toolCallId ?? ""))) return [];
|
|
1132
2024
|
// 普通工具折叠时不显示独立行(摘要行已统计),独立展开走原 renderer。
|
|
1133
2025
|
if (this.expanded === true) {
|
|
1134
2026
|
return patch.toolOriginalRender.call(this, width);
|
|
@@ -1137,16 +2029,16 @@ export function installCompactMode(deps: CompactModeInstallDeps): CompactModeHoo
|
|
|
1137
2029
|
};
|
|
1138
2030
|
|
|
1139
2031
|
patch.toolInstalledUpdateDisplay = function (this: any) {
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
this.expanded = false;
|
|
2032
|
+
const id = String(this.toolCallId ?? "");
|
|
2033
|
+
if (patch.active && config.mode === "compact" && expandedRoundToolIds.has(id)) {
|
|
2034
|
+
// 用户点开的保留展开;其余(含全局展开被收回的)继续强制折叠。
|
|
2035
|
+
if (this.expanded !== true) explicitRoundToolIds.delete(id);
|
|
2036
|
+
else if (!explicitRoundToolIds.has(id)) this.expanded = false;
|
|
1146
2037
|
}
|
|
1147
2038
|
const result = patch.toolOriginalUpdateDisplay.call(this);
|
|
1148
2039
|
if (!patch.active) return result;
|
|
1149
2040
|
trackedToolComponents.add(this);
|
|
2041
|
+
if (config.mode === "compact") syncTailForTool(id);
|
|
1150
2042
|
return result;
|
|
1151
2043
|
};
|
|
1152
2044
|
|
|
@@ -1170,6 +2062,9 @@ export function installCompactMode(deps: CompactModeInstallDeps): CompactModeHoo
|
|
|
1170
2062
|
patch.dispose = () => {
|
|
1171
2063
|
if (!patch.active) return;
|
|
1172
2064
|
patch.active = false;
|
|
2065
|
+
if (roundToolExpansion?.markUserExpanded === markRoundToolUserExpanded) {
|
|
2066
|
+
roundToolExpansion = undefined;
|
|
2067
|
+
}
|
|
1173
2068
|
if (assistantPrototype.updateContent === patch.assistantInstalled) {
|
|
1174
2069
|
assistantPrototype.updateContent = patch.assistantOriginal;
|
|
1175
2070
|
}
|
|
@@ -1179,18 +2074,19 @@ export function installCompactMode(deps: CompactModeInstallDeps): CompactModeHoo
|
|
|
1179
2074
|
if (toolPrototype.updateDisplay === patch.toolInstalledUpdateDisplay) {
|
|
1180
2075
|
toolPrototype.updateDisplay = patch.toolOriginalUpdateDisplay;
|
|
1181
2076
|
}
|
|
1182
|
-
|
|
2077
|
+
patchRegistry.dispose(COMPACT_MODE_PATCH_KEY, patch);
|
|
1183
2078
|
for (const component of trackedAssistantComponents) detachAssistantExpansion(component);
|
|
1184
2079
|
trackedAssistantComponents.clear();
|
|
1185
2080
|
trackedToolComponents.clear();
|
|
1186
2081
|
hoveredAssistantComponent = undefined;
|
|
1187
2082
|
resetRounds();
|
|
2083
|
+
compactRunStatusGetter = undefined;
|
|
1188
2084
|
};
|
|
1189
2085
|
|
|
1190
2086
|
assistantPrototype.updateContent = patch.assistantInstalled;
|
|
1191
2087
|
toolPrototype.render = patch.toolInstalledRender;
|
|
1192
2088
|
toolPrototype.updateDisplay = patch.toolInstalledUpdateDisplay;
|
|
1193
|
-
|
|
2089
|
+
patchRegistry.install(COMPACT_MODE_PATCH_KEY, patch);
|
|
1194
2090
|
|
|
1195
2091
|
const syncGlobalExpanded = (ctx: any): void => {
|
|
1196
2092
|
let globalExpanded = false;
|