@timurproko/a1 0.2.6 → 0.2.7-dev.1221
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 +196 -908
- package/dist/app/session-shell/session-shell.d.ts +3 -8
- package/dist/app/session-shell/session-shell.js +324 -835
- package/dist/app/session-shell/session-viewport-controller.d.ts +2 -2
- package/dist/app/session-shell/session-viewport-controller.js +29 -15
- 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 +102 -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 +205 -153
- 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 -1
- package/dist/composition/settings-route-host.js +29 -10
- package/dist/composition/settings-search-input.d.ts +9 -0
- package/dist/composition/settings-search-input.js +58 -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 +242 -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 +76 -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-screen-app.d.ts +2 -0
- package/dist/features/owned-ui/reference-screen-app.js +12 -2
- package/dist/features/owned-ui/settings-app.d.ts +12 -2
- package/dist/features/owned-ui/settings-app.js +116 -81
- 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 +90 -6
- 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 +73 -4
- 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.js +35 -30
- package/dist/ui/components/index.d.ts +2 -2
- package/dist/ui/components/index.js +1 -1
- 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/selection-copy.d.ts +2 -0
- package/dist/ui/components/selection-copy.js +13 -0
- 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.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 +176 -55
- 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 +648 -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,31 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { backgroundSgrSpan, heldNativeHyperlinkStyle, hyperlinkSgrSpan, nativeHyperlinkStyle, overlaySpan, } from "../../ui/components/spans.js";
|
|
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 { backgroundSgrSpan, nativeHyperlinkStyle, } from "../../ui/components/spans.js";
|
|
6
5
|
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
6
|
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";
|
|
7
|
+
import { createPiShellTranscriptComponent, } from "../../integrations/pi/components/shell-presenters-transcript.js";
|
|
8
|
+
import { onPiThemeChange, piTheme } from "../../integrations/pi/components/upstream/theme/theme.js";
|
|
9
|
+
import { piShellTruncateToWidth, } from "../../integrations/pi/components/shell-shared-facade.js";
|
|
10
|
+
import {} from "../../integrations/pi/tui-runtime/input-presentation-coordinator.js";
|
|
16
11
|
import { PromptChipStore } from "./prompt-chips.js";
|
|
17
|
-
import { EditorHyperlinkBudget } from "./editor-hyperlink-budget.js";
|
|
18
12
|
import { SessionViewportController } from "./session-viewport-controller.js";
|
|
19
|
-
import {
|
|
20
|
-
|
|
13
|
+
import { createShellEditor } from "./shell-editor-factory.js";
|
|
14
|
+
import { ExtensionSurface } from "./extension-surface.js";
|
|
15
|
+
import { DockChrome } from "./dock-chrome.js";
|
|
16
|
+
import { WorkflowOutput } from "./workflow-output.js";
|
|
17
|
+
import { InputSurfaceSwitch } from "./input-surface-switch.js";
|
|
18
|
+
import { PinnedLayout } from "./shell-layout.js";
|
|
19
|
+
import { ViewportLayout } from "./viewport-layout.js";
|
|
20
|
+
import { nativeTranscriptLinkColor, TranscriptDocument } from "./transcript-document.js";
|
|
21
|
+
/** Composes session state into the owned transcript, viewport, editor, and dock. */
|
|
21
22
|
export class OwnedUiSessionShellRoot {
|
|
22
23
|
editor;
|
|
23
24
|
header;
|
|
24
25
|
resources;
|
|
25
26
|
#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();
|
|
27
|
+
#document;
|
|
31
28
|
#themeUnsubscribe;
|
|
32
|
-
#transcriptOrder = [];
|
|
33
29
|
#view;
|
|
34
30
|
#status;
|
|
35
31
|
#footer;
|
|
@@ -37,15 +33,10 @@ export class OwnedUiSessionShellRoot {
|
|
|
37
33
|
#extensionRenderers;
|
|
38
34
|
#imageAssets;
|
|
39
35
|
#promptChips;
|
|
40
|
-
#editorHyperlinks = new EditorHyperlinkBudget();
|
|
41
36
|
#pasteDiagnosticId = 0;
|
|
42
|
-
#terminalPasteDiagnosticRequest;
|
|
43
37
|
#customViewport;
|
|
44
38
|
#submittedPromptComposer;
|
|
45
39
|
#viewportController;
|
|
46
|
-
#viewportTheme;
|
|
47
|
-
#onViewportFrame;
|
|
48
|
-
#onInputSurfaceChanged;
|
|
49
40
|
#onPromptSuggestionEligibilityChanged;
|
|
50
41
|
#componentRuntime;
|
|
51
42
|
#toolsExpanded = false;
|
|
@@ -55,33 +46,11 @@ export class OwnedUiSessionShellRoot {
|
|
|
55
46
|
#showImages = true;
|
|
56
47
|
#imageWidthCells = 80;
|
|
57
48
|
#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;
|
|
49
|
+
#workflowOutput;
|
|
50
|
+
#input;
|
|
51
|
+
#layout;
|
|
52
|
+
#extensions = new ExtensionSurface();
|
|
53
|
+
#dockChrome;
|
|
85
54
|
#imageSubmissionsSending = 0;
|
|
86
55
|
constructor(view, cwd, handlers, startup = {}, agentDir, extensionRenderers = {
|
|
87
56
|
getMessageRenderer: () => undefined,
|
|
@@ -94,21 +63,13 @@ export class OwnedUiSessionShellRoot {
|
|
|
94
63
|
...(handlers.pasteDiagnostics === undefined ? {} : { onEvent: handlers.pasteDiagnostics }),
|
|
95
64
|
...(handlers.pastePreparation === undefined ? {} : { preparation: handlers.pastePreparation }),
|
|
96
65
|
...(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;
|
|
66
|
+
this.#submittedPromptComposer = this.#customViewport ? this.#createSubmittedPromptComposer() : undefined;
|
|
104
67
|
this.#cwd = cwd;
|
|
68
|
+
this.#document = this.#createDocument(cwd);
|
|
105
69
|
this.#extensionRenderers = extensionRenderers;
|
|
106
70
|
this.#imageAssets = imageAssets;
|
|
107
71
|
this.#componentRuntime = handlers;
|
|
108
|
-
this.#onViewportFrame = handlers.onViewportFrame;
|
|
109
|
-
this.#onInputSurfaceChanged = handlers.onInputSurfaceChanged;
|
|
110
72
|
this.#onPromptSuggestionEligibilityChanged = handlers.onPromptSuggestionEligibilityChanged;
|
|
111
|
-
this.#dockInputReuseEnabled = handlers.enableDockInputReuse ?? true;
|
|
112
73
|
this.header = createPiShellHeader({
|
|
113
74
|
...startup,
|
|
114
75
|
...(this.#customViewport ? { getKeybindings: () => this.editor.keybindingConfig() } : {}),
|
|
@@ -119,81 +80,16 @@ export class OwnedUiSessionShellRoot {
|
|
|
119
80
|
frame: progressStatusFrame,
|
|
120
81
|
}, handlers);
|
|
121
82
|
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),
|
|
83
|
+
this.#footer = createPiShellFooter(this.#extensions.withStatuses(view), cwd, this.#customViewport ? "a1" : "pi", () => this.#footerLevel, handlers.footerSessionName ?? true);
|
|
84
|
+
this.editor = createShellEditor({
|
|
85
|
+
handlers,
|
|
86
|
+
customViewport: this.#customViewport,
|
|
178
87
|
cwd,
|
|
179
88
|
...(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 }),
|
|
89
|
+
promptChips: this.#promptChips,
|
|
90
|
+
editorPointerSelecting: () => this.#viewportController.editorPointerSelecting,
|
|
91
|
+
nextPasteDiagnosticRequest: () => --this.#pasteDiagnosticId,
|
|
92
|
+
toggleToolsExpanded: () => this.#setToolsExpanded(!this.#toolsExpanded),
|
|
197
93
|
});
|
|
198
94
|
this.#queued = createPiQueuedInputStatus(view.editor.queuedSubmissions, this.#customViewport ? "custom-viewport" : "pinned", this.#customViewport ? () => this.editor.keybindingConfig() : undefined);
|
|
199
95
|
this.#viewportController = new SessionViewportController({
|
|
@@ -205,33 +101,85 @@ export class OwnedUiSessionShellRoot {
|
|
|
205
101
|
...(handlers.pasteDiagnostics === undefined ? {} : { pasteDiagnostics: handlers.pasteDiagnostics }),
|
|
206
102
|
nextPasteDiagnosticRequest: () => --this.#pasteDiagnosticId,
|
|
207
103
|
});
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
104
|
+
this.#dockChrome = new DockChrome({
|
|
105
|
+
invalidateChrome: () => this.#invalidateChrome(),
|
|
106
|
+
requestRender: () => this.#componentRuntime.requestRender(),
|
|
107
|
+
viewport: this.#viewportController,
|
|
108
|
+
});
|
|
109
|
+
this.#workflowOutput = new WorkflowOutput({
|
|
110
|
+
customViewport: this.#customViewport,
|
|
111
|
+
document: this.#document,
|
|
112
|
+
dock: this.#dockChrome,
|
|
113
|
+
transcriptLength: () => this.#view.transcript.length,
|
|
114
|
+
outputPad: () => this.#outputPad,
|
|
115
|
+
hotkeysPresentation: () => this.hotkeysPresentation(),
|
|
116
|
+
componentRuntime: this.#componentRuntime,
|
|
117
|
+
invalidate: () => this.invalidate(),
|
|
118
|
+
});
|
|
218
119
|
this.editor.setThinkingLevel(view.thinkingLevel);
|
|
219
|
-
this.#
|
|
120
|
+
this.#input = new InputSurfaceSwitch(this.editor, {
|
|
121
|
+
dockInputReuse: this.#customViewport && (handlers.enableDockInputReuse ?? true),
|
|
122
|
+
viewport: this.#viewportController,
|
|
123
|
+
...(handlers.onInputSurfaceChanged === undefined ? {} : { onChanged: handlers.onInputSurfaceChanged }),
|
|
124
|
+
});
|
|
125
|
+
this.#layout = this.#createLayout(handlers.onViewportFrame);
|
|
220
126
|
for (const block of view.transcript) {
|
|
221
127
|
if (block.kind === "user")
|
|
222
128
|
this.editor.addToHistory(block.text);
|
|
223
129
|
}
|
|
224
|
-
this.#
|
|
225
|
-
// Invariant:
|
|
226
|
-
//
|
|
130
|
+
this.#document.sync(view.transcript);
|
|
131
|
+
// Invariant: rendered rows expire when
|
|
132
|
+
// their active theme changes.
|
|
227
133
|
this.#themeUnsubscribe = onPiThemeChange(() => {
|
|
228
134
|
this.invalidate();
|
|
229
135
|
this.#componentRuntime.requestRender(true);
|
|
230
136
|
});
|
|
231
137
|
}
|
|
138
|
+
// Invariant: the document reads the shell's chrome lazily, so it may be built before the header and viewport.
|
|
139
|
+
#createDocument(cwd) {
|
|
140
|
+
return new TranscriptDocument({
|
|
141
|
+
customViewport: this.#customViewport,
|
|
142
|
+
cwd,
|
|
143
|
+
mount: block => this.#mountTranscript(block),
|
|
144
|
+
chrome: {
|
|
145
|
+
diagnostics: () => this.#view.diagnostics,
|
|
146
|
+
headerRows: width => this.#extensions.header !== null ? this.#extensions.header.render(width)
|
|
147
|
+
: this.#customViewport ? [] : this.header.render(width),
|
|
148
|
+
resourceRows: width => this.#customViewport ? [] : this.resources.render(width),
|
|
149
|
+
hasExtensionHeader: () => this.#extensions.header !== null,
|
|
150
|
+
thinkingVisible: () => this.#thinkingVisible,
|
|
151
|
+
timeFormat: () => this.#viewportController.config.timeFormat,
|
|
152
|
+
},
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
// Invariant: one layout per root; the render path never consults the layout mode again.
|
|
156
|
+
#createLayout(onFrame) {
|
|
157
|
+
const ports = {
|
|
158
|
+
viewport: this.#viewportController,
|
|
159
|
+
input: this.#input,
|
|
160
|
+
editor: this.editor,
|
|
161
|
+
promptChips: this.#promptChips,
|
|
162
|
+
dockChrome: this.#dockChrome,
|
|
163
|
+
theme: VIEWPORT_THEME,
|
|
164
|
+
...(onFrame === undefined ? {} : { onFrame }),
|
|
165
|
+
rows: () => this.#componentRuntime.getRows(),
|
|
166
|
+
dock: width => this.#renderDockLayout(width),
|
|
167
|
+
document: width => this.#document.layout(width),
|
|
168
|
+
queued: width => this.#renderQueued(width),
|
|
169
|
+
liveStatus: width => this.#renderStatus(width),
|
|
170
|
+
diagnostics: () => this.#view.diagnostics,
|
|
171
|
+
};
|
|
172
|
+
return this.#customViewport ? new ViewportLayout(ports) : new PinnedLayout(ports);
|
|
173
|
+
}
|
|
174
|
+
#createSubmittedPromptComposer() {
|
|
175
|
+
return {
|
|
176
|
+
layout: (width, source) => submittedPromptLayout(width, source, this.#viewportController.config.timeFormat ?? "8:00 PM"),
|
|
177
|
+
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)),
|
|
178
|
+
};
|
|
179
|
+
}
|
|
232
180
|
setEditorPaddingX(padding) {
|
|
233
181
|
this.editor.setPaddingX(padding);
|
|
234
|
-
this.#
|
|
182
|
+
this.#document.invalidate("editor-padding");
|
|
235
183
|
}
|
|
236
184
|
setAutocompleteMaxVisible(maxVisible) {
|
|
237
185
|
this.editor.setAutocompleteMaxVisible(maxVisible);
|
|
@@ -241,7 +189,7 @@ export class OwnedUiSessionShellRoot {
|
|
|
241
189
|
return;
|
|
242
190
|
this.#outputPad = padding;
|
|
243
191
|
this.#status.setOutputPad(padding);
|
|
244
|
-
for (const component of this.#
|
|
192
|
+
for (const component of this.#document.components())
|
|
245
193
|
component.setOutputPad(padding);
|
|
246
194
|
this.#invalidateTranscriptPresentation();
|
|
247
195
|
}
|
|
@@ -249,7 +197,7 @@ export class OwnedUiSessionShellRoot {
|
|
|
249
197
|
if (this.#thinkingVisible === !hidden)
|
|
250
198
|
return;
|
|
251
199
|
this.#thinkingVisible = !hidden;
|
|
252
|
-
for (const component of this.#
|
|
200
|
+
for (const component of this.#document.components())
|
|
253
201
|
component.setHideThinkingBlock(hidden);
|
|
254
202
|
this.#invalidateTranscriptPresentation();
|
|
255
203
|
}
|
|
@@ -257,7 +205,7 @@ export class OwnedUiSessionShellRoot {
|
|
|
257
205
|
if (this.#mermaidRenderingMode === mode)
|
|
258
206
|
return;
|
|
259
207
|
this.#mermaidRenderingMode = mode;
|
|
260
|
-
for (const component of this.#
|
|
208
|
+
for (const component of this.#document.components())
|
|
261
209
|
component.setMermaidRenderingMode(mode);
|
|
262
210
|
this.#invalidateTranscriptPresentation();
|
|
263
211
|
}
|
|
@@ -266,7 +214,7 @@ export class OwnedUiSessionShellRoot {
|
|
|
266
214
|
return;
|
|
267
215
|
this.#showImages = showImages;
|
|
268
216
|
this.#imageWidthCells = imageWidthCells;
|
|
269
|
-
for (const component of this.#
|
|
217
|
+
for (const component of this.#document.components())
|
|
270
218
|
component.setImagePresentation(showImages, imageWidthCells);
|
|
271
219
|
this.#invalidateTranscriptPresentation();
|
|
272
220
|
}
|
|
@@ -286,7 +234,7 @@ export class OwnedUiSessionShellRoot {
|
|
|
286
234
|
return this.#promptChips.prepareSubmission(text);
|
|
287
235
|
}
|
|
288
236
|
reconcilePromptImageLimitNotice(text = this.editor.getText()) {
|
|
289
|
-
if (this.#
|
|
237
|
+
if (this.#dockChrome.notice?.errorCode !== "image-count" || !this.#promptChips.imageLimitState(text).corrected)
|
|
290
238
|
return false;
|
|
291
239
|
this.dismissNotice();
|
|
292
240
|
return true;
|
|
@@ -332,22 +280,22 @@ export class OwnedUiSessionShellRoot {
|
|
|
332
280
|
const becameReady = this.#view.lifecycle !== "ready" && view.lifecycle === "ready";
|
|
333
281
|
if (view.diagnostics.length !== this.#view.diagnostics.length
|
|
334
282
|
|| view.diagnostics.some((diagnostic, index) => diagnostic.sequence !== this.#view.diagnostics[index]?.sequence)) {
|
|
335
|
-
this.#
|
|
283
|
+
this.#document.invalidate("diagnostics");
|
|
336
284
|
}
|
|
337
285
|
const promptAccepted = this.#view.lifecycle !== "busy" && view.lifecycle === "busy";
|
|
338
286
|
this.#view = view;
|
|
339
287
|
// Invariant: the idle new-session confirmation yields to the first truthful live status;
|
|
340
288
|
// ordinary dock notices deliberately remain visible below Working during a run.
|
|
341
|
-
if (promptAccepted && this.#
|
|
342
|
-
this.#
|
|
289
|
+
if (promptAccepted && this.#dockChrome.notice?.kind === "new-session")
|
|
290
|
+
this.#dockChrome.forgetNotice();
|
|
343
291
|
if (promptAccepted && this.#imageSubmissionsSending > 0) {
|
|
344
292
|
this.#imageSubmissionsSending = 0;
|
|
345
293
|
this.#syncWorkingOverride();
|
|
346
294
|
}
|
|
347
295
|
this.#status.update(view);
|
|
348
|
-
this.#footer.update(this.#
|
|
296
|
+
this.#footer.update(this.#extensions.withStatuses(view));
|
|
349
297
|
this.#queued.update(view.editor.queuedSubmissions);
|
|
350
|
-
this.#
|
|
298
|
+
this.#document.sync(view.transcript);
|
|
351
299
|
this.editor.setSubmitEnabled(view.lifecycle !== "stopping" && view.lifecycle !== "stopped" && view.lifecycle !== "failed");
|
|
352
300
|
this.editor.setThinkingLevel(view.thinkingLevel);
|
|
353
301
|
// Performance: semantic updates already invalidate affected transcript blocks. Chrome is a separate authority.
|
|
@@ -361,24 +309,7 @@ export class OwnedUiSessionShellRoot {
|
|
|
361
309
|
* walk of the whole transcript.
|
|
362
310
|
*/
|
|
363
311
|
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();
|
|
312
|
+
this.#document.apply(block);
|
|
382
313
|
}
|
|
383
314
|
#mountTranscript(block) {
|
|
384
315
|
// Invariant: the notice answers the reader's last command, so only their next prompt or
|
|
@@ -386,129 +317,18 @@ export class OwnedUiSessionShellRoot {
|
|
|
386
317
|
if (block.kind === "user" || block.kind === "bash")
|
|
387
318
|
this.dismissNotice();
|
|
388
319
|
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.#
|
|
320
|
+
if (this.#document.component(block.id) !== created)
|
|
390
321
|
return;
|
|
391
|
-
this.#
|
|
392
|
-
this.#
|
|
393
|
-
this.#
|
|
394
|
-
this.#dockInputCandidate = false;
|
|
395
|
-
this.#dockInputRevision = undefined;
|
|
322
|
+
this.#document.blockChanged(block.id);
|
|
323
|
+
this.#layout.invalidate();
|
|
324
|
+
this.#input.clearDockInputCandidate();
|
|
396
325
|
this.#componentRuntime.requestRender();
|
|
397
326
|
} });
|
|
398
327
|
created.setExpanded(this.#toolsExpanded);
|
|
399
328
|
return created;
|
|
400
329
|
}
|
|
401
330
|
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;
|
|
331
|
+
return this.#layout.render(width);
|
|
512
332
|
}
|
|
513
333
|
viewportFrameDescriptor() {
|
|
514
334
|
return this.#viewportController.frame?.descriptor ?? null;
|
|
@@ -516,10 +336,10 @@ export class OwnedUiSessionShellRoot {
|
|
|
516
336
|
/** Read-only, payload-free evidence; observing a pending frame must never render or repair it. */
|
|
517
337
|
viewportPresentationEvidence() {
|
|
518
338
|
const frame = this.#viewportController.frame;
|
|
519
|
-
const snapshot = this.#
|
|
339
|
+
const snapshot = this.#layout.composed();
|
|
520
340
|
return {
|
|
521
|
-
candidate: this.#dockInputCandidate,
|
|
522
|
-
candidateRevision: this.#dockInputCandidate ? this.#dockInputRevision ?? null : null,
|
|
341
|
+
candidate: this.#input.dockInputCandidate,
|
|
342
|
+
candidateRevision: this.#input.dockInputCandidate ? this.#input.dockInputRevision ?? null : null,
|
|
523
343
|
currentRevision: this.#viewportController.presentationRevision,
|
|
524
344
|
composedRevision: snapshot?.viewportRevision ?? null,
|
|
525
345
|
currentPointer: this.#viewportController.pointerPosition ?? null,
|
|
@@ -540,27 +360,16 @@ export class OwnedUiSessionShellRoot {
|
|
|
540
360
|
return this.#viewportController.hasSelection || this.editor.hasSelection();
|
|
541
361
|
}
|
|
542
362
|
setViewportConfig(config) {
|
|
543
|
-
if (this.#viewportController.setConfig(config))
|
|
544
|
-
this.#
|
|
363
|
+
if (this.#viewportController.setConfig(config)) {
|
|
364
|
+
this.#document.invalidate("viewport-config");
|
|
365
|
+
this.#layout.invalidate();
|
|
366
|
+
}
|
|
545
367
|
}
|
|
546
368
|
setFullscreenCopyOnSelect(enabled) {
|
|
547
369
|
this.#viewportController.setCopyOnSelect(enabled);
|
|
548
370
|
}
|
|
549
371
|
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();
|
|
372
|
+
this.#dockChrome.showCopyAcknowledgement(message);
|
|
564
373
|
}
|
|
565
374
|
resumeViewportFollowing() {
|
|
566
375
|
this.#viewportController.resumeFollowing();
|
|
@@ -570,13 +379,7 @@ export class OwnedUiSessionShellRoot {
|
|
|
570
379
|
}
|
|
571
380
|
/** A new session binding owns new mounts even when it reuses semantic invocation ids. */
|
|
572
381
|
resetTranscript() {
|
|
573
|
-
|
|
574
|
-
component.dispose?.();
|
|
575
|
-
this.#transcript.clear();
|
|
576
|
-
this.#blocksById.clear();
|
|
577
|
-
this.#transcriptOrder = [];
|
|
578
|
-
this.#renderedRows.clear();
|
|
579
|
-
this.#documentLayouts.clear();
|
|
382
|
+
this.#document.reset();
|
|
580
383
|
}
|
|
581
384
|
resetViewport() {
|
|
582
385
|
this.#viewportController.reset();
|
|
@@ -594,18 +397,17 @@ export class OwnedUiSessionShellRoot {
|
|
|
594
397
|
handleViewportPreInput(data, allowWheel = true, now = Date.now(), editorActive = this.usesDefaultInputSurface()) {
|
|
595
398
|
return this.#viewportController.handlePreInput(data, allowWheel, now, editorActive);
|
|
596
399
|
}
|
|
597
|
-
#renderDock(width) {
|
|
598
|
-
return this.#renderDockLayout(width).rows;
|
|
599
|
-
}
|
|
600
400
|
#renderDockLayout(width) {
|
|
601
401
|
const queued = this.#customViewport ? [] : this.#renderQueued(width);
|
|
602
402
|
const statusRows = this.#customViewport ? this.#status.renderDock(width) : this.#renderStatus(width);
|
|
603
|
-
const
|
|
604
|
-
const
|
|
605
|
-
const
|
|
606
|
-
|
|
403
|
+
const noticeRows = this.#dockChrome.renderNotice(width, this.#outputPad);
|
|
404
|
+
const transientRows = [...queued, ...statusRows, ...noticeRows];
|
|
405
|
+
const messageStart = queued.length;
|
|
406
|
+
const copyableMessageRows = Array.from({ length: statusRows.length + noticeRows.length }, (_value, index) => ({ row: messageStart + index }));
|
|
407
|
+
const aboveWidgets = this.#extensions.renderWidgets("aboveEditor", width);
|
|
408
|
+
const input = this.#input.surface.render(width);
|
|
607
409
|
const body = this.usesDefaultInputSurface() ? this.editor.bodyGeometry?.() : undefined;
|
|
608
|
-
const belowWidgets = this.#renderWidgets("belowEditor", width);
|
|
410
|
+
const belowWidgets = this.#extensions.renderWidgets("belowEditor", width);
|
|
609
411
|
const footer = this.#renderFooter(width);
|
|
610
412
|
const rowsWithoutTransient = [...aboveWidgets, ...input, ...belowWidgets, ...footer];
|
|
611
413
|
const inputOffset = transientRows.length + aboveWidgets.length;
|
|
@@ -614,68 +416,12 @@ export class OwnedUiSessionShellRoot {
|
|
|
614
416
|
inputOffset,
|
|
615
417
|
editorOffset: inputOffset + (body?.rowOffset ?? 0),
|
|
616
418
|
inputRows: body?.rowCount ?? input.length,
|
|
419
|
+
footerRows: footer.length,
|
|
420
|
+
copyableMessageRows,
|
|
617
421
|
};
|
|
618
422
|
}
|
|
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
423
|
dismissNotice() {
|
|
675
|
-
|
|
676
|
-
return;
|
|
677
|
-
this.#dockNotice = undefined;
|
|
678
|
-
this.#invalidateChrome();
|
|
424
|
+
this.#dockChrome.dismissNotice();
|
|
679
425
|
}
|
|
680
426
|
#renderQueued(width) {
|
|
681
427
|
return this.#view.editor.queuedSubmissions.length === 0 ? [] : this.#queued.render(width);
|
|
@@ -685,284 +431,30 @@ export class OwnedUiSessionShellRoot {
|
|
|
685
431
|
return {
|
|
686
432
|
document: layoutPort(width => this.#renderDocument(width), () => this.invalidate()),
|
|
687
433
|
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.#
|
|
434
|
+
aboveWidgets: layoutPort(width => this.#extensions.renderWidgets("aboveEditor", width), () => this.#extensions.invalidate()),
|
|
689
435
|
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.#
|
|
436
|
+
editor: layoutPort(width => this.#input.surface.render(width), () => this.#input.surface.invalidate(), data => this.#input.surface.handleInput?.(data)),
|
|
437
|
+
belowWidgets: layoutPort(width => this.#extensions.renderWidgets("belowEditor", width), () => this.#extensions.invalidate()),
|
|
438
|
+
footer: layoutPort(width => this.#renderFooter(width), () => (this.#extensions.footer ?? this.#footer).invalidate()),
|
|
693
439
|
};
|
|
694
440
|
}
|
|
695
441
|
#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;
|
|
442
|
+
return this.#document.layout(width).rows;
|
|
836
443
|
}
|
|
837
444
|
transcriptComponent(id) {
|
|
838
|
-
return this.#
|
|
445
|
+
return this.#document.component(id);
|
|
839
446
|
}
|
|
840
447
|
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");
|
|
448
|
+
return this.#document.exitTranscript(width);
|
|
853
449
|
}
|
|
854
450
|
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;
|
|
451
|
+
this.#workflowOutput.appendStatus(message);
|
|
878
452
|
}
|
|
879
453
|
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));
|
|
454
|
+
this.#workflowOutput.appendMessage(message, errorCode);
|
|
896
455
|
}
|
|
897
456
|
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);
|
|
457
|
+
this.#workflowOutput.appendResult(result, errorCode);
|
|
966
458
|
}
|
|
967
459
|
toggleThinkingVisibility() {
|
|
968
460
|
this.setHideThinkingBlock(this.#thinkingVisible);
|
|
@@ -974,39 +466,25 @@ export class OwnedUiSessionShellRoot {
|
|
|
974
466
|
this.#setToolsExpanded(expanded);
|
|
975
467
|
}
|
|
976
468
|
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?.();
|
|
469
|
+
this.#extensions.placeWidget(key, component, placement);
|
|
984
470
|
this.invalidate();
|
|
985
471
|
}
|
|
986
472
|
setExtensionHeader(component) {
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
this.#extensionHeader = component;
|
|
990
|
-
this.#documentLayouts.clear();
|
|
473
|
+
this.#extensions.replaceHeader(component);
|
|
474
|
+
this.#document.invalidate("extension-header");
|
|
991
475
|
this.invalidate();
|
|
992
476
|
}
|
|
993
477
|
setExtensionFooter(component) {
|
|
994
|
-
|
|
995
|
-
this.#extensionFooter?.dispose?.();
|
|
996
|
-
this.#extensionFooter = component;
|
|
478
|
+
this.#extensions.replaceFooter(component);
|
|
997
479
|
this.invalidate();
|
|
998
480
|
}
|
|
999
481
|
setExtensionStatus(key, text) {
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
else
|
|
1003
|
-
this.#extensionStatuses.set(key, text);
|
|
1004
|
-
this.#footer.update(this.#viewWithExtensionStatuses(this.#view));
|
|
482
|
+
this.#extensions.setStatus(key, text);
|
|
483
|
+
this.#footer.update(this.#extensions.withStatuses(this.#view));
|
|
1005
484
|
this.invalidate();
|
|
1006
485
|
}
|
|
1007
|
-
setExtensionWorking(message, visible = this.#
|
|
1008
|
-
this.#
|
|
1009
|
-
this.#extensionWorkingVisible = visible;
|
|
486
|
+
setExtensionWorking(message, visible = this.#extensions.working.visible) {
|
|
487
|
+
this.#extensions.setWorking(message, visible);
|
|
1010
488
|
this.#syncWorkingOverride();
|
|
1011
489
|
this.invalidate();
|
|
1012
490
|
}
|
|
@@ -1028,7 +506,7 @@ export class OwnedUiSessionShellRoot {
|
|
|
1028
506
|
const sending = this.#imageSubmissionsSending > 0;
|
|
1029
507
|
this.#status.setWorkingOverride(sending
|
|
1030
508
|
? "Sending…"
|
|
1031
|
-
: this.#
|
|
509
|
+
: this.#extensions.working.visible ? this.#extensions.working.message : undefined, sending);
|
|
1032
510
|
}
|
|
1033
511
|
hotkeysPresentation() {
|
|
1034
512
|
return {
|
|
@@ -1038,34 +516,17 @@ export class OwnedUiSessionShellRoot {
|
|
|
1038
516
|
};
|
|
1039
517
|
}
|
|
1040
518
|
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();
|
|
519
|
+
this.#dockChrome.forgetNotice();
|
|
520
|
+
this.#dockChrome.clearCopyAcknowledgement(false);
|
|
521
|
+
this.#workflowOutput.reset();
|
|
1052
522
|
this.invalidate();
|
|
1053
523
|
}
|
|
1054
524
|
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;
|
|
525
|
+
this.#extensions.reset();
|
|
1064
526
|
this.#syncWorkingOverride();
|
|
1065
|
-
this.#footer.update(this.#
|
|
527
|
+
this.#footer.update(this.#extensions.withStatuses(this.#view));
|
|
1066
528
|
// Invariant: an extension renderer may have drawn transcript blocks that are now unrendered by it.
|
|
1067
|
-
this.#
|
|
1068
|
-
this.#documentLayouts.clear();
|
|
529
|
+
this.#document.invalidate("extension-renderers");
|
|
1069
530
|
this.invalidate();
|
|
1070
531
|
}
|
|
1071
532
|
addExtensionNotification(message, type) {
|
|
@@ -1078,263 +539,97 @@ export class OwnedUiSessionShellRoot {
|
|
|
1078
539
|
extensionFooterData() {
|
|
1079
540
|
return {
|
|
1080
541
|
getGitBranch: () => this.#view.status.footer?.branch ?? null,
|
|
1081
|
-
getExtensionStatuses: () =>
|
|
542
|
+
getExtensionStatuses: () => this.#extensions.statuses(),
|
|
1082
543
|
getAvailableProviderCount: () => this.#view.status.footer?.availableProviderCount ?? 1,
|
|
1083
544
|
onBranchChange: () => () => { },
|
|
1084
545
|
};
|
|
1085
546
|
}
|
|
1086
547
|
usesDefaultInputSurface() {
|
|
1087
|
-
return this.#
|
|
548
|
+
return this.#input.usesEditor;
|
|
1088
549
|
}
|
|
1089
550
|
inputCoordinationSurface() {
|
|
1090
|
-
return this.#
|
|
551
|
+
return this.#input.coordination;
|
|
1091
552
|
}
|
|
1092
553
|
viewportCompositionEvidence() {
|
|
1093
|
-
return
|
|
554
|
+
return this.#layout.compositions();
|
|
1094
555
|
}
|
|
1095
556
|
transcriptRenderCount() {
|
|
1096
|
-
return this.#
|
|
557
|
+
return this.#document.renderCount;
|
|
1097
558
|
}
|
|
1098
559
|
setFooterLevel(visible) { this.#footerLevel = visible; }
|
|
1099
560
|
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();
|
|
561
|
+
if (this.#input.set(component, disposePrevious, coordination))
|
|
562
|
+
this.invalidate();
|
|
1116
563
|
}
|
|
1117
564
|
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
|
-
}
|
|
565
|
+
this.#input.handleInput(data);
|
|
1128
566
|
}
|
|
1129
567
|
invalidate() {
|
|
1130
|
-
this.#
|
|
1131
|
-
this.#documentLayouts.clear();
|
|
1132
|
-
for (const component of this.#transcript.values())
|
|
1133
|
-
component.invalidate();
|
|
568
|
+
this.#document.invalidate("repaint");
|
|
1134
569
|
this.#invalidateChrome();
|
|
1135
570
|
}
|
|
1136
571
|
#invalidateChrome() {
|
|
1137
572
|
this.header.invalidate();
|
|
1138
573
|
this.resources.invalidate();
|
|
1139
574
|
this.editor.invalidate();
|
|
1140
|
-
if (this.#
|
|
1141
|
-
this.#
|
|
1142
|
-
this.#
|
|
575
|
+
if (!this.#input.usesEditor)
|
|
576
|
+
this.#input.surface.invalidate();
|
|
577
|
+
this.#extensions.invalidate();
|
|
1143
578
|
this.#status.invalidate();
|
|
1144
579
|
this.#footer.invalidate();
|
|
1145
580
|
this.#queued.invalidate();
|
|
1146
581
|
}
|
|
1147
582
|
setFocused(focused) {
|
|
1148
|
-
this.#
|
|
583
|
+
this.#input.surface.setFocused?.(focused);
|
|
1149
584
|
if (focused)
|
|
1150
585
|
this.#onPromptSuggestionEligibilityChanged?.();
|
|
1151
586
|
}
|
|
1152
587
|
dispose() {
|
|
1153
|
-
this.#clearCopyAcknowledgement(false);
|
|
588
|
+
this.#dockChrome.clearCopyAcknowledgement(false);
|
|
1154
589
|
this.clearViewportPointerState();
|
|
1155
590
|
this.#themeUnsubscribe();
|
|
1156
591
|
this.header.dispose?.();
|
|
1157
592
|
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();
|
|
593
|
+
this.#document.dispose();
|
|
594
|
+
this.#layout.invalidate();
|
|
595
|
+
this.#input.dispose();
|
|
596
|
+
this.#extensions.dispose();
|
|
1173
597
|
this.editor.dispose?.();
|
|
1174
598
|
this.#status.dispose?.();
|
|
1175
599
|
this.#footer.dispose?.();
|
|
1176
600
|
this.#queued.dispose?.();
|
|
1177
601
|
}
|
|
1178
602
|
#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();
|
|
603
|
+
this.#document.invalidate("block-presentation");
|
|
1253
604
|
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
605
|
}
|
|
1262
606
|
#renderStatus(width) {
|
|
1263
607
|
return this.#customViewport ? this.#status.renderLive(width) : this.#status.render(width);
|
|
1264
608
|
}
|
|
1265
609
|
#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();
|
|
610
|
+
const footer = this.#extensions.footer ?? this.#footer;
|
|
611
|
+
if (!this.#customViewport)
|
|
612
|
+
return footer.render(width);
|
|
613
|
+
width = Math.max(0, width - 2);
|
|
614
|
+
return footer.render(width).map(row => ` ${piShellTruncateToWidth(row, width)}`);
|
|
1292
615
|
}
|
|
1293
616
|
#setToolsExpanded(expanded) {
|
|
1294
617
|
this.#toolsExpanded = expanded;
|
|
1295
618
|
this.header.setExpanded(expanded);
|
|
1296
619
|
this.resources.setExpanded(expanded);
|
|
1297
|
-
for (const component of this.#
|
|
620
|
+
for (const component of this.#document.components())
|
|
1298
621
|
component.setExpanded(expanded);
|
|
1299
622
|
// Invariant: expansion changes what a block draws without changing its revision.
|
|
1300
|
-
this.#
|
|
1301
|
-
this.#documentLayouts.clear();
|
|
623
|
+
this.#document.invalidate("tools-expanded");
|
|
1302
624
|
this.invalidate();
|
|
1303
625
|
}
|
|
1304
626
|
}
|
|
1305
|
-
const COPY_ACKNOWLEDGEMENT_DURATION_MS = 1_000;
|
|
1306
627
|
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
628
|
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
629
|
/** Repaint a source prompt with viewport chrome while preserving text, links, and foreground roles. */
|
|
1319
630
|
function withoutTerminalBackground(text) {
|
|
1320
631
|
return text.replace(TERMINAL_BACKGROUND, "");
|
|
1321
632
|
}
|
|
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
633
|
/**
|
|
1339
634
|
* The pinned-profile screen: a followed transcript scroll above the dock. The runtime mounts one such layout,
|
|
1340
635
|
* so its single `transcript` scroll view is shared by whichever session the parts currently resolve to.
|
|
@@ -1544,20 +839,13 @@ function isPackageExtensionSource(sourceInfo) {
|
|
|
1544
839
|
const source = sourceInfo?.source ?? "";
|
|
1545
840
|
return source.startsWith("npm:") || source.startsWith("git:");
|
|
1546
841
|
}
|
|
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
|
-
}
|
|
842
|
+
// Performance: stable painter identities let the neutral viewport retain row-level
|
|
843
|
+
// selection variants while each callback still resolves the live Pi theme.
|
|
844
|
+
const VIEWPORT_THEME = {
|
|
845
|
+
track: text => `${SCROLLBAR_CELL_RESET}${piTheme().fg("dim", text)}`,
|
|
846
|
+
thumb: text => `${SCROLLBAR_CELL_RESET}${piTheme().fg("accent", text)}`,
|
|
847
|
+
sticky: (text, hovered) => piTheme().bg(hovered ? "selectedBg" : "toolPendingBg", piTheme().fg("text", withoutTerminalBackground(text))),
|
|
848
|
+
quietSticky: text => `\u001b[2m${text.replace(/\u001b\[(?:0|22)m/g, "$&\u001b[2m")}\u001b[22m`,
|
|
849
|
+
bottomControl: (text, hovered) => piTheme().bg(hovered ? "selectedBg" : "toolPendingBg", piTheme().fg("text", text)),
|
|
850
|
+
selection: (line, from, to) => backgroundSgrSpan(line, from, to, "\u001b[48;2;38;79;120m", "\u001b[49m"),
|
|
851
|
+
};
|