@timurproko/a1 0.2.6 → 0.2.7-dev.1222
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 +41 -3
- package/bin/automatic-update.js +7 -0
- package/bin/cli.js +4 -0
- package/bin/guardian.js +5 -0
- package/bin/tab-launch-context.d.ts +27 -0
- package/bin/tab-launch-context.js +94 -0
- package/bin/ui.js +107 -59
- package/dist/app/session-shell/clipboard-diagnostics.js +1 -1
- package/dist/app/session-shell/dialog-coordinator.d.ts +42 -0
- package/dist/app/session-shell/dialog-coordinator.js +133 -0
- package/dist/app/session-shell/dock-chrome.d.ts +34 -0
- package/dist/app/session-shell/dock-chrome.js +109 -0
- package/dist/app/session-shell/exit-notice.d.ts +1 -1
- package/dist/app/session-shell/exit-notice.js +1 -1
- package/dist/app/session-shell/extension-surface.d.ts +30 -0
- package/dist/app/session-shell/extension-surface.js +105 -0
- package/dist/app/session-shell/helper-pool.d.ts +3 -5
- package/dist/app/session-shell/helper-pool.js +3 -5
- package/dist/app/session-shell/input-surface-switch.d.ts +36 -0
- package/dist/app/session-shell/input-surface-switch.js +85 -0
- package/dist/app/session-shell/login-interaction.d.ts +27 -0
- package/dist/app/session-shell/login-interaction.js +91 -0
- package/dist/app/session-shell/prompt-submission.d.ts +48 -0
- package/dist/app/session-shell/prompt-submission.js +276 -0
- package/dist/app/session-shell/resident-tab-coordinator.d.ts +38 -0
- package/dist/app/session-shell/resident-tab-coordinator.js +77 -0
- package/dist/app/session-shell/response-copy-helper.js +21 -6
- package/dist/app/session-shell/response-copy-protocol.d.ts +1 -1
- package/dist/app/session-shell/response-copy-transport.d.ts +16 -6
- package/dist/app/session-shell/response-copy-transport.js +69 -17
- package/dist/app/session-shell/session-concern.d.ts +22 -0
- package/dist/app/session-shell/session-concern.js +29 -0
- package/dist/app/session-shell/session-lifecycle.d.ts +29 -0
- package/dist/app/session-shell/session-lifecycle.js +49 -0
- package/dist/app/session-shell/session-shell-root.d.ts +42 -6
- package/dist/app/session-shell/session-shell-root.js +197 -908
- package/dist/app/session-shell/session-shell.d.ts +3 -8
- package/dist/app/session-shell/session-shell.js +327 -835
- package/dist/app/session-shell/session-viewport-controller.d.ts +2 -1
- package/dist/app/session-shell/session-viewport-controller.js +41 -38
- package/dist/app/session-shell/settings-bindings.d.ts +17 -0
- package/dist/app/session-shell/settings-bindings.js +91 -0
- package/dist/app/session-shell/shell-editor-factory.d.ts +27 -0
- package/dist/app/session-shell/shell-editor-factory.js +105 -0
- package/dist/app/session-shell/shell-host.d.ts +6 -0
- package/dist/app/session-shell/shell-host.js +34 -7
- package/dist/app/session-shell/shell-layout.d.ts +64 -0
- package/dist/app/session-shell/shell-layout.js +18 -0
- package/dist/app/session-shell/suggestion-surface.d.ts +25 -0
- package/dist/app/session-shell/suggestion-surface.js +63 -0
- package/dist/app/session-shell/transcript-document.d.ts +78 -0
- package/dist/app/session-shell/transcript-document.js +365 -0
- package/dist/app/session-shell/viewport-layout.d.ts +31 -0
- package/dist/app/session-shell/viewport-layout.js +160 -0
- package/dist/app/session-shell/workflow-output.d.ts +33 -0
- package/dist/app/session-shell/workflow-output.js +174 -0
- package/dist/app/session-shell/workflow-router.d.ts +25 -0
- package/dist/app/session-shell/workflow-router.js +39 -0
- package/dist/cli/dispatch.d.ts +11 -1
- package/dist/cli/dispatch.js +79 -5
- package/dist/cli/tabs-request.d.ts +7 -0
- package/dist/cli/tabs-request.js +1 -0
- package/dist/cli/tabs.d.ts +14 -0
- package/dist/cli/tabs.js +151 -0
- package/dist/cli/version-stats.js +38 -3
- package/dist/composition/owned-settings.d.ts +12 -0
- package/dist/composition/owned-settings.js +36 -0
- package/dist/composition/owned-ui.d.ts +27 -28
- package/dist/composition/owned-ui.js +207 -153
- package/dist/composition/reference-selection-options.d.ts +3 -0
- package/dist/composition/reference-selection-options.js +16 -0
- package/dist/composition/resident-tab-bridge.d.ts +25 -0
- package/dist/composition/resident-tab-bridge.js +180 -0
- package/dist/composition/session-backend.d.ts +19 -0
- package/dist/composition/session-backend.js +1 -0
- package/dist/composition/session-runtime-registry.d.ts +20 -0
- package/dist/composition/session-runtime-registry.js +85 -0
- package/dist/composition/settings-route-host.d.ts +16 -2
- package/dist/composition/settings-route-host.js +42 -17
- package/dist/composition/settings-search-input.d.ts +9 -0
- package/dist/composition/settings-search-input.js +61 -0
- package/dist/composition/startup-route.d.ts +32 -0
- package/dist/composition/startup-route.js +72 -0
- package/dist/composition/utility-tab.d.ts +28 -0
- package/dist/composition/utility-tab.js +244 -0
- package/dist/contracts/owned-ui/index.d.ts +1 -1
- package/dist/contracts/owned-ui/model.d.ts +3 -0
- package/dist/contracts/owned-ui/session-backend.d.ts +3 -0
- package/dist/contracts/owned-ui/session-workflows.d.ts +11 -4
- package/dist/contracts/presentation/index.d.ts +11 -0
- package/dist/contracts/presentation/index.js +12 -0
- package/dist/features/launch/development-launch.js +6 -0
- package/dist/features/launch/index.d.ts +1 -1
- package/dist/features/launch/intent.d.ts +3 -1
- package/dist/features/launch/intent.js +7 -4
- package/dist/features/launch/profile-paths.js +4 -0
- package/dist/features/owned-ui/plus-menu-dialog.d.ts +22 -0
- package/dist/features/owned-ui/plus-menu-dialog.js +84 -0
- package/dist/features/owned-ui/project-trust-dialog.d.ts +1 -1
- package/dist/features/owned-ui/project-trust-dialog.js +6 -8
- package/dist/features/owned-ui/project-trust-prompt.d.ts +1 -0
- package/dist/features/owned-ui/project-trust-prompt.js +7 -3
- package/dist/features/owned-ui/reference-document-selection.d.ts +30 -0
- package/dist/features/owned-ui/reference-document-selection.js +179 -0
- package/dist/features/owned-ui/reference-screen-app.d.ts +6 -1
- package/dist/features/owned-ui/reference-screen-app.js +51 -10
- package/dist/features/owned-ui/settings-app.d.ts +13 -2
- package/dist/features/owned-ui/settings-app.js +136 -97
- package/dist/features/owned-ui/settings-search-input.d.ts +29 -0
- package/dist/features/owned-ui/settings-search-input.js +1 -0
- package/dist/features/prompt-history/index.d.ts +1 -1
- package/dist/features/prompt-history/paths.d.ts +2 -1
- package/dist/features/prompt-history/paths.js +10 -2
- package/dist/features/prompt-history/service.d.ts +5 -0
- package/dist/features/prompt-history/service.js +1 -1
- package/dist/features/resident-tab/auto-name.d.ts +11 -0
- package/dist/features/resident-tab/auto-name.js +44 -0
- package/dist/features/resident-tab/index.d.ts +5 -0
- package/dist/features/resident-tab/index.js +7 -0
- package/dist/features/resident-tab/shortcut-bindings-file.d.ts +8 -0
- package/dist/features/resident-tab/shortcut-bindings-file.js +40 -0
- package/dist/features/resident-tab/shortcut-declarations.d.ts +13 -0
- package/dist/features/resident-tab/shortcut-declarations.js +26 -0
- package/dist/features/resident-tab/shortcut-projection.d.ts +29 -0
- package/dist/features/resident-tab/shortcut-projection.js +64 -0
- package/dist/features/resident-tab/status-deriver.d.ts +10 -0
- package/dist/features/resident-tab/status-deriver.js +12 -0
- package/dist/foundation/launch-context/index.d.ts +12 -0
- package/dist/foundation/launch-context/index.js +26 -0
- package/dist/foundation/launch-guardian/exit-watcher.d.ts +14 -0
- package/dist/foundation/launch-guardian/exit-watcher.js +94 -0
- package/dist/foundation/launch-guardian/main.d.ts +7 -1
- package/dist/foundation/launch-guardian/main.js +60 -7
- package/dist/foundation/lifecycle/session-repository-context.js +16 -2
- package/dist/foundation/process-containment/{native-guardian-containment.d.ts → addon-containment.d.ts} +7 -5
- package/dist/foundation/process-containment/addon-containment.js +139 -0
- package/dist/foundation/process-containment/addon-process-inspector.d.ts +11 -0
- package/dist/foundation/process-containment/addon-process-inspector.js +36 -0
- package/dist/foundation/process-containment/addon.d.ts +42 -0
- package/dist/foundation/process-containment/addon.js +57 -0
- package/dist/foundation/process-containment/artifact.d.ts +1 -1
- package/dist/foundation/process-containment/artifact.js +6 -5
- package/dist/foundation/process-containment/contracts.d.ts +2 -0
- package/dist/foundation/process-containment/index.d.ts +4 -4
- package/dist/foundation/process-containment/index.js +3 -3
- package/dist/foundation/process-containment/native-process-inspector.d.ts +2 -1
- package/dist/foundation/process-containment/native-process-inspector.js +10 -10
- package/dist/foundation/release/automatic-update.d.ts +37 -0
- package/dist/foundation/release/automatic-update.js +265 -0
- package/dist/foundation/release/bootstrap.d.ts +7 -9
- package/dist/foundation/release/bootstrap.js +107 -24
- package/dist/foundation/release/cohort-state.d.ts +1 -1
- package/dist/foundation/release/cohort-state.js +1 -1
- package/dist/foundation/release/dependency-certification.js +54 -19
- package/dist/foundation/release/early-accent.d.ts +12 -0
- package/dist/foundation/release/early-accent.js +59 -0
- package/dist/foundation/release/index.d.ts +2 -2
- package/dist/foundation/release/index.js +2 -2
- package/dist/foundation/release/latest-release.d.ts +24 -2
- package/dist/foundation/release/latest-release.js +51 -1
- package/dist/foundation/release/progress-palette.generated.d.ts +137 -3
- package/dist/foundation/release/progress-palette.generated.js +137 -3
- package/dist/foundation/release/release-gc.d.ts +3 -0
- package/dist/foundation/release/release-gc.js +59 -8
- package/dist/foundation/release/release-store.d.ts +12 -0
- package/dist/foundation/release/release-store.js +124 -2
- package/dist/foundation/release/update-execution-lease.d.ts +48 -0
- package/dist/foundation/release/update-execution-lease.js +161 -0
- package/dist/foundation/release/update.d.ts +12 -7
- package/dist/foundation/release/update.js +281 -196
- package/dist/foundation/resident-tabs/bounded-host-command.d.ts +18 -0
- package/dist/foundation/resident-tabs/bounded-host-command.js +39 -0
- package/dist/foundation/resident-tabs/bridge-client.d.ts +70 -0
- package/dist/foundation/resident-tabs/bridge-client.js +321 -0
- package/dist/foundation/resident-tabs/doctor.d.ts +28 -0
- package/dist/foundation/resident-tabs/doctor.js +166 -0
- package/dist/foundation/resident-tabs/draft-checkpoint.d.ts +27 -0
- package/dist/foundation/resident-tabs/draft-checkpoint.js +145 -0
- package/dist/foundation/resident-tabs/ensure-terminal-host.d.ts +36 -0
- package/dist/foundation/resident-tabs/ensure-terminal-host.js +151 -0
- package/dist/foundation/resident-tabs/fault-injection.d.ts +8 -0
- package/dist/foundation/resident-tabs/fault-injection.js +26 -0
- package/dist/foundation/resident-tabs/fixture-control.d.ts +7 -0
- package/dist/foundation/resident-tabs/fixture-control.js +35 -0
- package/dist/foundation/resident-tabs/host-entry.d.ts +1 -0
- package/dist/foundation/resident-tabs/host-entry.js +17 -0
- package/dist/foundation/resident-tabs/index.d.ts +12 -0
- package/dist/foundation/resident-tabs/index.js +6 -0
- package/dist/foundation/resident-tabs/maintenance.d.ts +39 -0
- package/dist/foundation/resident-tabs/maintenance.js +75 -0
- package/dist/foundation/resident-tabs/progress-observation-schema.d.ts +34 -0
- package/dist/foundation/resident-tabs/progress-observation-schema.js +1 -0
- package/dist/foundation/resident-tabs/progress-observation.d.ts +4 -0
- package/dist/foundation/resident-tabs/progress-observation.js +150 -0
- package/dist/foundation/resident-tabs/prompt-journal.d.ts +63 -0
- package/dist/foundation/resident-tabs/prompt-journal.js +213 -0
- package/dist/foundation/resident-tabs/protocol.d.ts +12 -0
- package/dist/foundation/resident-tabs/protocol.js +195 -0
- package/dist/foundation/resident-tabs/recovery.d.ts +26 -0
- package/dist/foundation/resident-tabs/recovery.js +205 -0
- package/dist/foundation/resident-tabs/release-holds.d.ts +28 -0
- package/dist/foundation/resident-tabs/release-holds.js +75 -0
- package/dist/foundation/resident-tabs/routes.d.ts +5 -0
- package/dist/foundation/resident-tabs/routes.js +5 -0
- package/dist/foundation/resident-tabs/routing.d.ts +11 -0
- package/dist/foundation/resident-tabs/routing.js +10 -0
- package/dist/foundation/resident-tabs/session-sync.d.ts +14 -0
- package/dist/foundation/resident-tabs/session-sync.js +37 -0
- package/dist/foundation/resident-tabs/settings.d.ts +12 -0
- package/dist/foundation/resident-tabs/settings.js +58 -0
- package/dist/foundation/resident-tabs/terminal-host-artifact.d.ts +14 -0
- package/dist/foundation/resident-tabs/terminal-host-artifact.js +78 -0
- package/dist/foundation/resident-tabs/zip-archive.d.ts +9 -0
- package/dist/foundation/resident-tabs/zip-archive.js +56 -0
- package/dist/foundation/session-writer-lock/index.d.ts +30 -0
- package/dist/foundation/session-writer-lock/index.js +203 -0
- package/dist/foundation/startup/startup-descriptor.js +2 -2
- package/dist/foundation/terminal-cleanup/index.d.ts +1 -1
- package/dist/foundation/terminal-cleanup/index.js +1 -1
- package/dist/foundation/terminal-cleanup/terminal-reset.d.ts +6 -0
- package/dist/foundation/terminal-cleanup/terminal-reset.js +6 -0
- package/dist/integrations/pi/components/editor-interaction.d.ts +2 -0
- package/dist/integrations/pi/components/modal-frame.d.ts +17 -1
- package/dist/integrations/pi/components/modal-frame.js +48 -9
- package/dist/integrations/pi/components/models-dialog.js +4 -52
- package/dist/integrations/pi/components/owned-editor-ux.d.ts +6 -1
- package/dist/integrations/pi/components/owned-editor-ux.js +138 -11
- package/dist/integrations/pi/components/resources/builtin-themes.d.ts +4 -4
- package/dist/integrations/pi/components/resources/builtin-themes.js +4 -4
- package/dist/integrations/pi/components/share-operation-dialog.d.ts +2 -0
- package/dist/integrations/pi/components/share-operation-dialog.js +2 -1
- package/dist/integrations/pi/components/shell-editor-autocomplete.js +58 -36
- package/dist/integrations/pi/components/shell-extension-ui.js +79 -60
- package/dist/integrations/pi/components/shell-footer-status.d.ts +1 -1
- package/dist/integrations/pi/components/shell-footer-status.js +21 -5
- package/dist/integrations/pi/components/shell-hotkey-sections.js +1 -1
- package/dist/integrations/pi/components/shell-presenters-info.d.ts +5 -2
- package/dist/integrations/pi/components/shell-presenters-info.js +18 -4
- package/dist/integrations/pi/components/shell-presenters-transcript.js +4 -1
- package/dist/integrations/pi/components/shell-selectors-dialogs.js +5 -1
- package/dist/integrations/pi/components/shell-shared-facade.d.ts +12 -1
- package/dist/integrations/pi/components/skills-dialog.js +2 -2
- package/dist/integrations/pi/components/upstream/adjacent/core/keybindings.d.ts +1 -1
- package/dist/integrations/pi/components/upstream/adjacent/core/keybindings.js +17 -4
- package/dist/integrations/pi/components/upstream/assets/earendil-image.json +2 -2
- package/dist/integrations/pi/components/upstream/components/countdown-timer.d.ts +1 -1
- package/dist/integrations/pi/components/upstream/components/earendil-announcement.js +1 -1
- package/dist/integrations/pi/components/upstream/components/extension-editor.d.ts +1 -1
- package/dist/integrations/pi/components/upstream/components/extension-editor.js +1 -1
- package/dist/integrations/pi/components/upstream/components/extension-input.js +1 -1
- package/dist/integrations/pi/components/upstream/components/extension-selector.js +1 -1
- package/dist/integrations/pi/components/upstream/components/mermaid.js +1 -1
- package/dist/integrations/pi/components/upstream/components/owned-editor.d.ts +8 -6
- package/dist/integrations/pi/components/upstream/components/owned-editor.js +16 -9
- package/dist/integrations/pi/components/upstream/components/pi-logo.js +1 -1
- package/dist/integrations/pi/components/upstream/components/scoped-models-selector.d.ts +4 -4
- package/dist/integrations/pi/components/upstream/components/scoped-models-selector.js +2 -2
- package/dist/integrations/pi/components/upstream/components/session-footer.d.ts +2 -1
- package/dist/integrations/pi/components/upstream/components/session-footer.js +31 -25
- package/dist/integrations/pi/components/upstream/components/session-selector-search.js +1 -1
- package/dist/integrations/pi/components/upstream/components/session-selector.js +8 -7
- package/dist/integrations/pi/components/upstream/components/skill-invocation-message.d.ts +1 -1
- package/dist/integrations/pi/components/upstream/components/skill-invocation-message.js +1 -1
- package/dist/integrations/pi/components/upstream/components/status-indicator.d.ts +2 -2
- package/dist/integrations/pi/components/upstream/components/status-indicator.js +3 -3
- package/dist/integrations/pi/components/upstream/components/themed-text.d.ts +1 -1
- package/dist/integrations/pi/components/upstream/components/themed-text.js +1 -1
- package/dist/integrations/pi/components/upstream/components/thinking-selector.d.ts +8 -7
- package/dist/integrations/pi/components/upstream/components/thinking-selector.js +10 -9
- package/dist/integrations/pi/components/upstream/components/tool-execution.d.ts +2 -6
- package/dist/integrations/pi/components/upstream/components/tool-execution.js +103 -101
- package/dist/integrations/pi/components/upstream/components/tree-selector.d.ts +9 -9
- package/dist/integrations/pi/components/upstream/components/tree-selector.js +13 -21
- package/dist/integrations/pi/components/upstream/components/trust-selector.js +1 -1
- package/dist/integrations/pi/components/upstream/external-editor.js +1 -1
- package/dist/integrations/pi/components/upstream/history/editor-core.d.ts +2 -2
- package/dist/integrations/pi/components/upstream/history/editor-core.js +7 -5
- package/dist/integrations/pi/components/upstream/history/kill-ring.d.ts +1 -1
- package/dist/integrations/pi/components/upstream/history/kill-ring.js +1 -1
- package/dist/integrations/pi/components/upstream/history/printable-key.js +1 -1
- package/dist/integrations/pi/components/upstream/history/text-helpers.js +1 -1
- package/dist/integrations/pi/components/upstream/history/undo-stack.d.ts +1 -1
- package/dist/integrations/pi/components/upstream/history/undo-stack.js +1 -1
- package/dist/integrations/pi/components/upstream/history/word-navigation.js +1 -1
- package/dist/integrations/pi/components/upstream/model-search.d.ts +1 -1
- package/dist/integrations/pi/components/upstream/program-status-reporter.d.ts +1 -1
- package/dist/integrations/pi/components/upstream/theme/theme-controller.d.ts +1 -1
- package/dist/integrations/pi/components/upstream/theme/theme.d.ts +5 -0
- package/dist/integrations/pi/components/upstream/theme/theme.js +29 -2
- package/dist/integrations/pi/engine/adapter.d.ts +5 -77
- package/dist/integrations/pi/engine/adapter.js +95 -394
- package/dist/integrations/pi/engine/command-dispatch.d.ts +13 -0
- package/dist/integrations/pi/engine/command-dispatch.js +57 -0
- package/dist/integrations/pi/engine/host.js +2 -2
- package/dist/integrations/pi/engine/offline-settings-port.d.ts +23 -0
- package/dist/integrations/pi/engine/offline-settings-port.js +53 -0
- package/dist/integrations/pi/engine/pi-settings-metadata.json +14 -2
- package/dist/integrations/pi/engine/ports.d.ts +56 -0
- package/dist/integrations/pi/engine/ports.js +93 -0
- package/dist/integrations/pi/engine/prompt-suggestions.d.ts +1 -0
- package/dist/integrations/pi/engine/prompt-suggestions.js +40 -0
- package/dist/integrations/pi/engine/resources/changelog.json +4 -4
- package/dist/integrations/pi/engine/runtime-integration.js +102 -29
- package/dist/integrations/pi/engine/session-runtime.d.ts +0 -6
- package/dist/integrations/pi/engine/session-runtime.js +13 -5
- package/dist/integrations/pi/engine/session-selection.d.ts +3 -0
- package/dist/integrations/pi/engine/session-selection.js +45 -9
- package/dist/integrations/pi/engine/settings-bridge.d.ts +5 -0
- package/dist/integrations/pi/engine/settings-bridge.js +15 -0
- package/dist/integrations/pi/engine/settings-port.js +11 -15
- package/dist/integrations/pi/engine/view-state.d.ts +42 -0
- package/dist/integrations/pi/engine/view-state.js +126 -0
- package/dist/integrations/pi/engine/workflow-contexts.js +10 -1
- package/dist/integrations/pi/engine/workflow-runner.js +74 -5
- package/dist/integrations/pi/engine/workflow-support.d.ts +12 -0
- package/dist/integrations/pi/engine/workflow-support.js +36 -0
- package/dist/integrations/pi/engine/workflows.d.ts +3 -1
- package/dist/integrations/pi/engine/workflows.js +1 -0
- package/dist/integrations/pi/startup-public.js +257 -310
- package/dist/integrations/pi/startup-public.manifest.json +564 -440
- package/dist/integrations/pi/tui-runtime/adapter.d.ts +6 -0
- package/dist/integrations/pi/tui-runtime/adapter.js +32 -1
- package/dist/integrations/pi/tui-runtime/contracts.d.ts +5 -0
- package/dist/integrations/pi/tui-runtime/damage-aware-terminal.d.ts +12 -1
- package/dist/integrations/pi/tui-runtime/damage-aware-terminal.js +31 -2
- package/dist/native/darwin-arm64/manifest.json +10 -7
- package/dist/native/darwin-arm64/process-guardian.node +0 -0
- package/dist/native/darwin-arm64/terminal-host-THIRD-PARTY-NOTICES.txt +10500 -0
- package/dist/native/darwin-arm64/terminal-host.manifest.json +46 -0
- package/dist/native/darwin-arm64/terminal-host.node +0 -0
- package/dist/native/darwin-x64/manifest.json +23 -0
- package/dist/native/darwin-x64/process-guardian.node +0 -0
- package/dist/native/darwin-x64/terminal-host-THIRD-PARTY-NOTICES.txt +10737 -0
- package/dist/native/darwin-x64/terminal-host.manifest.json +46 -0
- package/dist/native/darwin-x64/terminal-host.node +0 -0
- package/dist/native/linux-x64/manifest.json +10 -7
- package/dist/native/linux-x64/process-guardian.node +0 -0
- package/dist/native/linux-x64/terminal-host-THIRD-PARTY-NOTICES.txt +11226 -0
- package/dist/native/linux-x64/terminal-host.manifest.json +46 -0
- package/dist/native/linux-x64/terminal-host.node +0 -0
- package/dist/native/win32-x64/manifest.json +10 -7
- package/dist/native/win32-x64/process-guardian.node +0 -0
- package/dist/native/win32-x64/terminal-host-THIRD-PARTY-NOTICES.txt +10757 -0
- package/dist/native/win32-x64/terminal-host.manifest.json +46 -0
- package/dist/native/win32-x64/terminal-host.node +0 -0
- package/dist/product-identity.d.ts +3 -3
- package/dist/product-identity.js +4 -4
- package/dist/product-identity.json +16 -2
- package/dist/runtime-payload-inventory.json +6330 -6677
- package/dist/ui/apps/host.d.ts +2 -0
- package/dist/ui/apps/host.js +7 -1
- package/dist/ui/components/dialog-panel.d.ts +17 -8
- package/dist/ui/components/dialog-panel.js +117 -41
- package/dist/ui/components/index.d.ts +5 -5
- package/dist/ui/components/index.js +4 -4
- package/dist/ui/components/line-input.d.ts +4 -1
- package/dist/ui/components/line-input.js +3 -1
- package/dist/ui/components/prompt-input.d.ts +11 -8
- package/dist/ui/components/prompt-input.js +15 -13
- package/dist/ui/components/scrollbar.d.ts +1 -1
- package/dist/ui/components/scrollbar.js +1 -1
- package/dist/ui/components/selection-copy.d.ts +3 -3
- package/dist/ui/components/selection-copy.js +16 -1
- package/dist/ui/components/spans.js +2 -2
- package/dist/ui/components/submitted-prompt.d.ts +9 -6
- package/dist/ui/components/submitted-prompt.js +20 -12
- package/dist/ui/components/surface.js +4 -2
- package/dist/ui/components/text-selection.d.ts +1 -2
- package/dist/ui/components/text-selection.js +5 -2
- package/dist/ui/components/text.d.ts +3 -6
- package/dist/ui/components/text.js +6 -8
- package/dist/ui/components/transcript-viewport.d.ts +9 -2
- package/dist/ui/components/transcript-viewport.js +179 -58
- package/dist/ui/settings/declarations.d.ts +90 -16
- package/dist/ui/settings/declarations.js +67 -14
- package/dist/ui/settings/early-accent.d.ts +81 -0
- package/dist/ui/settings/early-accent.js +50 -0
- package/dist/ui/settings/index.d.ts +4 -2
- package/dist/ui/settings/index.js +2 -1
- package/dist/ui/settings/manager.d.ts +16 -3
- package/dist/ui/settings/manager.js +122 -33
- package/dist/ui/settings/migrations.d.ts +6 -0
- package/dist/ui/settings/migrations.js +73 -0
- package/dist/ui/settings/profile-document.d.ts +26 -0
- package/dist/ui/settings/profile-document.js +164 -0
- package/dist/ui/settings/progress-palette.generated.d.ts +139 -0
- package/dist/ui/settings/progress-palette.generated.js +139 -0
- package/dist/ui/settings/sections.d.ts +4 -0
- package/dist/ui/settings/sections.js +9 -3
- package/docs/architecture/boundaries.md +24 -9
- package/docs/architecture/evidence/resident-tabs/linux-x64.md +37 -0
- package/docs/architecture/evidence/resident-tabs/macos-arm64.md +36 -0
- package/docs/architecture/evidence/resident-tabs/macos-x64.md +35 -0
- package/docs/architecture/evidence/resident-tabs/windows-x64.md +36 -0
- package/docs/architecture/internal-naming.md +3 -1
- package/docs/architecture/process-guardian-provenance.md +18 -10
- package/docs/architecture/project-structure.md +8 -0
- package/docs/architecture/resident-tabs-requirements.md +18 -0
- package/docs/architecture/resident-tabs.md +649 -0
- package/docs/architecture/response-copy-delivery.md +1 -1
- package/docs/architecture/terminal-host-proof-gate.md +2 -0
- package/docs/architecture/terminal-host-provenance.md +16 -4
- package/docs/architecture/tool-image-presentation.md +2 -1
- package/docs/architecture/toolchain.md +11 -9
- package/docs/ci-release-runbook.md +14 -7
- package/docs/features/launch-profiles.md +11 -8
- package/docs/features/prompt-history.md +41 -20
- package/docs/manual-resident-tabs-acceptance.md +51 -0
- package/docs/openspec-archive-automation.md +49 -22
- package/docs/validation.md +49 -9
- package/package.json +19 -6
- package/dist/foundation/process-containment/darwin-process-inspector.d.ts +0 -5
- package/dist/foundation/process-containment/darwin-process-inspector.js +0 -7
- package/dist/foundation/process-containment/native-guardian-containment.js +0 -152
- package/dist/foundation/process-containment/windows-process-inspector.d.ts +0 -19
- package/dist/foundation/process-containment/windows-process-inspector.js +0 -56
- package/dist/native/darwin-arm64/process-guardian +0 -0
- package/dist/native/linux-x64/process-guardian +0 -0
- package/dist/native/win32-x64/process-guardian.exe +0 -0
|
@@ -1,35 +1,32 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
1
|
+
import { ImageAttachmentError } from "../../contracts/owned-ui/index.js";
|
|
2
|
+
import { promptArrow } from "../../ui/components/prompt-input.js";
|
|
3
|
+
import { composeSubmittedPromptRows, submittedPromptLayout, } from "../../ui/components/submitted-prompt.js";
|
|
4
|
+
import { nativeHyperlinkStyle } from "../../ui/components/spans.js";
|
|
5
|
+
import { paintTextSelection } from "../../ui/components/text-selection.js";
|
|
6
6
|
import { progressStatusFrame, progressStatusText } from "../../ui/components/progress-status.js";
|
|
7
|
-
import { displayWidth, faint, stripAnsi } from "../../ui/components/text.js";
|
|
8
|
-
import { createPiShellEditor } from "../../integrations/pi/components/shell-editor-autocomplete.js";
|
|
9
7
|
import { createPiQueuedInputStatus, createPiShellFooter, createPiShellHeader, createPiShellLoadedResources, createPiShellStatus, } from "../../integrations/pi/components/shell-footer-status.js";
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import { piShellHyperlink, piShellTruncateToWidth, piShellVisibleWidth, } from "../../integrations/pi/components/shell-shared-facade.js";
|
|
15
|
-
import { classifyPiTuiInput, } from "../../integrations/pi/tui-runtime/input-presentation-coordinator.js";
|
|
8
|
+
import { createPiShellTranscriptComponent, } from "../../integrations/pi/components/shell-presenters-transcript.js";
|
|
9
|
+
import { onPiThemeChange, piTheme } from "../../integrations/pi/components/upstream/theme/theme.js";
|
|
10
|
+
import { piShellTruncateToWidth, } from "../../integrations/pi/components/shell-shared-facade.js";
|
|
11
|
+
import {} from "../../integrations/pi/tui-runtime/input-presentation-coordinator.js";
|
|
16
12
|
import { PromptChipStore } from "./prompt-chips.js";
|
|
17
|
-
import { EditorHyperlinkBudget } from "./editor-hyperlink-budget.js";
|
|
18
13
|
import { SessionViewportController } from "./session-viewport-controller.js";
|
|
19
|
-
import {
|
|
20
|
-
|
|
14
|
+
import { createShellEditor } from "./shell-editor-factory.js";
|
|
15
|
+
import { ExtensionSurface } from "./extension-surface.js";
|
|
16
|
+
import { DockChrome } from "./dock-chrome.js";
|
|
17
|
+
import { WorkflowOutput } from "./workflow-output.js";
|
|
18
|
+
import { InputSurfaceSwitch } from "./input-surface-switch.js";
|
|
19
|
+
import { PinnedLayout } from "./shell-layout.js";
|
|
20
|
+
import { ViewportLayout } from "./viewport-layout.js";
|
|
21
|
+
import { nativeTranscriptLinkColor, TranscriptDocument } from "./transcript-document.js";
|
|
22
|
+
/** Composes session state into the owned transcript, viewport, editor, and dock. */
|
|
21
23
|
export class OwnedUiSessionShellRoot {
|
|
22
24
|
editor;
|
|
23
25
|
header;
|
|
24
26
|
resources;
|
|
25
27
|
#cwd;
|
|
26
|
-
#
|
|
27
|
-
#blocksById = new Map();
|
|
28
|
-
#renderedRows = new Map();
|
|
29
|
-
// Performance: document layouts are shared by wheel, rail, jump, and selection frames.
|
|
30
|
-
#documentLayouts = new Map();
|
|
28
|
+
#document;
|
|
31
29
|
#themeUnsubscribe;
|
|
32
|
-
#transcriptOrder = [];
|
|
33
30
|
#view;
|
|
34
31
|
#status;
|
|
35
32
|
#footer;
|
|
@@ -37,15 +34,10 @@ export class OwnedUiSessionShellRoot {
|
|
|
37
34
|
#extensionRenderers;
|
|
38
35
|
#imageAssets;
|
|
39
36
|
#promptChips;
|
|
40
|
-
#editorHyperlinks = new EditorHyperlinkBudget();
|
|
41
37
|
#pasteDiagnosticId = 0;
|
|
42
|
-
#terminalPasteDiagnosticRequest;
|
|
43
38
|
#customViewport;
|
|
44
39
|
#submittedPromptComposer;
|
|
45
40
|
#viewportController;
|
|
46
|
-
#viewportTheme;
|
|
47
|
-
#onViewportFrame;
|
|
48
|
-
#onInputSurfaceChanged;
|
|
49
41
|
#onPromptSuggestionEligibilityChanged;
|
|
50
42
|
#componentRuntime;
|
|
51
43
|
#toolsExpanded = false;
|
|
@@ -55,33 +47,11 @@ export class OwnedUiSessionShellRoot {
|
|
|
55
47
|
#showImages = true;
|
|
56
48
|
#imageWidthCells = 80;
|
|
57
49
|
#outputPad = 1;
|
|
58
|
-
#
|
|
59
|
-
#
|
|
60
|
-
#
|
|
61
|
-
#
|
|
62
|
-
|
|
63
|
-
#dockNotice;
|
|
64
|
-
// Invariant: bare A1 docks the release notice above the live status; closing it lasts for this session only.
|
|
65
|
-
#releaseNoticeDismissed = false;
|
|
66
|
-
#releaseCloseTarget;
|
|
67
|
-
#copyAcknowledgement;
|
|
68
|
-
#copyAcknowledgementTimer;
|
|
69
|
-
#inputSurface;
|
|
70
|
-
#inputSurfaceCoordination = "editor";
|
|
71
|
-
#dockInputReuseEnabled;
|
|
72
|
-
#dockInputCandidate = false;
|
|
73
|
-
// Invariant: receipt-time eligibility is evidence, never authorization for composition-time reuse.
|
|
74
|
-
#dockInputRevision;
|
|
75
|
-
#visibleViewportSnapshot;
|
|
76
|
-
#fullViewportCompositions = 0;
|
|
77
|
-
#dockOnlyViewportCompositions = 0;
|
|
78
|
-
#transcriptBlockRenders = 0;
|
|
79
|
-
#extensionHeader = null;
|
|
80
|
-
#extensionFooter = null;
|
|
81
|
-
#extensionWidgets = new Map();
|
|
82
|
-
#extensionStatuses = new Map();
|
|
83
|
-
#extensionWorkingMessage;
|
|
84
|
-
#extensionWorkingVisible = true;
|
|
50
|
+
#workflowOutput;
|
|
51
|
+
#input;
|
|
52
|
+
#layout;
|
|
53
|
+
#extensions = new ExtensionSurface();
|
|
54
|
+
#dockChrome;
|
|
85
55
|
#imageSubmissionsSending = 0;
|
|
86
56
|
constructor(view, cwd, handlers, startup = {}, agentDir, extensionRenderers = {
|
|
87
57
|
getMessageRenderer: () => undefined,
|
|
@@ -94,21 +64,13 @@ export class OwnedUiSessionShellRoot {
|
|
|
94
64
|
...(handlers.pasteDiagnostics === undefined ? {} : { onEvent: handlers.pasteDiagnostics }),
|
|
95
65
|
...(handlers.pastePreparation === undefined ? {} : { preparation: handlers.pastePreparation }),
|
|
96
66
|
...(handlers.promptImageLimit === undefined ? {} : { imageLimit: handlers.promptImageLimit }) });
|
|
97
|
-
this.#submittedPromptComposer = this.#customViewport
|
|
98
|
-
? {
|
|
99
|
-
layout: submittedPromptLayout,
|
|
100
|
-
compose: (rows, width, source, style) => composeSubmittedPromptRows(rows, width, source, { ...style, prefix: text => promptArrow(text, piTheme()) })
|
|
101
|
-
.map(row => nativeHyperlinkStyle(row, nativeTranscriptLinkColor)),
|
|
102
|
-
}
|
|
103
|
-
: undefined;
|
|
67
|
+
this.#submittedPromptComposer = this.#customViewport ? this.#createSubmittedPromptComposer() : undefined;
|
|
104
68
|
this.#cwd = cwd;
|
|
69
|
+
this.#document = this.#createDocument(cwd);
|
|
105
70
|
this.#extensionRenderers = extensionRenderers;
|
|
106
71
|
this.#imageAssets = imageAssets;
|
|
107
72
|
this.#componentRuntime = handlers;
|
|
108
|
-
this.#onViewportFrame = handlers.onViewportFrame;
|
|
109
|
-
this.#onInputSurfaceChanged = handlers.onInputSurfaceChanged;
|
|
110
73
|
this.#onPromptSuggestionEligibilityChanged = handlers.onPromptSuggestionEligibilityChanged;
|
|
111
|
-
this.#dockInputReuseEnabled = handlers.enableDockInputReuse ?? true;
|
|
112
74
|
this.header = createPiShellHeader({
|
|
113
75
|
...startup,
|
|
114
76
|
...(this.#customViewport ? { getKeybindings: () => this.editor.keybindingConfig() } : {}),
|
|
@@ -119,81 +81,16 @@ export class OwnedUiSessionShellRoot {
|
|
|
119
81
|
frame: progressStatusFrame,
|
|
120
82
|
}, handlers);
|
|
121
83
|
this.#status.setProgressPresentation(this.#customViewport ? "custom-viewport" : "pinned");
|
|
122
|
-
this.#footer = createPiShellFooter(this.#
|
|
123
|
-
this.editor =
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
...(handlers.keybindings === undefined ? {} : { keybindings: handlers.keybindings }),
|
|
127
|
-
paintEditorSelection: (line, from, to, atomic) => backgroundSgrSpan(line, from, to, atomic ? "\u001b[7m" : "\u001b[27m\u001b[48;2;38;79;120m", atomic ? "\u001b[27m" : "\u001b[49m", piShellVisibleWidth),
|
|
128
|
-
transformPastedContent: content => {
|
|
129
|
-
try {
|
|
130
|
-
return this.#promptChips.transformPastedContent(content, this.editor.getText());
|
|
131
|
-
}
|
|
132
|
-
catch (error) {
|
|
133
|
-
handlers.onPasteRejected?.(error);
|
|
134
|
-
return "";
|
|
135
|
-
}
|
|
136
|
-
},
|
|
137
|
-
...(this.#customViewport ? {
|
|
138
|
-
beginClipboardPaste: () => this.#promptChips.beginPaste(this.editor.getText(), handlers.captureClipboardPaste?.() ?? { kind: "provided", read: signal => handlers.readClipboardContent?.(signal) ?? Promise.resolve(null) }, error => handlers.onPasteRejected?.(error), () => handlers.requestRender()),
|
|
139
|
-
onPasteInput: (bytes, phase) => {
|
|
140
|
-
try {
|
|
141
|
-
if (phase === "framing")
|
|
142
|
-
this.#terminalPasteDiagnosticRequest = --this.#pasteDiagnosticId;
|
|
143
|
-
const request = this.#terminalPasteDiagnosticRequest ?? --this.#pasteDiagnosticId;
|
|
144
|
-
handlers.pasteDiagnostics?.({ request, phase, bytes, atMs: performance.now(), pending: 0, transport: "terminal",
|
|
145
|
-
...(phase === "settled" ? { outcome: "ready" } : {}) });
|
|
146
|
-
if (phase === "settled")
|
|
147
|
-
this.#terminalPasteDiagnosticRequest = undefined;
|
|
148
|
-
}
|
|
149
|
-
catch { /* Invariant: payload-free diagnostics cannot interfere with the input path. */ }
|
|
150
|
-
},
|
|
151
|
-
deferTextPaste: text => !canPreparePasteInline(text),
|
|
152
|
-
beginTextPaste: text => this.#promptChips.beginPaste(this.editor.getText(), { kind: "text", text }, error => handlers.onPasteRejected?.(error)),
|
|
153
|
-
} : {}),
|
|
154
|
-
editorAtomicRanges: line => this.#promptChips.atomicRanges(line),
|
|
155
|
-
editorHiddenRanges: line => this.#promptChips.hiddenRanges(line),
|
|
156
|
-
decorateEditorRow: (row, width, rowIndex) => {
|
|
157
|
-
if (rowIndex === 0)
|
|
158
|
-
this.#editorHyperlinks.reset();
|
|
159
|
-
const plain = stripAnsi(row);
|
|
160
|
-
const dimmed = this.#promptChips.unsentRanges(plain).reduce((result, range) => backgroundSgrSpan(result, piShellVisibleWidth(plain.slice(0, range.start)), piShellVisibleWidth(plain.slice(0, range.end)), "\u001b[2m", "\u001b[22m", piShellVisibleWidth), row);
|
|
161
|
-
const ranges = this.#promptChips.hyperlinkRanges(plain);
|
|
162
|
-
if (ranges.length === 0 || !this.#editorHyperlinks.takeCleanup())
|
|
163
|
-
return dimmed;
|
|
164
|
-
const linkResetAndTail = `\u001b]8;;\u001b\\\u001b[24m${" ".repeat(Math.max(0, width - piShellVisibleWidth(dimmed)))}`;
|
|
165
|
-
// Platform: VS15 is zero-column and default-ignorable. It breaks Windows Terminal's
|
|
166
|
-
// plain-text URL detector only in the held-button paint; semantic text stays exact.
|
|
167
|
-
const paintRow = this.#viewportController.editorPointerSelecting
|
|
168
|
-
? dimmed.replaceAll("https://", "https:\uFE0E//").replaceAll("http://", "http:\uFE0E//")
|
|
169
|
-
: dimmed;
|
|
170
|
-
const decorated = this.#viewportController.editorPointerSelecting
|
|
171
|
-
? ranges.reduce((result, range) => backgroundSgrSpan(result, piShellVisibleWidth(plain.slice(0, range.start)), piShellVisibleWidth(plain.slice(0, range.end)), "\u001b[4:4m", "\u001b[24m", piShellVisibleWidth), paintRow)
|
|
172
|
-
: ranges.filter(range => this.#editorHyperlinks.take(range.target)).reduce((result, range) => hyperlinkSgrSpan(result, piShellVisibleWidth(plain.slice(0, range.start)), piShellVisibleWidth(plain.slice(0, range.end)), range.target, piShellVisibleWidth), dimmed);
|
|
173
|
-
// Platform: Windows Terminal can retain stale native dotted-link cells when a mutable
|
|
174
|
-
// prompt replaces a longer URL. Explicit non-link spaces overwrite that tail.
|
|
175
|
-
return `${decorated}${linkResetAndTail}`;
|
|
176
|
-
},
|
|
177
|
-
expandCopiedEditorText: text => this.#promptChips.expandCopiedText(text),
|
|
84
|
+
this.#footer = createPiShellFooter(this.#extensions.withStatuses(view), cwd, this.#customViewport ? "a1" : "pi", () => this.#footerLevel, handlers.footerSessionName ?? true);
|
|
85
|
+
this.editor = createShellEditor({
|
|
86
|
+
handlers,
|
|
87
|
+
customViewport: this.#customViewport,
|
|
178
88
|
cwd,
|
|
179
89
|
...(agentDir === undefined ? {} : { agentDir }),
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
measure: piShellVisibleWidth,
|
|
185
|
-
truncate: piShellTruncateToWidth,
|
|
186
|
-
}),
|
|
187
|
-
styleSuggestion: faint,
|
|
188
|
-
styleSuggestionCaret: caretCell,
|
|
189
|
-
},
|
|
190
|
-
} : {}),
|
|
191
|
-
onChange: text => {
|
|
192
|
-
handlers.onEditorChange?.(text);
|
|
193
|
-
// Concurrency: Pi clears before onSubmit; capture waiting references before pruning removed chips.
|
|
194
|
-
queueMicrotask(() => this.#promptChips.reconcileDraft(this.editor.getText()));
|
|
195
|
-
},
|
|
196
|
-
...(handlers.onPromptSuggestionAccepted === undefined ? {} : { onPromptSuggestionAccepted: handlers.onPromptSuggestionAccepted }),
|
|
90
|
+
promptChips: this.#promptChips,
|
|
91
|
+
editorPointerSelecting: () => this.#viewportController.editorPointerSelecting,
|
|
92
|
+
nextPasteDiagnosticRequest: () => --this.#pasteDiagnosticId,
|
|
93
|
+
toggleToolsExpanded: () => this.#setToolsExpanded(!this.#toolsExpanded),
|
|
197
94
|
});
|
|
198
95
|
this.#queued = createPiQueuedInputStatus(view.editor.queuedSubmissions, this.#customViewport ? "custom-viewport" : "pinned", this.#customViewport ? () => this.editor.keybindingConfig() : undefined);
|
|
199
96
|
this.#viewportController = new SessionViewportController({
|
|
@@ -205,33 +102,85 @@ export class OwnedUiSessionShellRoot {
|
|
|
205
102
|
...(handlers.pasteDiagnostics === undefined ? {} : { pasteDiagnostics: handlers.pasteDiagnostics }),
|
|
206
103
|
nextPasteDiagnosticRequest: () => --this.#pasteDiagnosticId,
|
|
207
104
|
});
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
105
|
+
this.#dockChrome = new DockChrome({
|
|
106
|
+
invalidateChrome: () => this.#invalidateChrome(),
|
|
107
|
+
requestRender: () => this.#componentRuntime.requestRender(),
|
|
108
|
+
viewport: this.#viewportController,
|
|
109
|
+
});
|
|
110
|
+
this.#workflowOutput = new WorkflowOutput({
|
|
111
|
+
customViewport: this.#customViewport,
|
|
112
|
+
document: this.#document,
|
|
113
|
+
dock: this.#dockChrome,
|
|
114
|
+
transcriptLength: () => this.#view.transcript.length,
|
|
115
|
+
outputPad: () => this.#outputPad,
|
|
116
|
+
hotkeysPresentation: () => this.hotkeysPresentation(),
|
|
117
|
+
componentRuntime: this.#componentRuntime,
|
|
118
|
+
invalidate: () => this.invalidate(),
|
|
119
|
+
});
|
|
218
120
|
this.editor.setThinkingLevel(view.thinkingLevel);
|
|
219
|
-
this.#
|
|
121
|
+
this.#input = new InputSurfaceSwitch(this.editor, {
|
|
122
|
+
dockInputReuse: this.#customViewport && (handlers.enableDockInputReuse ?? true),
|
|
123
|
+
viewport: this.#viewportController,
|
|
124
|
+
...(handlers.onInputSurfaceChanged === undefined ? {} : { onChanged: handlers.onInputSurfaceChanged }),
|
|
125
|
+
});
|
|
126
|
+
this.#layout = this.#createLayout(handlers.onViewportFrame);
|
|
220
127
|
for (const block of view.transcript) {
|
|
221
128
|
if (block.kind === "user")
|
|
222
129
|
this.editor.addToHistory(block.text);
|
|
223
130
|
}
|
|
224
|
-
this.#
|
|
225
|
-
// Invariant:
|
|
226
|
-
//
|
|
131
|
+
this.#document.sync(view.transcript);
|
|
132
|
+
// Invariant: rendered rows expire when
|
|
133
|
+
// their active theme changes.
|
|
227
134
|
this.#themeUnsubscribe = onPiThemeChange(() => {
|
|
228
135
|
this.invalidate();
|
|
229
136
|
this.#componentRuntime.requestRender(true);
|
|
230
137
|
});
|
|
231
138
|
}
|
|
139
|
+
// Invariant: the document reads the shell's chrome lazily, so it may be built before the header and viewport.
|
|
140
|
+
#createDocument(cwd) {
|
|
141
|
+
return new TranscriptDocument({
|
|
142
|
+
customViewport: this.#customViewport,
|
|
143
|
+
cwd,
|
|
144
|
+
mount: block => this.#mountTranscript(block),
|
|
145
|
+
chrome: {
|
|
146
|
+
diagnostics: () => this.#view.diagnostics,
|
|
147
|
+
headerRows: width => this.#extensions.header !== null ? this.#extensions.header.render(width)
|
|
148
|
+
: this.#customViewport ? [] : this.header.render(width),
|
|
149
|
+
resourceRows: width => this.#customViewport ? [] : this.resources.render(width),
|
|
150
|
+
hasExtensionHeader: () => this.#extensions.header !== null,
|
|
151
|
+
thinkingVisible: () => this.#thinkingVisible,
|
|
152
|
+
timeFormat: () => this.#viewportController.config.timeFormat,
|
|
153
|
+
},
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
// Invariant: one layout per root; the render path never consults the layout mode again.
|
|
157
|
+
#createLayout(onFrame) {
|
|
158
|
+
const ports = {
|
|
159
|
+
viewport: this.#viewportController,
|
|
160
|
+
input: this.#input,
|
|
161
|
+
editor: this.editor,
|
|
162
|
+
promptChips: this.#promptChips,
|
|
163
|
+
dockChrome: this.#dockChrome,
|
|
164
|
+
theme: VIEWPORT_THEME,
|
|
165
|
+
...(onFrame === undefined ? {} : { onFrame }),
|
|
166
|
+
rows: () => this.#componentRuntime.getRows(),
|
|
167
|
+
dock: width => this.#renderDockLayout(width),
|
|
168
|
+
document: width => this.#document.layout(width),
|
|
169
|
+
queued: width => this.#renderQueued(width),
|
|
170
|
+
liveStatus: width => this.#renderStatus(width),
|
|
171
|
+
diagnostics: () => this.#view.diagnostics,
|
|
172
|
+
};
|
|
173
|
+
return this.#customViewport ? new ViewportLayout(ports) : new PinnedLayout(ports);
|
|
174
|
+
}
|
|
175
|
+
#createSubmittedPromptComposer() {
|
|
176
|
+
return {
|
|
177
|
+
layout: (width, source) => submittedPromptLayout(width, source, this.#viewportController.config.timeFormat ?? "8:00 PM"),
|
|
178
|
+
compose: (rows, width, source, style) => composeSubmittedPromptRows(rows, width, source, { ...style, prefix: text => promptArrow(text, piTheme()) }, this.#viewportController.config.timeFormat ?? "8:00 PM").map(row => nativeHyperlinkStyle(row, nativeTranscriptLinkColor)),
|
|
179
|
+
};
|
|
180
|
+
}
|
|
232
181
|
setEditorPaddingX(padding) {
|
|
233
182
|
this.editor.setPaddingX(padding);
|
|
234
|
-
this.#
|
|
183
|
+
this.#document.invalidate("editor-padding");
|
|
235
184
|
}
|
|
236
185
|
setAutocompleteMaxVisible(maxVisible) {
|
|
237
186
|
this.editor.setAutocompleteMaxVisible(maxVisible);
|
|
@@ -241,7 +190,7 @@ export class OwnedUiSessionShellRoot {
|
|
|
241
190
|
return;
|
|
242
191
|
this.#outputPad = padding;
|
|
243
192
|
this.#status.setOutputPad(padding);
|
|
244
|
-
for (const component of this.#
|
|
193
|
+
for (const component of this.#document.components())
|
|
245
194
|
component.setOutputPad(padding);
|
|
246
195
|
this.#invalidateTranscriptPresentation();
|
|
247
196
|
}
|
|
@@ -249,7 +198,7 @@ export class OwnedUiSessionShellRoot {
|
|
|
249
198
|
if (this.#thinkingVisible === !hidden)
|
|
250
199
|
return;
|
|
251
200
|
this.#thinkingVisible = !hidden;
|
|
252
|
-
for (const component of this.#
|
|
201
|
+
for (const component of this.#document.components())
|
|
253
202
|
component.setHideThinkingBlock(hidden);
|
|
254
203
|
this.#invalidateTranscriptPresentation();
|
|
255
204
|
}
|
|
@@ -257,7 +206,7 @@ export class OwnedUiSessionShellRoot {
|
|
|
257
206
|
if (this.#mermaidRenderingMode === mode)
|
|
258
207
|
return;
|
|
259
208
|
this.#mermaidRenderingMode = mode;
|
|
260
|
-
for (const component of this.#
|
|
209
|
+
for (const component of this.#document.components())
|
|
261
210
|
component.setMermaidRenderingMode(mode);
|
|
262
211
|
this.#invalidateTranscriptPresentation();
|
|
263
212
|
}
|
|
@@ -266,7 +215,7 @@ export class OwnedUiSessionShellRoot {
|
|
|
266
215
|
return;
|
|
267
216
|
this.#showImages = showImages;
|
|
268
217
|
this.#imageWidthCells = imageWidthCells;
|
|
269
|
-
for (const component of this.#
|
|
218
|
+
for (const component of this.#document.components())
|
|
270
219
|
component.setImagePresentation(showImages, imageWidthCells);
|
|
271
220
|
this.#invalidateTranscriptPresentation();
|
|
272
221
|
}
|
|
@@ -286,7 +235,7 @@ export class OwnedUiSessionShellRoot {
|
|
|
286
235
|
return this.#promptChips.prepareSubmission(text);
|
|
287
236
|
}
|
|
288
237
|
reconcilePromptImageLimitNotice(text = this.editor.getText()) {
|
|
289
|
-
if (this.#
|
|
238
|
+
if (this.#dockChrome.notice?.errorCode !== "image-count" || !this.#promptChips.imageLimitState(text).corrected)
|
|
290
239
|
return false;
|
|
291
240
|
this.dismissNotice();
|
|
292
241
|
return true;
|
|
@@ -332,22 +281,22 @@ export class OwnedUiSessionShellRoot {
|
|
|
332
281
|
const becameReady = this.#view.lifecycle !== "ready" && view.lifecycle === "ready";
|
|
333
282
|
if (view.diagnostics.length !== this.#view.diagnostics.length
|
|
334
283
|
|| view.diagnostics.some((diagnostic, index) => diagnostic.sequence !== this.#view.diagnostics[index]?.sequence)) {
|
|
335
|
-
this.#
|
|
284
|
+
this.#document.invalidate("diagnostics");
|
|
336
285
|
}
|
|
337
286
|
const promptAccepted = this.#view.lifecycle !== "busy" && view.lifecycle === "busy";
|
|
338
287
|
this.#view = view;
|
|
339
288
|
// Invariant: the idle new-session confirmation yields to the first truthful live status;
|
|
340
289
|
// ordinary dock notices deliberately remain visible below Working during a run.
|
|
341
|
-
if (promptAccepted && this.#
|
|
342
|
-
this.#
|
|
290
|
+
if (promptAccepted && this.#dockChrome.notice?.kind === "new-session")
|
|
291
|
+
this.#dockChrome.forgetNotice();
|
|
343
292
|
if (promptAccepted && this.#imageSubmissionsSending > 0) {
|
|
344
293
|
this.#imageSubmissionsSending = 0;
|
|
345
294
|
this.#syncWorkingOverride();
|
|
346
295
|
}
|
|
347
296
|
this.#status.update(view);
|
|
348
|
-
this.#footer.update(this.#
|
|
297
|
+
this.#footer.update(this.#extensions.withStatuses(view));
|
|
349
298
|
this.#queued.update(view.editor.queuedSubmissions);
|
|
350
|
-
this.#
|
|
299
|
+
this.#document.sync(view.transcript);
|
|
351
300
|
this.editor.setSubmitEnabled(view.lifecycle !== "stopping" && view.lifecycle !== "stopped" && view.lifecycle !== "failed");
|
|
352
301
|
this.editor.setThinkingLevel(view.thinkingLevel);
|
|
353
302
|
// Performance: semantic updates already invalidate affected transcript blocks. Chrome is a separate authority.
|
|
@@ -361,24 +310,7 @@ export class OwnedUiSessionShellRoot {
|
|
|
361
310
|
* walk of the whole transcript.
|
|
362
311
|
*/
|
|
363
312
|
applyTranscriptBlock(block) {
|
|
364
|
-
|
|
365
|
-
// Invariant: a full-view/final presentation may preempt queued partials. Never revive an older revision.
|
|
366
|
-
if (current !== undefined && !acceptsTranscriptUpdate(current, block))
|
|
367
|
-
return;
|
|
368
|
-
this.#blocksById.set(block.id, block);
|
|
369
|
-
const component = this.#transcript.get(block.id);
|
|
370
|
-
if (component === undefined) {
|
|
371
|
-
const created = this.#mountTranscript(block);
|
|
372
|
-
this.#transcript.set(block.id, created);
|
|
373
|
-
this.#transcriptOrder.push(block.id);
|
|
374
|
-
}
|
|
375
|
-
else if (component.revision !== block.revision) {
|
|
376
|
-
component.update(block);
|
|
377
|
-
}
|
|
378
|
-
// Performance: one chunk touches one block, and the updated component tracks its own dirtiness.
|
|
379
|
-
// Invalidating the whole shell here would re-wrap the entire transcript per chunk.
|
|
380
|
-
this.#renderedRows.delete(block.id);
|
|
381
|
-
this.#documentLayouts.clear();
|
|
313
|
+
this.#document.apply(block);
|
|
382
314
|
}
|
|
383
315
|
#mountTranscript(block) {
|
|
384
316
|
// Invariant: the notice answers the reader's last command, so only their next prompt or
|
|
@@ -386,129 +318,18 @@ export class OwnedUiSessionShellRoot {
|
|
|
386
318
|
if (block.kind === "user" || block.kind === "bash")
|
|
387
319
|
this.dismissNotice();
|
|
388
320
|
const created = createPiShellTranscriptComponent(block, this.#cwd, this.#extensionRenderers, this.#submittedPromptComposer, this.#outputPad, !this.#thinkingVisible, this.#mermaidRenderingMode, this.#showImages, this.#imageWidthCells, this.#imageAssets, { ...this.#componentRuntime, changed: () => {
|
|
389
|
-
if (this.#
|
|
321
|
+
if (this.#document.component(block.id) !== created)
|
|
390
322
|
return;
|
|
391
|
-
this.#
|
|
392
|
-
this.#
|
|
393
|
-
this.#
|
|
394
|
-
this.#dockInputCandidate = false;
|
|
395
|
-
this.#dockInputRevision = undefined;
|
|
323
|
+
this.#document.blockChanged(block.id);
|
|
324
|
+
this.#layout.invalidate();
|
|
325
|
+
this.#input.clearDockInputCandidate();
|
|
396
326
|
this.#componentRuntime.requestRender();
|
|
397
327
|
} });
|
|
398
328
|
created.setExpanded(this.#toolsExpanded);
|
|
399
329
|
return created;
|
|
400
330
|
}
|
|
401
331
|
render(width) {
|
|
402
|
-
|
|
403
|
-
this.#viewportController.setEditorPointerFrame(undefined);
|
|
404
|
-
return [...this.#renderDocument(width), ...this.#renderDock(width)];
|
|
405
|
-
}
|
|
406
|
-
const height = Math.max(0, this.#componentRuntime.getRows());
|
|
407
|
-
const dock = this.#renderDockLayout(width);
|
|
408
|
-
const copyAcknowledgement = this.#renderCopyAcknowledgement(width);
|
|
409
|
-
// Invariant: auto/always own one stable final-column gutter. Every scrollable
|
|
410
|
-
// surface ends before it; the dock remains full-width and hidden mode returns it.
|
|
411
|
-
const documentWidth = this.#viewportController.config.scrollbarAppearance === "hidden"
|
|
412
|
-
? width
|
|
413
|
-
: Math.max(1, width - 1);
|
|
414
|
-
const document = this.#renderDocumentLayout(documentWidth);
|
|
415
|
-
const steeringRows = this.#renderQueued(documentWidth);
|
|
416
|
-
const releaseBanner = this.#renderReleaseBanner(documentWidth);
|
|
417
|
-
// Rationale: the release notice rides the bottom-aligned tail directly above Working, so it
|
|
418
|
-
// never displaces transcript content and scrolls away with the live status.
|
|
419
|
-
// Invariant: when idle the banner's last row sits on the line Working would occupy.
|
|
420
|
-
const statusRows = [...releaseBanner?.rows ?? [], ...this.#renderStatus(documentWidth)];
|
|
421
|
-
const transientSignature = transientRowsSignature(steeringRows, statusRows);
|
|
422
|
-
const snapshot = this.#visibleViewportSnapshot;
|
|
423
|
-
// Invariant: paint-only feedback may cover either viewport or dock cells, so
|
|
424
|
-
// ordinary dock-only reuse cannot safely omit its overlay composition.
|
|
425
|
-
const dockInputCandidate = this.#dockInputCandidate && copyAcknowledgement === undefined;
|
|
426
|
-
// Concurrency: input may change after a keyboard receipt but before its paint.
|
|
427
|
-
// Capture only the live inputs this composition will actually represent.
|
|
428
|
-
const inputSurface = this.#inputSurface;
|
|
429
|
-
const dockRows = dock.rows;
|
|
430
|
-
const selectableDocumentRowCount = document.rows.length;
|
|
431
|
-
const dockStartRow = height - dockRows.length + 1;
|
|
432
|
-
const editorOffset = dock.editorOffset;
|
|
433
|
-
this.#viewportController.setInputSurfaceFrame(this.usesDefaultInputSurface() ? undefined : {
|
|
434
|
-
component: this.#inputSurface,
|
|
435
|
-
columnStart: 1,
|
|
436
|
-
columnEnd: width,
|
|
437
|
-
rowStart: Math.max(1, dockStartRow + editorOffset),
|
|
438
|
-
rowEnd: Math.min(height, dockStartRow + editorOffset + dock.inputRows - 1),
|
|
439
|
-
});
|
|
440
|
-
this.#viewportController.setEditorPointerFrame(this.usesDefaultInputSurface()
|
|
441
|
-
? {
|
|
442
|
-
rowStart: dockStartRow + editorOffset,
|
|
443
|
-
rowEnd: dockStartRow + editorOffset + dock.inputRows - 1,
|
|
444
|
-
}
|
|
445
|
-
: undefined);
|
|
446
|
-
const viewportRevision = this.#viewportController.presentationRevision;
|
|
447
|
-
const selectionRevision = this.#viewportController.selectionRevision;
|
|
448
|
-
const pointerPosition = this.#viewportController.pointerPosition;
|
|
449
|
-
this.#dockInputCandidate = false;
|
|
450
|
-
let frame = dockInputCandidate
|
|
451
|
-
&& snapshot !== undefined
|
|
452
|
-
&& snapshot.width === width
|
|
453
|
-
&& snapshot.height === height
|
|
454
|
-
&& snapshot.documentRows === document.rows
|
|
455
|
-
&& snapshot.transientSignature === transientSignature
|
|
456
|
-
&& snapshot.promptAnchors === document.promptAnchors
|
|
457
|
-
&& snapshot.dockLength === dockRows.length
|
|
458
|
-
&& snapshot.inputSurface === inputSurface
|
|
459
|
-
&& snapshot.viewportRevision === viewportRevision
|
|
460
|
-
&& snapshot.selectionRevision === selectionRevision
|
|
461
|
-
? this.#viewportController.composeDockOnly(dockRows, width, height)
|
|
462
|
-
: null;
|
|
463
|
-
if (frame === null) {
|
|
464
|
-
frame = this.#viewportController.compose({
|
|
465
|
-
// Performance: do not copy the complete document on unchanged dock-only input.
|
|
466
|
-
// Steering and Working retain their one non-persistent viewport-tail ownership.
|
|
467
|
-
documentRows: [...document.rows, ...steeringRows, ...statusRows],
|
|
468
|
-
...(this.#viewportController.transcriptPointerSelecting
|
|
469
|
-
? { paintDocumentRow: heldNativeHyperlinkStyle }
|
|
470
|
-
: {}),
|
|
471
|
-
// Invariant: this boundary tracks persistent transcript rows for streaming and
|
|
472
|
-
// geometry; transient Steering and Working rows remain complete-frame selectable.
|
|
473
|
-
selectableDocumentRowCount,
|
|
474
|
-
...(document.liveTailStartRow === undefined ? {} : { liveTailStartRow: document.liveTailStartRow }),
|
|
475
|
-
bottomAlignedTailRowCount: steeringRows.length + statusRows.length,
|
|
476
|
-
dockRows,
|
|
477
|
-
promptAnchors: document.promptAnchors,
|
|
478
|
-
width,
|
|
479
|
-
height,
|
|
480
|
-
// Invariant: the control belongs immediately above the complete dock and
|
|
481
|
-
// floats over transient viewport content rather than consuming a dock row.
|
|
482
|
-
bottomControlRow: Math.max(0, height - Math.min(height, dockRows.length) - 1),
|
|
483
|
-
...(copyAcknowledgement === undefined || height <= 0 ? {} : {
|
|
484
|
-
frameOverlay: {
|
|
485
|
-
row: Math.max(1, Math.min(height, dockStartRow + dock.inputOffset - 1)),
|
|
486
|
-
text: copyAcknowledgement,
|
|
487
|
-
},
|
|
488
|
-
}),
|
|
489
|
-
theme: this.#viewportTheme,
|
|
490
|
-
});
|
|
491
|
-
this.#fullViewportCompositions += 1;
|
|
492
|
-
}
|
|
493
|
-
else
|
|
494
|
-
this.#dockOnlyViewportCompositions += 1;
|
|
495
|
-
this.#locateReleaseClose(frame, releaseBanner, document.rows.length + steeringRows.length + statusRows.length, statusRows.length);
|
|
496
|
-
this.#visibleViewportSnapshot = {
|
|
497
|
-
width,
|
|
498
|
-
height,
|
|
499
|
-
documentRows: document.rows,
|
|
500
|
-
transientSignature,
|
|
501
|
-
promptAnchors: document.promptAnchors,
|
|
502
|
-
dockLength: dockRows.length,
|
|
503
|
-
inputSurface,
|
|
504
|
-
// Invariant: a callback during composition cannot bless older rows with its
|
|
505
|
-
// newer revision. Its ordinary pending render will publish the newer state.
|
|
506
|
-
viewportRevision,
|
|
507
|
-
selectionRevision: frame.descriptor.selectionRevision,
|
|
508
|
-
pointerPosition,
|
|
509
|
-
};
|
|
510
|
-
this.#onViewportFrame?.(frame);
|
|
511
|
-
return frame.rows;
|
|
332
|
+
return this.#layout.render(width);
|
|
512
333
|
}
|
|
513
334
|
viewportFrameDescriptor() {
|
|
514
335
|
return this.#viewportController.frame?.descriptor ?? null;
|
|
@@ -516,10 +337,10 @@ export class OwnedUiSessionShellRoot {
|
|
|
516
337
|
/** Read-only, payload-free evidence; observing a pending frame must never render or repair it. */
|
|
517
338
|
viewportPresentationEvidence() {
|
|
518
339
|
const frame = this.#viewportController.frame;
|
|
519
|
-
const snapshot = this.#
|
|
340
|
+
const snapshot = this.#layout.composed();
|
|
520
341
|
return {
|
|
521
|
-
candidate: this.#dockInputCandidate,
|
|
522
|
-
candidateRevision: this.#dockInputCandidate ? this.#dockInputRevision ?? null : null,
|
|
342
|
+
candidate: this.#input.dockInputCandidate,
|
|
343
|
+
candidateRevision: this.#input.dockInputCandidate ? this.#input.dockInputRevision ?? null : null,
|
|
523
344
|
currentRevision: this.#viewportController.presentationRevision,
|
|
524
345
|
composedRevision: snapshot?.viewportRevision ?? null,
|
|
525
346
|
currentPointer: this.#viewportController.pointerPosition ?? null,
|
|
@@ -540,27 +361,16 @@ export class OwnedUiSessionShellRoot {
|
|
|
540
361
|
return this.#viewportController.hasSelection || this.editor.hasSelection();
|
|
541
362
|
}
|
|
542
363
|
setViewportConfig(config) {
|
|
543
|
-
if (this.#viewportController.setConfig(config))
|
|
544
|
-
this.#
|
|
364
|
+
if (this.#viewportController.setConfig(config)) {
|
|
365
|
+
this.#document.invalidate("viewport-config");
|
|
366
|
+
this.#layout.invalidate();
|
|
367
|
+
}
|
|
545
368
|
}
|
|
546
369
|
setFullscreenCopyOnSelect(enabled) {
|
|
547
370
|
this.#viewportController.setCopyOnSelect(enabled);
|
|
548
371
|
}
|
|
549
372
|
showCopyAcknowledgement(message) {
|
|
550
|
-
this.#
|
|
551
|
-
this.#copyAcknowledgement = message;
|
|
552
|
-
const timer = setTimeout(() => {
|
|
553
|
-
if (this.#copyAcknowledgementTimer !== timer)
|
|
554
|
-
return;
|
|
555
|
-
this.#copyAcknowledgementTimer = undefined;
|
|
556
|
-
this.#copyAcknowledgement = undefined;
|
|
557
|
-
this.#invalidateChrome();
|
|
558
|
-
this.#componentRuntime.requestRender();
|
|
559
|
-
}, COPY_ACKNOWLEDGEMENT_DURATION_MS);
|
|
560
|
-
timer.unref?.();
|
|
561
|
-
this.#copyAcknowledgementTimer = timer;
|
|
562
|
-
this.#invalidateChrome();
|
|
563
|
-
this.#componentRuntime.requestRender();
|
|
373
|
+
this.#dockChrome.showCopyAcknowledgement(message);
|
|
564
374
|
}
|
|
565
375
|
resumeViewportFollowing() {
|
|
566
376
|
this.#viewportController.resumeFollowing();
|
|
@@ -570,13 +380,7 @@ export class OwnedUiSessionShellRoot {
|
|
|
570
380
|
}
|
|
571
381
|
/** A new session binding owns new mounts even when it reuses semantic invocation ids. */
|
|
572
382
|
resetTranscript() {
|
|
573
|
-
|
|
574
|
-
component.dispose?.();
|
|
575
|
-
this.#transcript.clear();
|
|
576
|
-
this.#blocksById.clear();
|
|
577
|
-
this.#transcriptOrder = [];
|
|
578
|
-
this.#renderedRows.clear();
|
|
579
|
-
this.#documentLayouts.clear();
|
|
383
|
+
this.#document.reset();
|
|
580
384
|
}
|
|
581
385
|
resetViewport() {
|
|
582
386
|
this.#viewportController.reset();
|
|
@@ -594,18 +398,17 @@ export class OwnedUiSessionShellRoot {
|
|
|
594
398
|
handleViewportPreInput(data, allowWheel = true, now = Date.now(), editorActive = this.usesDefaultInputSurface()) {
|
|
595
399
|
return this.#viewportController.handlePreInput(data, allowWheel, now, editorActive);
|
|
596
400
|
}
|
|
597
|
-
#renderDock(width) {
|
|
598
|
-
return this.#renderDockLayout(width).rows;
|
|
599
|
-
}
|
|
600
401
|
#renderDockLayout(width) {
|
|
601
402
|
const queued = this.#customViewport ? [] : this.#renderQueued(width);
|
|
602
403
|
const statusRows = this.#customViewport ? this.#status.renderDock(width) : this.#renderStatus(width);
|
|
603
|
-
const
|
|
604
|
-
const
|
|
605
|
-
const
|
|
606
|
-
|
|
404
|
+
const noticeRows = this.#dockChrome.renderNotice(width, this.#outputPad);
|
|
405
|
+
const transientRows = [...queued, ...statusRows, ...noticeRows];
|
|
406
|
+
const messageStart = queued.length;
|
|
407
|
+
const copyableMessageRows = Array.from({ length: statusRows.length + noticeRows.length }, (_value, index) => ({ row: messageStart + index }));
|
|
408
|
+
const aboveWidgets = this.#extensions.renderWidgets("aboveEditor", width);
|
|
409
|
+
const input = this.#input.surface.render(width);
|
|
607
410
|
const body = this.usesDefaultInputSurface() ? this.editor.bodyGeometry?.() : undefined;
|
|
608
|
-
const belowWidgets = this.#renderWidgets("belowEditor", width);
|
|
411
|
+
const belowWidgets = this.#extensions.renderWidgets("belowEditor", width);
|
|
609
412
|
const footer = this.#renderFooter(width);
|
|
610
413
|
const rowsWithoutTransient = [...aboveWidgets, ...input, ...belowWidgets, ...footer];
|
|
611
414
|
const inputOffset = transientRows.length + aboveWidgets.length;
|
|
@@ -614,68 +417,12 @@ export class OwnedUiSessionShellRoot {
|
|
|
614
417
|
inputOffset,
|
|
615
418
|
editorOffset: inputOffset + (body?.rowOffset ?? 0),
|
|
616
419
|
inputRows: body?.rowCount ?? input.length,
|
|
420
|
+
footerRows: footer.length,
|
|
421
|
+
copyableMessageRows,
|
|
617
422
|
};
|
|
618
423
|
}
|
|
619
|
-
#renderCopyAcknowledgement(width) {
|
|
620
|
-
if (this.#copyAcknowledgement === undefined || width <= 0)
|
|
621
|
-
return undefined;
|
|
622
|
-
return piTheme().fg("accent", piShellTruncateToWidth(this.#copyAcknowledgement, width));
|
|
623
|
-
}
|
|
624
|
-
#clearCopyAcknowledgement(requestRender) {
|
|
625
|
-
if (this.#copyAcknowledgementTimer !== undefined)
|
|
626
|
-
clearTimeout(this.#copyAcknowledgementTimer);
|
|
627
|
-
this.#copyAcknowledgementTimer = undefined;
|
|
628
|
-
if (this.#copyAcknowledgement === undefined)
|
|
629
|
-
return;
|
|
630
|
-
this.#copyAcknowledgement = undefined;
|
|
631
|
-
this.#invalidateChrome();
|
|
632
|
-
if (requestRender)
|
|
633
|
-
this.#componentRuntime.requestRender();
|
|
634
|
-
}
|
|
635
|
-
#renderReleaseBanner(width) {
|
|
636
|
-
if (this.#releaseNoticeDismissed)
|
|
637
|
-
return undefined;
|
|
638
|
-
const diagnostic = this.#view.diagnostics.findLast(item => item.code === "release-update");
|
|
639
|
-
const release = diagnostic === undefined ? null : parseReleaseUpdateDiagnostic(diagnostic.message);
|
|
640
|
-
if (release === null)
|
|
641
|
-
return undefined;
|
|
642
|
-
return renderPiShellReleaseUpdateBanner(release, width, this.#viewportController.tailControlHovered(this.#releaseCloseTarget));
|
|
643
|
-
}
|
|
644
|
-
#locateReleaseClose(frame, banner, tailEnd, tailLength) {
|
|
645
|
-
// Invariant: the tail is the document suffix, so the control's document row is fixed relative to its
|
|
646
|
-
// end; alignment gap rows precede it and the viewport scroll decides whether it is on screen.
|
|
647
|
-
const bannerStart = tailEnd + frame.descriptor.transientAlignmentGapRows - tailLength - frame.scrollTop + 1;
|
|
648
|
-
const rowStart = banner === undefined ? 0 : Math.max(1, bannerStart + banner.close.rowStart);
|
|
649
|
-
const rowEnd = banner === undefined ? 0 : Math.min(frame.hits.viewportHeight, bannerStart + banner.close.rowEnd);
|
|
650
|
-
this.#releaseCloseTarget = banner !== undefined && rowStart <= rowEnd
|
|
651
|
-
? { rowStart, rowEnd, columnStart: banner.close.columnStart, columnEnd: banner.close.columnEnd }
|
|
652
|
-
: undefined;
|
|
653
|
-
this.#viewportController.setTailControl(this.#releaseCloseTarget === undefined ? undefined : {
|
|
654
|
-
...this.#releaseCloseTarget,
|
|
655
|
-
activate: () => {
|
|
656
|
-
this.#releaseNoticeDismissed = true;
|
|
657
|
-
this.#releaseCloseTarget = undefined;
|
|
658
|
-
},
|
|
659
|
-
});
|
|
660
|
-
}
|
|
661
|
-
#renderDockNotice(width) {
|
|
662
|
-
const notice = this.#dockNotice;
|
|
663
|
-
if (notice === undefined)
|
|
664
|
-
return [];
|
|
665
|
-
if (notice.kind === "status") {
|
|
666
|
-
// Compatibility: Pi pads its status text by one cell regardless of the output pad setting.
|
|
667
|
-
return ["", ...renderPiShellStatusText(notice.message, width, PINNED_PI_LAYOUT.outputPad)];
|
|
668
|
-
}
|
|
669
|
-
return renderPiShellCommandMessage({
|
|
670
|
-
kind: notice.kind === "new-session" ? "accent" : notice.kind,
|
|
671
|
-
message: notice.message,
|
|
672
|
-
}, width, this.#outputPad);
|
|
673
|
-
}
|
|
674
424
|
dismissNotice() {
|
|
675
|
-
|
|
676
|
-
return;
|
|
677
|
-
this.#dockNotice = undefined;
|
|
678
|
-
this.#invalidateChrome();
|
|
425
|
+
this.#dockChrome.dismissNotice();
|
|
679
426
|
}
|
|
680
427
|
#renderQueued(width) {
|
|
681
428
|
return this.#view.editor.queuedSubmissions.length === 0 ? [] : this.#queued.render(width);
|
|
@@ -685,284 +432,30 @@ export class OwnedUiSessionShellRoot {
|
|
|
685
432
|
return {
|
|
686
433
|
document: layoutPort(width => this.#renderDocument(width), () => this.invalidate()),
|
|
687
434
|
queued: layoutPort(width => this.#view.editor.queuedSubmissions.length === 0 ? [] : this.#queued.render(width), () => this.#queued.invalidate()),
|
|
688
|
-
aboveWidgets: layoutPort(width => this.#renderWidgets("aboveEditor", width), () => this.#
|
|
435
|
+
aboveWidgets: layoutPort(width => this.#extensions.renderWidgets("aboveEditor", width), () => this.#extensions.invalidate()),
|
|
689
436
|
status: layoutPort(width => this.#renderStatus(width), () => this.#status.invalidate()),
|
|
690
|
-
editor: layoutPort(width => this.#
|
|
691
|
-
belowWidgets: layoutPort(width => this.#renderWidgets("belowEditor", width), () => this.#
|
|
692
|
-
footer: layoutPort(width => this.#renderFooter(width), () => (this.#
|
|
437
|
+
editor: layoutPort(width => this.#input.surface.render(width), () => this.#input.surface.invalidate(), data => this.#input.surface.handleInput?.(data)),
|
|
438
|
+
belowWidgets: layoutPort(width => this.#extensions.renderWidgets("belowEditor", width), () => this.#extensions.invalidate()),
|
|
439
|
+
footer: layoutPort(width => this.#renderFooter(width), () => (this.#extensions.footer ?? this.#footer).invalidate()),
|
|
693
440
|
};
|
|
694
441
|
}
|
|
695
442
|
#renderDocument(width) {
|
|
696
|
-
return this.#
|
|
697
|
-
}
|
|
698
|
-
#renderDocumentLayout(width) {
|
|
699
|
-
// Performance: extension headers may animate independently of transcript revisions, so
|
|
700
|
-
// only the stable built-in document participates in this frame cache.
|
|
701
|
-
const cached = this.#extensionHeader === null ? this.#documentLayouts.get(width) : undefined;
|
|
702
|
-
if (cached !== undefined)
|
|
703
|
-
return cached;
|
|
704
|
-
const diagnostics = this.#view.diagnostics;
|
|
705
|
-
const startupRows = diagnostics
|
|
706
|
-
// Compatibility: the pinned route keeps trust failures with Pi-style startup diagnostics;
|
|
707
|
-
// bare A1 presents them in its prompt-adjacent notice dock instead.
|
|
708
|
-
.filter(diagnostic => diagnostic.code === "engine-startup"
|
|
709
|
-
|| !this.#customViewport && diagnostic.code === "project-trust")
|
|
710
|
-
.flatMap(diagnostic => renderPiShellStartupDiagnostic(diagnostic, width));
|
|
711
|
-
const resourceRows = this.#customViewport ? [] : [...this.resources.render(width)];
|
|
712
|
-
if (resourceRows.at(-1) === "")
|
|
713
|
-
resourceRows.pop();
|
|
714
|
-
const headerRows = this.#extensionHeader !== null
|
|
715
|
-
? this.#extensionHeader.render(width)
|
|
716
|
-
: this.#customViewport ? [] : this.header.render(width);
|
|
717
|
-
const rows = [
|
|
718
|
-
...startupRows,
|
|
719
|
-
...headerRows,
|
|
720
|
-
...resourceRows,
|
|
721
|
-
];
|
|
722
|
-
const promptAnchors = [];
|
|
723
|
-
// Rationale: a block that is still streaming re-renders every row it owns, so the frame
|
|
724
|
-
// reports where its rows begin. Damage-aware painting uses that to tell a live block's
|
|
725
|
-
// own reflow apart from a repaint that reaches settled rows.
|
|
726
|
-
let liveTailStartRow;
|
|
727
|
-
for (let index = 0; index < this.#transcriptOrder.length; index += 1) {
|
|
728
|
-
const id = this.#transcriptOrder[index];
|
|
729
|
-
const block = this.#blocksById.get(id);
|
|
730
|
-
if (!this.#thinkingVisible && block?.kind === "thinking")
|
|
731
|
-
continue;
|
|
732
|
-
const promptLike = block !== undefined
|
|
733
|
-
&& (block.kind === "user" || this.#customViewport && isPiPromptStyleCompaction(block));
|
|
734
|
-
// Invariant: the custom viewport has already removed its rail gutter from this width.
|
|
735
|
-
// Prompt-like and ordinary blocks therefore share one right content edge.
|
|
736
|
-
const blockWidth = width;
|
|
737
|
-
const blockRows = this.#blockRows(id, block, blockWidth);
|
|
738
|
-
if (promptLike) {
|
|
739
|
-
// Compatibility: the first natural prompt gets one breathing row at the document top.
|
|
740
|
-
// Once scrolling advances, the prompt itself reaches row zero and then
|
|
741
|
-
// becomes sticky there, so the spacer is never pinned with it.
|
|
742
|
-
if (this.#customViewport && index === 0)
|
|
743
|
-
rows.push("");
|
|
744
|
-
else if (index > 0)
|
|
745
|
-
rows.push("");
|
|
746
|
-
}
|
|
747
|
-
const firstRow = rows.length;
|
|
748
|
-
if (liveTailStartRow === undefined && block !== undefined && block.status !== "finalized") {
|
|
749
|
-
liveTailStartRow = firstRow;
|
|
750
|
-
}
|
|
751
|
-
rows.push(...blockRows);
|
|
752
|
-
if (promptLike && blockRows[0] !== undefined) {
|
|
753
|
-
promptAnchors.push({
|
|
754
|
-
id: block.id,
|
|
755
|
-
firstRow,
|
|
756
|
-
lastRow: Math.max(firstRow, rows.length - 1),
|
|
757
|
-
sourceRow: pinnedPromptSourceRow(block, blockRows[0], blockWidth),
|
|
758
|
-
prominentSourceRow: blockRows[0],
|
|
759
|
-
});
|
|
760
|
-
}
|
|
761
|
-
}
|
|
762
|
-
// Invariant: the A1 release notice precedes the extension-package notice, matching pinned Pi's order.
|
|
763
|
-
// Bare A1 docks it in the viewport tail instead of the document.
|
|
764
|
-
rows.push(...diagnostics
|
|
765
|
-
.filter(diagnostic => !this.#customViewport && diagnostic.code === "release-update")
|
|
766
|
-
.flatMap(diagnostic => {
|
|
767
|
-
const release = parseReleaseUpdateDiagnostic(diagnostic.message);
|
|
768
|
-
return release === null ? [] : renderPiShellReleaseUpdateNotice(release, width);
|
|
769
|
-
}));
|
|
770
|
-
rows.push(...diagnostics
|
|
771
|
-
.filter(diagnostic => diagnostic.code === "package-updates")
|
|
772
|
-
.flatMap(diagnostic => renderPiShellPackageUpdateNotice(diagnostic.message.split("\n").filter(line => line.startsWith("- ")).map(line => line.slice(2)), width)));
|
|
773
|
-
rows.push(...diagnostics
|
|
774
|
-
.filter(diagnostic => diagnostic.code === "changelog-collapsed" || diagnostic.code === "changelog-expanded")
|
|
775
|
-
// Rationale: bare A1 translates either startup diagnostic into transient dock chrome;
|
|
776
|
-
// the pinned layout retains Pi's collapsed hint or expanded document.
|
|
777
|
-
.flatMap(diagnostic => this.#customViewport
|
|
778
|
-
? []
|
|
779
|
-
: diagnostic.code === "changelog-collapsed"
|
|
780
|
-
? createPiShellCollapsedChangelog().render(width)
|
|
781
|
-
: createPiShellChangelog(diagnostic.message).render(width)));
|
|
782
|
-
rows.push(...diagnostics
|
|
783
|
-
.filter(diagnostic => diagnostic.code !== "engine-startup" && diagnostic.code !== "project-trust"
|
|
784
|
-
&& diagnostic.code !== "release-update"
|
|
785
|
-
&& diagnostic.code !== "package-updates" && diagnostic.code !== "changelog-collapsed"
|
|
786
|
-
&& diagnostic.code !== "changelog-expanded")
|
|
787
|
-
.slice(-3)
|
|
788
|
-
.flatMap(diagnostic => renderPiShellTranscriptBlock({
|
|
789
|
-
id: `diagnostic-${diagnostic.sequence}`,
|
|
790
|
-
kind: diagnostic.severity === "error" ? "error" : "system",
|
|
791
|
-
status: "finalized",
|
|
792
|
-
revision: diagnostic.sequence,
|
|
793
|
-
title: diagnostic.code,
|
|
794
|
-
text: diagnostic.message,
|
|
795
|
-
payload: {},
|
|
796
|
-
}, width, this.#cwd)));
|
|
797
|
-
const layout = { rows: Object.freeze(rows), promptAnchors: Object.freeze(promptAnchors), liveTailStartRow };
|
|
798
|
-
if (this.#extensionHeader === null) {
|
|
799
|
-
this.#documentLayouts.set(width, layout);
|
|
800
|
-
// Performance: the custom viewport commonly probes full width and reserved-rail width.
|
|
801
|
-
while (this.#documentLayouts.size > 2)
|
|
802
|
-
this.#documentLayouts.delete(this.#documentLayouts.keys().next().value);
|
|
803
|
-
}
|
|
804
|
-
return layout;
|
|
805
|
-
}
|
|
806
|
-
// Performance: finalized blocks cache by revision and width; live blocks always render.
|
|
807
|
-
#blockRows(id, block, width) {
|
|
808
|
-
const component = this.#transcript.get(id);
|
|
809
|
-
if (component === undefined)
|
|
810
|
-
return [];
|
|
811
|
-
const render = () => {
|
|
812
|
-
this.#transcriptBlockRenders += 1;
|
|
813
|
-
const rows = component.render(width);
|
|
814
|
-
if (!this.#customViewport || block?.kind === "user")
|
|
815
|
-
return rows;
|
|
816
|
-
return rows.map(row => nativeHyperlinkStyle(row, nativeTranscriptLinkColor));
|
|
817
|
-
};
|
|
818
|
-
if (block === undefined || block.status !== "finalized")
|
|
819
|
-
return render();
|
|
820
|
-
let byWidth = this.#renderedRows.get(id);
|
|
821
|
-
const cached = byWidth?.get(width);
|
|
822
|
-
const presentationRevision = component.presentationRevision ?? 0;
|
|
823
|
-
if (cached?.revision === block.revision && cached.presentationRevision === presentationRevision)
|
|
824
|
-
return cached.rows;
|
|
825
|
-
const rows = render();
|
|
826
|
-
if (byWidth === undefined) {
|
|
827
|
-
byWidth = new Map();
|
|
828
|
-
this.#renderedRows.set(id, byWidth);
|
|
829
|
-
}
|
|
830
|
-
byWidth.set(width, { revision: block.revision, presentationRevision, rows });
|
|
831
|
-
// Invariant: bare A1 probes full width before reserving the overflowing rail column;
|
|
832
|
-
// retain both widths without turning resize history into an unbounded cache.
|
|
833
|
-
while (byWidth.size > 2)
|
|
834
|
-
byWidth.delete(byWidth.keys().next().value);
|
|
835
|
-
return rows;
|
|
443
|
+
return this.#document.layout(width).rows;
|
|
836
444
|
}
|
|
837
445
|
transcriptComponent(id) {
|
|
838
|
-
return this.#
|
|
446
|
+
return this.#document.component(id);
|
|
839
447
|
}
|
|
840
448
|
exitTranscript(width) {
|
|
841
|
-
|
|
842
|
-
for (const id of this.#transcriptOrder) {
|
|
843
|
-
const block = this.#blocksById.get(id);
|
|
844
|
-
if (block === undefined || (!this.#thinkingVisible && block.kind === "thinking"))
|
|
845
|
-
continue;
|
|
846
|
-
if (rows.length > 0 && (block.kind === "user" || this.#customViewport && isPiPromptStyleCompaction(block)))
|
|
847
|
-
rows.push("");
|
|
848
|
-
rows.push(...this.#blockRows(id, block, width).map(sanitizeExitTranscriptRow));
|
|
849
|
-
}
|
|
850
|
-
while (rows.at(-1) === "")
|
|
851
|
-
rows.pop();
|
|
852
|
-
return rows.join("\n");
|
|
449
|
+
return this.#document.exitTranscript(width);
|
|
853
450
|
}
|
|
854
451
|
appendWorkflowStatus(message) {
|
|
855
|
-
|
|
856
|
-
// reader's eye already is, instead of a transcript row that opens an empty session at the
|
|
857
|
-
// top-left or sinks into a long feed. The pinned route keeps Pi's chat placement.
|
|
858
|
-
if (this.#customViewport) {
|
|
859
|
-
this.#dockNotice = { kind: "status", message };
|
|
860
|
-
this.#invalidateChrome();
|
|
861
|
-
return;
|
|
862
|
-
}
|
|
863
|
-
const previousId = this.#lastWorkflowStatusId;
|
|
864
|
-
if (previousId !== undefined
|
|
865
|
-
&& this.#transcriptOrder.at(-1) === previousId
|
|
866
|
-
&& this.#workflowStatusAnchors.get(previousId) === this.#view.transcript.length) {
|
|
867
|
-
this.#workflowStatusMessages.set(previousId, message);
|
|
868
|
-
this.#documentLayouts.clear();
|
|
869
|
-
this.invalidate();
|
|
870
|
-
return;
|
|
871
|
-
}
|
|
872
|
-
const id = this.#appendAnchoredWorkflowComponent(width => [
|
|
873
|
-
"",
|
|
874
|
-
...renderPiShellStatusText(this.#workflowStatusMessages.get(id) ?? message, width),
|
|
875
|
-
]);
|
|
876
|
-
this.#workflowStatusMessages.set(id, message);
|
|
877
|
-
this.#lastWorkflowStatusId = id;
|
|
452
|
+
this.#workflowOutput.appendStatus(message);
|
|
878
453
|
}
|
|
879
454
|
appendWorkflowMessage(message, errorCode) {
|
|
880
|
-
|
|
881
|
-
this.appendWorkflowStatus(message.message);
|
|
882
|
-
return;
|
|
883
|
-
}
|
|
884
|
-
if (this.#customViewport && (message.kind === "warning" || message.kind === "error")) {
|
|
885
|
-
this.#dockNotice = {
|
|
886
|
-
kind: errorCode === "image-count" ? "warning" : message.kind,
|
|
887
|
-
message: message.message,
|
|
888
|
-
...(errorCode === undefined ? {} : { errorCode }),
|
|
889
|
-
};
|
|
890
|
-
this.#invalidateChrome();
|
|
891
|
-
return;
|
|
892
|
-
}
|
|
893
|
-
this.#lastWorkflowStatusId = undefined;
|
|
894
|
-
const presentation = { kind: message.kind, message: message.message };
|
|
895
|
-
this.#appendAnchoredWorkflowComponent(width => renderPiShellCommandMessage(presentation, width, this.#outputPad));
|
|
455
|
+
this.#workflowOutput.appendMessage(message, errorCode);
|
|
896
456
|
}
|
|
897
457
|
appendWorkflowResult(result, errorCode) {
|
|
898
|
-
|
|
899
|
-
for (const message of result.messages)
|
|
900
|
-
this.appendWorkflowMessage(message, errorCode);
|
|
901
|
-
return;
|
|
902
|
-
}
|
|
903
|
-
if (result.messageKind === "silent" || (result.outcome === "cancelled" && result.messageKind === undefined))
|
|
904
|
-
return;
|
|
905
|
-
if (result.command === "reload" && result.outcome === "completed") {
|
|
906
|
-
this.appendWorkflowStatus(result.message);
|
|
907
|
-
return;
|
|
908
|
-
}
|
|
909
|
-
if (result.command === "session" && result.outcome === "completed" && result.presentation?.kind === "session-info") {
|
|
910
|
-
this.#lastWorkflowStatusId = undefined;
|
|
911
|
-
const sessionInfo = createPiShellSessionInfo(result.presentation);
|
|
912
|
-
this.#appendAnchoredWorkflowComponent(width => sessionInfo.render(width), () => sessionInfo.dispose?.());
|
|
913
|
-
return;
|
|
914
|
-
}
|
|
915
|
-
if (result.command === "hotkeys" && result.outcome === "completed") {
|
|
916
|
-
this.#lastWorkflowStatusId = undefined;
|
|
917
|
-
const presentation = this.hotkeysPresentation();
|
|
918
|
-
const hotkeys = createPiShellHotkeys(presentation.bindings, presentation.getShortcuts, presentation.profile);
|
|
919
|
-
this.#appendAnchoredWorkflowComponent(width => hotkeys.render(width), () => hotkeys.dispose?.());
|
|
920
|
-
return;
|
|
921
|
-
}
|
|
922
|
-
if (result.command === "changelog" && result.outcome === "completed") {
|
|
923
|
-
this.#lastWorkflowStatusId = undefined;
|
|
924
|
-
const changelog = createPiShellChangelog(result.detail ?? "No changelog entries found.");
|
|
925
|
-
this.#appendAnchoredWorkflowComponent(width => changelog.render(width), () => changelog.dispose?.());
|
|
926
|
-
return;
|
|
927
|
-
}
|
|
928
|
-
if (result.outcome === "failed") {
|
|
929
|
-
this.appendWorkflowMessage({
|
|
930
|
-
kind: result.messageKind === "warning" ? "warning" : "error",
|
|
931
|
-
message: result.message,
|
|
932
|
-
}, errorCode);
|
|
933
|
-
return;
|
|
934
|
-
}
|
|
935
|
-
if (result.outcome === "completed" && (result.command === "quit" || result.command === "compact"))
|
|
936
|
-
return;
|
|
937
|
-
if (result.outcome === "completed" && result.command === "new" && this.#customViewport) {
|
|
938
|
-
// Rationale: bare A1 keeps the replacement session visually empty while preserving Pi's accent notice shape.
|
|
939
|
-
this.#dockNotice = { kind: "new-session", message: result.message };
|
|
940
|
-
this.#invalidateChrome();
|
|
941
|
-
return;
|
|
942
|
-
}
|
|
943
|
-
if (result.outcome === "completed" && (result.command === "new" || result.command === "name" || result.command === "debug")) {
|
|
944
|
-
this.#lastWorkflowStatusId = undefined;
|
|
945
|
-
const presentation = { kind: result.command, message: result.message, ...(result.detail === undefined ? {} : { detail: result.detail }) };
|
|
946
|
-
this.#appendAnchoredWorkflowComponent(width => renderPiShellCommandMessage(presentation, width, this.#outputPad));
|
|
947
|
-
return;
|
|
948
|
-
}
|
|
949
|
-
if (result.command === "arminsayshi" && result.outcome === "completed") {
|
|
950
|
-
this.#lastWorkflowStatusId = undefined;
|
|
951
|
-
const armin = createPiShellArmin(this.#componentRuntime);
|
|
952
|
-
this.#appendAnchoredWorkflowComponent(width => ["", ...armin.render(width)], () => armin.dispose?.());
|
|
953
|
-
return;
|
|
954
|
-
}
|
|
955
|
-
if (result.command === "dementedelves" && result.outcome === "completed") {
|
|
956
|
-
this.#lastWorkflowStatusId = undefined;
|
|
957
|
-
const announcement = createPiShellEarendilAnnouncement();
|
|
958
|
-
this.#appendAnchoredWorkflowComponent(width => ["", ...announcement.render(width)], () => announcement.dispose?.());
|
|
959
|
-
return;
|
|
960
|
-
}
|
|
961
|
-
// Rationale: the pinned engine links both share URLs; the workflow result carries the plain URLs.
|
|
962
|
-
const message = result.command === "share" && result.detail
|
|
963
|
-
? `${linkShareUrl(result.message)}\nGist: ${piShellHyperlink(result.detail)}`
|
|
964
|
-
: result.message;
|
|
965
|
-
this.appendWorkflowStatus(message);
|
|
458
|
+
this.#workflowOutput.appendResult(result, errorCode);
|
|
966
459
|
}
|
|
967
460
|
toggleThinkingVisibility() {
|
|
968
461
|
this.setHideThinkingBlock(this.#thinkingVisible);
|
|
@@ -974,39 +467,25 @@ export class OwnedUiSessionShellRoot {
|
|
|
974
467
|
this.#setToolsExpanded(expanded);
|
|
975
468
|
}
|
|
976
469
|
setExtensionWidget(key, component, placement) {
|
|
977
|
-
|
|
978
|
-
if (component === null)
|
|
979
|
-
this.#extensionWidgets.delete(key);
|
|
980
|
-
else
|
|
981
|
-
this.#extensionWidgets.set(key, { component, placement });
|
|
982
|
-
if (previous !== undefined && previous !== component)
|
|
983
|
-
previous.dispose?.();
|
|
470
|
+
this.#extensions.placeWidget(key, component, placement);
|
|
984
471
|
this.invalidate();
|
|
985
472
|
}
|
|
986
473
|
setExtensionHeader(component) {
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
this.#extensionHeader = component;
|
|
990
|
-
this.#documentLayouts.clear();
|
|
474
|
+
this.#extensions.replaceHeader(component);
|
|
475
|
+
this.#document.invalidate("extension-header");
|
|
991
476
|
this.invalidate();
|
|
992
477
|
}
|
|
993
478
|
setExtensionFooter(component) {
|
|
994
|
-
|
|
995
|
-
this.#extensionFooter?.dispose?.();
|
|
996
|
-
this.#extensionFooter = component;
|
|
479
|
+
this.#extensions.replaceFooter(component);
|
|
997
480
|
this.invalidate();
|
|
998
481
|
}
|
|
999
482
|
setExtensionStatus(key, text) {
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
else
|
|
1003
|
-
this.#extensionStatuses.set(key, text);
|
|
1004
|
-
this.#footer.update(this.#viewWithExtensionStatuses(this.#view));
|
|
483
|
+
this.#extensions.setStatus(key, text);
|
|
484
|
+
this.#footer.update(this.#extensions.withStatuses(this.#view));
|
|
1005
485
|
this.invalidate();
|
|
1006
486
|
}
|
|
1007
|
-
setExtensionWorking(message, visible = this.#
|
|
1008
|
-
this.#
|
|
1009
|
-
this.#extensionWorkingVisible = visible;
|
|
487
|
+
setExtensionWorking(message, visible = this.#extensions.working.visible) {
|
|
488
|
+
this.#extensions.setWorking(message, visible);
|
|
1010
489
|
this.#syncWorkingOverride();
|
|
1011
490
|
this.invalidate();
|
|
1012
491
|
}
|
|
@@ -1028,7 +507,7 @@ export class OwnedUiSessionShellRoot {
|
|
|
1028
507
|
const sending = this.#imageSubmissionsSending > 0;
|
|
1029
508
|
this.#status.setWorkingOverride(sending
|
|
1030
509
|
? "Sending…"
|
|
1031
|
-
: this.#
|
|
510
|
+
: this.#extensions.working.visible ? this.#extensions.working.message : undefined, sending);
|
|
1032
511
|
}
|
|
1033
512
|
hotkeysPresentation() {
|
|
1034
513
|
return {
|
|
@@ -1038,34 +517,17 @@ export class OwnedUiSessionShellRoot {
|
|
|
1038
517
|
};
|
|
1039
518
|
}
|
|
1040
519
|
resetWorkflowPresentation() {
|
|
1041
|
-
this.#
|
|
1042
|
-
this.#clearCopyAcknowledgement(false);
|
|
1043
|
-
|
|
1044
|
-
this.#transcript.get(id)?.dispose?.();
|
|
1045
|
-
this.#transcript.delete(id);
|
|
1046
|
-
}
|
|
1047
|
-
this.#workflowStatusAnchors.clear();
|
|
1048
|
-
this.#workflowStatusMessages.clear();
|
|
1049
|
-
this.#transcriptOrder = this.#transcriptOrder.filter(id => !id.startsWith("workflow-status-"));
|
|
1050
|
-
this.#lastWorkflowStatusId = undefined;
|
|
1051
|
-
this.#documentLayouts.clear();
|
|
520
|
+
this.#dockChrome.forgetNotice();
|
|
521
|
+
this.#dockChrome.clearCopyAcknowledgement(false);
|
|
522
|
+
this.#workflowOutput.reset();
|
|
1052
523
|
this.invalidate();
|
|
1053
524
|
}
|
|
1054
525
|
resetExtensionUi() {
|
|
1055
|
-
this.#
|
|
1056
|
-
this.#extensionFooter?.dispose?.();
|
|
1057
|
-
for (const { component } of this.#extensionWidgets.values())
|
|
1058
|
-
component.dispose?.();
|
|
1059
|
-
this.#extensionHeader = null;
|
|
1060
|
-
this.#extensionFooter = null;
|
|
1061
|
-
this.#extensionWidgets.clear();
|
|
1062
|
-
this.#extensionStatuses.clear();
|
|
1063
|
-
this.#extensionWorkingMessage = undefined;
|
|
526
|
+
this.#extensions.reset();
|
|
1064
527
|
this.#syncWorkingOverride();
|
|
1065
|
-
this.#footer.update(this.#
|
|
528
|
+
this.#footer.update(this.#extensions.withStatuses(this.#view));
|
|
1066
529
|
// Invariant: an extension renderer may have drawn transcript blocks that are now unrendered by it.
|
|
1067
|
-
this.#
|
|
1068
|
-
this.#documentLayouts.clear();
|
|
530
|
+
this.#document.invalidate("extension-renderers");
|
|
1069
531
|
this.invalidate();
|
|
1070
532
|
}
|
|
1071
533
|
addExtensionNotification(message, type) {
|
|
@@ -1078,263 +540,97 @@ export class OwnedUiSessionShellRoot {
|
|
|
1078
540
|
extensionFooterData() {
|
|
1079
541
|
return {
|
|
1080
542
|
getGitBranch: () => this.#view.status.footer?.branch ?? null,
|
|
1081
|
-
getExtensionStatuses: () =>
|
|
543
|
+
getExtensionStatuses: () => this.#extensions.statuses(),
|
|
1082
544
|
getAvailableProviderCount: () => this.#view.status.footer?.availableProviderCount ?? 1,
|
|
1083
545
|
onBranchChange: () => () => { },
|
|
1084
546
|
};
|
|
1085
547
|
}
|
|
1086
548
|
usesDefaultInputSurface() {
|
|
1087
|
-
return this.#
|
|
549
|
+
return this.#input.usesEditor;
|
|
1088
550
|
}
|
|
1089
551
|
inputCoordinationSurface() {
|
|
1090
|
-
return this.#
|
|
552
|
+
return this.#input.coordination;
|
|
1091
553
|
}
|
|
1092
554
|
viewportCompositionEvidence() {
|
|
1093
|
-
return
|
|
555
|
+
return this.#layout.compositions();
|
|
1094
556
|
}
|
|
1095
557
|
transcriptRenderCount() {
|
|
1096
|
-
return this.#
|
|
558
|
+
return this.#document.renderCount;
|
|
1097
559
|
}
|
|
1098
560
|
setFooterLevel(visible) { this.#footerLevel = visible; }
|
|
1099
561
|
setInputSurface(component, disposePrevious = true, coordination = "owned") {
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
if (next === this.#inputSurface) {
|
|
1103
|
-
this.#inputSurfaceCoordination = nextCoordination;
|
|
1104
|
-
return;
|
|
1105
|
-
}
|
|
1106
|
-
this.#dockInputCandidate = false;
|
|
1107
|
-
this.#viewportController.invalidateInputSurface();
|
|
1108
|
-
this.#inputSurface.setFocused?.(false);
|
|
1109
|
-
if (disposePrevious && this.#inputSurface !== this.editor)
|
|
1110
|
-
this.#inputSurface.dispose?.();
|
|
1111
|
-
this.#inputSurface = next;
|
|
1112
|
-
this.#inputSurfaceCoordination = nextCoordination;
|
|
1113
|
-
this.#onInputSurfaceChanged?.();
|
|
1114
|
-
this.#inputSurface.setFocused?.(true);
|
|
1115
|
-
this.invalidate();
|
|
562
|
+
if (this.#input.set(component, disposePrevious, coordination))
|
|
563
|
+
this.invalidate();
|
|
1116
564
|
}
|
|
1117
565
|
handleInput(data) {
|
|
1118
|
-
this.#
|
|
1119
|
-
&& classifyPiTuiInput(data, this.#inputSurfaceCoordination) === "safe";
|
|
1120
|
-
this.#dockInputRevision = this.#dockInputCandidate ? this.#viewportController.presentationRevision : undefined;
|
|
1121
|
-
try {
|
|
1122
|
-
this.#inputSurface.handleInput?.(data);
|
|
1123
|
-
}
|
|
1124
|
-
catch (error) {
|
|
1125
|
-
this.#dockInputCandidate = false;
|
|
1126
|
-
throw error;
|
|
1127
|
-
}
|
|
566
|
+
this.#input.handleInput(data);
|
|
1128
567
|
}
|
|
1129
568
|
invalidate() {
|
|
1130
|
-
this.#
|
|
1131
|
-
this.#documentLayouts.clear();
|
|
1132
|
-
for (const component of this.#transcript.values())
|
|
1133
|
-
component.invalidate();
|
|
569
|
+
this.#document.invalidate("repaint");
|
|
1134
570
|
this.#invalidateChrome();
|
|
1135
571
|
}
|
|
1136
572
|
#invalidateChrome() {
|
|
1137
573
|
this.header.invalidate();
|
|
1138
574
|
this.resources.invalidate();
|
|
1139
575
|
this.editor.invalidate();
|
|
1140
|
-
if (this.#
|
|
1141
|
-
this.#
|
|
1142
|
-
this.#
|
|
576
|
+
if (!this.#input.usesEditor)
|
|
577
|
+
this.#input.surface.invalidate();
|
|
578
|
+
this.#extensions.invalidate();
|
|
1143
579
|
this.#status.invalidate();
|
|
1144
580
|
this.#footer.invalidate();
|
|
1145
581
|
this.#queued.invalidate();
|
|
1146
582
|
}
|
|
1147
583
|
setFocused(focused) {
|
|
1148
|
-
this.#
|
|
584
|
+
this.#input.surface.setFocused?.(focused);
|
|
1149
585
|
if (focused)
|
|
1150
586
|
this.#onPromptSuggestionEligibilityChanged?.();
|
|
1151
587
|
}
|
|
1152
588
|
dispose() {
|
|
1153
|
-
this.#clearCopyAcknowledgement(false);
|
|
589
|
+
this.#dockChrome.clearCopyAcknowledgement(false);
|
|
1154
590
|
this.clearViewportPointerState();
|
|
1155
591
|
this.#themeUnsubscribe();
|
|
1156
592
|
this.header.dispose?.();
|
|
1157
593
|
this.resources.dispose?.();
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
this.#
|
|
1161
|
-
this.#
|
|
1162
|
-
this.#documentLayouts.clear();
|
|
1163
|
-
this.#visibleViewportSnapshot = undefined;
|
|
1164
|
-
this.#dockInputCandidate = false;
|
|
1165
|
-
this.#dockInputRevision = undefined;
|
|
1166
|
-
if (this.#inputSurface !== this.editor)
|
|
1167
|
-
this.#inputSurface.dispose?.();
|
|
1168
|
-
this.#extensionHeader?.dispose?.();
|
|
1169
|
-
this.#extensionFooter?.dispose?.();
|
|
1170
|
-
for (const { component } of this.#extensionWidgets.values())
|
|
1171
|
-
component.dispose?.();
|
|
1172
|
-
this.#extensionWidgets.clear();
|
|
594
|
+
this.#document.dispose();
|
|
595
|
+
this.#layout.invalidate();
|
|
596
|
+
this.#input.dispose();
|
|
597
|
+
this.#extensions.dispose();
|
|
1173
598
|
this.editor.dispose?.();
|
|
1174
599
|
this.#status.dispose?.();
|
|
1175
600
|
this.#footer.dispose?.();
|
|
1176
601
|
this.#queued.dispose?.();
|
|
1177
602
|
}
|
|
1178
603
|
#invalidateTranscriptPresentation() {
|
|
1179
|
-
this.#
|
|
1180
|
-
this.#documentLayouts.clear();
|
|
1181
|
-
this.invalidate();
|
|
1182
|
-
}
|
|
1183
|
-
#syncTranscript(blocks) {
|
|
1184
|
-
const previousIds = this.#transcriptOrder.filter(id => !id.startsWith("workflow-status-"));
|
|
1185
|
-
const transcriptChanged = previousIds.length !== blocks.length || blocks.some((block, index) => {
|
|
1186
|
-
const previous = this.#blocksById.get(block.id);
|
|
1187
|
-
return previousIds[index] !== block.id || previous?.revision !== block.revision;
|
|
1188
|
-
});
|
|
1189
|
-
if (transcriptChanged)
|
|
1190
|
-
this.#documentLayouts.clear();
|
|
1191
|
-
this.#blocksById.clear();
|
|
1192
|
-
for (const block of blocks)
|
|
1193
|
-
this.#blocksById.set(block.id, block);
|
|
1194
|
-
const nextIds = new Set(blocks.map(block => block.id));
|
|
1195
|
-
for (const [id, component] of this.#transcript) {
|
|
1196
|
-
if (id.startsWith("workflow-status-") || nextIds.has(id))
|
|
1197
|
-
continue;
|
|
1198
|
-
component.dispose?.();
|
|
1199
|
-
this.#transcript.delete(id);
|
|
1200
|
-
this.#renderedRows.delete(id);
|
|
1201
|
-
}
|
|
1202
|
-
for (const block of blocks) {
|
|
1203
|
-
const component = this.#transcript.get(block.id);
|
|
1204
|
-
if (component === undefined) {
|
|
1205
|
-
const created = this.#mountTranscript(block);
|
|
1206
|
-
this.#transcript.set(block.id, created);
|
|
1207
|
-
}
|
|
1208
|
-
else if (component.revision !== block.revision) {
|
|
1209
|
-
component.update(block);
|
|
1210
|
-
}
|
|
1211
|
-
}
|
|
1212
|
-
const statusIds = [...this.#workflowStatusAnchors.keys()];
|
|
1213
|
-
const order = [];
|
|
1214
|
-
for (let index = 0; index <= blocks.length; index += 1) {
|
|
1215
|
-
for (const statusId of statusIds) {
|
|
1216
|
-
if (this.#workflowStatusAnchors.get(statusId) === index)
|
|
1217
|
-
order.push(statusId);
|
|
1218
|
-
}
|
|
1219
|
-
const block = blocks[index];
|
|
1220
|
-
if (block !== undefined)
|
|
1221
|
-
order.push(block.id);
|
|
1222
|
-
}
|
|
1223
|
-
const placed = new Set(order);
|
|
1224
|
-
for (const statusId of statusIds) {
|
|
1225
|
-
if (!placed.has(statusId))
|
|
1226
|
-
order.push(statusId);
|
|
1227
|
-
}
|
|
1228
|
-
this.#transcriptOrder = order;
|
|
1229
|
-
}
|
|
1230
|
-
#appendAnchoredWorkflowComponent(render, dispose) {
|
|
1231
|
-
// Invariant: transcript-bound workflow output supersedes a pending acknowledgement.
|
|
1232
|
-
this.dismissNotice();
|
|
1233
|
-
this.#workflowTranscriptSequence += 1;
|
|
1234
|
-
const id = `workflow-status-${this.#workflowTranscriptSequence}`;
|
|
1235
|
-
const component = {
|
|
1236
|
-
id,
|
|
1237
|
-
revision: 1,
|
|
1238
|
-
render,
|
|
1239
|
-
handleInput() { },
|
|
1240
|
-
invalidate() { },
|
|
1241
|
-
update() { },
|
|
1242
|
-
setExpanded() { },
|
|
1243
|
-
setOutputPad() { },
|
|
1244
|
-
setHideThinkingBlock() { },
|
|
1245
|
-
setMermaidRenderingMode() { },
|
|
1246
|
-
setImagePresentation() { },
|
|
1247
|
-
...(dispose === undefined ? {} : { dispose }),
|
|
1248
|
-
};
|
|
1249
|
-
this.#transcript.set(id, component);
|
|
1250
|
-
this.#workflowStatusAnchors.set(id, this.#view.transcript.length);
|
|
1251
|
-
this.#transcriptOrder.push(id);
|
|
1252
|
-
this.#documentLayouts.clear();
|
|
604
|
+
this.#document.invalidate("block-presentation");
|
|
1253
605
|
this.invalidate();
|
|
1254
|
-
return id;
|
|
1255
|
-
}
|
|
1256
|
-
#renderWidgets(placement, width) {
|
|
1257
|
-
const rows = [...this.#extensionWidgets.values()]
|
|
1258
|
-
.filter(widget => widget.placement === placement)
|
|
1259
|
-
.flatMap(widget => widget.component.render(width));
|
|
1260
|
-
return placement === "aboveEditor" ? ["", ...rows] : rows;
|
|
1261
606
|
}
|
|
1262
607
|
#renderStatus(width) {
|
|
1263
608
|
return this.#customViewport ? this.#status.renderLive(width) : this.#status.render(width);
|
|
1264
609
|
}
|
|
1265
610
|
#renderFooter(width) {
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
for (const [key, text] of this.#extensionStatuses)
|
|
1272
|
-
statuses.set(key, text);
|
|
1273
|
-
return {
|
|
1274
|
-
...view,
|
|
1275
|
-
status: {
|
|
1276
|
-
...view.status,
|
|
1277
|
-
footer: {
|
|
1278
|
-
...footer,
|
|
1279
|
-
branch: footer?.branch ?? null,
|
|
1280
|
-
sessionName: footer?.sessionName ?? null,
|
|
1281
|
-
availableProviderCount: footer?.availableProviderCount ?? 1,
|
|
1282
|
-
extensionStatuses: [...statuses],
|
|
1283
|
-
},
|
|
1284
|
-
},
|
|
1285
|
-
};
|
|
1286
|
-
}
|
|
1287
|
-
#invalidateExtensions() {
|
|
1288
|
-
this.#extensionHeader?.invalidate();
|
|
1289
|
-
this.#extensionFooter?.invalidate();
|
|
1290
|
-
for (const { component } of this.#extensionWidgets.values())
|
|
1291
|
-
component.invalidate();
|
|
611
|
+
const footer = this.#extensions.footer ?? this.#footer;
|
|
612
|
+
if (!this.#customViewport)
|
|
613
|
+
return footer.render(width);
|
|
614
|
+
width = Math.max(0, width - 2);
|
|
615
|
+
return footer.render(width).map(row => ` ${piShellTruncateToWidth(row, width)}`);
|
|
1292
616
|
}
|
|
1293
617
|
#setToolsExpanded(expanded) {
|
|
1294
618
|
this.#toolsExpanded = expanded;
|
|
1295
619
|
this.header.setExpanded(expanded);
|
|
1296
620
|
this.resources.setExpanded(expanded);
|
|
1297
|
-
for (const component of this.#
|
|
621
|
+
for (const component of this.#document.components())
|
|
1298
622
|
component.setExpanded(expanded);
|
|
1299
623
|
// Invariant: expansion changes what a block draws without changing its revision.
|
|
1300
|
-
this.#
|
|
1301
|
-
this.#documentLayouts.clear();
|
|
624
|
+
this.#document.invalidate("tools-expanded");
|
|
1302
625
|
this.invalidate();
|
|
1303
626
|
}
|
|
1304
627
|
}
|
|
1305
|
-
const COPY_ACKNOWLEDGEMENT_DURATION_MS = 1_000;
|
|
1306
628
|
const SCROLLBAR_CELL_RESET = "\u001b[22;23;24;25;27;28;29;39;54;55m";
|
|
1307
|
-
const KITTY_IMAGE_CONTROL = /\u001b_G[\s\S]*?\u001b\\/g;
|
|
1308
|
-
const ITERM_IMAGE_CONTROL = /\u001b]1337;File=[^\u0007]*(?:\u0007|\u001b\\)/g;
|
|
1309
|
-
/** Keep semantic SGR/OSC styling while excluding non-replayable image payloads. */
|
|
1310
|
-
function sanitizeExitTranscriptRow(row) {
|
|
1311
|
-
return row.replace(KITTY_IMAGE_CONTROL, "").replace(ITERM_IMAGE_CONTROL, "");
|
|
1312
|
-
}
|
|
1313
629
|
const TERMINAL_BACKGROUND = /\u001b\[(?:4[0-9]|10[0-7]|48(?:[;:][0-9;:]*)?)m/g;
|
|
1314
|
-
/** Web URLs use link blue; file targets retain A1's cyan interactive accent. */
|
|
1315
|
-
function nativeTranscriptLinkColor(text, target) {
|
|
1316
|
-
return piTheme().fg(/^file:/iu.test(target) ? "accent" : "mdLink", text);
|
|
1317
|
-
}
|
|
1318
630
|
/** Repaint a source prompt with viewport chrome while preserving text, links, and foreground roles. */
|
|
1319
631
|
function withoutTerminalBackground(text) {
|
|
1320
632
|
return text.replace(TERMINAL_BACKGROUND, "");
|
|
1321
633
|
}
|
|
1322
|
-
/** Preserve the stable metadata-grey foreground for every pinned timestamp state. */
|
|
1323
|
-
function pinnedPromptSourceRow(block, sourceRow, width) {
|
|
1324
|
-
if (typeof block.payload !== "object" || block.payload === null)
|
|
1325
|
-
return sourceRow;
|
|
1326
|
-
const value = block.payload.timestamp;
|
|
1327
|
-
if (typeof value !== "number" || !Number.isFinite(value))
|
|
1328
|
-
return sourceRow;
|
|
1329
|
-
const timestamp = formatSubmittedPromptTime(value);
|
|
1330
|
-
if (timestamp === null || submittedPromptLayout(width, value).timestamp === null)
|
|
1331
|
-
return sourceRow;
|
|
1332
|
-
const rowWidth = displayWidth(sourceRow);
|
|
1333
|
-
const timestampWidth = displayWidth(timestamp);
|
|
1334
|
-
if (rowWidth < timestampWidth)
|
|
1335
|
-
return sourceRow;
|
|
1336
|
-
return overlaySpan(sourceRow, rowWidth - timestampWidth, rowWidth, paintPiSubmittedPromptTimestamp(timestamp));
|
|
1337
|
-
}
|
|
1338
634
|
/**
|
|
1339
635
|
* The pinned-profile screen: a followed transcript scroll above the dock. The runtime mounts one such layout,
|
|
1340
636
|
* so its single `transcript` scroll view is shared by whichever session the parts currently resolve to.
|
|
@@ -1544,20 +840,13 @@ function isPackageExtensionSource(sourceInfo) {
|
|
|
1544
840
|
const source = sourceInfo?.source ?? "";
|
|
1545
841
|
return source.startsWith("npm:") || source.startsWith("git:");
|
|
1546
842
|
}
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
return { version: match[1], command: match[2], changelogUrl: url };
|
|
1558
|
-
}
|
|
1559
|
-
/** "Share URL: <url>" with the URL made clickable; other share wordings pass through unchanged. */
|
|
1560
|
-
function linkShareUrl(message) {
|
|
1561
|
-
const match = /^Share URL: (\S+)$/.exec(message);
|
|
1562
|
-
return match === null ? message : `Share URL: ${piShellHyperlink(match[1])}`;
|
|
1563
|
-
}
|
|
843
|
+
// Performance: stable painter identities let the neutral viewport retain row-level
|
|
844
|
+
// selection variants while each callback still resolves the live Pi theme.
|
|
845
|
+
const VIEWPORT_THEME = {
|
|
846
|
+
track: text => `${SCROLLBAR_CELL_RESET}${piTheme().fg("dim", text)}`,
|
|
847
|
+
thumb: text => `${SCROLLBAR_CELL_RESET}${piTheme().fg("accent", text)}`,
|
|
848
|
+
sticky: (text, hovered) => piTheme().bg(hovered ? "selectedBg" : "toolPendingBg", piTheme().fg("text", withoutTerminalBackground(text))),
|
|
849
|
+
quietSticky: text => `\u001b[2m${text.replace(/\u001b\[(?:0|22)m/g, "$&\u001b[2m")}\u001b[22m`,
|
|
850
|
+
bottomControl: (text, hovered) => piTheme().bg(hovered ? "selectedBg" : "toolPendingBg", piTheme().fg("text", text)),
|
|
851
|
+
selection: paintTextSelection,
|
|
852
|
+
};
|