@askdkc/dsh-cli 0.11.3
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/LICENSE +21 -0
- package/README.md +321 -0
- package/bin/dsh-tui.js +1366 -0
- package/cordis.patch.yml +482 -0
- package/cordis.yml +250 -0
- package/dsh-ecosystem-spec/protocols/profile-definitions.d.ts +13 -0
- package/dsh-ecosystem-spec/protocols/profile-definitions.js +59 -0
- package/dsh-ecosystem-spec/protocols/tui-channel-http.d.ts +15 -0
- package/dsh-ecosystem-spec/protocols/tui-channel-http.js +32 -0
- package/dsh-ecosystem-spec/protocols/tui-channel.d.ts +59 -0
- package/dsh-ecosystem-spec/protocols/tui-channel.js +166 -0
- package/dsh-ecosystem-spec/protocols/tui-contributions.d.ts +42 -0
- package/dsh-ecosystem-spec/protocols/tui-contributions.js +143 -0
- package/dsh-ecosystem-spec/registry/README.md +14 -0
- package/dsh-ecosystem-spec/registry/contracts/decision-events-v1alpha1.json +13 -0
- package/dsh-ecosystem-spec/registry/contracts/scene-v1alpha1.json +13 -0
- package/dsh-ecosystem-spec/registry/contracts/settings-section-v1alpha1.json +13 -0
- package/dsh-ecosystem-spec/registry/contracts/tui-channel-v1alpha1.json +30 -0
- package/dsh-ecosystem-spec/registry/contracts/workspace-provider-v1alpha1.json +6 -0
- package/dsh-ecosystem-spec/registry/host-descriptor.tui.example.json +56 -0
- package/dsh-ecosystem-spec/registry/permissions-0.1.json +13 -0
- package/dsh-ecosystem-spec/registry/registry-0.15.json +149 -0
- package/dsh-ecosystem-spec/schemas/conformance-claim.schema.json +15 -0
- package/dsh-ecosystem-spec/schemas/effect-ledger-record.schema.json +16 -0
- package/dsh-ecosystem-spec/schemas/host-descriptor.schema.json +26 -0
- package/lib/types/activityPrefs.d.ts +63 -0
- package/lib/types/activityPrefs.d.ts.map +1 -0
- package/lib/types/activityPrefs.js +126 -0
- package/lib/types/adapter/channel/actions.d.ts +17 -0
- package/lib/types/adapter/channel/actions.d.ts.map +1 -0
- package/lib/types/adapter/channel/actions.js +28 -0
- package/lib/types/adapter/channel/consumer.d.ts +34 -0
- package/lib/types/adapter/channel/consumer.d.ts.map +1 -0
- package/lib/types/adapter/channel/consumer.js +87 -0
- package/lib/types/adapter/channel/features.d.ts +12 -0
- package/lib/types/adapter/channel/features.d.ts.map +1 -0
- package/lib/types/adapter/channel/features.js +41 -0
- package/lib/types/adapter/channel/host-registry.d.ts +40 -0
- package/lib/types/adapter/channel/host-registry.d.ts.map +1 -0
- package/lib/types/adapter/channel/host-registry.js +120 -0
- package/lib/types/adapter/channel/index.d.ts +22 -0
- package/lib/types/adapter/channel/index.d.ts.map +1 -0
- package/lib/types/adapter/channel/index.js +21 -0
- package/lib/types/adapter/channel/internal-token.d.ts +12 -0
- package/lib/types/adapter/channel/internal-token.d.ts.map +1 -0
- package/lib/types/adapter/channel/internal-token.js +11 -0
- package/lib/types/adapter/channel/plugins.d.ts +17 -0
- package/lib/types/adapter/channel/plugins.d.ts.map +1 -0
- package/lib/types/adapter/channel/plugins.js +47 -0
- package/lib/types/adapter/channel/projection.d.ts +15 -0
- package/lib/types/adapter/channel/projection.d.ts.map +1 -0
- package/lib/types/adapter/channel/projection.js +59 -0
- package/lib/types/adapter/channel/provider.d.ts +52 -0
- package/lib/types/adapter/channel/provider.d.ts.map +1 -0
- package/lib/types/adapter/channel/provider.js +231 -0
- package/lib/types/adapter/channel/read-view.d.ts +4 -0
- package/lib/types/adapter/channel/read-view.d.ts.map +1 -0
- package/lib/types/adapter/channel/read-view.js +132 -0
- package/lib/types/adapter/channel/session-projection.d.ts +56 -0
- package/lib/types/adapter/channel/session-projection.d.ts.map +1 -0
- package/lib/types/adapter/channel/session-projection.js +249 -0
- package/lib/types/adapter/channel/state.d.ts +11 -0
- package/lib/types/adapter/channel/state.d.ts.map +1 -0
- package/lib/types/adapter/channel/state.js +57 -0
- package/lib/types/adapter/channel/transcript.d.ts +15 -0
- package/lib/types/adapter/channel/transcript.d.ts.map +1 -0
- package/lib/types/adapter/channel/transcript.js +22 -0
- package/lib/types/adapter/channel/ui-policy.d.ts +109 -0
- package/lib/types/adapter/channel/ui-policy.d.ts.map +1 -0
- package/lib/types/adapter/channel/ui-policy.js +170 -0
- package/lib/types/adapter/channel/ui.d.ts +16 -0
- package/lib/types/adapter/channel/ui.d.ts.map +1 -0
- package/lib/types/adapter/channel/ui.js +173 -0
- package/lib/types/adapter/index.d.ts +20 -0
- package/lib/types/adapter/index.d.ts.map +1 -0
- package/lib/types/adapter/index.js +19 -0
- package/lib/types/adapter/kernel/contract-catalog.d.ts +12 -0
- package/lib/types/adapter/kernel/contract-catalog.d.ts.map +1 -0
- package/lib/types/adapter/kernel/contract-catalog.js +11 -0
- package/lib/types/adapter/kernel/diagnostics.d.ts +18 -0
- package/lib/types/adapter/kernel/diagnostics.d.ts.map +1 -0
- package/lib/types/adapter/kernel/diagnostics.js +16 -0
- package/lib/types/adapter/kernel/driver-types.d.ts +16 -0
- package/lib/types/adapter/kernel/driver-types.d.ts.map +1 -0
- package/lib/types/adapter/kernel/driver-types.js +8 -0
- package/lib/types/adapter/kernel/host-facade.d.ts +65 -0
- package/lib/types/adapter/kernel/host-facade.d.ts.map +1 -0
- package/lib/types/adapter/kernel/host-facade.js +216 -0
- package/lib/types/adapter/kernel/host-probe-access.d.ts +71 -0
- package/lib/types/adapter/kernel/host-probe-access.d.ts.map +1 -0
- package/lib/types/adapter/kernel/host-probe-access.js +121 -0
- package/lib/types/adapter/kernel/index.d.ts +20 -0
- package/lib/types/adapter/kernel/index.d.ts.map +1 -0
- package/lib/types/adapter/kernel/index.js +19 -0
- package/lib/types/adapter/kernel/kernel-runtime.d.ts +138 -0
- package/lib/types/adapter/kernel/kernel-runtime.d.ts.map +1 -0
- package/lib/types/adapter/kernel/kernel-runtime.js +572 -0
- package/lib/types/adapter/kernel/ledger.d.ts +55 -0
- package/lib/types/adapter/kernel/ledger.d.ts.map +1 -0
- package/lib/types/adapter/kernel/ledger.js +33 -0
- package/lib/types/adapter/kernel/legacy-facade.d.ts +22 -0
- package/lib/types/adapter/kernel/legacy-facade.d.ts.map +1 -0
- package/lib/types/adapter/kernel/legacy-facade.js +34 -0
- package/lib/types/adapter/kernel/lifecycle.d.ts +38 -0
- package/lib/types/adapter/kernel/lifecycle.d.ts.map +1 -0
- package/lib/types/adapter/kernel/lifecycle.js +53 -0
- package/lib/types/adapter/kernel/replay-isolation.d.ts +27 -0
- package/lib/types/adapter/kernel/replay-isolation.d.ts.map +1 -0
- package/lib/types/adapter/kernel/replay-isolation.js +45 -0
- package/lib/types/adapter/kernel/replay.d.ts +132 -0
- package/lib/types/adapter/kernel/replay.d.ts.map +1 -0
- package/lib/types/adapter/kernel/replay.js +428 -0
- package/lib/types/adapter/kernel/runtime-context.d.ts +15 -0
- package/lib/types/adapter/kernel/runtime-context.d.ts.map +1 -0
- package/lib/types/adapter/kernel/runtime-context.js +24 -0
- package/lib/types/adapter/kernel/runtime.d.ts +70 -0
- package/lib/types/adapter/kernel/runtime.d.ts.map +1 -0
- package/lib/types/adapter/kernel/runtime.js +327 -0
- package/lib/types/adapter/kernel/slices/channel.d.ts +3 -0
- package/lib/types/adapter/kernel/slices/channel.d.ts.map +1 -0
- package/lib/types/adapter/kernel/slices/channel.js +8 -0
- package/lib/types/adapter/kernel/slices/decisions.d.ts +3 -0
- package/lib/types/adapter/kernel/slices/decisions.d.ts.map +1 -0
- package/lib/types/adapter/kernel/slices/decisions.js +10 -0
- package/lib/types/adapter/kernel/slices/extensions.d.ts +8 -0
- package/lib/types/adapter/kernel/slices/extensions.d.ts.map +1 -0
- package/lib/types/adapter/kernel/slices/extensions.js +43 -0
- package/lib/types/adapter/kernel/slices/index.d.ts +19 -0
- package/lib/types/adapter/kernel/slices/index.d.ts.map +1 -0
- package/lib/types/adapter/kernel/slices/index.js +36 -0
- package/lib/types/adapter/kernel/slices/presentation.d.ts +3 -0
- package/lib/types/adapter/kernel/slices/presentation.d.ts.map +1 -0
- package/lib/types/adapter/kernel/slices/presentation.js +14 -0
- package/lib/types/adapter/kernel/slices/scenes.d.ts +3 -0
- package/lib/types/adapter/kernel/slices/scenes.d.ts.map +1 -0
- package/lib/types/adapter/kernel/slices/scenes.js +14 -0
- package/lib/types/adapter/kernel/slices/settings.d.ts +3 -0
- package/lib/types/adapter/kernel/slices/settings.d.ts.map +1 -0
- package/lib/types/adapter/kernel/slices/settings.js +12 -0
- package/lib/types/adapter/kernel/slices/types.d.ts +20 -0
- package/lib/types/adapter/kernel/slices/types.d.ts.map +1 -0
- package/lib/types/adapter/kernel/slices/types.js +12 -0
- package/lib/types/adapter/kernel/slices/workspace.d.ts +3 -0
- package/lib/types/adapter/kernel/slices/workspace.d.ts.map +1 -0
- package/lib/types/adapter/kernel/slices/workspace.js +16 -0
- package/lib/types/adapter/ports/channel-catalog.d.ts +90 -0
- package/lib/types/adapter/ports/channel-catalog.d.ts.map +1 -0
- package/lib/types/adapter/ports/channel-catalog.js +2 -0
- package/lib/types/adapter/ports/channel-display.d.ts +90 -0
- package/lib/types/adapter/ports/channel-display.d.ts.map +1 -0
- package/lib/types/adapter/ports/channel-display.js +2 -0
- package/lib/types/adapter/ports/channel-session.d.ts +229 -0
- package/lib/types/adapter/ports/channel-session.d.ts.map +1 -0
- package/lib/types/adapter/ports/channel-session.js +2 -0
- package/lib/types/adapter/ports/channel-settings.d.ts +279 -0
- package/lib/types/adapter/ports/channel-settings.d.ts.map +1 -0
- package/lib/types/adapter/ports/channel-settings.js +1 -0
- package/lib/types/adapter/ports/channel-ui.d.ts +593 -0
- package/lib/types/adapter/ports/channel-ui.d.ts.map +1 -0
- package/lib/types/adapter/ports/channel-ui.js +1 -0
- package/lib/types/adapter/ports/channel-view.d.ts +663 -0
- package/lib/types/adapter/ports/channel-view.d.ts.map +1 -0
- package/lib/types/adapter/ports/channel-view.js +2 -0
- package/lib/types/adapter/ports/channel-workspace.d.ts +68 -0
- package/lib/types/adapter/ports/channel-workspace.d.ts.map +1 -0
- package/lib/types/adapter/ports/channel-workspace.js +2 -0
- package/lib/types/adapter/ports/channel.d.ts +148 -0
- package/lib/types/adapter/ports/channel.d.ts.map +1 -0
- package/lib/types/adapter/ports/channel.js +22 -0
- package/lib/types/adapter/ports/decisions.d.ts +18 -0
- package/lib/types/adapter/ports/decisions.d.ts.map +1 -0
- package/lib/types/adapter/ports/decisions.js +9 -0
- package/lib/types/adapter/ports/descriptor.d.ts +25 -0
- package/lib/types/adapter/ports/descriptor.d.ts.map +1 -0
- package/lib/types/adapter/ports/descriptor.js +11 -0
- package/lib/types/adapter/ports/extensions.d.ts +86 -0
- package/lib/types/adapter/ports/extensions.d.ts.map +1 -0
- package/lib/types/adapter/ports/extensions.js +9 -0
- package/lib/types/adapter/ports/index.d.ts +23 -0
- package/lib/types/adapter/ports/index.d.ts.map +1 -0
- package/lib/types/adapter/ports/index.js +14 -0
- package/lib/types/adapter/ports/owner.d.ts +19 -0
- package/lib/types/adapter/ports/owner.d.ts.map +1 -0
- package/lib/types/adapter/ports/owner.js +9 -0
- package/lib/types/adapter/ports/presentation.d.ts +81 -0
- package/lib/types/adapter/ports/presentation.d.ts.map +1 -0
- package/lib/types/adapter/ports/presentation.js +10 -0
- package/lib/types/adapter/ports/scenes.d.ts +24 -0
- package/lib/types/adapter/ports/scenes.d.ts.map +1 -0
- package/lib/types/adapter/ports/scenes.js +8 -0
- package/lib/types/adapter/ports/settings.d.ts +44 -0
- package/lib/types/adapter/ports/settings.d.ts.map +1 -0
- package/lib/types/adapter/ports/settings.js +9 -0
- package/lib/types/adapter/ports/workspace.d.ts +62 -0
- package/lib/types/adapter/ports/workspace.d.ts.map +1 -0
- package/lib/types/adapter/ports/workspace.js +8 -0
- package/lib/types/adapter/spec/index.d.ts +11 -0
- package/lib/types/adapter/spec/index.d.ts.map +1 -0
- package/lib/types/adapter/spec/index.js +10 -0
- package/lib/types/adapter/spec/profile.d.ts +10 -0
- package/lib/types/adapter/spec/profile.d.ts.map +1 -0
- package/lib/types/adapter/spec/profile.js +9 -0
- package/lib/types/adapter/spec/protocol-constants.d.ts +60 -0
- package/lib/types/adapter/spec/protocol-constants.d.ts.map +1 -0
- package/lib/types/adapter/spec/protocol-constants.js +129 -0
- package/lib/types/adapter/spec/tui-contributions.d.ts +18 -0
- package/lib/types/adapter/spec/tui-contributions.d.ts.map +1 -0
- package/lib/types/adapter/spec/tui-contributions.js +19 -0
- package/lib/types/adapter/standard/admission.d.ts +11 -0
- package/lib/types/adapter/standard/admission.d.ts.map +1 -0
- package/lib/types/adapter/standard/admission.js +10 -0
- package/lib/types/adapter/standard/descriptor.d.ts +74 -0
- package/lib/types/adapter/standard/descriptor.d.ts.map +1 -0
- package/lib/types/adapter/standard/descriptor.js +356 -0
- package/lib/types/adapter/standard/grants.d.ts +30 -0
- package/lib/types/adapter/standard/grants.d.ts.map +1 -0
- package/lib/types/adapter/standard/grants.js +219 -0
- package/lib/types/adapter/standard/index.d.ts +25 -0
- package/lib/types/adapter/standard/index.d.ts.map +1 -0
- package/lib/types/adapter/standard/index.js +24 -0
- package/lib/types/adapter/standard/negotiate.d.ts +11 -0
- package/lib/types/adapter/standard/negotiate.d.ts.map +1 -0
- package/lib/types/adapter/standard/negotiate.js +97 -0
- package/lib/types/adapter/standard/permission-scope.d.ts +27 -0
- package/lib/types/adapter/standard/permission-scope.d.ts.map +1 -0
- package/lib/types/adapter/standard/permission-scope.js +68 -0
- package/lib/types/adapter/standard/protocols.d.ts +12 -0
- package/lib/types/adapter/standard/protocols.d.ts.map +1 -0
- package/lib/types/adapter/standard/protocols.js +11 -0
- package/lib/types/adapter/standard/registry.d.ts +23 -0
- package/lib/types/adapter/standard/registry.d.ts.map +1 -0
- package/lib/types/adapter/standard/registry.js +240 -0
- package/lib/types/adapter/standard/schema-check.d.ts +17 -0
- package/lib/types/adapter/standard/schema-check.d.ts.map +1 -0
- package/lib/types/adapter/standard/schema-check.js +167 -0
- package/lib/types/adapter/standard/tui-extension.d.ts +25 -0
- package/lib/types/adapter/standard/tui-extension.d.ts.map +1 -0
- package/lib/types/adapter/standard/tui-extension.js +45 -0
- package/lib/types/adapter/standard/types.d.ts +108 -0
- package/lib/types/adapter/standard/types.d.ts.map +1 -0
- package/lib/types/adapter/standard/types.js +10 -0
- package/lib/types/adapter/standard/validate.d.ts +21 -0
- package/lib/types/adapter/standard/validate.d.ts.map +1 -0
- package/lib/types/adapter/standard/validate.js +139 -0
- package/lib/types/adapter/upstream/channel-driver.d.ts +22 -0
- package/lib/types/adapter/upstream/channel-driver.d.ts.map +1 -0
- package/lib/types/adapter/upstream/channel-driver.js +424 -0
- package/lib/types/adapter/upstream/decisions-driver.d.ts +15 -0
- package/lib/types/adapter/upstream/decisions-driver.d.ts.map +1 -0
- package/lib/types/adapter/upstream/decisions-driver.js +141 -0
- package/lib/types/adapter/upstream/detection.d.ts +26 -0
- package/lib/types/adapter/upstream/detection.d.ts.map +1 -0
- package/lib/types/adapter/upstream/detection.js +9 -0
- package/lib/types/adapter/upstream/driver.d.ts +50 -0
- package/lib/types/adapter/upstream/driver.d.ts.map +1 -0
- package/lib/types/adapter/upstream/driver.js +8 -0
- package/lib/types/adapter/upstream/extensions-driver.d.ts +24 -0
- package/lib/types/adapter/upstream/extensions-driver.d.ts.map +1 -0
- package/lib/types/adapter/upstream/extensions-driver.js +697 -0
- package/lib/types/adapter/upstream/host-descriptor-driver.d.ts +45 -0
- package/lib/types/adapter/upstream/host-descriptor-driver.d.ts.map +1 -0
- package/lib/types/adapter/upstream/host-descriptor-driver.js +606 -0
- package/lib/types/adapter/upstream/index.d.ts +18 -0
- package/lib/types/adapter/upstream/index.d.ts.map +1 -0
- package/lib/types/adapter/upstream/index.js +15 -0
- package/lib/types/adapter/upstream/presentation-driver.d.ts +17 -0
- package/lib/types/adapter/upstream/presentation-driver.d.ts.map +1 -0
- package/lib/types/adapter/upstream/presentation-driver.js +129 -0
- package/lib/types/adapter/upstream/scenes-driver.d.ts +18 -0
- package/lib/types/adapter/upstream/scenes-driver.d.ts.map +1 -0
- package/lib/types/adapter/upstream/scenes-driver.js +170 -0
- package/lib/types/adapter/upstream/settings-driver.d.ts +20 -0
- package/lib/types/adapter/upstream/settings-driver.d.ts.map +1 -0
- package/lib/types/adapter/upstream/settings-driver.js +173 -0
- package/lib/types/adapter/upstream/workspace-driver.d.ts +19 -0
- package/lib/types/adapter/upstream/workspace-driver.d.ts.map +1 -0
- package/lib/types/adapter/upstream/workspace-driver.js +198 -0
- package/lib/types/api.d.ts +7 -0
- package/lib/types/api.d.ts.map +1 -0
- package/lib/types/api.js +1 -0
- package/lib/types/bootstrap/state.d.ts +11 -0
- package/lib/types/bootstrap/state.d.ts.map +1 -0
- package/lib/types/bootstrap/state.js +10 -0
- package/lib/types/command-trees.d.ts +4 -0
- package/lib/types/command-trees.d.ts.map +1 -0
- package/lib/types/command-trees.js +4 -0
- package/lib/types/commands.d.ts +87 -0
- package/lib/types/commands.d.ts.map +1 -0
- package/lib/types/commands.js +218 -0
- package/lib/types/components/ActivityLine.d.ts +34 -0
- package/lib/types/components/ActivityLine.d.ts.map +1 -0
- package/lib/types/components/ActivityLine.js +39 -0
- package/lib/types/components/ActivityPicker.d.ts +16 -0
- package/lib/types/components/ActivityPicker.d.ts.map +1 -0
- package/lib/types/components/ActivityPicker.js +23 -0
- package/lib/types/components/AutoRecapRow.d.ts +23 -0
- package/lib/types/components/AutoRecapRow.d.ts.map +1 -0
- package/lib/types/components/AutoRecapRow.js +23 -0
- package/lib/types/components/BalanceReportRow.d.ts +24 -0
- package/lib/types/components/BalanceReportRow.d.ts.map +1 -0
- package/lib/types/components/BalanceReportRow.js +87 -0
- package/lib/types/components/BtwPanel.d.ts +16 -0
- package/lib/types/components/BtwPanel.d.ts.map +1 -0
- package/lib/types/components/BtwPanel.js +47 -0
- package/lib/types/components/Chat/JobCard.d.ts +23 -0
- package/lib/types/components/Chat/JobCard.d.ts.map +1 -0
- package/lib/types/components/Chat/JobCard.js +93 -0
- package/lib/types/components/Chat/SubagentMessage.d.ts +20 -0
- package/lib/types/components/Chat/SubagentMessage.d.ts.map +1 -0
- package/lib/types/components/Chat/SubagentMessage.js +83 -0
- package/lib/types/components/ColorPicker.d.ts +17 -0
- package/lib/types/components/ColorPicker.d.ts.map +1 -0
- package/lib/types/components/ColorPicker.js +20 -0
- package/lib/types/components/CommandSuggestions.d.ts +34 -0
- package/lib/types/components/CommandSuggestions.d.ts.map +1 -0
- package/lib/types/components/CommandSuggestions.js +61 -0
- package/lib/types/components/ContextBarView.d.ts +41 -0
- package/lib/types/components/ContextBarView.d.ts.map +1 -0
- package/lib/types/components/ContextBarView.js +50 -0
- package/lib/types/components/EffortChargeGlyph.d.ts +26 -0
- package/lib/types/components/EffortChargeGlyph.d.ts.map +1 -0
- package/lib/types/components/EffortChargeGlyph.js +70 -0
- package/lib/types/components/EffortInputBorder.d.ts +47 -0
- package/lib/types/components/EffortInputBorder.d.ts.map +1 -0
- package/lib/types/components/EffortInputBorder.js +90 -0
- package/lib/types/components/EffortSlider.d.ts +18 -0
- package/lib/types/components/EffortSlider.d.ts.map +1 -0
- package/lib/types/components/EffortSlider.js +19 -0
- package/lib/types/components/EffortTierBadge.d.ts +26 -0
- package/lib/types/components/EffortTierBadge.d.ts.map +1 -0
- package/lib/types/components/EffortTierBadge.js +97 -0
- package/lib/types/components/ExtensionDialog.d.ts +40 -0
- package/lib/types/components/ExtensionDialog.d.ts.map +1 -0
- package/lib/types/components/ExtensionDialog.js +253 -0
- package/lib/types/components/FileActionsPanel.d.ts +22 -0
- package/lib/types/components/FileActionsPanel.d.ts.map +1 -0
- package/lib/types/components/FileActionsPanel.js +28 -0
- package/lib/types/components/FileSuggestions.d.ts +18 -0
- package/lib/types/components/FileSuggestions.d.ts.map +1 -0
- package/lib/types/components/FileSuggestions.js +78 -0
- package/lib/types/components/GoalTodoPanel.d.ts +38 -0
- package/lib/types/components/GoalTodoPanel.d.ts.map +1 -0
- package/lib/types/components/GoalTodoPanel.js +131 -0
- package/lib/types/components/HelpMenu.d.ts +29 -0
- package/lib/types/components/HelpMenu.d.ts.map +1 -0
- package/lib/types/components/HelpMenu.js +39 -0
- package/lib/types/components/HistorySearchDialog.d.ts +18 -0
- package/lib/types/components/HistorySearchDialog.d.ts.map +1 -0
- package/lib/types/components/HistorySearchDialog.js +47 -0
- package/lib/types/components/ImagePreviewOverlay.d.ts +51 -0
- package/lib/types/components/ImagePreviewOverlay.d.ts.map +1 -0
- package/lib/types/components/ImagePreviewOverlay.js +269 -0
- package/lib/types/components/JobsPanel.d.ts +17 -0
- package/lib/types/components/JobsPanel.d.ts.map +1 -0
- package/lib/types/components/JobsPanel.js +111 -0
- package/lib/types/components/LangPicker.d.ts +14 -0
- package/lib/types/components/LangPicker.d.ts.map +1 -0
- package/lib/types/components/LangPicker.js +17 -0
- package/lib/types/components/LoadedContextPanel.d.ts +20 -0
- package/lib/types/components/LoadedContextPanel.d.ts.map +1 -0
- package/lib/types/components/LoadedContextPanel.js +32 -0
- package/lib/types/components/LogoV2.d.ts +48 -0
- package/lib/types/components/LogoV2.d.ts.map +1 -0
- package/lib/types/components/LogoV2.js +239 -0
- package/lib/types/components/Markdown.d.ts +46 -0
- package/lib/types/components/Markdown.d.ts.map +1 -0
- package/lib/types/components/Markdown.js +178 -0
- package/lib/types/components/MarkdownTable.d.ts +13 -0
- package/lib/types/components/MarkdownTable.d.ts.map +1 -0
- package/lib/types/components/MarkdownTable.js +155 -0
- package/lib/types/components/MermaidDiagram.d.ts +13 -0
- package/lib/types/components/MermaidDiagram.d.ts.map +1 -0
- package/lib/types/components/MermaidDiagram.js +61 -0
- package/lib/types/components/MessageList.d.ts +144 -0
- package/lib/types/components/MessageList.d.ts.map +1 -0
- package/lib/types/components/MessageList.js +1170 -0
- package/lib/types/components/MigratePicker.d.ts +30 -0
- package/lib/types/components/MigratePicker.d.ts.map +1 -0
- package/lib/types/components/MigratePicker.js +38 -0
- package/lib/types/components/ModelPicker.d.ts +42 -0
- package/lib/types/components/ModelPicker.d.ts.map +1 -0
- package/lib/types/components/ModelPicker.js +51 -0
- package/lib/types/components/OverlayAbove.d.ts +48 -0
- package/lib/types/components/OverlayAbove.d.ts.map +1 -0
- package/lib/types/components/OverlayAbove.js +98 -0
- package/lib/types/components/PageMargin.d.ts +58 -0
- package/lib/types/components/PageMargin.d.ts.map +1 -0
- package/lib/types/components/PageMargin.js +38 -0
- package/lib/types/components/PermissionsPicker.d.ts +20 -0
- package/lib/types/components/PermissionsPicker.d.ts.map +1 -0
- package/lib/types/components/PermissionsPicker.js +33 -0
- package/lib/types/components/PlanPicker.d.ts +15 -0
- package/lib/types/components/PlanPicker.d.ts.map +1 -0
- package/lib/types/components/PlanPicker.js +18 -0
- package/lib/types/components/PluginSceneBoundary.d.ts +31 -0
- package/lib/types/components/PluginSceneBoundary.d.ts.map +1 -0
- package/lib/types/components/PluginSceneBoundary.js +29 -0
- package/lib/types/components/PluginStatusViewBoundary.d.ts +26 -0
- package/lib/types/components/PluginStatusViewBoundary.d.ts.map +1 -0
- package/lib/types/components/PluginStatusViewBoundary.js +22 -0
- package/lib/types/components/PresetPicker.d.ts +20 -0
- package/lib/types/components/PresetPicker.d.ts.map +1 -0
- package/lib/types/components/PresetPicker.js +24 -0
- package/lib/types/components/PromptEditor.d.ts +48 -0
- package/lib/types/components/PromptEditor.d.ts.map +1 -0
- package/lib/types/components/PromptEditor.js +75 -0
- package/lib/types/components/PromptInput.d.ts +163 -0
- package/lib/types/components/PromptInput.d.ts.map +1 -0
- package/lib/types/components/PromptInput.js +3503 -0
- package/lib/types/components/RecapPanel.d.ts +25 -0
- package/lib/types/components/RecapPanel.d.ts.map +1 -0
- package/lib/types/components/RecapPanel.js +55 -0
- package/lib/types/components/RewindPicker.d.ts +37 -0
- package/lib/types/components/RewindPicker.d.ts.map +1 -0
- package/lib/types/components/RewindPicker.js +66 -0
- package/lib/types/components/ScrollbarGutter.d.ts +43 -0
- package/lib/types/components/ScrollbarGutter.d.ts.map +1 -0
- package/lib/types/components/ScrollbarGutter.js +197 -0
- package/lib/types/components/SearchBox.d.ts +24 -0
- package/lib/types/components/SearchBox.d.ts.map +1 -0
- package/lib/types/components/SearchBox.js +132 -0
- package/lib/types/components/Select.d.ts +29 -0
- package/lib/types/components/Select.d.ts.map +1 -0
- package/lib/types/components/Select.js +21 -0
- package/lib/types/components/SkillsPicker.d.ts +21 -0
- package/lib/types/components/SkillsPicker.d.ts.map +1 -0
- package/lib/types/components/SkillsPicker.js +52 -0
- package/lib/types/components/Spinner/GlimmerMessage.d.ts +16 -0
- package/lib/types/components/Spinner/GlimmerMessage.d.ts.map +1 -0
- package/lib/types/components/Spinner/GlimmerMessage.js +57 -0
- package/lib/types/components/Spinner/SpinnerAnimationRow.d.ts +33 -0
- package/lib/types/components/Spinner/SpinnerAnimationRow.d.ts.map +1 -0
- package/lib/types/components/Spinner/SpinnerAnimationRow.js +168 -0
- package/lib/types/components/Spinner/SpinnerGlyph.d.ts +16 -0
- package/lib/types/components/Spinner/SpinnerGlyph.d.ts.map +1 -0
- package/lib/types/components/Spinner/SpinnerGlyph.js +36 -0
- package/lib/types/components/Spinner/spinnerMode.d.ts +2 -0
- package/lib/types/components/Spinner/spinnerMode.d.ts.map +1 -0
- package/lib/types/components/Spinner/spinnerMode.js +1 -0
- package/lib/types/components/Spinner/spinnerUtils.d.ts +36 -0
- package/lib/types/components/Spinner/spinnerUtils.d.ts.map +1 -0
- package/lib/types/components/Spinner/spinnerUtils.js +94 -0
- package/lib/types/components/Spinner/useStalledAnimation.d.ts +16 -0
- package/lib/types/components/Spinner/useStalledAnimation.d.ts.map +1 -0
- package/lib/types/components/Spinner/useStalledAnimation.js +52 -0
- package/lib/types/components/SplitDiffView.d.ts +25 -0
- package/lib/types/components/SplitDiffView.d.ts.map +1 -0
- package/lib/types/components/SplitDiffView.js +279 -0
- package/lib/types/components/StreamingMarkdown.d.ts +6 -0
- package/lib/types/components/StreamingMarkdown.d.ts.map +1 -0
- package/lib/types/components/StreamingMarkdown.js +279 -0
- package/lib/types/components/SubagentCard.d.ts +10 -0
- package/lib/types/components/SubagentCard.d.ts.map +1 -0
- package/lib/types/components/SubagentCard.js +39 -0
- package/lib/types/components/SubagentDashboard.d.ts +17 -0
- package/lib/types/components/SubagentDashboard.d.ts.map +1 -0
- package/lib/types/components/SubagentDashboard.js +58 -0
- package/lib/types/components/SubagentDetailScene.d.ts +16 -0
- package/lib/types/components/SubagentDetailScene.d.ts.map +1 -0
- package/lib/types/components/SubagentDetailScene.js +147 -0
- package/lib/types/components/SuggestionCard.d.ts +65 -0
- package/lib/types/components/SuggestionCard.d.ts.map +1 -0
- package/lib/types/components/SuggestionCard.js +77 -0
- package/lib/types/components/SyntaxText.d.ts +15 -0
- package/lib/types/components/SyntaxText.d.ts.map +1 -0
- package/lib/types/components/SyntaxText.js +34 -0
- package/lib/types/components/ThemePicker.d.ts +27 -0
- package/lib/types/components/ThemePicker.d.ts.map +1 -0
- package/lib/types/components/ThemePicker.js +55 -0
- package/lib/types/components/ThinkingToggle.d.ts +13 -0
- package/lib/types/components/ThinkingToggle.d.ts.map +1 -0
- package/lib/types/components/ThinkingToggle.js +25 -0
- package/lib/types/components/TimelineRail.d.ts +53 -0
- package/lib/types/components/TimelineRail.d.ts.map +1 -0
- package/lib/types/components/TimelineRail.js +197 -0
- package/lib/types/components/TipsPanel.d.ts +14 -0
- package/lib/types/components/TipsPanel.d.ts.map +1 -0
- package/lib/types/components/TipsPanel.js +37 -0
- package/lib/types/components/ToolUseLoader.d.ts +18 -0
- package/lib/types/components/ToolUseLoader.d.ts.map +1 -0
- package/lib/types/components/ToolUseLoader.js +51 -0
- package/lib/types/components/Tooltip.d.ts +67 -0
- package/lib/types/components/Tooltip.d.ts.map +1 -0
- package/lib/types/components/Tooltip.js +266 -0
- package/lib/types/components/TurnInterruptedRow.d.ts +6 -0
- package/lib/types/components/TurnInterruptedRow.d.ts.map +1 -0
- package/lib/types/components/TurnInterruptedRow.js +9 -0
- package/lib/types/components/Whale.d.ts +42 -0
- package/lib/types/components/Whale.d.ts.map +1 -0
- package/lib/types/components/Whale.js +108 -0
- package/lib/types/components/WorkingSpinner.d.ts +26 -0
- package/lib/types/components/WorkingSpinner.d.ts.map +1 -0
- package/lib/types/components/WorkingSpinner.js +67 -0
- package/lib/types/components/WorkspaceFlowPicker.d.ts +20 -0
- package/lib/types/components/WorkspaceFlowPicker.d.ts.map +1 -0
- package/lib/types/components/WorkspaceFlowPicker.js +21 -0
- package/lib/types/components/WorkspaceMenuPicker.d.ts +19 -0
- package/lib/types/components/WorkspaceMenuPicker.d.ts.map +1 -0
- package/lib/types/components/WorkspaceMenuPicker.js +19 -0
- package/lib/types/components/WorkspacePicker.d.ts +12 -0
- package/lib/types/components/WorkspacePicker.d.ts.map +1 -0
- package/lib/types/components/WorkspacePicker.js +13 -0
- package/lib/types/components/activityFrames.d.ts +23 -0
- package/lib/types/components/activityFrames.d.ts.map +1 -0
- package/lib/types/components/activityFrames.js +31 -0
- package/lib/types/components/approvals/ApprovalPanel.d.ts +24 -0
- package/lib/types/components/approvals/ApprovalPanel.d.ts.map +1 -0
- package/lib/types/components/approvals/ApprovalPanel.js +53 -0
- package/lib/types/components/bigfont.d.ts +28 -0
- package/lib/types/components/bigfont.d.ts.map +1 -0
- package/lib/types/components/bigfont.js +91 -0
- package/lib/types/components/design-system/Byline.d.ts +14 -0
- package/lib/types/components/design-system/Byline.d.ts.map +1 -0
- package/lib/types/components/design-system/Byline.js +17 -0
- package/lib/types/components/design-system/Divider.d.ts +61 -0
- package/lib/types/components/design-system/Divider.d.ts.map +1 -0
- package/lib/types/components/design-system/Divider.js +115 -0
- package/lib/types/components/design-system/HintLine.d.ts +11 -0
- package/lib/types/components/design-system/HintLine.d.ts.map +1 -0
- package/lib/types/components/design-system/HintLine.js +14 -0
- package/lib/types/components/design-system/ListItem.d.ts +42 -0
- package/lib/types/components/design-system/ListItem.d.ts.map +1 -0
- package/lib/types/components/design-system/ListItem.js +84 -0
- package/lib/types/components/design-system/LoadingState.d.ts +19 -0
- package/lib/types/components/design-system/LoadingState.d.ts.map +1 -0
- package/lib/types/components/design-system/LoadingState.js +22 -0
- package/lib/types/components/design-system/Pane.d.ts +17 -0
- package/lib/types/components/design-system/Pane.d.ts.map +1 -0
- package/lib/types/components/design-system/Pane.js +15 -0
- package/lib/types/components/design-system/ProgressBar.d.ts +21 -0
- package/lib/types/components/design-system/ProgressBar.d.ts.map +1 -0
- package/lib/types/components/design-system/ProgressBar.js +26 -0
- package/lib/types/components/design-system/StatusIcon.d.ts +15 -0
- package/lib/types/components/design-system/StatusIcon.d.ts.map +1 -0
- package/lib/types/components/design-system/StatusIcon.js +20 -0
- package/lib/types/components/design-system/ThemeProvider.d.ts +21 -0
- package/lib/types/components/design-system/ThemeProvider.d.ts.map +1 -0
- package/lib/types/components/design-system/ThemeProvider.js +256 -0
- package/lib/types/components/design-system/ThemedBox.d.ts +60 -0
- package/lib/types/components/design-system/ThemedBox.d.ts.map +1 -0
- package/lib/types/components/design-system/ThemedBox.js +29 -0
- package/lib/types/components/design-system/ThemedText.d.ts +61 -0
- package/lib/types/components/design-system/ThemedText.d.ts.map +1 -0
- package/lib/types/components/design-system/ThemedText.js +42 -0
- package/lib/types/components/listWindow.d.ts +26 -0
- package/lib/types/components/listWindow.d.ts.map +1 -0
- package/lib/types/components/listWindow.js +47 -0
- package/lib/types/components/messages/AssistantTextMessage.d.ts +20 -0
- package/lib/types/components/messages/AssistantTextMessage.d.ts.map +1 -0
- package/lib/types/components/messages/AssistantTextMessage.js +18 -0
- package/lib/types/components/messages/AssistantThinkingMessage.d.ts +40 -0
- package/lib/types/components/messages/AssistantThinkingMessage.d.ts.map +1 -0
- package/lib/types/components/messages/AssistantThinkingMessage.js +84 -0
- package/lib/types/components/messages/AssistantToolUseMessage.d.ts +72 -0
- package/lib/types/components/messages/AssistantToolUseMessage.d.ts.map +1 -0
- package/lib/types/components/messages/AssistantToolUseMessage.js +517 -0
- package/lib/types/components/messages/MessageMetadata.d.ts +14 -0
- package/lib/types/components/messages/MessageMetadata.d.ts.map +1 -0
- package/lib/types/components/messages/MessageMetadata.js +19 -0
- package/lib/types/components/messages/TranscriptImages.d.ts +21 -0
- package/lib/types/components/messages/TranscriptImages.d.ts.map +1 -0
- package/lib/types/components/messages/TranscriptImages.js +83 -0
- package/lib/types/components/messages/UserPromptMessage.d.ts +18 -0
- package/lib/types/components/messages/UserPromptMessage.d.ts.map +1 -0
- package/lib/types/components/messages/UserPromptMessage.js +28 -0
- package/lib/types/components/messages/imageInspection.d.ts +33 -0
- package/lib/types/components/messages/imageInspection.d.ts.map +1 -0
- package/lib/types/components/messages/imageInspection.js +107 -0
- package/lib/types/components/messages/originalImage.d.ts +5 -0
- package/lib/types/components/messages/originalImage.d.ts.map +1 -0
- package/lib/types/components/messages/originalImage.js +56 -0
- package/lib/types/components/messages/transcriptImageDecode.d.ts +9 -0
- package/lib/types/components/messages/transcriptImageDecode.d.ts.map +1 -0
- package/lib/types/components/messages/transcriptImageDecode.js +144 -0
- package/lib/types/components/messages/useImageInspection.d.ts +10 -0
- package/lib/types/components/messages/useImageInspection.d.ts.map +1 -0
- package/lib/types/components/messages/useImageInspection.js +91 -0
- package/lib/types/components/overlayBudget.d.ts +39 -0
- package/lib/types/components/overlayBudget.d.ts.map +1 -0
- package/lib/types/components/overlayBudget.js +43 -0
- package/lib/types/components/promptDraftCache.d.ts +102 -0
- package/lib/types/components/promptDraftCache.d.ts.map +1 -0
- package/lib/types/components/promptDraftCache.js +76 -0
- package/lib/types/components/questions/AskUserQuestionPanel.d.ts +94 -0
- package/lib/types/components/questions/AskUserQuestionPanel.d.ts.map +1 -0
- package/lib/types/components/questions/AskUserQuestionPanel.js +527 -0
- package/lib/types/components/questions/PlanReviewPanel.d.ts +55 -0
- package/lib/types/components/questions/PlanReviewPanel.d.ts.map +1 -0
- package/lib/types/components/questions/PlanReviewPanel.js +351 -0
- package/lib/types/components/questions/QuestionMinimizedBar.d.ts +12 -0
- package/lib/types/components/questions/QuestionMinimizedBar.d.ts.map +1 -0
- package/lib/types/components/questions/QuestionMinimizedBar.js +43 -0
- package/lib/types/components/sessions/SessionListRow.d.ts +58 -0
- package/lib/types/components/sessions/SessionListRow.d.ts.map +1 -0
- package/lib/types/components/sessions/SessionListRow.js +108 -0
- package/lib/types/components/sessions/SessionPreview.d.ts +25 -0
- package/lib/types/components/sessions/SessionPreview.d.ts.map +1 -0
- package/lib/types/components/sessions/SessionPreview.js +62 -0
- package/lib/types/components/sessions/WorkspaceListRow.d.ts +17 -0
- package/lib/types/components/sessions/WorkspaceListRow.d.ts.map +1 -0
- package/lib/types/components/sessions/WorkspaceListRow.js +27 -0
- package/lib/types/components/shimmer.d.ts +35 -0
- package/lib/types/components/shimmer.d.ts.map +1 -0
- package/lib/types/components/shimmer.js +43 -0
- package/lib/types/components/smoothReveal.d.ts +57 -0
- package/lib/types/components/smoothReveal.d.ts.map +1 -0
- package/lib/types/components/smoothReveal.js +305 -0
- package/lib/types/components/trajectory/HotspotView.d.ts +20 -0
- package/lib/types/components/trajectory/HotspotView.d.ts.map +1 -0
- package/lib/types/components/trajectory/HotspotView.js +89 -0
- package/lib/types/components/trajectory/Inspector.d.ts +31 -0
- package/lib/types/components/trajectory/Inspector.d.ts.map +1 -0
- package/lib/types/components/trajectory/Inspector.js +67 -0
- package/lib/types/components/trajectory/Ledger.d.ts +28 -0
- package/lib/types/components/trajectory/Ledger.d.ts.map +1 -0
- package/lib/types/components/trajectory/Ledger.js +130 -0
- package/lib/types/components/trajectory/MiniWake.d.ts +24 -0
- package/lib/types/components/trajectory/MiniWake.d.ts.map +1 -0
- package/lib/types/components/trajectory/MiniWake.js +99 -0
- package/lib/types/components/trajectory/WaveBand.d.ts +42 -0
- package/lib/types/components/trajectory/WaveBand.d.ts.map +1 -0
- package/lib/types/components/trajectory/WaveBand.js +150 -0
- package/lib/types/components/whaleFrames.d.ts +81 -0
- package/lib/types/components/whaleFrames.d.ts.map +1 -0
- package/lib/types/components/whaleFrames.js +768 -0
- package/lib/types/components/whaleIdle.d.ts +66 -0
- package/lib/types/components/whaleIdle.d.ts.map +1 -0
- package/lib/types/components/whaleIdle.js +174 -0
- package/lib/types/components/whaleLayers.d.ts +28 -0
- package/lib/types/components/whaleLayers.d.ts.map +1 -0
- package/lib/types/components/whaleLayers.js +90 -0
- package/lib/types/components/workspaces/HomeWorkspaceRow.d.ts +49 -0
- package/lib/types/components/workspaces/HomeWorkspaceRow.d.ts.map +1 -0
- package/lib/types/components/workspaces/HomeWorkspaceRow.js +50 -0
- package/lib/types/customTheme.d.ts +108 -0
- package/lib/types/customTheme.d.ts.map +1 -0
- package/lib/types/customTheme.js +281 -0
- package/lib/types/deepseekBalance.d.ts +19 -0
- package/lib/types/deepseekBalance.d.ts.map +1 -0
- package/lib/types/deepseekBalance.js +72 -0
- package/lib/types/deepseekPricing.d.ts +83 -0
- package/lib/types/deepseekPricing.d.ts.map +1 -0
- package/lib/types/deepseekPricing.js +140 -0
- package/lib/types/dsh-adapter/activity-store.d.ts +221 -0
- package/lib/types/dsh-adapter/activity-store.d.ts.map +1 -0
- package/lib/types/dsh-adapter/activity-store.js +359 -0
- package/lib/types/dsh-adapter/agent-view.d.ts +66 -0
- package/lib/types/dsh-adapter/agent-view.d.ts.map +1 -0
- package/lib/types/dsh-adapter/agent-view.js +153 -0
- package/lib/types/dsh-adapter/approvals.d.ts +225 -0
- package/lib/types/dsh-adapter/approvals.d.ts.map +1 -0
- package/lib/types/dsh-adapter/approvals.js +465 -0
- package/lib/types/dsh-adapter/bundled-presets.d.ts +10 -0
- package/lib/types/dsh-adapter/bundled-presets.d.ts.map +1 -0
- package/lib/types/dsh-adapter/bundled-presets.js +80 -0
- package/lib/types/dsh-adapter/channel/action-readiness.d.ts +20 -0
- package/lib/types/dsh-adapter/channel/action-readiness.d.ts.map +1 -0
- package/lib/types/dsh-adapter/channel/action-readiness.js +86 -0
- package/lib/types/dsh-adapter/channel/agent-view-projection.d.ts +64 -0
- package/lib/types/dsh-adapter/channel/agent-view-projection.d.ts.map +1 -0
- package/lib/types/dsh-adapter/channel/agent-view-projection.js +312 -0
- package/lib/types/dsh-adapter/channel/background-action.d.ts +41 -0
- package/lib/types/dsh-adapter/channel/background-action.d.ts.map +1 -0
- package/lib/types/dsh-adapter/channel/background-action.js +110 -0
- package/lib/types/dsh-adapter/channel/binding-events.d.ts +67 -0
- package/lib/types/dsh-adapter/channel/binding-events.d.ts.map +1 -0
- package/lib/types/dsh-adapter/channel/binding-events.js +170 -0
- package/lib/types/dsh-adapter/channel/binding.d.ts +58 -0
- package/lib/types/dsh-adapter/channel/binding.d.ts.map +1 -0
- package/lib/types/dsh-adapter/channel/binding.js +287 -0
- package/lib/types/dsh-adapter/channel/command-completions.d.ts +29 -0
- package/lib/types/dsh-adapter/channel/command-completions.d.ts.map +1 -0
- package/lib/types/dsh-adapter/channel/command-completions.js +94 -0
- package/lib/types/dsh-adapter/channel/commands.d.ts +8 -0
- package/lib/types/dsh-adapter/channel/commands.d.ts.map +1 -0
- package/lib/types/dsh-adapter/channel/commands.js +9 -0
- package/lib/types/dsh-adapter/channel/compaction.d.ts +18 -0
- package/lib/types/dsh-adapter/channel/compaction.d.ts.map +1 -0
- package/lib/types/dsh-adapter/channel/compaction.js +114 -0
- package/lib/types/dsh-adapter/channel/composer-images.d.ts +62 -0
- package/lib/types/dsh-adapter/channel/composer-images.d.ts.map +1 -0
- package/lib/types/dsh-adapter/channel/composer-images.js +316 -0
- package/lib/types/dsh-adapter/channel/composer-mentions.d.ts +15 -0
- package/lib/types/dsh-adapter/channel/composer-mentions.d.ts.map +1 -0
- package/lib/types/dsh-adapter/channel/composer-mentions.js +200 -0
- package/lib/types/dsh-adapter/channel/context-bookkeeping.d.ts +34 -0
- package/lib/types/dsh-adapter/channel/context-bookkeeping.d.ts.map +1 -0
- package/lib/types/dsh-adapter/channel/context-bookkeeping.js +31 -0
- package/lib/types/dsh-adapter/channel/decisions.d.ts +19 -0
- package/lib/types/dsh-adapter/channel/decisions.d.ts.map +1 -0
- package/lib/types/dsh-adapter/channel/decisions.js +90 -0
- package/lib/types/dsh-adapter/channel/emitter.d.ts +10 -0
- package/lib/types/dsh-adapter/channel/emitter.d.ts.map +1 -0
- package/lib/types/dsh-adapter/channel/emitter.js +71 -0
- package/lib/types/dsh-adapter/channel/external-commands.d.ts +31 -0
- package/lib/types/dsh-adapter/channel/external-commands.d.ts.map +1 -0
- package/lib/types/dsh-adapter/channel/external-commands.js +187 -0
- package/lib/types/dsh-adapter/channel/file-actions.d.ts +20 -0
- package/lib/types/dsh-adapter/channel/file-actions.d.ts.map +1 -0
- package/lib/types/dsh-adapter/channel/file-actions.js +45 -0
- package/lib/types/dsh-adapter/channel/ide-selection.d.ts +58 -0
- package/lib/types/dsh-adapter/channel/ide-selection.d.ts.map +1 -0
- package/lib/types/dsh-adapter/channel/ide-selection.js +226 -0
- package/lib/types/dsh-adapter/channel/input-actions.d.ts +13 -0
- package/lib/types/dsh-adapter/channel/input-actions.d.ts.map +1 -0
- package/lib/types/dsh-adapter/channel/input-actions.js +135 -0
- package/lib/types/dsh-adapter/channel/input-delivery.d.ts +42 -0
- package/lib/types/dsh-adapter/channel/input-delivery.d.ts.map +1 -0
- package/lib/types/dsh-adapter/channel/input-delivery.js +313 -0
- package/lib/types/dsh-adapter/channel/job-projection.d.ts +28 -0
- package/lib/types/dsh-adapter/channel/job-projection.d.ts.map +1 -0
- package/lib/types/dsh-adapter/channel/job-projection.js +150 -0
- package/lib/types/dsh-adapter/channel/lifetime-resources.d.ts +9 -0
- package/lib/types/dsh-adapter/channel/lifetime-resources.d.ts.map +1 -0
- package/lib/types/dsh-adapter/channel/lifetime-resources.js +33 -0
- package/lib/types/dsh-adapter/channel/loaded-context.d.ts +40 -0
- package/lib/types/dsh-adapter/channel/loaded-context.d.ts.map +1 -0
- package/lib/types/dsh-adapter/channel/loaded-context.js +62 -0
- package/lib/types/dsh-adapter/channel/local-actions.d.ts +54 -0
- package/lib/types/dsh-adapter/channel/local-actions.d.ts.map +1 -0
- package/lib/types/dsh-adapter/channel/local-actions.js +109 -0
- package/lib/types/dsh-adapter/channel/mentions.d.ts +29 -0
- package/lib/types/dsh-adapter/channel/mentions.d.ts.map +1 -0
- package/lib/types/dsh-adapter/channel/mentions.js +235 -0
- package/lib/types/dsh-adapter/channel/mode-actions.d.ts +30 -0
- package/lib/types/dsh-adapter/channel/mode-actions.d.ts.map +1 -0
- package/lib/types/dsh-adapter/channel/mode-actions.js +247 -0
- package/lib/types/dsh-adapter/channel/mode-permission-actions.d.ts +35 -0
- package/lib/types/dsh-adapter/channel/mode-permission-actions.d.ts.map +1 -0
- package/lib/types/dsh-adapter/channel/mode-permission-actions.js +199 -0
- package/lib/types/dsh-adapter/channel/mode-permission.d.ts +58 -0
- package/lib/types/dsh-adapter/channel/mode-permission.d.ts.map +1 -0
- package/lib/types/dsh-adapter/channel/mode-permission.js +240 -0
- package/lib/types/dsh-adapter/channel/mode-roster.d.ts +51 -0
- package/lib/types/dsh-adapter/channel/mode-roster.d.ts.map +1 -0
- package/lib/types/dsh-adapter/channel/mode-roster.js +119 -0
- package/lib/types/dsh-adapter/channel/model-actions.d.ts +49 -0
- package/lib/types/dsh-adapter/channel/model-actions.d.ts.map +1 -0
- package/lib/types/dsh-adapter/channel/model-actions.js +351 -0
- package/lib/types/dsh-adapter/channel/model-switch.d.ts +31 -0
- package/lib/types/dsh-adapter/channel/model-switch.d.ts.map +1 -0
- package/lib/types/dsh-adapter/channel/model-switch.js +116 -0
- package/lib/types/dsh-adapter/channel/notifications.d.ts +9 -0
- package/lib/types/dsh-adapter/channel/notifications.d.ts.map +1 -0
- package/lib/types/dsh-adapter/channel/notifications.js +24 -0
- package/lib/types/dsh-adapter/channel/owner.d.ts +18 -0
- package/lib/types/dsh-adapter/channel/owner.d.ts.map +1 -0
- package/lib/types/dsh-adapter/channel/owner.js +73 -0
- package/lib/types/dsh-adapter/channel/paths.d.ts +31 -0
- package/lib/types/dsh-adapter/channel/paths.d.ts.map +1 -0
- package/lib/types/dsh-adapter/channel/paths.js +147 -0
- package/lib/types/dsh-adapter/channel/permissions.d.ts +40 -0
- package/lib/types/dsh-adapter/channel/permissions.d.ts.map +1 -0
- package/lib/types/dsh-adapter/channel/permissions.js +187 -0
- package/lib/types/dsh-adapter/channel/preferences.d.ts +3 -0
- package/lib/types/dsh-adapter/channel/preferences.d.ts.map +1 -0
- package/lib/types/dsh-adapter/channel/preferences.js +103 -0
- package/lib/types/dsh-adapter/channel/projection-helpers.d.ts +13 -0
- package/lib/types/dsh-adapter/channel/projection-helpers.d.ts.map +1 -0
- package/lib/types/dsh-adapter/channel/projection-helpers.js +63 -0
- package/lib/types/dsh-adapter/channel/projection.d.ts +44 -0
- package/lib/types/dsh-adapter/channel/projection.d.ts.map +1 -0
- package/lib/types/dsh-adapter/channel/projection.js +1031 -0
- package/lib/types/dsh-adapter/channel/reports.d.ts +32 -0
- package/lib/types/dsh-adapter/channel/reports.d.ts.map +1 -0
- package/lib/types/dsh-adapter/channel/reports.js +163 -0
- package/lib/types/dsh-adapter/channel/session-actions.d.ts +13 -0
- package/lib/types/dsh-adapter/channel/session-actions.d.ts.map +1 -0
- package/lib/types/dsh-adapter/channel/session-actions.js +29 -0
- package/lib/types/dsh-adapter/channel/session-adoption.d.ts +32 -0
- package/lib/types/dsh-adapter/channel/session-adoption.d.ts.map +1 -0
- package/lib/types/dsh-adapter/channel/session-adoption.js +36 -0
- package/lib/types/dsh-adapter/channel/session-fork.d.ts +19 -0
- package/lib/types/dsh-adapter/channel/session-fork.d.ts.map +1 -0
- package/lib/types/dsh-adapter/channel/session-fork.js +109 -0
- package/lib/types/dsh-adapter/channel/session-live-adoption.d.ts +35 -0
- package/lib/types/dsh-adapter/channel/session-live-adoption.d.ts.map +1 -0
- package/lib/types/dsh-adapter/channel/session-live-adoption.js +64 -0
- package/lib/types/dsh-adapter/channel/session-metadata.d.ts +91 -0
- package/lib/types/dsh-adapter/channel/session-metadata.d.ts.map +1 -0
- package/lib/types/dsh-adapter/channel/session-metadata.js +220 -0
- package/lib/types/dsh-adapter/channel/session-reset.d.ts +11 -0
- package/lib/types/dsh-adapter/channel/session-reset.d.ts.map +1 -0
- package/lib/types/dsh-adapter/channel/session-reset.js +43 -0
- package/lib/types/dsh-adapter/channel/session-resume.d.ts +65 -0
- package/lib/types/dsh-adapter/channel/session-resume.d.ts.map +1 -0
- package/lib/types/dsh-adapter/channel/session-resume.js +438 -0
- package/lib/types/dsh-adapter/channel/session-rewind.d.ts +19 -0
- package/lib/types/dsh-adapter/channel/session-rewind.d.ts.map +1 -0
- package/lib/types/dsh-adapter/channel/session-rewind.js +146 -0
- package/lib/types/dsh-adapter/channel/session-tree-actions.d.ts +19 -0
- package/lib/types/dsh-adapter/channel/session-tree-actions.d.ts.map +1 -0
- package/lib/types/dsh-adapter/channel/session-tree-actions.js +154 -0
- package/lib/types/dsh-adapter/channel/session-tree.d.ts +10 -0
- package/lib/types/dsh-adapter/channel/session-tree.d.ts.map +1 -0
- package/lib/types/dsh-adapter/channel/session-tree.js +503 -0
- package/lib/types/dsh-adapter/channel/settings-host.d.ts +4 -0
- package/lib/types/dsh-adapter/channel/settings-host.d.ts.map +1 -0
- package/lib/types/dsh-adapter/channel/settings-host.js +257 -0
- package/lib/types/dsh-adapter/channel/skill-catalog.d.ts +33 -0
- package/lib/types/dsh-adapter/channel/skill-catalog.d.ts.map +1 -0
- package/lib/types/dsh-adapter/channel/skill-catalog.js +215 -0
- package/lib/types/dsh-adapter/channel/state.d.ts +51 -0
- package/lib/types/dsh-adapter/channel/state.d.ts.map +1 -0
- package/lib/types/dsh-adapter/channel/state.js +31 -0
- package/lib/types/dsh-adapter/channel/subagent-projection.d.ts +67 -0
- package/lib/types/dsh-adapter/channel/subagent-projection.d.ts.map +1 -0
- package/lib/types/dsh-adapter/channel/subagent-projection.js +307 -0
- package/lib/types/dsh-adapter/channel/transcript.d.ts +73 -0
- package/lib/types/dsh-adapter/channel/transcript.d.ts.map +1 -0
- package/lib/types/dsh-adapter/channel/transcript.js +306 -0
- package/lib/types/dsh-adapter/channel/types.d.ts +167 -0
- package/lib/types/dsh-adapter/channel/types.d.ts.map +1 -0
- package/lib/types/dsh-adapter/channel/types.js +1 -0
- package/lib/types/dsh-adapter/channel/usage.d.ts +13 -0
- package/lib/types/dsh-adapter/channel/usage.d.ts.map +1 -0
- package/lib/types/dsh-adapter/channel/usage.js +48 -0
- package/lib/types/dsh-adapter/channel/workspace-actions.d.ts +23 -0
- package/lib/types/dsh-adapter/channel/workspace-actions.d.ts.map +1 -0
- package/lib/types/dsh-adapter/channel/workspace-actions.js +96 -0
- package/lib/types/dsh-adapter/channel-facade-actions.d.ts +25 -0
- package/lib/types/dsh-adapter/channel-facade-actions.d.ts.map +1 -0
- package/lib/types/dsh-adapter/channel-facade-actions.js +55 -0
- package/lib/types/dsh-adapter/channel-scene-outlet.d.ts +6 -0
- package/lib/types/dsh-adapter/channel-scene-outlet.d.ts.map +1 -0
- package/lib/types/dsh-adapter/channel-scene-outlet.js +13 -0
- package/lib/types/dsh-adapter/channel-ui.d.ts +9 -0
- package/lib/types/dsh-adapter/channel-ui.d.ts.map +1 -0
- package/lib/types/dsh-adapter/channel-ui.js +128 -0
- package/lib/types/dsh-adapter/channel.d.ts +23 -0
- package/lib/types/dsh-adapter/channel.d.ts.map +1 -0
- package/lib/types/dsh-adapter/channel.js +999 -0
- package/lib/types/dsh-adapter/childStderr.d.ts +63 -0
- package/lib/types/dsh-adapter/childStderr.d.ts.map +1 -0
- package/lib/types/dsh-adapter/childStderr.js +180 -0
- package/lib/types/dsh-adapter/command-attribution.d.ts +51 -0
- package/lib/types/dsh-adapter/command-attribution.d.ts.map +1 -0
- package/lib/types/dsh-adapter/command-attribution.js +93 -0
- package/lib/types/dsh-adapter/command-errors.d.ts +42 -0
- package/lib/types/dsh-adapter/command-errors.d.ts.map +1 -0
- package/lib/types/dsh-adapter/command-errors.js +63 -0
- package/lib/types/dsh-adapter/command-trees.d.ts +34 -0
- package/lib/types/dsh-adapter/command-trees.d.ts.map +1 -0
- package/lib/types/dsh-adapter/command-trees.js +111 -0
- package/lib/types/dsh-adapter/compat/index.d.ts +29 -0
- package/lib/types/dsh-adapter/compat/index.d.ts.map +1 -0
- package/lib/types/dsh-adapter/compat/index.js +28 -0
- package/lib/types/dsh-adapter/compat/liveSession.d.ts +79 -0
- package/lib/types/dsh-adapter/compat/liveSession.d.ts.map +1 -0
- package/lib/types/dsh-adapter/compat/liveSession.js +223 -0
- package/lib/types/dsh-adapter/compat/messages.d.ts +15 -0
- package/lib/types/dsh-adapter/compat/messages.d.ts.map +1 -0
- package/lib/types/dsh-adapter/compat/messages.js +15 -0
- package/lib/types/dsh-adapter/compat/persistence.d.ts +21 -0
- package/lib/types/dsh-adapter/compat/persistence.d.ts.map +1 -0
- package/lib/types/dsh-adapter/compat/persistence.js +17 -0
- package/lib/types/dsh-adapter/compat/sessionLog.d.ts +309 -0
- package/lib/types/dsh-adapter/compat/sessionLog.d.ts.map +1 -0
- package/lib/types/dsh-adapter/compat/sessionLog.js +1177 -0
- package/lib/types/dsh-adapter/compat/settings.d.ts +35 -0
- package/lib/types/dsh-adapter/compat/settings.d.ts.map +1 -0
- package/lib/types/dsh-adapter/compat/settings.js +57 -0
- package/lib/types/dsh-adapter/compat/shell.d.ts +25 -0
- package/lib/types/dsh-adapter/compat/shell.d.ts.map +1 -0
- package/lib/types/dsh-adapter/compat/shell.js +11 -0
- package/lib/types/dsh-adapter/component-identity.d.ts +33 -0
- package/lib/types/dsh-adapter/component-identity.d.ts.map +1 -0
- package/lib/types/dsh-adapter/component-identity.js +108 -0
- package/lib/types/dsh-adapter/contract.d.ts +91 -0
- package/lib/types/dsh-adapter/contract.d.ts.map +1 -0
- package/lib/types/dsh-adapter/contract.js +276 -0
- package/lib/types/dsh-adapter/credentialRefGuard.d.ts +40 -0
- package/lib/types/dsh-adapter/credentialRefGuard.d.ts.map +1 -0
- package/lib/types/dsh-adapter/credentialRefGuard.js +43 -0
- package/lib/types/dsh-adapter/decision-guard.d.ts +128 -0
- package/lib/types/dsh-adapter/decision-guard.d.ts.map +1 -0
- package/lib/types/dsh-adapter/decision-guard.js +460 -0
- package/lib/types/dsh-adapter/dialogs.d.ts +145 -0
- package/lib/types/dsh-adapter/dialogs.d.ts.map +1 -0
- package/lib/types/dsh-adapter/dialogs.js +329 -0
- package/lib/types/dsh-adapter/effect-ledger.d.ts +96 -0
- package/lib/types/dsh-adapter/effect-ledger.d.ts.map +1 -0
- package/lib/types/dsh-adapter/effect-ledger.js +290 -0
- package/lib/types/dsh-adapter/extension-events.d.ts +186 -0
- package/lib/types/dsh-adapter/extension-events.d.ts.map +1 -0
- package/lib/types/dsh-adapter/extension-events.js +187 -0
- package/lib/types/dsh-adapter/extensions.d.ts +36 -0
- package/lib/types/dsh-adapter/extensions.d.ts.map +1 -0
- package/lib/types/dsh-adapter/extensions.js +51 -0
- package/lib/types/dsh-adapter/host-access.d.ts +37 -0
- package/lib/types/dsh-adapter/host-access.d.ts.map +1 -0
- package/lib/types/dsh-adapter/host-access.js +652 -0
- package/lib/types/dsh-adapter/host-grants.d.ts +23 -0
- package/lib/types/dsh-adapter/host-grants.d.ts.map +1 -0
- package/lib/types/dsh-adapter/host-grants.js +45 -0
- package/lib/types/dsh-adapter/ide-channel.d.ts +224 -0
- package/lib/types/dsh-adapter/ide-channel.d.ts.map +1 -0
- package/lib/types/dsh-adapter/ide-channel.js +556 -0
- package/lib/types/dsh-adapter/index.d.ts +151 -0
- package/lib/types/dsh-adapter/index.d.ts.map +1 -0
- package/lib/types/dsh-adapter/index.js +119 -0
- package/lib/types/dsh-adapter/inject-channel.d.ts +108 -0
- package/lib/types/dsh-adapter/inject-channel.d.ts.map +1 -0
- package/lib/types/dsh-adapter/inject-channel.js +247 -0
- package/lib/types/dsh-adapter/invariant.d.ts +20 -0
- package/lib/types/dsh-adapter/invariant.d.ts.map +1 -0
- package/lib/types/dsh-adapter/invariant.js +26 -0
- package/lib/types/dsh-adapter/jobs.d.ts +101 -0
- package/lib/types/dsh-adapter/jobs.d.ts.map +1 -0
- package/lib/types/dsh-adapter/jobs.js +177 -0
- package/lib/types/dsh-adapter/message-observer.d.ts +186 -0
- package/lib/types/dsh-adapter/message-observer.d.ts.map +1 -0
- package/lib/types/dsh-adapter/message-observer.js +682 -0
- package/lib/types/dsh-adapter/migrate/adapters/claude-code.d.ts +3 -0
- package/lib/types/dsh-adapter/migrate/adapters/claude-code.d.ts.map +1 -0
- package/lib/types/dsh-adapter/migrate/adapters/claude-code.js +191 -0
- package/lib/types/dsh-adapter/migrate/adapters/codex.d.ts +3 -0
- package/lib/types/dsh-adapter/migrate/adapters/codex.d.ts.map +1 -0
- package/lib/types/dsh-adapter/migrate/adapters/codex.js +133 -0
- package/lib/types/dsh-adapter/migrate/adapters/grok-build.d.ts +3 -0
- package/lib/types/dsh-adapter/migrate/adapters/grok-build.d.ts.map +1 -0
- package/lib/types/dsh-adapter/migrate/adapters/grok-build.js +175 -0
- package/lib/types/dsh-adapter/migrate/adapters/omp.d.ts +3 -0
- package/lib/types/dsh-adapter/migrate/adapters/omp.d.ts.map +1 -0
- package/lib/types/dsh-adapter/migrate/adapters/omp.js +132 -0
- package/lib/types/dsh-adapter/migrate/adapters/scan.d.ts +6 -0
- package/lib/types/dsh-adapter/migrate/adapters/scan.d.ts.map +1 -0
- package/lib/types/dsh-adapter/migrate/adapters/scan.js +38 -0
- package/lib/types/dsh-adapter/migrate/adapters/zcode.d.ts +3 -0
- package/lib/types/dsh-adapter/migrate/adapters/zcode.d.ts.map +1 -0
- package/lib/types/dsh-adapter/migrate/adapters/zcode.js +107 -0
- package/lib/types/dsh-adapter/migrate/bin-path.d.ts +6 -0
- package/lib/types/dsh-adapter/migrate/bin-path.d.ts.map +1 -0
- package/lib/types/dsh-adapter/migrate/bin-path.js +30 -0
- package/lib/types/dsh-adapter/migrate/cli.d.ts +9 -0
- package/lib/types/dsh-adapter/migrate/cli.d.ts.map +1 -0
- package/lib/types/dsh-adapter/migrate/cli.js +105 -0
- package/lib/types/dsh-adapter/migrate/index.d.ts +29 -0
- package/lib/types/dsh-adapter/migrate/index.d.ts.map +1 -0
- package/lib/types/dsh-adapter/migrate/index.js +108 -0
- package/lib/types/dsh-adapter/migrate/picker.d.ts +64 -0
- package/lib/types/dsh-adapter/migrate/picker.d.ts.map +1 -0
- package/lib/types/dsh-adapter/migrate/picker.js +86 -0
- package/lib/types/dsh-adapter/migrate/recent-agents.d.ts +40 -0
- package/lib/types/dsh-adapter/migrate/recent-agents.d.ts.map +1 -0
- package/lib/types/dsh-adapter/migrate/recent-agents.js +100 -0
- package/lib/types/dsh-adapter/migrate/sessionize.d.ts +19 -0
- package/lib/types/dsh-adapter/migrate/sessionize.d.ts.map +1 -0
- package/lib/types/dsh-adapter/migrate/sessionize.js +101 -0
- package/lib/types/dsh-adapter/migrate/types.d.ts +52 -0
- package/lib/types/dsh-adapter/migrate/types.d.ts.map +1 -0
- package/lib/types/dsh-adapter/migrate/types.js +11 -0
- package/lib/types/dsh-adapter/migrate/uuid.d.ts +7 -0
- package/lib/types/dsh-adapter/migrate/uuid.d.ts.map +1 -0
- package/lib/types/dsh-adapter/migrate/uuid.js +25 -0
- package/lib/types/dsh-adapter/packaged-presets.d.ts +22 -0
- package/lib/types/dsh-adapter/packaged-presets.d.ts.map +1 -0
- package/lib/types/dsh-adapter/packaged-presets.js +94 -0
- package/lib/types/dsh-adapter/plugin-host.d.ts +205 -0
- package/lib/types/dsh-adapter/plugin-host.d.ts.map +1 -0
- package/lib/types/dsh-adapter/plugin-host.js +762 -0
- package/lib/types/dsh-adapter/plugin-storage.d.ts +118 -0
- package/lib/types/dsh-adapter/plugin-storage.d.ts.map +1 -0
- package/lib/types/dsh-adapter/plugin-storage.js +536 -0
- package/lib/types/dsh-adapter/plugin.d.ts +70 -0
- package/lib/types/dsh-adapter/plugin.d.ts.map +1 -0
- package/lib/types/dsh-adapter/plugin.js +2140 -0
- package/lib/types/dsh-adapter/plugins-info.d.ts +43 -0
- package/lib/types/dsh-adapter/plugins-info.d.ts.map +1 -0
- package/lib/types/dsh-adapter/plugins-info.js +293 -0
- package/lib/types/dsh-adapter/preset-resolution.d.ts +57 -0
- package/lib/types/dsh-adapter/preset-resolution.d.ts.map +1 -0
- package/lib/types/dsh-adapter/preset-resolution.js +52 -0
- package/lib/types/dsh-adapter/presets.d.ts +110 -0
- package/lib/types/dsh-adapter/presets.d.ts.map +1 -0
- package/lib/types/dsh-adapter/presets.js +161 -0
- package/lib/types/dsh-adapter/promptDebug.d.ts +4 -0
- package/lib/types/dsh-adapter/promptDebug.d.ts.map +1 -0
- package/lib/types/dsh-adapter/promptDebug.js +163 -0
- package/lib/types/dsh-adapter/providerGuard.d.ts +75 -0
- package/lib/types/dsh-adapter/providerGuard.d.ts.map +1 -0
- package/lib/types/dsh-adapter/providerGuard.js +104 -0
- package/lib/types/dsh-adapter/providerWizard.d.ts +42 -0
- package/lib/types/dsh-adapter/providerWizard.d.ts.map +1 -0
- package/lib/types/dsh-adapter/providerWizard.js +921 -0
- package/lib/types/dsh-adapter/questions-answerer.d.ts +51 -0
- package/lib/types/dsh-adapter/questions-answerer.d.ts.map +1 -0
- package/lib/types/dsh-adapter/questions-answerer.js +64 -0
- package/lib/types/dsh-adapter/questions.d.ts +130 -0
- package/lib/types/dsh-adapter/questions.d.ts.map +1 -0
- package/lib/types/dsh-adapter/questions.js +282 -0
- package/lib/types/dsh-adapter/recap.d.ts +41 -0
- package/lib/types/dsh-adapter/recap.d.ts.map +1 -0
- package/lib/types/dsh-adapter/recap.js +111 -0
- package/lib/types/dsh-adapter/renderers.d.ts +58 -0
- package/lib/types/dsh-adapter/renderers.d.ts.map +1 -0
- package/lib/types/dsh-adapter/renderers.js +198 -0
- package/lib/types/dsh-adapter/sanitize.d.ts +45 -0
- package/lib/types/dsh-adapter/sanitize.d.ts.map +1 -0
- package/lib/types/dsh-adapter/sanitize.js +84 -0
- package/lib/types/dsh-adapter/scenes.d.ts +87 -0
- package/lib/types/dsh-adapter/scenes.d.ts.map +1 -0
- package/lib/types/dsh-adapter/scenes.js +223 -0
- package/lib/types/dsh-adapter/session-mount-heartbeat.d.ts +53 -0
- package/lib/types/dsh-adapter/session-mount-heartbeat.d.ts.map +1 -0
- package/lib/types/dsh-adapter/session-mount-heartbeat.js +100 -0
- package/lib/types/dsh-adapter/sessionTree.d.ts +232 -0
- package/lib/types/dsh-adapter/sessionTree.d.ts.map +1 -0
- package/lib/types/dsh-adapter/sessionTree.js +984 -0
- package/lib/types/dsh-adapter/sessions/digest.d.ts +54 -0
- package/lib/types/dsh-adapter/sessions/digest.d.ts.map +1 -0
- package/lib/types/dsh-adapter/sessions/digest.js +478 -0
- package/lib/types/dsh-adapter/sessions/frames.d.ts +103 -0
- package/lib/types/dsh-adapter/sessions/frames.d.ts.map +1 -0
- package/lib/types/dsh-adapter/sessions/frames.js +263 -0
- package/lib/types/dsh-adapter/sessions/header.d.ts +56 -0
- package/lib/types/dsh-adapter/sessions/header.d.ts.map +1 -0
- package/lib/types/dsh-adapter/sessions/header.js +85 -0
- package/lib/types/dsh-adapter/sessions/index.d.ts +17 -0
- package/lib/types/dsh-adapter/sessions/index.d.ts.map +1 -0
- package/lib/types/dsh-adapter/sessions/index.js +15 -0
- package/lib/types/dsh-adapter/sessions/list.d.ts +64 -0
- package/lib/types/dsh-adapter/sessions/list.d.ts.map +1 -0
- package/lib/types/dsh-adapter/sessions/list.js +331 -0
- package/lib/types/dsh-adapter/sessions/recovery.d.ts +17 -0
- package/lib/types/dsh-adapter/sessions/recovery.d.ts.map +1 -0
- package/lib/types/dsh-adapter/sessions/recovery.js +109 -0
- package/lib/types/dsh-adapter/sessions/store.d.ts +60 -0
- package/lib/types/dsh-adapter/sessions/store.d.ts.map +1 -0
- package/lib/types/dsh-adapter/sessions/store.js +221 -0
- package/lib/types/dsh-adapter/sessions/types.d.ts +21 -0
- package/lib/types/dsh-adapter/sessions/types.d.ts.map +1 -0
- package/lib/types/dsh-adapter/sessions/types.js +1 -0
- package/lib/types/dsh-adapter/settings-sections.d.ts +48 -0
- package/lib/types/dsh-adapter/settings-sections.d.ts.map +1 -0
- package/lib/types/dsh-adapter/settings-sections.js +253 -0
- package/lib/types/dsh-adapter/settingsEditor.d.ts +100 -0
- package/lib/types/dsh-adapter/settingsEditor.d.ts.map +1 -0
- package/lib/types/dsh-adapter/settingsEditor.js +238 -0
- package/lib/types/dsh-adapter/sharp.d.ts +10 -0
- package/lib/types/dsh-adapter/sharp.d.ts.map +1 -0
- package/lib/types/dsh-adapter/sharp.js +59 -0
- package/lib/types/dsh-adapter/shortcuts.d.ts +82 -0
- package/lib/types/dsh-adapter/shortcuts.d.ts.map +1 -0
- package/lib/types/dsh-adapter/shortcuts.js +252 -0
- package/lib/types/dsh-adapter/sideQuestion.d.ts +36 -0
- package/lib/types/dsh-adapter/sideQuestion.d.ts.map +1 -0
- package/lib/types/dsh-adapter/sideQuestion.js +59 -0
- package/lib/types/dsh-adapter/status.d.ts +148 -0
- package/lib/types/dsh-adapter/status.d.ts.map +1 -0
- package/lib/types/dsh-adapter/status.js +435 -0
- package/lib/types/dsh-adapter/subagents.d.ts +64 -0
- package/lib/types/dsh-adapter/subagents.d.ts.map +1 -0
- package/lib/types/dsh-adapter/subagents.js +366 -0
- package/lib/types/dsh-adapter/themes.d.ts +52 -0
- package/lib/types/dsh-adapter/themes.d.ts.map +1 -0
- package/lib/types/dsh-adapter/themes.js +307 -0
- package/lib/types/dsh-adapter/toast.d.ts +80 -0
- package/lib/types/dsh-adapter/toast.d.ts.map +1 -0
- package/lib/types/dsh-adapter/toast.js +171 -0
- package/lib/types/dsh-adapter/trajectory/aggregate.d.ts +47 -0
- package/lib/types/dsh-adapter/trajectory/aggregate.d.ts.map +1 -0
- package/lib/types/dsh-adapter/trajectory/aggregate.js +208 -0
- package/lib/types/dsh-adapter/trajectory/guards.d.ts +196 -0
- package/lib/types/dsh-adapter/trajectory/guards.d.ts.map +1 -0
- package/lib/types/dsh-adapter/trajectory/guards.js +273 -0
- package/lib/types/dsh-adapter/trajectory/index.d.ts +16 -0
- package/lib/types/dsh-adapter/trajectory/index.d.ts.map +1 -0
- package/lib/types/dsh-adapter/trajectory/index.js +15 -0
- package/lib/types/dsh-adapter/trajectory/inspect.d.ts +40 -0
- package/lib/types/dsh-adapter/trajectory/inspect.d.ts.map +1 -0
- package/lib/types/dsh-adapter/trajectory/inspect.js +160 -0
- package/lib/types/dsh-adapter/trajectory/projection.d.ts +146 -0
- package/lib/types/dsh-adapter/trajectory/projection.d.ts.map +1 -0
- package/lib/types/dsh-adapter/trajectory/projection.js +710 -0
- package/lib/types/dsh-adapter/trajectory/types.d.ts +232 -0
- package/lib/types/dsh-adapter/trajectory/types.d.ts.map +1 -0
- package/lib/types/dsh-adapter/trajectory/types.js +70 -0
- package/lib/types/dsh-adapter/trajectory/wave.d.ts +45 -0
- package/lib/types/dsh-adapter/trajectory/wave.d.ts.map +1 -0
- package/lib/types/dsh-adapter/trajectory/wave.js +210 -0
- package/lib/types/dsh-adapter/transcript-images.d.ts +17 -0
- package/lib/types/dsh-adapter/transcript-images.d.ts.map +1 -0
- package/lib/types/dsh-adapter/transcript-images.js +96 -0
- package/lib/types/dsh-adapter/types.d.ts +23 -0
- package/lib/types/dsh-adapter/types.d.ts.map +1 -0
- package/lib/types/dsh-adapter/types.js +1 -0
- package/lib/types/dsh-adapter/workspace.d.ts +22 -0
- package/lib/types/dsh-adapter/workspace.d.ts.map +1 -0
- package/lib/types/dsh-adapter/workspace.js +16 -0
- package/lib/types/dsh-adapter/workspaces.d.ts +125 -0
- package/lib/types/dsh-adapter/workspaces.d.ts.map +1 -0
- package/lib/types/dsh-adapter/workspaces.js +543 -0
- package/lib/types/effortPrefs.d.ts +48 -0
- package/lib/types/effortPrefs.d.ts.map +1 -0
- package/lib/types/effortPrefs.js +95 -0
- package/lib/types/extensions.d.ts +18 -0
- package/lib/types/extensions.d.ts.map +1 -0
- package/lib/types/extensions.js +27 -0
- package/lib/types/force-production-react.d.ts +1 -0
- package/lib/types/force-production-react.d.ts.map +1 -0
- package/lib/types/force-production-react.js +12 -0
- package/lib/types/herdr.d.ts +28 -0
- package/lib/types/herdr.d.ts.map +1 -0
- package/lib/types/herdr.js +133 -0
- package/lib/types/history.d.ts +39 -0
- package/lib/types/history.d.ts.map +1 -0
- package/lib/types/history.js +177 -0
- package/lib/types/homePrefs.d.ts +15 -0
- package/lib/types/homePrefs.d.ts.map +1 -0
- package/lib/types/homePrefs.js +53 -0
- package/lib/types/hooks/useBlink.d.ts +10 -0
- package/lib/types/hooks/useBlink.d.ts.map +1 -0
- package/lib/types/hooks/useBlink.js +19 -0
- package/lib/types/hooks/useExternalVersion.d.ts +32 -0
- package/lib/types/hooks/useExternalVersion.d.ts.map +1 -0
- package/lib/types/hooks/useExternalVersion.js +53 -0
- package/lib/types/hooks/useRevealVersion.d.ts +21 -0
- package/lib/types/hooks/useRevealVersion.d.ts.map +1 -0
- package/lib/types/hooks/useRevealVersion.js +33 -0
- package/lib/types/hooks/useRuntimeThemeSnapshot.d.ts +10 -0
- package/lib/types/hooks/useRuntimeThemeSnapshot.d.ts.map +1 -0
- package/lib/types/hooks/useRuntimeThemeSnapshot.js +32 -0
- package/lib/types/i18n.d.ts +4433 -0
- package/lib/types/i18n.d.ts.map +1 -0
- package/lib/types/i18n.js +1493 -0
- package/lib/types/index.d.ts +3 -0
- package/lib/types/index.d.ts.map +1 -0
- package/lib/types/index.js +4 -0
- package/lib/types/ink/Ansi.d.ts +9 -0
- package/lib/types/ink/Ansi.d.ts.map +1 -0
- package/lib/types/ink/Ansi.js +45 -0
- package/lib/types/ink/bidi.d.ts +18 -0
- package/lib/types/ink/bidi.d.ts.map +1 -0
- package/lib/types/ink/bidi.js +119 -0
- package/lib/types/ink/clearTerminal.d.ts +22 -0
- package/lib/types/ink/clearTerminal.d.ts.map +1 -0
- package/lib/types/ink/clearTerminal.js +72 -0
- package/lib/types/ink/colorize.d.ts +43 -0
- package/lib/types/ink/colorize.d.ts.map +1 -0
- package/lib/types/ink/colorize.js +233 -0
- package/lib/types/ink/components/AlternateScreen.d.ts +8 -0
- package/lib/types/ink/components/AlternateScreen.d.ts.map +1 -0
- package/lib/types/ink/components/AlternateScreen.js +29 -0
- package/lib/types/ink/components/App.d.ts +149 -0
- package/lib/types/ink/components/App.d.ts.map +1 -0
- package/lib/types/ink/components/App.js +1027 -0
- package/lib/types/ink/components/AppContext.d.ts +17 -0
- package/lib/types/ink/components/AppContext.d.ts.map +1 -0
- package/lib/types/ink/components/AppContext.js +12 -0
- package/lib/types/ink/components/Box.d.ts +85 -0
- package/lib/types/ink/components/Box.d.ts.map +1 -0
- package/lib/types/ink/components/Box.js +22 -0
- package/lib/types/ink/components/Button.d.ts +37 -0
- package/lib/types/ink/components/Button.d.ts.map +1 -0
- package/lib/types/ink/components/Button.js +23 -0
- package/lib/types/ink/components/ClockContext.d.ts +17 -0
- package/lib/types/ink/components/ClockContext.d.ts.map +1 -0
- package/lib/types/ink/components/ClockContext.js +99 -0
- package/lib/types/ink/components/CursorDeclarationContext.d.ts +30 -0
- package/lib/types/ink/components/CursorDeclarationContext.d.ts.map +1 -0
- package/lib/types/ink/components/CursorDeclarationContext.js +6 -0
- package/lib/types/ink/components/ErrorOverview.d.ts +7 -0
- package/lib/types/ink/components/ErrorOverview.d.ts.map +1 -0
- package/lib/types/ink/components/ErrorOverview.js +49 -0
- package/lib/types/ink/components/Image.d.ts +28 -0
- package/lib/types/ink/components/Image.d.ts.map +1 -0
- package/lib/types/ink/components/Image.js +44 -0
- package/lib/types/ink/components/Link.d.ts +8 -0
- package/lib/types/ink/components/Link.d.ts.map +1 -0
- package/lib/types/ink/components/Link.js +7 -0
- package/lib/types/ink/components/Newline.d.ts +7 -0
- package/lib/types/ink/components/Newline.d.ts.map +1 -0
- package/lib/types/ink/components/Newline.js +5 -0
- package/lib/types/ink/components/NoSelect.d.ts +9 -0
- package/lib/types/ink/components/NoSelect.d.ts.map +1 -0
- package/lib/types/ink/components/NoSelect.js +6 -0
- package/lib/types/ink/components/RawAnsi.d.ts +9 -0
- package/lib/types/ink/components/RawAnsi.d.ts.map +1 -0
- package/lib/types/ink/components/RawAnsi.js +7 -0
- package/lib/types/ink/components/ScrollBox.d.ts +78 -0
- package/lib/types/ink/components/ScrollBox.d.ts.map +1 -0
- package/lib/types/ink/components/ScrollBox.js +309 -0
- package/lib/types/ink/components/Spacer.d.ts +4 -0
- package/lib/types/ink/components/Spacer.d.ts.map +1 -0
- package/lib/types/ink/components/Spacer.js +6 -0
- package/lib/types/ink/components/StdinContext.d.ts +32 -0
- package/lib/types/ink/components/StdinContext.d.ts.map +1 -0
- package/lib/types/ink/components/StdinContext.js +16 -0
- package/lib/types/ink/components/TerminalFocusContext.d.ts +11 -0
- package/lib/types/ink/components/TerminalFocusContext.d.ts.map +1 -0
- package/lib/types/ink/components/TerminalFocusContext.js +11 -0
- package/lib/types/ink/components/TerminalSizeContext.d.ts +6 -0
- package/lib/types/ink/components/TerminalSizeContext.d.ts.map +1 -0
- package/lib/types/ink/components/TerminalSizeContext.js +2 -0
- package/lib/types/ink/components/Text.d.ts +64 -0
- package/lib/types/ink/components/Text.d.ts.map +1 -0
- package/lib/types/ink/components/Text.js +19 -0
- package/lib/types/ink/constants.d.ts +12 -0
- package/lib/types/ink/constants.d.ts.map +1 -0
- package/lib/types/ink/constants.js +11 -0
- package/lib/types/ink/cursor.d.ts +11 -0
- package/lib/types/ink/cursor.d.ts.map +1 -0
- package/lib/types/ink/cursor.js +1 -0
- package/lib/types/ink/devtools.d.ts +7 -0
- package/lib/types/ink/devtools.d.ts.map +1 -0
- package/lib/types/ink/devtools.js +1 -0
- package/lib/types/ink/dom.d.ts +194 -0
- package/lib/types/ink/dom.d.ts.map +1 -0
- package/lib/types/ink/dom.js +424 -0
- package/lib/types/ink/events/click-event.d.ts +23 -0
- package/lib/types/ink/events/click-event.d.ts.map +1 -0
- package/lib/types/ink/events/click-event.js +33 -0
- package/lib/types/ink/events/context-menu-event.d.ts +19 -0
- package/lib/types/ink/events/context-menu-event.d.ts.map +1 -0
- package/lib/types/ink/events/context-menu-event.js +20 -0
- package/lib/types/ink/events/dispatcher.d.ts +63 -0
- package/lib/types/ink/events/dispatcher.d.ts.map +1 -0
- package/lib/types/ink/events/dispatcher.js +194 -0
- package/lib/types/ink/events/drag-event.d.ts +44 -0
- package/lib/types/ink/events/drag-event.d.ts.map +1 -0
- package/lib/types/ink/events/drag-event.js +39 -0
- package/lib/types/ink/events/emitter.d.ts +20 -0
- package/lib/types/ink/events/emitter.d.ts.map +1 -0
- package/lib/types/ink/events/emitter.js +43 -0
- package/lib/types/ink/events/event-handlers.d.ts +65 -0
- package/lib/types/ink/events/event-handlers.d.ts.map +1 -0
- package/lib/types/ink/events/event-handlers.js +40 -0
- package/lib/types/ink/events/event.d.ts +19 -0
- package/lib/types/ink/events/event.d.ts.map +1 -0
- package/lib/types/ink/events/event.js +22 -0
- package/lib/types/ink/events/focus-event.d.ts +19 -0
- package/lib/types/ink/events/focus-event.d.ts.map +1 -0
- package/lib/types/ink/events/focus-event.js +21 -0
- package/lib/types/ink/events/input-event.d.ts +72 -0
- package/lib/types/ink/events/input-event.d.ts.map +1 -0
- package/lib/types/ink/events/input-event.js +185 -0
- package/lib/types/ink/events/keyboard-event.d.ts +40 -0
- package/lib/types/ink/events/keyboard-event.d.ts.map +1 -0
- package/lib/types/ink/events/keyboard-event.js +65 -0
- package/lib/types/ink/events/paste-event.d.ts +11 -0
- package/lib/types/ink/events/paste-event.d.ts.map +1 -0
- package/lib/types/ink/events/paste-event.js +13 -0
- package/lib/types/ink/events/pointer-event.d.ts +61 -0
- package/lib/types/ink/events/pointer-event.d.ts.map +1 -0
- package/lib/types/ink/events/pointer-event.js +61 -0
- package/lib/types/ink/events/resize-event.d.ts +13 -0
- package/lib/types/ink/events/resize-event.d.ts.map +1 -0
- package/lib/types/ink/events/resize-event.js +16 -0
- package/lib/types/ink/events/terminal-event.d.ts +112 -0
- package/lib/types/ink/events/terminal-event.d.ts.map +1 -0
- package/lib/types/ink/events/terminal-event.js +131 -0
- package/lib/types/ink/events/terminal-focus-event.d.ts +21 -0
- package/lib/types/ink/events/terminal-focus-event.d.ts.map +1 -0
- package/lib/types/ink/events/terminal-focus-event.js +18 -0
- package/lib/types/ink/events/wheel-event.d.ts +17 -0
- package/lib/types/ink/events/wheel-event.d.ts.map +1 -0
- package/lib/types/ink/events/wheel-event.js +20 -0
- package/lib/types/ink/flush-tick.d.ts +26 -0
- package/lib/types/ink/flush-tick.d.ts.map +1 -0
- package/lib/types/ink/flush-tick.js +31 -0
- package/lib/types/ink/focus.d.ts +74 -0
- package/lib/types/ink/focus.d.ts.map +1 -0
- package/lib/types/ink/focus.js +188 -0
- package/lib/types/ink/frame.d.ts +128 -0
- package/lib/types/ink/frame.d.ts.map +1 -0
- package/lib/types/ink/frame.js +43 -0
- package/lib/types/ink/geometry-trace.d.ts +51 -0
- package/lib/types/ink/geometry-trace.d.ts.map +1 -0
- package/lib/types/ink/geometry-trace.js +83 -0
- package/lib/types/ink/get-max-width.d.ts +12 -0
- package/lib/types/ink/get-max-width.d.ts.map +1 -0
- package/lib/types/ink/get-max-width.js +12 -0
- package/lib/types/ink/hit-test.d.ts +181 -0
- package/lib/types/ink/hit-test.d.ts.map +1 -0
- package/lib/types/ink/hit-test.js +540 -0
- package/lib/types/ink/hooks/use-animation-frame.d.ts +27 -0
- package/lib/types/ink/hooks/use-animation-frame.d.ts.map +1 -0
- package/lib/types/ink/hooks/use-animation-frame.js +48 -0
- package/lib/types/ink/hooks/use-app.d.ts +7 -0
- package/lib/types/ink/hooks/use-app.d.ts.map +1 -0
- package/lib/types/ink/hooks/use-app.js +7 -0
- package/lib/types/ink/hooks/use-copy-on-select.d.ts +26 -0
- package/lib/types/ink/hooks/use-copy-on-select.d.ts.map +1 -0
- package/lib/types/ink/hooks/use-copy-on-select.js +54 -0
- package/lib/types/ink/hooks/use-declared-cursor.d.ts +31 -0
- package/lib/types/ink/hooks/use-declared-cursor.d.ts.map +1 -0
- package/lib/types/ink/hooks/use-declared-cursor.js +65 -0
- package/lib/types/ink/hooks/use-input.d.ts +41 -0
- package/lib/types/ink/hooks/use-input.d.ts.map +1 -0
- package/lib/types/ink/hooks/use-input.js +81 -0
- package/lib/types/ink/hooks/use-interval.d.ts +23 -0
- package/lib/types/ink/hooks/use-interval.d.ts.map +1 -0
- package/lib/types/ink/hooks/use-interval.js +58 -0
- package/lib/types/ink/hooks/use-search-highlight.d.ts +34 -0
- package/lib/types/ink/hooks/use-search-highlight.d.ts.map +1 -0
- package/lib/types/ink/hooks/use-search-highlight.js +35 -0
- package/lib/types/ink/hooks/use-selection.d.ts +44 -0
- package/lib/types/ink/hooks/use-selection.d.ts.map +1 -0
- package/lib/types/ink/hooks/use-selection.js +64 -0
- package/lib/types/ink/hooks/use-stdin.d.ts +8 -0
- package/lib/types/ink/hooks/use-stdin.d.ts.map +1 -0
- package/lib/types/ink/hooks/use-stdin.js +8 -0
- package/lib/types/ink/hooks/use-tab-status.d.ts +18 -0
- package/lib/types/ink/hooks/use-tab-status.d.ts.map +1 -0
- package/lib/types/ink/hooks/use-tab-status.js +58 -0
- package/lib/types/ink/hooks/use-terminal-focus.d.ts +11 -0
- package/lib/types/ink/hooks/use-terminal-focus.d.ts.map +1 -0
- package/lib/types/ink/hooks/use-terminal-focus.js +15 -0
- package/lib/types/ink/hooks/use-terminal-images.d.ts +14 -0
- package/lib/types/ink/hooks/use-terminal-images.d.ts.map +1 -0
- package/lib/types/ink/hooks/use-terminal-images.js +22 -0
- package/lib/types/ink/hooks/use-terminal-size.d.ts +6 -0
- package/lib/types/ink/hooks/use-terminal-size.d.ts.map +1 -0
- package/lib/types/ink/hooks/use-terminal-size.js +12 -0
- package/lib/types/ink/hooks/use-terminal-title.d.ts +14 -0
- package/lib/types/ink/hooks/use-terminal-title.d.ts.map +1 -0
- package/lib/types/ink/hooks/use-terminal-title.js +30 -0
- package/lib/types/ink/hooks/use-terminal-viewport.d.ts +32 -0
- package/lib/types/ink/hooks/use-terminal-viewport.d.ts.map +1 -0
- package/lib/types/ink/hooks/use-terminal-viewport.js +79 -0
- package/lib/types/ink/ink.d.ts +571 -0
- package/lib/types/ink/ink.d.ts.map +1 -0
- package/lib/types/ink/ink.js +2818 -0
- package/lib/types/ink/input-suppression.d.ts +20 -0
- package/lib/types/ink/input-suppression.d.ts.map +1 -0
- package/lib/types/ink/input-suppression.js +26 -0
- package/lib/types/ink/instances.d.ts +10 -0
- package/lib/types/ink/instances.d.ts.map +1 -0
- package/lib/types/ink/instances.js +13 -0
- package/lib/types/ink/kitty-graphics.d.ts +41 -0
- package/lib/types/ink/kitty-graphics.d.ts.map +1 -0
- package/lib/types/ink/kitty-graphics.js +229 -0
- package/lib/types/ink/layout/engine.d.ts +7 -0
- package/lib/types/ink/layout/engine.d.ts.map +1 -0
- package/lib/types/ink/layout/engine.js +8 -0
- package/lib/types/ink/layout/geometry.d.ts +86 -0
- package/lib/types/ink/layout/geometry.d.ts.map +1 -0
- package/lib/types/ink/layout/geometry.js +95 -0
- package/lib/types/ink/layout/node.d.ts +153 -0
- package/lib/types/ink/layout/node.d.ts.map +1 -0
- package/lib/types/ink/layout/node.js +72 -0
- package/lib/types/ink/layout/yoga.d.ts +70 -0
- package/lib/types/ink/layout/yoga.d.ts.map +1 -0
- package/lib/types/ink/layout/yoga.js +252 -0
- package/lib/types/ink/line-width-cache.d.ts +9 -0
- package/lib/types/ink/line-width-cache.d.ts.map +1 -0
- package/lib/types/ink/line-width-cache.js +45 -0
- package/lib/types/ink/log-update.d.ts +57 -0
- package/lib/types/ink/log-update.d.ts.map +1 -0
- package/lib/types/ink/log-update.js +1000 -0
- package/lib/types/ink/measure-element.d.ts +19 -0
- package/lib/types/ink/measure-element.d.ts.map +1 -0
- package/lib/types/ink/measure-element.js +10 -0
- package/lib/types/ink/measure-text.d.ts +13 -0
- package/lib/types/ink/measure-text.d.ts.map +1 -0
- package/lib/types/ink/measure-text.js +38 -0
- package/lib/types/ink/node-cache.d.ts +54 -0
- package/lib/types/ink/node-cache.d.ts.map +1 -0
- package/lib/types/ink/node-cache.js +45 -0
- package/lib/types/ink/optimizer.d.ts +18 -0
- package/lib/types/ink/optimizer.d.ts.map +1 -0
- package/lib/types/ink/optimizer.js +83 -0
- package/lib/types/ink/output.d.ts +300 -0
- package/lib/types/ink/output.d.ts.map +1 -0
- package/lib/types/ink/output.js +1205 -0
- package/lib/types/ink/parse-keypress.d.ts +217 -0
- package/lib/types/ink/parse-keypress.d.ts.map +1 -0
- package/lib/types/ink/parse-keypress.js +1550 -0
- package/lib/types/ink/reconciler.d.ts +43 -0
- package/lib/types/ink/reconciler.d.ts.map +1 -0
- package/lib/types/ink/reconciler.js +414 -0
- package/lib/types/ink/render-border.d.ts +41 -0
- package/lib/types/ink/render-border.d.ts.map +1 -0
- package/lib/types/ink/render-border.js +159 -0
- package/lib/types/ink/render-node-to-output.d.ts +199 -0
- package/lib/types/ink/render-node-to-output.d.ts.map +1 -0
- package/lib/types/ink/render-node-to-output.js +1758 -0
- package/lib/types/ink/render-to-screen.d.ts +65 -0
- package/lib/types/ink/render-to-screen.d.ts.map +1 -0
- package/lib/types/ink/render-to-screen.js +189 -0
- package/lib/types/ink/renderer.d.ts +34 -0
- package/lib/types/ink/renderer.d.ts.map +1 -0
- package/lib/types/ink/renderer.js +181 -0
- package/lib/types/ink/root.d.ts +109 -0
- package/lib/types/ink/root.d.ts.map +1 -0
- package/lib/types/ink/root.js +96 -0
- package/lib/types/ink/screen.d.ts +545 -0
- package/lib/types/ink/screen.d.ts.map +1 -0
- package/lib/types/ink/screen.js +1607 -0
- package/lib/types/ink/selection.d.ts +529 -0
- package/lib/types/ink/selection.d.ts.map +1 -0
- package/lib/types/ink/selection.js +1292 -0
- package/lib/types/ink/sixel-codec.d.ts +41 -0
- package/lib/types/ink/sixel-codec.d.ts.map +1 -0
- package/lib/types/ink/sixel-codec.js +128 -0
- package/lib/types/ink/sixel-graphics.d.ts +74 -0
- package/lib/types/ink/sixel-graphics.d.ts.map +1 -0
- package/lib/types/ink/sixel-graphics.js +498 -0
- package/lib/types/ink/sixel-worker.d.ts +2 -0
- package/lib/types/ink/sixel-worker.d.ts.map +1 -0
- package/lib/types/ink/sixel-worker.js +12 -0
- package/lib/types/ink/squash-text-nodes.d.ts +30 -0
- package/lib/types/ink/squash-text-nodes.d.ts.map +1 -0
- package/lib/types/ink/squash-text-nodes.js +63 -0
- package/lib/types/ink/stringWidth.d.ts +3 -0
- package/lib/types/ink/stringWidth.d.ts.map +1 -0
- package/lib/types/ink/stringWidth.js +248 -0
- package/lib/types/ink/styles.d.ts +346 -0
- package/lib/types/ink/styles.d.ts.map +1 -0
- package/lib/types/ink/styles.js +307 -0
- package/lib/types/ink/supports-hyperlinks.d.ts +20 -0
- package/lib/types/ink/supports-hyperlinks.d.ts.map +1 -0
- package/lib/types/ink/supports-hyperlinks.js +44 -0
- package/lib/types/ink/tabstops.d.ts +10 -0
- package/lib/types/ink/tabstops.d.ts.map +1 -0
- package/lib/types/ink/tabstops.js +47 -0
- package/lib/types/ink/terminal-focus-state.d.ts +35 -0
- package/lib/types/ink/terminal-focus-state.d.ts.map +1 -0
- package/lib/types/ink/terminal-focus-state.js +57 -0
- package/lib/types/ink/terminal-image-protocol.d.ts +4 -0
- package/lib/types/ink/terminal-image-protocol.d.ts.map +1 -0
- package/lib/types/ink/terminal-image-protocol.js +8 -0
- package/lib/types/ink/terminal-image.d.ts +102 -0
- package/lib/types/ink/terminal-image.d.ts.map +1 -0
- package/lib/types/ink/terminal-image.js +223 -0
- package/lib/types/ink/terminal-querier.d.ts +203 -0
- package/lib/types/ink/terminal-querier.d.ts.map +1 -0
- package/lib/types/ink/terminal-querier.js +286 -0
- package/lib/types/ink/terminal.d.ts +192 -0
- package/lib/types/ink/terminal.d.ts.map +1 -0
- package/lib/types/ink/terminal.js +436 -0
- package/lib/types/ink/termio/ansi.d.ts +75 -0
- package/lib/types/ink/termio/ansi.d.ts.map +1 -0
- package/lib/types/ink/termio/ansi.js +79 -0
- package/lib/types/ink/termio/csi.d.ts +263 -0
- package/lib/types/ink/termio/csi.d.ts.map +1 -0
- package/lib/types/ink/termio/csi.js +367 -0
- package/lib/types/ink/termio/dec.d.ts +61 -0
- package/lib/types/ink/termio/dec.d.ts.map +1 -0
- package/lib/types/ink/termio/dec.js +76 -0
- package/lib/types/ink/termio/esc.d.ts +15 -0
- package/lib/types/ink/termio/esc.d.ts.map +1 -0
- package/lib/types/ink/termio/esc.js +57 -0
- package/lib/types/ink/termio/osc.d.ts +198 -0
- package/lib/types/ink/termio/osc.d.ts.map +1 -0
- package/lib/types/ink/termio/osc.js +475 -0
- package/lib/types/ink/termio/parser.d.ts +46 -0
- package/lib/types/ink/termio/parser.d.ts.map +1 -0
- package/lib/types/ink/termio/parser.js +366 -0
- package/lib/types/ink/termio/sgr.d.ts +17 -0
- package/lib/types/ink/termio/sgr.d.ts.map +1 -0
- package/lib/types/ink/termio/sgr.js +300 -0
- package/lib/types/ink/termio/tokenize.d.ts +56 -0
- package/lib/types/ink/termio/tokenize.d.ts.map +1 -0
- package/lib/types/ink/termio/tokenize.js +290 -0
- package/lib/types/ink/termio/types.d.ts +211 -0
- package/lib/types/ink/termio/types.d.ts.map +1 -0
- package/lib/types/ink/termio/types.js +68 -0
- package/lib/types/ink/termio.d.ts +25 -0
- package/lib/types/ink/termio.d.ts.map +1 -0
- package/lib/types/ink/termio.js +24 -0
- package/lib/types/ink/timeline-rail.d.ts +134 -0
- package/lib/types/ink/timeline-rail.d.ts.map +1 -0
- package/lib/types/ink/timeline-rail.js +175 -0
- package/lib/types/ink/transcript-highlight.d.ts +25 -0
- package/lib/types/ink/transcript-highlight.d.ts.map +1 -0
- package/lib/types/ink/transcript-highlight.js +82 -0
- package/lib/types/ink/truncateToWidth.d.ts +7 -0
- package/lib/types/ink/truncateToWidth.d.ts.map +1 -0
- package/lib/types/ink/truncateToWidth.js +18 -0
- package/lib/types/ink/update-overflow-guard.d.ts +62 -0
- package/lib/types/ink/update-overflow-guard.d.ts.map +1 -0
- package/lib/types/ink/update-overflow-guard.js +164 -0
- package/lib/types/ink/useTerminalNotification.d.ts +45 -0
- package/lib/types/ink/useTerminalNotification.d.ts.map +1 -0
- package/lib/types/ink/useTerminalNotification.js +70 -0
- package/lib/types/ink/warn.d.ts +7 -0
- package/lib/types/ink/warn.d.ts.map +1 -0
- package/lib/types/ink/warn.js +15 -0
- package/lib/types/ink/widest-line.d.ts +7 -0
- package/lib/types/ink/widest-line.d.ts.map +1 -0
- package/lib/types/ink/widest-line.js +19 -0
- package/lib/types/ink/wrap-text.d.ts +10 -0
- package/lib/types/ink/wrap-text.d.ts.map +1 -0
- package/lib/types/ink/wrap-text.js +103 -0
- package/lib/types/ink/wrapAnsi.d.ts +17 -0
- package/lib/types/ink/wrapAnsi.d.ts.map +1 -0
- package/lib/types/ink/wrapAnsi.js +38 -0
- package/lib/types/jsx-runtime.d.ts +2 -0
- package/lib/types/jsx-runtime.d.ts.map +1 -0
- package/lib/types/jsx-runtime.js +13 -0
- package/lib/types/migrationPrefs.d.ts +60 -0
- package/lib/types/migrationPrefs.d.ts.map +1 -0
- package/lib/types/migrationPrefs.js +97 -0
- package/lib/types/minimalMode.d.ts +13 -0
- package/lib/types/minimalMode.d.ts.map +1 -0
- package/lib/types/minimalMode.js +17 -0
- package/lib/types/modelGroups.d.ts +72 -0
- package/lib/types/modelGroups.d.ts.map +1 -0
- package/lib/types/modelGroups.js +97 -0
- package/lib/types/modelPrefs.d.ts +37 -0
- package/lib/types/modelPrefs.d.ts.map +1 -0
- package/lib/types/modelPrefs.js +66 -0
- package/lib/types/modelRecents.d.ts +39 -0
- package/lib/types/modelRecents.d.ts.map +1 -0
- package/lib/types/modelRecents.js +97 -0
- package/lib/types/modelRoute.d.ts +87 -0
- package/lib/types/modelRoute.d.ts.map +1 -0
- package/lib/types/modelRoute.js +125 -0
- package/lib/types/native-ts/yoga-layout/enums.d.ts +183 -0
- package/lib/types/native-ts/yoga-layout/enums.d.ts.map +1 -0
- package/lib/types/native-ts/yoga-layout/enums.js +150 -0
- package/lib/types/native-ts/yoga-layout/index.d.ts +804 -0
- package/lib/types/native-ts/yoga-layout/index.d.ts.map +1 -0
- package/lib/types/native-ts/yoga-layout/index.js +2614 -0
- package/lib/types/oauth.d.ts +15 -0
- package/lib/types/oauth.d.ts.map +1 -0
- package/lib/types/oauth.js +14 -0
- package/lib/types/plugin-host.d.ts +14 -0
- package/lib/types/plugin-host.d.ts.map +1 -0
- package/lib/types/plugin-host.js +19 -0
- package/lib/types/presetPrefs.d.ts +30 -0
- package/lib/types/presetPrefs.d.ts.map +1 -0
- package/lib/types/presetPrefs.js +66 -0
- package/lib/types/reload.d.ts +109 -0
- package/lib/types/reload.d.ts.map +1 -0
- package/lib/types/reload.js +118 -0
- package/lib/types/scenes.d.ts +4 -0
- package/lib/types/scenes.d.ts.map +1 -0
- package/lib/types/scenes.js +4 -0
- package/lib/types/screens/Chat.d.ts +82 -0
- package/lib/types/screens/Chat.d.ts.map +1 -0
- package/lib/types/screens/Chat.js +4299 -0
- package/lib/types/screens/SessionSupervisor.d.ts +84 -0
- package/lib/types/screens/SessionSupervisor.d.ts.map +1 -0
- package/lib/types/screens/SessionSupervisor.js +350 -0
- package/lib/types/screens/SessionTree.d.ts +27 -0
- package/lib/types/screens/SessionTree.d.ts.map +1 -0
- package/lib/types/screens/SessionTree.js +583 -0
- package/lib/types/screens/Settings.d.ts +22 -0
- package/lib/types/screens/Settings.d.ts.map +1 -0
- package/lib/types/screens/Settings.js +592 -0
- package/lib/types/screens/StatusLine.d.ts +48 -0
- package/lib/types/screens/StatusLine.d.ts.map +1 -0
- package/lib/types/screens/StatusLine.js +453 -0
- package/lib/types/screens/StatusMetrics.d.ts +176 -0
- package/lib/types/screens/StatusMetrics.d.ts.map +1 -0
- package/lib/types/screens/StatusMetrics.js +372 -0
- package/lib/types/screens/TrajectoryScene.d.ts +16 -0
- package/lib/types/screens/TrajectoryScene.d.ts.map +1 -0
- package/lib/types/screens/TrajectoryScene.js +374 -0
- package/lib/types/screens/chatOverlay.d.ts +267 -0
- package/lib/types/screens/chatOverlay.d.ts.map +1 -0
- package/lib/types/screens/chatOverlay.js +132 -0
- package/lib/types/screens/sessionSupervisor/model.d.ts +83 -0
- package/lib/types/screens/sessionSupervisor/model.d.ts.map +1 -0
- package/lib/types/screens/sessionSupervisor/model.js +69 -0
- package/lib/types/screens/sessionSupervisor/useSessionSupervisor.d.ts +120 -0
- package/lib/types/screens/sessionSupervisor/useSessionSupervisor.d.ts.map +1 -0
- package/lib/types/screens/sessionSupervisor/useSessionSupervisor.js +639 -0
- package/lib/types/sessionHistory.d.ts +59 -0
- package/lib/types/sessionHistory.d.ts.map +1 -0
- package/lib/types/sessionHistory.js +223 -0
- package/lib/types/sessionModes.d.ts +83 -0
- package/lib/types/sessionModes.d.ts.map +1 -0
- package/lib/types/sessionModes.js +141 -0
- package/lib/types/sessionMounts.d.ts +272 -0
- package/lib/types/sessionMounts.d.ts.map +1 -0
- package/lib/types/sessionMounts.js +706 -0
- package/lib/types/sessionPins.d.ts +33 -0
- package/lib/types/sessionPins.d.ts.map +1 -0
- package/lib/types/sessionPins.js +181 -0
- package/lib/types/sessions/format.d.ts +125 -0
- package/lib/types/sessions/format.d.ts.map +1 -0
- package/lib/types/sessions/format.js +276 -0
- package/lib/types/sessions/resumeFailure.d.ts +38 -0
- package/lib/types/sessions/resumeFailure.d.ts.map +1 -0
- package/lib/types/sessions/resumeFailure.js +58 -0
- package/lib/types/sessions/view.d.ts +162 -0
- package/lib/types/sessions/view.d.ts.map +1 -0
- package/lib/types/sessions/view.js +350 -0
- package/lib/types/settings-sections.d.ts +4 -0
- package/lib/types/settings-sections.d.ts.map +1 -0
- package/lib/types/settings-sections.js +4 -0
- package/lib/types/terminal-utils/cliHighlight.d.ts +20 -0
- package/lib/types/terminal-utils/cliHighlight.d.ts.map +1 -0
- package/lib/types/terminal-utils/cliHighlight.js +53 -0
- package/lib/types/terminal-utils/figures.d.ts +26 -0
- package/lib/types/terminal-utils/figures.d.ts.map +1 -0
- package/lib/types/terminal-utils/figures.js +36 -0
- package/lib/types/terminal-utils/format.d.ts +35 -0
- package/lib/types/terminal-utils/format.d.ts.map +1 -0
- package/lib/types/terminal-utils/format.js +63 -0
- package/lib/types/terminal-utils/hash.d.ts +25 -0
- package/lib/types/terminal-utils/hash.d.ts.map +1 -0
- package/lib/types/terminal-utils/hash.js +41 -0
- package/lib/types/terminal-utils/hyperlink.d.ts +27 -0
- package/lib/types/terminal-utils/hyperlink.d.ts.map +1 -0
- package/lib/types/terminal-utils/hyperlink.js +70 -0
- package/lib/types/terminal-utils/markdown.d.ts +58 -0
- package/lib/types/terminal-utils/markdown.d.ts.map +1 -0
- package/lib/types/terminal-utils/markdown.js +441 -0
- package/lib/types/terminal-utils/mermaid.d.ts +44 -0
- package/lib/types/terminal-utils/mermaid.d.ts.map +1 -0
- package/lib/types/terminal-utils/mermaid.js +83 -0
- package/lib/types/terminal-utils/sessionColors.d.ts +27 -0
- package/lib/types/terminal-utils/sessionColors.d.ts.map +1 -0
- package/lib/types/terminal-utils/sessionColors.js +39 -0
- package/lib/types/terminal-utils/spinnerVerbs.d.ts +3 -0
- package/lib/types/terminal-utils/spinnerVerbs.d.ts.map +1 -0
- package/lib/types/terminal-utils/spinnerVerbs.js +21 -0
- package/lib/types/terminal-utils/syntaxRuns.d.ts +16 -0
- package/lib/types/terminal-utils/syntaxRuns.d.ts.map +1 -0
- package/lib/types/terminal-utils/syntaxRuns.js +79 -0
- package/lib/types/terminal-utils/syntaxTheme.d.ts +12 -0
- package/lib/types/terminal-utils/syntaxTheme.d.ts.map +1 -0
- package/lib/types/terminal-utils/syntaxTheme.js +76 -0
- package/lib/types/theme.d.ts +201 -0
- package/lib/types/theme.d.ts.map +1 -0
- package/lib/types/theme.js +525 -0
- package/lib/types/themeCatalog.d.ts +36 -0
- package/lib/types/themeCatalog.d.ts.map +1 -0
- package/lib/types/themeCatalog.js +159 -0
- package/lib/types/themePrefs.d.ts +28 -0
- package/lib/types/themePrefs.d.ts.map +1 -0
- package/lib/types/themePrefs.js +59 -0
- package/lib/types/tips.d.ts +30 -0
- package/lib/types/tips.d.ts.map +1 -0
- package/lib/types/tips.js +899 -0
- package/lib/types/trajectory/effortIgnition.d.ts +66 -0
- package/lib/types/trajectory/effortIgnition.d.ts.map +1 -0
- package/lib/types/trajectory/effortIgnition.js +142 -0
- package/lib/types/trajectory/format.d.ts +100 -0
- package/lib/types/trajectory/format.d.ts.map +1 -0
- package/lib/types/trajectory/format.js +213 -0
- package/lib/types/trajectory/motion.d.ts +99 -0
- package/lib/types/trajectory/motion.d.ts.map +1 -0
- package/lib/types/trajectory/motion.js +143 -0
- package/lib/types/trajectory/query.d.ts +83 -0
- package/lib/types/trajectory/query.d.ts.map +1 -0
- package/lib/types/trajectory/query.js +187 -0
- package/lib/types/trajectoryPrefs.d.ts +36 -0
- package/lib/types/trajectoryPrefs.d.ts.map +1 -0
- package/lib/types/trajectoryPrefs.js +65 -0
- package/lib/types/tuiDisplayPrefs.d.ts +49 -0
- package/lib/types/tuiDisplayPrefs.d.ts.map +1 -0
- package/lib/types/tuiDisplayPrefs.js +168 -0
- package/lib/types/ui.d.ts +29 -0
- package/lib/types/ui.d.ts.map +1 -0
- package/lib/types/ui.js +25 -0
- package/lib/types/update.d.ts +550 -0
- package/lib/types/update.d.ts.map +1 -0
- package/lib/types/update.js +1751 -0
- package/lib/types/utils/clipboard.d.ts +122 -0
- package/lib/types/utils/clipboard.d.ts.map +1 -0
- package/lib/types/utils/clipboard.js +577 -0
- package/lib/types/utils/credentials.d.ts +13 -0
- package/lib/types/utils/credentials.d.ts.map +1 -0
- package/lib/types/utils/credentials.js +41 -0
- package/lib/types/utils/debug.d.ts +18 -0
- package/lib/types/utils/debug.d.ts.map +1 -0
- package/lib/types/utils/debug.js +37 -0
- package/lib/types/utils/earlyInput.d.ts +6 -0
- package/lib/types/utils/earlyInput.d.ts.map +1 -0
- package/lib/types/utils/earlyInput.js +5 -0
- package/lib/types/utils/env.d.ts +8 -0
- package/lib/types/utils/env.d.ts.map +1 -0
- package/lib/types/utils/env.js +7 -0
- package/lib/types/utils/envUtils.d.ts +8 -0
- package/lib/types/utils/envUtils.d.ts.map +1 -0
- package/lib/types/utils/envUtils.js +13 -0
- package/lib/types/utils/execFileNoThrow.d.ts +17 -0
- package/lib/types/utils/execFileNoThrow.d.ts.map +1 -0
- package/lib/types/utils/execFileNoThrow.js +65 -0
- package/lib/types/utils/externalEditor.d.ts +116 -0
- package/lib/types/utils/externalEditor.d.ts.map +1 -0
- package/lib/types/utils/externalEditor.js +270 -0
- package/lib/types/utils/fileSuggestions.d.ts +12 -0
- package/lib/types/utils/fileSuggestions.d.ts.map +1 -0
- package/lib/types/utils/fileSuggestions.js +40 -0
- package/lib/types/utils/fileTarget.d.ts +46 -0
- package/lib/types/utils/fileTarget.d.ts.map +1 -0
- package/lib/types/utils/fileTarget.js +178 -0
- package/lib/types/utils/fold-long-lines.d.ts +38 -0
- package/lib/types/utils/fold-long-lines.d.ts.map +1 -0
- package/lib/types/utils/fold-long-lines.js +91 -0
- package/lib/types/utils/fullscreen.d.ts +7 -0
- package/lib/types/utils/fullscreen.d.ts.map +1 -0
- package/lib/types/utils/fullscreen.js +9 -0
- package/lib/types/utils/imageResize.d.ts +90 -0
- package/lib/types/utils/imageResize.d.ts.map +1 -0
- package/lib/types/utils/imageResize.js +272 -0
- package/lib/types/utils/intl.d.ts +7 -0
- package/lib/types/utils/intl.d.ts.map +1 -0
- package/lib/types/utils/intl.js +9 -0
- package/lib/types/utils/keymap.d.ts +138 -0
- package/lib/types/utils/keymap.d.ts.map +1 -0
- package/lib/types/utils/keymap.js +351 -0
- package/lib/types/utils/loaded-context.d.ts +25 -0
- package/lib/types/utils/loaded-context.d.ts.map +1 -0
- package/lib/types/utils/loaded-context.js +72 -0
- package/lib/types/utils/log.d.ts +7 -0
- package/lib/types/utils/log.d.ts.map +1 -0
- package/lib/types/utils/log.js +9 -0
- package/lib/types/utils/mentions.d.ts +53 -0
- package/lib/types/utils/mentions.d.ts.map +1 -0
- package/lib/types/utils/mentions.js +133 -0
- package/lib/types/utils/modifiers.d.ts +43 -0
- package/lib/types/utils/modifiers.d.ts.map +1 -0
- package/lib/types/utils/modifiers.js +45 -0
- package/lib/types/utils/narration.d.ts +11 -0
- package/lib/types/utils/narration.d.ts.map +1 -0
- package/lib/types/utils/narration.js +16 -0
- package/lib/types/utils/openExternal.d.ts +50 -0
- package/lib/types/utils/openExternal.d.ts.map +1 -0
- package/lib/types/utils/openExternal.js +170 -0
- package/lib/types/utils/pastedImagePath.d.ts +51 -0
- package/lib/types/utils/pastedImagePath.d.ts.map +1 -0
- package/lib/types/utils/pastedImagePath.js +194 -0
- package/lib/types/utils/paths.d.ts +16 -0
- package/lib/types/utils/paths.d.ts.map +1 -0
- package/lib/types/utils/paths.js +19 -0
- package/lib/types/utils/semver.d.ts +9 -0
- package/lib/types/utils/semver.d.ts.map +1 -0
- package/lib/types/utils/semver.js +24 -0
- package/lib/types/utils/shellQuote.d.ts +14 -0
- package/lib/types/utils/shellQuote.d.ts.map +1 -0
- package/lib/types/utils/shellQuote.js +39 -0
- package/lib/types/utils/sliceAnsi.d.ts +12 -0
- package/lib/types/utils/sliceAnsi.d.ts.map +1 -0
- package/lib/types/utils/sliceAnsi.js +79 -0
- package/lib/types/utils/truncateMiddle.d.ts +9 -0
- package/lib/types/utils/truncateMiddle.d.ts.map +1 -0
- package/lib/types/utils/truncateMiddle.js +28 -0
- package/lib/types/utils/urlGuard.d.ts +28 -0
- package/lib/types/utils/urlGuard.d.ts.map +1 -0
- package/lib/types/utils/urlGuard.js +21 -0
- package/lib/types/utils/workspaceRoot.d.ts +21 -0
- package/lib/types/utils/workspaceRoot.d.ts.map +1 -0
- package/lib/types/utils/workspaceRoot.js +47 -0
- package/lib/types/working-activity.d.ts +32 -0
- package/lib/types/working-activity.d.ts.map +1 -0
- package/lib/types/working-activity.js +38 -0
- package/lib/types/workspaces.d.ts +5 -0
- package/lib/types/workspaces.d.ts.map +1 -0
- package/lib/types/workspaces.js +4 -0
- package/node_modules/@askdkc/dsh-auth/LICENSE +21 -0
- package/node_modules/@askdkc/dsh-auth/README.ja.md +164 -0
- package/node_modules/@askdkc/dsh-auth/README.md +163 -0
- package/node_modules/@askdkc/dsh-auth/cordis.patch.yml +11 -0
- package/node_modules/@askdkc/dsh-auth/dsh-plugin.json +44 -0
- package/node_modules/@askdkc/dsh-auth/lib/command.d.ts +14 -0
- package/node_modules/@askdkc/dsh-auth/lib/command.js +70 -0
- package/node_modules/@askdkc/dsh-auth/lib/credentials.d.ts +63 -0
- package/node_modules/@askdkc/dsh-auth/lib/credentials.js +204 -0
- package/node_modules/@askdkc/dsh-auth/lib/custom-profiles.d.ts +26 -0
- package/node_modules/@askdkc/dsh-auth/lib/custom-profiles.js +142 -0
- package/node_modules/@askdkc/dsh-auth/lib/index.d.ts +96 -0
- package/node_modules/@askdkc/dsh-auth/lib/index.js +263 -0
- package/node_modules/@askdkc/dsh-auth/lib/interaction.d.ts +68 -0
- package/node_modules/@askdkc/dsh-auth/lib/interaction.js +193 -0
- package/node_modules/@askdkc/dsh-auth/lib/nous-oauth.d.ts +5 -0
- package/node_modules/@askdkc/dsh-auth/lib/nous-oauth.js +112 -0
- package/node_modules/@askdkc/dsh-auth/lib/opener.d.ts +32 -0
- package/node_modules/@askdkc/dsh-auth/lib/opener.js +101 -0
- package/node_modules/@askdkc/dsh-auth/lib/pi-ai.d.ts +23 -0
- package/node_modules/@askdkc/dsh-auth/lib/pi-ai.js +27 -0
- package/node_modules/@askdkc/dsh-auth/lib/profiles.d.ts +45 -0
- package/node_modules/@askdkc/dsh-auth/lib/profiles.js +110 -0
- package/node_modules/@askdkc/dsh-auth/lib/service.d.ts +80 -0
- package/node_modules/@askdkc/dsh-auth/lib/service.js +224 -0
- package/node_modules/@askdkc/dsh-auth/package.json +70 -0
- package/node_modules/@dsh-std/command/CHANGELOG.md +8 -0
- package/node_modules/@dsh-std/command/LICENSE +21 -0
- package/node_modules/@dsh-std/command/README.md +26 -0
- package/node_modules/@dsh-std/command/README.zh.md +28 -0
- package/node_modules/@dsh-std/command/lib/index.d.ts +144 -0
- package/node_modules/@dsh-std/command/lib/index.d.ts.map +1 -0
- package/node_modules/@dsh-std/command/lib/index.js +252 -0
- package/node_modules/@dsh-std/command/lib/index.js.map +1 -0
- package/node_modules/@dsh-std/command/package.json +22 -0
- package/node_modules/@dsh-std/connection/CHANGELOG.md +8 -0
- package/node_modules/@dsh-std/connection/LICENSE +21 -0
- package/node_modules/@dsh-std/connection/README.md +42 -0
- package/node_modules/@dsh-std/connection/README.zh.md +42 -0
- package/node_modules/@dsh-std/connection/lib/connection-CdGcSQnu.d.ts +125 -0
- package/node_modules/@dsh-std/connection/lib/connection-CdGcSQnu.d.ts.map +1 -0
- package/node_modules/@dsh-std/connection/lib/index.d.ts +89 -0
- package/node_modules/@dsh-std/connection/lib/index.d.ts.map +1 -0
- package/node_modules/@dsh-std/connection/lib/index.js +158 -0
- package/node_modules/@dsh-std/connection/lib/index.js.map +1 -0
- package/node_modules/@dsh-std/connection/lib/memory.d.ts +24 -0
- package/node_modules/@dsh-std/connection/lib/memory.d.ts.map +1 -0
- package/node_modules/@dsh-std/connection/lib/memory.js +258 -0
- package/node_modules/@dsh-std/connection/lib/memory.js.map +1 -0
- package/node_modules/@dsh-std/connection/lib/resolve-3X6ausgz.js +226 -0
- package/node_modules/@dsh-std/connection/lib/resolve-3X6ausgz.js.map +1 -0
- package/node_modules/@dsh-std/connection/package.json +20 -0
- package/node_modules/@dsh-std/core/CHANGELOG.md +8 -0
- package/node_modules/@dsh-std/core/LICENSE +21 -0
- package/node_modules/@dsh-std/core/README.md +13 -0
- package/node_modules/@dsh-std/core/README.zh.md +13 -0
- package/node_modules/@dsh-std/core/lib/index.d.ts +112 -0
- package/node_modules/@dsh-std/core/lib/index.d.ts.map +1 -0
- package/node_modules/@dsh-std/core/lib/index.js +424 -0
- package/node_modules/@dsh-std/core/lib/index.js.map +1 -0
- package/node_modules/@dsh-std/core/package.json +21 -0
- package/node_modules/@dsh-std/manifest/CHANGELOG.md +9 -0
- package/node_modules/@dsh-std/manifest/LICENSE +21 -0
- package/node_modules/@dsh-std/manifest/README.md +5 -0
- package/node_modules/@dsh-std/manifest/README.zh.md +5 -0
- package/node_modules/@dsh-std/manifest/lib/index.d.ts +182 -0
- package/node_modules/@dsh-std/manifest/lib/index.d.ts.map +1 -0
- package/node_modules/@dsh-std/manifest/lib/index.js +625 -0
- package/node_modules/@dsh-std/manifest/lib/index.js.map +1 -0
- package/node_modules/@dsh-std/manifest/package.json +20 -0
- package/node_modules/@dsh-std/manifest/schema/dsh-plugin-0.15.schema.json +144 -0
- package/node_modules/@dsh-std/messages/CHANGELOG.md +8 -0
- package/node_modules/@dsh-std/messages/LICENSE +21 -0
- package/node_modules/@dsh-std/messages/README.md +5 -0
- package/node_modules/@dsh-std/messages/README.zh.md +7 -0
- package/node_modules/@dsh-std/messages/lib/index.d.ts +50 -0
- package/node_modules/@dsh-std/messages/lib/index.d.ts.map +1 -0
- package/node_modules/@dsh-std/messages/lib/index.js +151 -0
- package/node_modules/@dsh-std/messages/lib/index.js.map +1 -0
- package/node_modules/@dsh-std/messages/package.json +20 -0
- package/node_modules/@dsh-std/messages/schema/message-observer-envelope-0.15.schema.json +54 -0
- package/node_modules/@dsh-std/presentation/CHANGELOG.md +9 -0
- package/node_modules/@dsh-std/presentation/LICENSE +21 -0
- package/node_modules/@dsh-std/presentation/README.md +27 -0
- package/node_modules/@dsh-std/presentation/README.zh.md +29 -0
- package/node_modules/@dsh-std/presentation/lib/callback.d.ts +2 -0
- package/node_modules/@dsh-std/presentation/lib/callback.js +179 -0
- package/node_modules/@dsh-std/presentation/lib/callback.js.map +1 -0
- package/node_modules/@dsh-std/presentation/lib/index.d.ts +262 -0
- package/node_modules/@dsh-std/presentation/lib/index.d.ts.map +1 -0
- package/node_modules/@dsh-std/presentation/lib/index.js +550 -0
- package/node_modules/@dsh-std/presentation/lib/index.js.map +1 -0
- package/node_modules/@dsh-std/presentation/lib/interaction.d.ts +2 -0
- package/node_modules/@dsh-std/presentation/lib/interaction.js +2 -0
- package/node_modules/@dsh-std/presentation/lib/operations.d.ts +2 -0
- package/node_modules/@dsh-std/presentation/lib/operations.js +2 -0
- package/node_modules/@dsh-std/presentation/package.json +25 -0
- package/node_modules/@dsh-std/storage/CHANGELOG.md +8 -0
- package/node_modules/@dsh-std/storage/LICENSE +21 -0
- package/node_modules/@dsh-std/storage/README.md +5 -0
- package/node_modules/@dsh-std/storage/README.zh.md +7 -0
- package/node_modules/@dsh-std/storage/lib/index.d.ts +48 -0
- package/node_modules/@dsh-std/storage/lib/index.d.ts.map +1 -0
- package/node_modules/@dsh-std/storage/lib/index.js +104 -0
- package/node_modules/@dsh-std/storage/lib/index.js.map +1 -0
- package/node_modules/@dsh-std/storage/package.json +20 -0
- package/node_modules/@dsh-std/storage/schema/local-storage-0.15.schema.json +56 -0
- package/package.json +492 -0
- package/presets/liangshen/.dsh-tui-managed.json +5 -0
- package/presets/liangshen/agent.cordis.yml +425 -0
- package/presets/liangshen/compaction-epoch.mjs +83 -0
- package/presets/liangshen/custom-bash.mjs +301 -0
- package/presets/liangshen/instruction-hint.mjs +190 -0
- package/presets/liangshen/preset.yml +3 -0
- package/presets/liangshen/session-events.mjs +8 -0
- package/presets/liangshen/skill-search.mjs +141 -0
- package/presets/liangshen/tool-bootstrap.mjs +300 -0
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/CHANGELOG.md +8 -0
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/LICENSE +21 -0
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/README.md +42 -0
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/README.zh.md +42 -0
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/lib/connection-CdGcSQnu.d.ts +125 -0
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/lib/connection-CdGcSQnu.d.ts.map +1 -0
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/lib/index.d.ts +89 -0
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/lib/index.d.ts.map +1 -0
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/lib/index.js +158 -0
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/lib/index.js.map +1 -0
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/lib/memory.d.ts +24 -0
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/lib/memory.d.ts.map +1 -0
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/lib/memory.js +258 -0
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/lib/memory.js.map +1 -0
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/lib/resolve-3X6ausgz.js +226 -0
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/lib/resolve-3X6ausgz.js.map +1 -0
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/package.json +20 -0
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/core/CHANGELOG.md +8 -0
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/core/LICENSE +21 -0
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/core/README.md +13 -0
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/core/README.zh.md +13 -0
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/core/lib/index.d.ts +112 -0
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/core/lib/index.d.ts.map +1 -0
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/core/lib/index.js +424 -0
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/core/lib/index.js.map +1 -0
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/core/package.json +21 -0
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/manifest/CHANGELOG.md +9 -0
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/manifest/LICENSE +21 -0
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/manifest/README.md +5 -0
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/manifest/README.zh.md +5 -0
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/manifest/lib/index.d.ts +182 -0
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/manifest/lib/index.d.ts.map +1 -0
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/manifest/lib/index.js +625 -0
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/manifest/lib/index.js.map +1 -0
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/manifest/package.json +20 -0
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/manifest/schema/dsh-plugin-0.15.schema.json +144 -0
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/CHANGELOG.md +9 -0
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/LICENSE +21 -0
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/README.md +27 -0
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/README.zh.md +29 -0
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/lib/callback.d.ts +2 -0
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/lib/callback.js +179 -0
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/lib/callback.js.map +1 -0
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/lib/index.d.ts +262 -0
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/lib/index.d.ts.map +1 -0
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/lib/index.js +550 -0
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/lib/index.js.map +1 -0
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/lib/interaction.d.ts +2 -0
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/lib/interaction.js +2 -0
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/lib/operations.d.ts +2 -0
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/lib/operations.js +2 -0
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/package.json +25 -0
- package/vendor/dsh-std/packages/connection/node_modules/@dsh-std/core/CHANGELOG.md +8 -0
- package/vendor/dsh-std/packages/connection/node_modules/@dsh-std/core/LICENSE +21 -0
- package/vendor/dsh-std/packages/connection/node_modules/@dsh-std/core/README.md +13 -0
- package/vendor/dsh-std/packages/connection/node_modules/@dsh-std/core/README.zh.md +13 -0
- package/vendor/dsh-std/packages/connection/node_modules/@dsh-std/core/lib/index.d.ts +112 -0
- package/vendor/dsh-std/packages/connection/node_modules/@dsh-std/core/lib/index.d.ts.map +1 -0
- package/vendor/dsh-std/packages/connection/node_modules/@dsh-std/core/lib/index.js +424 -0
- package/vendor/dsh-std/packages/connection/node_modules/@dsh-std/core/lib/index.js.map +1 -0
- package/vendor/dsh-std/packages/connection/node_modules/@dsh-std/core/package.json +21 -0
- package/vendor/dsh-std/packages/manifest/node_modules/@dsh-std/core/CHANGELOG.md +8 -0
- package/vendor/dsh-std/packages/manifest/node_modules/@dsh-std/core/LICENSE +21 -0
- package/vendor/dsh-std/packages/manifest/node_modules/@dsh-std/core/README.md +13 -0
- package/vendor/dsh-std/packages/manifest/node_modules/@dsh-std/core/README.zh.md +13 -0
- package/vendor/dsh-std/packages/manifest/node_modules/@dsh-std/core/lib/index.d.ts +112 -0
- package/vendor/dsh-std/packages/manifest/node_modules/@dsh-std/core/lib/index.d.ts.map +1 -0
- package/vendor/dsh-std/packages/manifest/node_modules/@dsh-std/core/lib/index.js +424 -0
- package/vendor/dsh-std/packages/manifest/node_modules/@dsh-std/core/lib/index.js.map +1 -0
- package/vendor/dsh-std/packages/manifest/node_modules/@dsh-std/core/package.json +21 -0
- package/vendor/dsh-std/packages/messages/node_modules/@dsh-std/core/CHANGELOG.md +8 -0
- package/vendor/dsh-std/packages/messages/node_modules/@dsh-std/core/LICENSE +21 -0
- package/vendor/dsh-std/packages/messages/node_modules/@dsh-std/core/README.md +13 -0
- package/vendor/dsh-std/packages/messages/node_modules/@dsh-std/core/README.zh.md +13 -0
- package/vendor/dsh-std/packages/messages/node_modules/@dsh-std/core/lib/index.d.ts +112 -0
- package/vendor/dsh-std/packages/messages/node_modules/@dsh-std/core/lib/index.d.ts.map +1 -0
- package/vendor/dsh-std/packages/messages/node_modules/@dsh-std/core/lib/index.js +424 -0
- package/vendor/dsh-std/packages/messages/node_modules/@dsh-std/core/lib/index.js.map +1 -0
- package/vendor/dsh-std/packages/messages/node_modules/@dsh-std/core/package.json +21 -0
- package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/CHANGELOG.md +8 -0
- package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/LICENSE +21 -0
- package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/README.md +42 -0
- package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/README.zh.md +42 -0
- package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/lib/connection-CdGcSQnu.d.ts +125 -0
- package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/lib/connection-CdGcSQnu.d.ts.map +1 -0
- package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/lib/index.d.ts +89 -0
- package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/lib/index.d.ts.map +1 -0
- package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/lib/index.js +158 -0
- package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/lib/index.js.map +1 -0
- package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/lib/memory.d.ts +24 -0
- package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/lib/memory.d.ts.map +1 -0
- package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/lib/memory.js +258 -0
- package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/lib/memory.js.map +1 -0
- package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/lib/resolve-3X6ausgz.js +226 -0
- package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/lib/resolve-3X6ausgz.js.map +1 -0
- package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/package.json +20 -0
- package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/core/CHANGELOG.md +8 -0
- package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/core/LICENSE +21 -0
- package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/core/README.md +13 -0
- package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/core/README.zh.md +13 -0
- package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/core/lib/index.d.ts +112 -0
- package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/core/lib/index.d.ts.map +1 -0
- package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/core/lib/index.js +424 -0
- package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/core/lib/index.js.map +1 -0
- package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/core/package.json +21 -0
- package/vendor/dsh-std/packages/storage/node_modules/@dsh-std/core/CHANGELOG.md +8 -0
- package/vendor/dsh-std/packages/storage/node_modules/@dsh-std/core/LICENSE +21 -0
- package/vendor/dsh-std/packages/storage/node_modules/@dsh-std/core/README.md +13 -0
- package/vendor/dsh-std/packages/storage/node_modules/@dsh-std/core/README.zh.md +13 -0
- package/vendor/dsh-std/packages/storage/node_modules/@dsh-std/core/lib/index.d.ts +112 -0
- package/vendor/dsh-std/packages/storage/node_modules/@dsh-std/core/lib/index.d.ts.map +1 -0
- package/vendor/dsh-std/packages/storage/node_modules/@dsh-std/core/lib/index.js +424 -0
- package/vendor/dsh-std/packages/storage/node_modules/@dsh-std/core/lib/index.js.map +1 -0
- package/vendor/dsh-std/packages/storage/node_modules/@dsh-std/core/package.json +21 -0
|
@@ -0,0 +1,3503 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import stripAnsi from 'strip-ansi';
|
|
4
|
+
import { constants as fsConstants } from 'node:fs';
|
|
5
|
+
import { open, unlink } from 'node:fs/promises';
|
|
6
|
+
import { basename, resolve } from 'node:path';
|
|
7
|
+
import { t } from '../i18n.js';
|
|
8
|
+
import { Box, Text, useInput, useTerminalSize, useTheme } from '../ui.js';
|
|
9
|
+
import { EffortChargeGlyph } from './EffortChargeGlyph.js';
|
|
10
|
+
import { EffortInputBorder } from './EffortInputBorder.js';
|
|
11
|
+
import { EffortTierBadge } from './EffortTierBadge.js';
|
|
12
|
+
import { isLightThemeActive } from '../theme.js';
|
|
13
|
+
import { sessionColorHex } from '../terminal-utils/sessionColors.js';
|
|
14
|
+
import { useDeclaredCursor } from '../ink/hooks/use-declared-cursor.js';
|
|
15
|
+
import { TerminalWriteContext } from '../ink/useTerminalNotification.js';
|
|
16
|
+
import { setClipboard } from '../ink/termio/osc.js';
|
|
17
|
+
import { noteAuxNumber } from '../ink/geometry-trace.js';
|
|
18
|
+
import instances from '../ink/instances.js';
|
|
19
|
+
import { stringWidth } from '../ink/stringWidth.js';
|
|
20
|
+
import { truncateToWidth } from '../ink/truncateToWidth.js';
|
|
21
|
+
import { getGraphemeSegmenter } from '../utils/intl.js';
|
|
22
|
+
import { formatClipboardInsert, readClipboard } from '../utils/clipboard.js';
|
|
23
|
+
import { imagePathMediaType, parsePastedImagePath, stageClipboardFilePaths } from '../utils/pastedImagePath.js';
|
|
24
|
+
import { editInExternalEditor } from '../utils/externalEditor.js';
|
|
25
|
+
import { setPromptEditorNode, EditorButton } from './PromptEditor.js';
|
|
26
|
+
import { isHiddenCommandName, parseCommandName } from '../commands.js';
|
|
27
|
+
import { appendHistory, HISTORY_LIMIT, loadHistoryOldestFirst } from '../history.js';
|
|
28
|
+
import { mentionAtCaret } from '../utils/mentions.js';
|
|
29
|
+
import { preserveSelection } from '../utils/fileSuggestions.js';
|
|
30
|
+
import { isMod } from '../utils/modifiers.js';
|
|
31
|
+
import { actionMatches } from '../utils/keymap.js';
|
|
32
|
+
import { CommandSuggestions } from './CommandSuggestions.js';
|
|
33
|
+
import { FileSuggestions } from './FileSuggestions.js';
|
|
34
|
+
import { HelpMenu } from './HelpMenu.js';
|
|
35
|
+
import { OverlayAbove } from './OverlayAbove.js';
|
|
36
|
+
import { SuggestionCard, cardContentWidth } from './SuggestionCard.js';
|
|
37
|
+
import { filterLiveImageBindings, isUsableDraftSnapshot, resolveBindingGeneration, } from './promptDraftCache.js';
|
|
38
|
+
/**
|
|
39
|
+
* Visible text of the session-entry control at the head of the input row:
|
|
40
|
+
* U+2338 APL FUNCTIONAL SYMBOL QUAD COLON plus a separator space.
|
|
41
|
+
*
|
|
42
|
+
* The glyph is chosen for ONE property above all others: the layout model and
|
|
43
|
+
* the terminal cell table must agree on how many columns it occupies. `\u2338`
|
|
44
|
+
* is a plain single-cell symbol with no emoji presentation, so
|
|
45
|
+
* `stringWidth` and the terminal both say 1 + 1 = 2 and
|
|
46
|
+
* {@link HOME_BUTTON_COLS} can be derived rather than guessed.
|
|
47
|
+
*
|
|
48
|
+
* The emoji house (`U+1F3E0`) was tried and rejected on this exact ground: it
|
|
49
|
+
* costs the terminal ONE cell while `stringWidth` charges the row TWO, and
|
|
50
|
+
* appending VARIATION SELECTOR-15 (text presentation) does not change either
|
|
51
|
+
* number. That standing one-column disagreement put every caret-relative
|
|
52
|
+
* column in the composer off by one, so no offset compensation made it work.
|
|
53
|
+
*/
|
|
54
|
+
const HOME_BUTTON_TEXT = '⌸ ';
|
|
55
|
+
const HOME_BUTTON_COLS = stringWidth(HOME_BUTTON_TEXT);
|
|
56
|
+
/**
|
|
57
|
+
* Paste fold with a visible preview and no black box:
|
|
58
|
+
* a paste that leaves the input this big folds into a one-line chip
|
|
59
|
+
* showing the line/char count PLUS the first line of content. Hover peeks
|
|
60
|
+
* at the full text (window pinned to the head); clicking the chip — or
|
|
61
|
+
* the `▾` prefix on the first row while expanded — toggles the fold; Esc
|
|
62
|
+
* or any editing key unfolds first. Enter still submits the FULL text:
|
|
63
|
+
* folding never drops data.
|
|
64
|
+
*/
|
|
65
|
+
const FOLD_MIN_LINES = 6;
|
|
66
|
+
const FOLD_MIN_CHARS = 600;
|
|
67
|
+
const isBigInput = (text) => text.split('\n').length >= FOLD_MIN_LINES || text.length >= FOLD_MIN_CHARS;
|
|
68
|
+
/**
|
|
69
|
+
* Editable prompt text must have one stable source-to-screen geometry. The
|
|
70
|
+
* renderer interprets ANSI as zero-width styling and expands tabs relative to
|
|
71
|
+
* global tab stops; keeping either in `value` would let wrapping/click mapping
|
|
72
|
+
* count different cells and could split an escape sequence during selection.
|
|
73
|
+
* Strip terminal controls and expand tabs at ingress while preserving newlines.
|
|
74
|
+
*/
|
|
75
|
+
const EDITABLE_CONTROL = /[\u0000-\u0009\u000b-\u001f\u007f]/u;
|
|
76
|
+
/** Normalize editable text so no terminal control characters remain in state. */
|
|
77
|
+
function sanitizeEditableText(text) {
|
|
78
|
+
// Fast path for ordinary and multi-line drafts: newline is intentionally
|
|
79
|
+
// absent from the probe, so a large clean paste returns without regex work.
|
|
80
|
+
if (!EDITABLE_CONTROL.test(text))
|
|
81
|
+
return text;
|
|
82
|
+
return stripAnsi(text)
|
|
83
|
+
.replace(/\r\n?/gu, '\n')
|
|
84
|
+
.replace(/\t/gu, ' ')
|
|
85
|
+
.replace(/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/gu, '');
|
|
86
|
+
}
|
|
87
|
+
const COMPOSER_IMAGE_TOKEN = /\[Image #\d+\]/gu;
|
|
88
|
+
/** Format label for one image media type, matching the image preview card's
|
|
89
|
+
* title (`JPEG`, `PNG`, `WEBP`, `GIF`). */
|
|
90
|
+
const mediaTypeLabel = (mediaType) => mediaType.replace(/^image\//u, '').replace(/\+xml$/u, '').toUpperCase();
|
|
91
|
+
/** Every `[Image #N]` in `text`, in order. */
|
|
92
|
+
function imageTokenSpans(text) {
|
|
93
|
+
const spans = [];
|
|
94
|
+
for (const match of text.matchAll(COMPOSER_IMAGE_TOKEN)) {
|
|
95
|
+
const start = match.index ?? 0;
|
|
96
|
+
spans.push({ start, end: start + match[0].length, token: match[0] });
|
|
97
|
+
}
|
|
98
|
+
return spans;
|
|
99
|
+
}
|
|
100
|
+
/** The span whose interior (exclusive of both edges) contains `offset`. */
|
|
101
|
+
function imageTokenAround(spans, offset) {
|
|
102
|
+
return spans.find(span => span.start < offset && offset < span.end);
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* A caret never rests inside a staged token: an offset in a span's interior
|
|
106
|
+
* moves to the edge `prefer` names — `'start'` (the token becomes the caret
|
|
107
|
+
* cluster), `'end'`, or whichever is nearer.
|
|
108
|
+
*/
|
|
109
|
+
function snapOffImageToken(spans, offset, prefer) {
|
|
110
|
+
const span = imageTokenAround(spans, offset);
|
|
111
|
+
if (span === undefined)
|
|
112
|
+
return offset;
|
|
113
|
+
if (prefer === 'start')
|
|
114
|
+
return span.start;
|
|
115
|
+
if (prefer === 'end')
|
|
116
|
+
return span.end;
|
|
117
|
+
return offset - span.start < span.end - offset ? span.start : span.end;
|
|
118
|
+
}
|
|
119
|
+
/** Expand a deletion or selection to include every staged token it touches. */
|
|
120
|
+
function expandImageTokenRange(spans, start, end) {
|
|
121
|
+
return {
|
|
122
|
+
start: snapOffImageToken(spans, start, 'start'),
|
|
123
|
+
end: snapOffImageToken(spans, end, 'end'),
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
/** Capabilities referenced by `text`, in first occurrence order. A raw token
|
|
127
|
+
* restored from disk/history has no sidecar entry and therefore stays inert. */
|
|
128
|
+
export function composerImageRefsForText(text, stagedByToken) {
|
|
129
|
+
const refs = [];
|
|
130
|
+
const seen = new Set();
|
|
131
|
+
for (const match of text.matchAll(COMPOSER_IMAGE_TOKEN)) {
|
|
132
|
+
const token = match[0];
|
|
133
|
+
if (seen.has(token))
|
|
134
|
+
continue;
|
|
135
|
+
seen.add(token);
|
|
136
|
+
const stageId = stagedByToken.get(token);
|
|
137
|
+
if (stageId !== undefined)
|
|
138
|
+
refs.push({ token, stageId });
|
|
139
|
+
}
|
|
140
|
+
return refs;
|
|
141
|
+
}
|
|
142
|
+
/** Read one regular file through one descriptor, bounded to `maxBytes + 1`.
|
|
143
|
+
* The extra byte detects a file that grows after fstat; a short read detects
|
|
144
|
+
* shrinkage. This avoids stat(path) → readFile(path)'s path-swap TOCTOU and
|
|
145
|
+
* never allocates from an untrusted size before the profile limit is checked. */
|
|
146
|
+
async function readBoundedRegularFile(path, maxBytes) {
|
|
147
|
+
// O_NONBLOCK keeps a pasted FIFO/device path from parking the UI before
|
|
148
|
+
// fstat can reject it; regular-file reads are unchanged.
|
|
149
|
+
const file = await open(path, fsConstants.O_RDONLY | fsConstants.O_NONBLOCK);
|
|
150
|
+
try {
|
|
151
|
+
const info = await file.stat();
|
|
152
|
+
if (!info.isFile())
|
|
153
|
+
throw new Error(`${basename(path)} is not a regular file`);
|
|
154
|
+
if (info.size > maxBytes)
|
|
155
|
+
throw new Error(`image exceeds this profile's per-image size limit`);
|
|
156
|
+
const data = Buffer.allocUnsafe(info.size + 1);
|
|
157
|
+
let offset = 0;
|
|
158
|
+
while (offset < data.byteLength) {
|
|
159
|
+
const { bytesRead } = await file.read(data, offset, data.byteLength - offset, offset);
|
|
160
|
+
if (bytesRead === 0)
|
|
161
|
+
break;
|
|
162
|
+
offset += bytesRead;
|
|
163
|
+
}
|
|
164
|
+
if (offset !== info.size)
|
|
165
|
+
throw new Error(`${basename(path)} changed while it was being read`);
|
|
166
|
+
return data.subarray(0, offset);
|
|
167
|
+
}
|
|
168
|
+
finally {
|
|
169
|
+
await file.close();
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
/** Index of the word boundary at or before `cursor` (readline alt+b). */
|
|
173
|
+
function wordBoundaryLeft(text, cursor) {
|
|
174
|
+
let index = cursor;
|
|
175
|
+
while (index > 0 && /\s/.test(text[index - 1]))
|
|
176
|
+
index--;
|
|
177
|
+
while (index > 0 && !/\s/.test(text[index - 1]))
|
|
178
|
+
index--;
|
|
179
|
+
return index;
|
|
180
|
+
}
|
|
181
|
+
/** Index of the word boundary after `cursor` (readline alt+f). */
|
|
182
|
+
function wordBoundaryRight(text, cursor) {
|
|
183
|
+
const length = text.length;
|
|
184
|
+
let index = cursor;
|
|
185
|
+
while (index < length && !/\s/.test(text[index]))
|
|
186
|
+
index++;
|
|
187
|
+
while (index < length && /\s/.test(text[index]))
|
|
188
|
+
index++;
|
|
189
|
+
return index;
|
|
190
|
+
}
|
|
191
|
+
// --- vim normal-mode helpers -----------------------------------------------
|
|
192
|
+
// `/vim` 编辑模式的 normal 键位几何:行/词移动与删除目标。空白分词
|
|
193
|
+
// (不区分 vim 的 word/WORD),对输入框场景足够且行为直观。
|
|
194
|
+
/** Offset of the current line's first character. */
|
|
195
|
+
function vimLineStart(text, cursor) {
|
|
196
|
+
return text.lastIndexOf('\n', cursor - 1) + 1;
|
|
197
|
+
}
|
|
198
|
+
/** Offset of the current line's last character (exclusive, no '\n'). */
|
|
199
|
+
function vimLineEnd(text, cursor) {
|
|
200
|
+
const next = text.indexOf('\n', cursor);
|
|
201
|
+
return next === -1 ? text.length : next;
|
|
202
|
+
}
|
|
203
|
+
/** Offset of the line's first non-whitespace character (`^`). */
|
|
204
|
+
function vimLineFirstNonBlank(text, cursor) {
|
|
205
|
+
const start = vimLineStart(text, cursor);
|
|
206
|
+
const end = vimLineEnd(text, cursor);
|
|
207
|
+
let i = start;
|
|
208
|
+
while (i < end && /\s/.test(text[i]))
|
|
209
|
+
i++;
|
|
210
|
+
return i;
|
|
211
|
+
}
|
|
212
|
+
/** Next word start (`w`): skip the rest of the current word, then leading
|
|
213
|
+
* whitespace. Whitespace-delimited, vim-style. */
|
|
214
|
+
function vimWordForward(text, cursor) {
|
|
215
|
+
const len = text.length;
|
|
216
|
+
let i = cursor;
|
|
217
|
+
if (i < len && !/\s/.test(text[i])) {
|
|
218
|
+
while (i < len && !/\s/.test(text[i]))
|
|
219
|
+
i++;
|
|
220
|
+
}
|
|
221
|
+
while (i < len && /\s/.test(text[i]))
|
|
222
|
+
i++;
|
|
223
|
+
return i;
|
|
224
|
+
}
|
|
225
|
+
/** Previous word start (`b`): from inside a word, its own start; from
|
|
226
|
+
* whitespace, the preceding word's start. */
|
|
227
|
+
function vimWordBackward(text, cursor) {
|
|
228
|
+
let i = cursor;
|
|
229
|
+
while (i > 0 && /\s/.test(text[i - 1]))
|
|
230
|
+
i--;
|
|
231
|
+
while (i > 0 && !/\s/.test(text[i - 1]))
|
|
232
|
+
i--;
|
|
233
|
+
return i;
|
|
234
|
+
}
|
|
235
|
+
/** End of the current word (`dw`): the whitespace boundary after `cursor`. */
|
|
236
|
+
function vimWordEnd(text, cursor) {
|
|
237
|
+
const len = text.length;
|
|
238
|
+
let i = cursor;
|
|
239
|
+
while (i < len && !/\s/.test(text[i]))
|
|
240
|
+
i++;
|
|
241
|
+
return i;
|
|
242
|
+
}
|
|
243
|
+
// --- grapheme-cluster geometry ---------------------------------------------
|
|
244
|
+
// The caret, editing keys, and wrapping MUST agree on one text unit. Mixing
|
|
245
|
+
// UTF-16 code units (arrows/backspace), code points (wrap), and display
|
|
246
|
+
// cells (stringWidth) lets the caret land inside a surrogate pair or a ZWJ
|
|
247
|
+
// emoji — the inverted caret then shows half a glyph, Backspace deletes
|
|
248
|
+
// half a character, and `line.slice()` splits clusters. All offsets below
|
|
249
|
+
// are UTF-16 indices snapped to grapheme boundaries via the shared
|
|
250
|
+
// Intl.Segmenter (utils/intl.ts).
|
|
251
|
+
/** Ascending grapheme boundary offsets of `text` (starts at 0, ends at
|
|
252
|
+
* `text.length`). Empty text yields `[0]`. */
|
|
253
|
+
function graphemeBoundaries(text) {
|
|
254
|
+
const bounds = [0];
|
|
255
|
+
for (const { index, segment } of getGraphemeSegmenter().segment(text)) {
|
|
256
|
+
const end = index + segment.length;
|
|
257
|
+
if (end > bounds[bounds.length - 1])
|
|
258
|
+
bounds.push(end);
|
|
259
|
+
}
|
|
260
|
+
return bounds;
|
|
261
|
+
}
|
|
262
|
+
/** Largest grapheme boundary `<= offset` (clamped into the text). Snaps a
|
|
263
|
+
* cursor that landed mid-cluster back onto a boundary. */
|
|
264
|
+
function boundaryAtOrBefore(bounds, offset) {
|
|
265
|
+
let lo = 0;
|
|
266
|
+
let hi = bounds.length - 1;
|
|
267
|
+
let ans = bounds[0];
|
|
268
|
+
while (lo <= hi) {
|
|
269
|
+
const mid = (lo + hi) >> 1;
|
|
270
|
+
if (bounds[mid] <= offset) {
|
|
271
|
+
ans = bounds[mid];
|
|
272
|
+
lo = mid + 1;
|
|
273
|
+
}
|
|
274
|
+
else {
|
|
275
|
+
hi = mid - 1;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
return ans;
|
|
279
|
+
}
|
|
280
|
+
/** Largest grapheme boundary strictly before `offset` (0 when none). */
|
|
281
|
+
function previousGraphemeBoundary(bounds, offset) {
|
|
282
|
+
let lo = 0;
|
|
283
|
+
let hi = bounds.length - 1;
|
|
284
|
+
let ans = 0;
|
|
285
|
+
while (lo <= hi) {
|
|
286
|
+
const mid = (lo + hi) >> 1;
|
|
287
|
+
if (bounds[mid] < offset) {
|
|
288
|
+
ans = bounds[mid];
|
|
289
|
+
lo = mid + 1;
|
|
290
|
+
}
|
|
291
|
+
else {
|
|
292
|
+
hi = mid - 1;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
return ans;
|
|
296
|
+
}
|
|
297
|
+
/** Smallest grapheme boundary strictly after `offset` (text.length when
|
|
298
|
+
* none). Returns `offset` unchanged when it already is the last boundary. */
|
|
299
|
+
function nextGraphemeBoundary(bounds, offset) {
|
|
300
|
+
let lo = 0;
|
|
301
|
+
let hi = bounds.length - 1;
|
|
302
|
+
let ans = bounds[hi] ?? 0;
|
|
303
|
+
while (lo <= hi) {
|
|
304
|
+
const mid = (lo + hi) >> 1;
|
|
305
|
+
if (bounds[mid] > offset) {
|
|
306
|
+
ans = bounds[mid];
|
|
307
|
+
hi = mid - 1;
|
|
308
|
+
}
|
|
309
|
+
else {
|
|
310
|
+
lo = mid + 1;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
return ans;
|
|
314
|
+
}
|
|
315
|
+
/** Snap an arbitrary UTF-16 offset onto a grapheme boundary of `text`,
|
|
316
|
+
* clamping into range. The safety net under every cursor write. */
|
|
317
|
+
function normalizeCursorOffset(text, offset) {
|
|
318
|
+
const clamped = Math.max(0, Math.min(offset, text.length));
|
|
319
|
+
return boundaryAtOrBefore(graphemeBoundaries(text), clamped);
|
|
320
|
+
}
|
|
321
|
+
/**
|
|
322
|
+
* The empty input deliberately shows NO placeholder text: terminal emulators
|
|
323
|
+
* paint the IME preedit (pinyin) at the physical cursor, which the app parks
|
|
324
|
+
* on the caret's cell, and the app receives no input events while a
|
|
325
|
+
* composition is active (Windows Terminal suppresses key events during TSF
|
|
326
|
+
* composition) — so it can never hide a placeholder in time. Keeping the row
|
|
327
|
+
* blank while empty is what guarantees the preedit has nothing to overlay.
|
|
328
|
+
*/
|
|
329
|
+
/** Max input rows before the visible viewport starts scrolling; the box keeps
|
|
330
|
+
* a stable height. */
|
|
331
|
+
const MAX_VISIBLE_LINES = 5;
|
|
332
|
+
/**
|
|
333
|
+
* Fixed chrome rows around the expanded editor's text area: round border
|
|
334
|
+
* (top+bottom), the title row, the status row, and the button row. The
|
|
335
|
+
* editor viewport gets `terminalRows - this` rows.
|
|
336
|
+
*/
|
|
337
|
+
const EDITOR_CHROME_ROWS = 5;
|
|
338
|
+
/**
|
|
339
|
+
* Double-click self-detection window: two clicks within this many ms and
|
|
340
|
+
* one cell (in either axis) of each other count as a double-click and
|
|
341
|
+
* select the word under the pointer. The drag protocol resets the ink
|
|
342
|
+
* multi-click chain on every press inside a drag target (the value box
|
|
343
|
+
* carries onDragStart), so the prompt detects the double-click itself.
|
|
344
|
+
*/
|
|
345
|
+
const DOUBLE_CLICK_MS = 500;
|
|
346
|
+
/**
|
|
347
|
+
* dsh-TUI prompt input: rounded border box (top+bottom borders
|
|
348
|
+
* only), `❯ ` prompt char (dimmed while a turn is working), the text with a
|
|
349
|
+
* block cursor at the cursor position, and above it the slash-command /
|
|
350
|
+
* file-completion suggestion card (SuggestionCard: rounded panel with the
|
|
351
|
+
* selected row behind a `❯` pointer in the theme's `suggestion` color).
|
|
352
|
+
*
|
|
353
|
+
* Empty input: a solid block caret on a blank cell and nothing else — no
|
|
354
|
+
* placeholder text, so the terminal-painted IME preedit (pinyin) at the
|
|
355
|
+
* parked cursor can never be overlaid on anything.
|
|
356
|
+
*
|
|
357
|
+
* Multi-line: Shift+Enter inserts a newline; ↑/↓ move between lines while
|
|
358
|
+
* the input spans multiple lines (history/command selection otherwise); the
|
|
359
|
+
* visible window scrolls to keep the caret row on screen past
|
|
360
|
+
* MAX_VISIBLE_LINES. Enter submits, backspace/delete edit, ←/→ move the
|
|
361
|
+
* cursor, Tab completes the selected command, Ctrl+G opens the draft in the
|
|
362
|
+
* external editor ($VISUAL/$EDITOR), Escape clears (or closes the help
|
|
363
|
+
* menu), `?` toggles the help menu. Windows ConPTY pipelines deliver
|
|
364
|
+
* whole lines with the Enter key lost: a trailing CR/LF in the input marks
|
|
365
|
+
* a complete line to submit.
|
|
366
|
+
*
|
|
367
|
+
* Enter submits immediately even while the model is streaming — as a STEER
|
|
368
|
+
* (Codex/pi semantics): the message is injected at the next step boundary
|
|
369
|
+
* of the running turn and the agent continues without aborting; Tab instead
|
|
370
|
+
* queues the message for after the turn (followup). Both appear in a
|
|
371
|
+
* pending preview above the input until delivered. Alt+Up pulls the last
|
|
372
|
+
* pending message back for editing; Esc (with pending messages while
|
|
373
|
+
* working) interrupts the turn and delivers them right away; Ctrl+Enter
|
|
374
|
+
* aborts the turn and sends the current input immediately.
|
|
375
|
+
*/
|
|
376
|
+
export function PromptInput({ channel, suspended = false, draftCache, helpOpen, onToggleHelp, onRunCommand, selectionActive, fillText, onFillConsumed, onRewindRequest, onBackgroundRequest, onOpenSessions, backgroundAgentsNeedingInput, controllerRef, onCaretImage, caretPreviewOpen = false, onDismissCaretPreview, }) {
|
|
377
|
+
const [themeName] = useTheme();
|
|
378
|
+
// Raw stdout writer for OSC 52 clipboard writes (selection copy) — must
|
|
379
|
+
// bypass the frame pipeline; null outside a mounted Ink App.
|
|
380
|
+
const writeRaw = React.useContext(TerminalWriteContext);
|
|
381
|
+
// The composer owns its text in local state, so it starts EMPTY and adopts
|
|
382
|
+
// whatever draft the owner stored in a MOUNT-TIME effect below. Reading the
|
|
383
|
+
// store in this initializer instead looks equivalent and is not: a non-empty
|
|
384
|
+
// first frame leaves `repro-resume-position` red — resuming a session parks
|
|
385
|
+
// the transcript mid-history instead of pinning it to the newest message.
|
|
386
|
+
// Adopting after mount keeps the first frame identical to a fresh composer
|
|
387
|
+
// while still handing the draft back.
|
|
388
|
+
const [value, setValue] = React.useState('');
|
|
389
|
+
const [cursor, setCursor] = React.useState(0);
|
|
390
|
+
/**
|
|
391
|
+
* Adopt the owner's draft ONCE, after mount.
|
|
392
|
+
*
|
|
393
|
+
* This is where a screen swap gives the draft back: the slot outlives this
|
|
394
|
+
* component, so a remount picks up what the user had written — text, caret,
|
|
395
|
+
* the image bindings behind the visible `[Image #N]` tokens, and the edit
|
|
396
|
+
* state around them (fold chip, fullscreen editor, vim mode).
|
|
397
|
+
*
|
|
398
|
+
* It runs as an effect rather than as the `useState` initial value on
|
|
399
|
+
* purpose. A composer whose FIRST frame is already non-empty moves the
|
|
400
|
+
* transcript's restored scroll position (`repro-resume-position`); adopting
|
|
401
|
+
* after the first commit keeps that frame identical to a fresh composer. The
|
|
402
|
+
* slot is consumed here, and the composer never writes to it while mounted,
|
|
403
|
+
* so the empty first value cannot overwrite the draft before this reads it.
|
|
404
|
+
*/
|
|
405
|
+
const adoptDraft = React.useRef(true);
|
|
406
|
+
React.useEffect(() => {
|
|
407
|
+
if (draftCache === undefined || !adoptDraft.current)
|
|
408
|
+
return;
|
|
409
|
+
adoptDraft.current = false;
|
|
410
|
+
const snapshot = draftCache.current;
|
|
411
|
+
draftCache.current = null;
|
|
412
|
+
if (!isUsableDraftSnapshot(snapshot, String(channel.agentId), resolveBindingGeneration(channel)))
|
|
413
|
+
return;
|
|
414
|
+
const text = snapshot.value;
|
|
415
|
+
// Carried edit state, restored ahead of the text: the chip, the
|
|
416
|
+
// fullscreen editor and the vim mode/submode come back with the draft —
|
|
417
|
+
// and they come back even with NO text, because they are modes rather
|
|
418
|
+
// than content. Fold ranges are safe by the CAPTURE invariant, not by
|
|
419
|
+
// restore order: a snapshot's block, when set, always sits inside the
|
|
420
|
+
// snapshot's own text (setInput keeps or drops it atomically), and at
|
|
421
|
+
// mount the caret is 0 so updateFoldBlock's caret-drag clamp cannot fire
|
|
422
|
+
// here. The transient state around them — editor scroll, vim undo stack,
|
|
423
|
+
// selection — does not come back.
|
|
424
|
+
updateFoldBlock(snapshot.foldBlock);
|
|
425
|
+
expandedRef.current = snapshot.expanded;
|
|
426
|
+
setExpanded(snapshot.expanded);
|
|
427
|
+
vimEnabledRef.current = snapshot.vimEnabled;
|
|
428
|
+
setVimEnabled(snapshot.vimEnabled);
|
|
429
|
+
vimInsertRef.current = snapshot.vimInsert;
|
|
430
|
+
setVimInsert(snapshot.vimInsert);
|
|
431
|
+
if (text === '')
|
|
432
|
+
return;
|
|
433
|
+
const restoredCursor = normalizeCursorOffset(text, snapshot.cursor);
|
|
434
|
+
replaceDraftImages(filterLiveImageBindings(snapshot.images, stageId => channel.hasStagedImage?.(stageId) === true).map(([token, stageId]) => ({ token, stageId })));
|
|
435
|
+
valueRef.current = text;
|
|
436
|
+
cursorRef.current = restoredCursor;
|
|
437
|
+
setValue(text);
|
|
438
|
+
setCursor(restoredCursor);
|
|
439
|
+
}, [draftCache]);
|
|
440
|
+
/**
|
|
441
|
+
* Mouse text selection: UTF-16 offsets [start, end) in `value`, snapped
|
|
442
|
+
* to grapheme boundaries, start ≤ end. Null = no selection. Created by
|
|
443
|
+
* drag, Shift+click extension and double-click word select; consumed by
|
|
444
|
+
* Backspace/Delete/typing; Esc clears it without touching the text.
|
|
445
|
+
* With a fold block the range always stays inside the head or the tail
|
|
446
|
+
* (it never crosses the chip row — clamped on every write).
|
|
447
|
+
*/
|
|
448
|
+
const [selection, setSelection] = React.useState(null);
|
|
449
|
+
const selectionRef = React.useRef(null);
|
|
450
|
+
/** Anchor offset of the in-flight drag gesture (set by dragstart). */
|
|
451
|
+
const dragAnchorRef = React.useRef(null);
|
|
452
|
+
/** Double-click self-detection: last click's timestamp/screen cell. */
|
|
453
|
+
const lastClickAtRef = React.useRef(0);
|
|
454
|
+
const lastClickColRef = React.useRef(-1);
|
|
455
|
+
const lastClickRowRef = React.useRef(-1);
|
|
456
|
+
/**
|
|
457
|
+
* vim editing mode (`/vim` toggle): when on, Esc switches the prompt to
|
|
458
|
+
* its NORMAL submode where bare keys are vim keys instead of text, and
|
|
459
|
+
* i/a/o (…) return to INSERT. Enabled in insert mode so the transition
|
|
460
|
+
* is seamless; the mode is session-scoped (not persisted).
|
|
461
|
+
*/
|
|
462
|
+
const [vimEnabled, setVimEnabled] = React.useState(false);
|
|
463
|
+
/** Insert submode (false = vim NORMAL). */
|
|
464
|
+
const [vimInsert, setVimInsert] = React.useState(true);
|
|
465
|
+
const vimEnabledRef = React.useRef(false);
|
|
466
|
+
const vimInsertRef = React.useRef(true);
|
|
467
|
+
vimEnabledRef.current = vimEnabled;
|
|
468
|
+
vimInsertRef.current = vimInsert;
|
|
469
|
+
/** Undo owns the draft's image bindings as well as its text and caret. */
|
|
470
|
+
const vimUndoRef = React.useRef([]);
|
|
471
|
+
/** Pending vim operator: `d` pressed, awaiting its second key. */
|
|
472
|
+
const vimPendingRef = React.useRef('');
|
|
473
|
+
/**
|
|
474
|
+
* Fold block: the [start, end) span of `value` that renders as
|
|
475
|
+
* a one-line chip while the text around it stays fully editable. Created
|
|
476
|
+
* by a big paste; only an EXPLICIT expand (chip/card click, Esc) or
|
|
477
|
+
* delete removes it — typing NEVER unfolds the block.
|
|
478
|
+
*/
|
|
479
|
+
const [foldBlock, setFoldBlock] = React.useState(null);
|
|
480
|
+
/** Synchronous mirror used by batched keys, controller clear, and mouse drag. */
|
|
481
|
+
const foldBlockRef = React.useRef(null);
|
|
482
|
+
/**
|
|
483
|
+
* Fullscreen draft editor (`expandEditor`, default Ctrl+Shift+E, or the
|
|
484
|
+
* ✎ affordance at the end of the input row). While expanded the SAME
|
|
485
|
+
* editing state renders into the PromptEditorLayer cover (published via
|
|
486
|
+
* setPromptEditorNode each render): Enter inserts a newline, Ctrl+Enter
|
|
487
|
+
* submits, Esc collapses. The fold chip is bypassed (full text shown).
|
|
488
|
+
*/
|
|
489
|
+
const [expanded, setExpanded] = React.useState(false);
|
|
490
|
+
const expandedRef = React.useRef(false);
|
|
491
|
+
/** First visible row of the expanded viewport (merged with caret-follow
|
|
492
|
+
* during render; wheel events advance it and tick a re-render). */
|
|
493
|
+
const expandedScrollRef = React.useRef(0);
|
|
494
|
+
/**
|
|
495
|
+
* Free-scroll latch: wheel browsing parks the viewport anywhere; the
|
|
496
|
+
* caret-follow merge skips while it is set, and the next real caret move
|
|
497
|
+
* (typing, arrows, click) re-engages following (GUI-editor semantics).
|
|
498
|
+
*/
|
|
499
|
+
const editorFreeScrollRef = React.useRef(false);
|
|
500
|
+
const prevCaretLineRef = React.useRef(-1);
|
|
501
|
+
const [, setExpandedTick] = React.useState(0);
|
|
502
|
+
const prevExpandedRef = React.useRef(false);
|
|
503
|
+
/**
|
|
504
|
+
* Feature gate (settings `dsh-tui.expandEditor`, on by default; a mock
|
|
505
|
+
* channel without the field also reads as on). Off hides the ✎
|
|
506
|
+
* affordance and refuses the shortcut — the editor cannot open.
|
|
507
|
+
*/
|
|
508
|
+
const expandEnabled = channel.expandEditor !== false;
|
|
509
|
+
/** Latest expanded viewport metrics for the useInput wheel branch. */
|
|
510
|
+
const editorViewportRef = React.useRef(null);
|
|
511
|
+
/** Hover state of the ⤢/✎ expand affordance in the input row. */
|
|
512
|
+
const [expandHovered, setExpandHovered] = React.useState(false);
|
|
513
|
+
/** Hover state of the ⌂ session-list affordance at the head of the row. */
|
|
514
|
+
const [homeHovered, setHomeHovered] = React.useState(false);
|
|
515
|
+
/** Pointer over the input box (drives the hover peek card). */
|
|
516
|
+
const [hovered, setHovered] = React.useState(false);
|
|
517
|
+
/** 120ms grace so the pointer crossing the input border row from the
|
|
518
|
+
* chip up onto the peek card never flickers the card. */
|
|
519
|
+
const hoverLeaveTimerRef = React.useRef(null);
|
|
520
|
+
const hoverEnter = React.useCallback(() => {
|
|
521
|
+
if (hoverLeaveTimerRef.current) {
|
|
522
|
+
clearTimeout(hoverLeaveTimerRef.current);
|
|
523
|
+
hoverLeaveTimerRef.current = null;
|
|
524
|
+
}
|
|
525
|
+
setHovered(true);
|
|
526
|
+
}, []);
|
|
527
|
+
const hoverLeave = React.useCallback(() => {
|
|
528
|
+
if (hoverLeaveTimerRef.current)
|
|
529
|
+
clearTimeout(hoverLeaveTimerRef.current);
|
|
530
|
+
hoverLeaveTimerRef.current = setTimeout(() => setHovered(false), 120);
|
|
531
|
+
}, []);
|
|
532
|
+
const valueRef = React.useRef(value);
|
|
533
|
+
const cursorRef = React.useRef(cursor);
|
|
534
|
+
const history = React.useRef([]);
|
|
535
|
+
const historyIndex = React.useRef(-1);
|
|
536
|
+
const historyDraft = React.useRef({ text: '', images: [] });
|
|
537
|
+
/** The persisted history is read lazily, once per mount (see seedHistory). */
|
|
538
|
+
const historySeeded = React.useRef(false);
|
|
539
|
+
/** Visible `[Image #N]` labels are presentation only; this sidecar carries
|
|
540
|
+
* the non-reusable capability for the current draft. History/rewind text
|
|
541
|
+
* restored without this map can never bind to a later image by accident. */
|
|
542
|
+
const draftImagesRef = React.useRef(new Map());
|
|
543
|
+
const draftImagesGenerationRef = React.useRef(channel.stagedImageGeneration?.() ?? 0);
|
|
544
|
+
/** Session generation fences one agent transcript; revision fences one
|
|
545
|
+
* logical composer draft inside that session. Ordinary typing deliberately
|
|
546
|
+
* keeps the revision so an async paste lands at the live caret, while any
|
|
547
|
+
* whole-draft replacement revokes the old continuation. */
|
|
548
|
+
const draftRevisionRef = React.useRef(0);
|
|
549
|
+
/** Unlike draftRevision (whole-draft replacement only), this advances on
|
|
550
|
+
* every text mutation so an async command can never clear a draft that was
|
|
551
|
+
* edited away and later changed back to the same bytes. */
|
|
552
|
+
const inputEditSequenceRef = React.useRef(0);
|
|
553
|
+
/** One registry command may own a draft at a time. Keeping the draft
|
|
554
|
+
* visible during admission must not make a second Enter dispatch it twice. */
|
|
555
|
+
const pendingCommandRef = React.useRef(null);
|
|
556
|
+
const nextImageNumberRef = React.useRef(1);
|
|
557
|
+
/** Serialize image staging started in one draft so consecutive terminal
|
|
558
|
+
* drops keep input order even when storage settles out of order. A new
|
|
559
|
+
* logical draft receives a fresh chain and never waits on old-session I/O. */
|
|
560
|
+
const imageStageChainRef = React.useRef(Promise.resolve());
|
|
561
|
+
const advanceDraftRevision = () => {
|
|
562
|
+
draftRevisionRef.current += 1;
|
|
563
|
+
imageStageChainRef.current = Promise.resolve();
|
|
564
|
+
};
|
|
565
|
+
const detachDraftImages = () => {
|
|
566
|
+
advanceDraftRevision();
|
|
567
|
+
draftImagesRef.current.clear();
|
|
568
|
+
clearVimUndo();
|
|
569
|
+
};
|
|
570
|
+
const stageIdIsRetained = (stageId) => {
|
|
571
|
+
for (const current of draftImagesRef.current.values()) {
|
|
572
|
+
if (current === stageId)
|
|
573
|
+
return true;
|
|
574
|
+
}
|
|
575
|
+
return vimUndoRef.current.some(entry => entry.images.some(image => image.stageId === stageId))
|
|
576
|
+
|| history.current.some(entry => entry.images.some(image => image.stageId === stageId))
|
|
577
|
+
|| historyDraft.current.images.some(image => image.stageId === stageId)
|
|
578
|
+
|| channel.pending.some(item => item.images?.some(image => image.stageId === stageId) === true);
|
|
579
|
+
};
|
|
580
|
+
const discardUnretainedImages = (stageIds) => {
|
|
581
|
+
for (const stageId of new Set(stageIds)) {
|
|
582
|
+
if (!stageIdIsRetained(stageId))
|
|
583
|
+
channel.discardStagedImage(stageId);
|
|
584
|
+
}
|
|
585
|
+
};
|
|
586
|
+
const clearVimUndo = () => {
|
|
587
|
+
const previous = vimUndoRef.current;
|
|
588
|
+
vimUndoRef.current = [];
|
|
589
|
+
discardUnretainedImages(previous.flatMap(entry => entry.images.map(image => image.stageId)));
|
|
590
|
+
};
|
|
591
|
+
const discardDraftImages = () => {
|
|
592
|
+
advanceDraftRevision();
|
|
593
|
+
const stageIds = [...draftImagesRef.current.values()];
|
|
594
|
+
clearVimUndo();
|
|
595
|
+
draftImagesRef.current.clear();
|
|
596
|
+
discardUnretainedImages(stageIds);
|
|
597
|
+
};
|
|
598
|
+
const replaceDraftImages = (images) => {
|
|
599
|
+
const previous = [...draftImagesRef.current.values()];
|
|
600
|
+
draftImagesRef.current.clear();
|
|
601
|
+
for (const ref of images) {
|
|
602
|
+
if (channel.hasStagedImage?.(ref.stageId) === true) {
|
|
603
|
+
draftImagesRef.current.set(ref.token, ref.stageId);
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
discardUnretainedImages(previous);
|
|
607
|
+
};
|
|
608
|
+
const syncImageGeneration = () => {
|
|
609
|
+
const generation = channel.stagedImageGeneration?.() ?? 0;
|
|
610
|
+
if (draftImagesGenerationRef.current !== generation) {
|
|
611
|
+
// The channel has already cleared every old-generation capability.
|
|
612
|
+
// Only detach the UI sidecar; calling discard would be redundant.
|
|
613
|
+
vimUndoRef.current = [];
|
|
614
|
+
detachDraftImages();
|
|
615
|
+
draftImagesGenerationRef.current = generation;
|
|
616
|
+
// Presentation numbering is session-local like the old channel-owned
|
|
617
|
+
// sequence. Any stale token still visible in the retained draft is
|
|
618
|
+
// skipped by bindStagedImage, so resetting cannot recreate an alias.
|
|
619
|
+
nextImageNumberRef.current = 1;
|
|
620
|
+
}
|
|
621
|
+
return generation;
|
|
622
|
+
};
|
|
623
|
+
const captureDraftImageLease = () => ({
|
|
624
|
+
generation: syncImageGeneration(),
|
|
625
|
+
revision: draftRevisionRef.current,
|
|
626
|
+
});
|
|
627
|
+
const draftImageLeaseIsCurrent = (lease) => syncImageGeneration() === lease.generation
|
|
628
|
+
&& draftRevisionRef.current === lease.revision;
|
|
629
|
+
// Channel emits on every session replacement. Clear the sidecar during
|
|
630
|
+
// that render while leaving the user's visible draft untouched; any raw
|
|
631
|
+
// tokens become explicit stale placeholders instead of aliases.
|
|
632
|
+
syncImageGeneration();
|
|
633
|
+
valueRef.current = value;
|
|
634
|
+
cursorRef.current = cursor;
|
|
635
|
+
// Nothing is written to the owner's draft slot here — see the unmount
|
|
636
|
+
// hand-off below. A commit-time write would run BEFORE the restore effect
|
|
637
|
+
// has consumed the slot, so the composer's own first (empty) value would
|
|
638
|
+
// erase the draft it just came back for.
|
|
639
|
+
// Publish the live controller (fresh closure over `value` every render).
|
|
640
|
+
// A prompt-slot panel withdraws the handle in the same commit: external
|
|
641
|
+
// injection must not append/submit a hidden command draft while it waits
|
|
642
|
+
// for a decision. clear() mirrors the double-tap-Esc clear.
|
|
643
|
+
React.useLayoutEffect(() => {
|
|
644
|
+
if (!controllerRef)
|
|
645
|
+
return;
|
|
646
|
+
if (suspended) {
|
|
647
|
+
controllerRef.current = null;
|
|
648
|
+
return;
|
|
649
|
+
}
|
|
650
|
+
controllerRef.current = {
|
|
651
|
+
hasText: () => value.length > 0,
|
|
652
|
+
text: () => valueRef.current,
|
|
653
|
+
previewImages: () => composerImageRefsForText(valueRef.current, draftImagesRef.current).flatMap(ref => {
|
|
654
|
+
const image = channel.stagedImage(ref.stageId);
|
|
655
|
+
return image === undefined ? [] : [{ image, title: ref.token.slice(1, -1) }];
|
|
656
|
+
}),
|
|
657
|
+
clear: () => {
|
|
658
|
+
if (valueRef.current !== '')
|
|
659
|
+
inputEditSequenceRef.current += 1;
|
|
660
|
+
valueRef.current = '';
|
|
661
|
+
cursorRef.current = 0;
|
|
662
|
+
selectionRef.current = null;
|
|
663
|
+
discardDraftImages();
|
|
664
|
+
foldBlockRef.current = null;
|
|
665
|
+
dragAnchorRef.current = null;
|
|
666
|
+
lastClickAtRef.current = 0;
|
|
667
|
+
lastClickColRef.current = -1;
|
|
668
|
+
lastClickRowRef.current = -1;
|
|
669
|
+
// Chat's idle Ctrl+C clear also withdraws the fullscreen editor —
|
|
670
|
+
// the draft it was editing is gone, the cover must not linger.
|
|
671
|
+
expandedRef.current = false;
|
|
672
|
+
setSelection(null);
|
|
673
|
+
setFoldBlock(null);
|
|
674
|
+
setExpanded(false);
|
|
675
|
+
setValue('');
|
|
676
|
+
setCursor(0);
|
|
677
|
+
},
|
|
678
|
+
append: (text) => {
|
|
679
|
+
const previous = valueRef.current;
|
|
680
|
+
const next = sanitizeEditableText(previous + text);
|
|
681
|
+
if (next !== previous)
|
|
682
|
+
inputEditSequenceRef.current += 1;
|
|
683
|
+
valueRef.current = next;
|
|
684
|
+
cursorRef.current = next.length;
|
|
685
|
+
setValue(next);
|
|
686
|
+
setCursor(next.length);
|
|
687
|
+
return next;
|
|
688
|
+
},
|
|
689
|
+
consumeSelectionCopy: () => {
|
|
690
|
+
const sel = selectionRef.current;
|
|
691
|
+
if (!sel)
|
|
692
|
+
return false;
|
|
693
|
+
const text = valueRef.current.slice(sel.start, sel.end);
|
|
694
|
+
void setClipboard(text).then(raw => {
|
|
695
|
+
if (raw)
|
|
696
|
+
writeRaw?.(raw);
|
|
697
|
+
});
|
|
698
|
+
// The selection stays: copy never clears it (Esc/typing/delete do).
|
|
699
|
+
return true;
|
|
700
|
+
},
|
|
701
|
+
toggleVim: () => {
|
|
702
|
+
const next = !vimEnabledRef.current;
|
|
703
|
+
vimEnabledRef.current = next;
|
|
704
|
+
setVimEnabled(next);
|
|
705
|
+
// Every toggle lands in INSERT: a fresh vim user keeps typing
|
|
706
|
+
// normally until they press Esc for the first time. Turning the
|
|
707
|
+
// mode off also clears the undo stack — a later re-enable must
|
|
708
|
+
// never `u` its way back past edits made while vim was off.
|
|
709
|
+
vimInsertRef.current = true;
|
|
710
|
+
setVimInsert(true);
|
|
711
|
+
vimPendingRef.current = '';
|
|
712
|
+
clearVimUndo();
|
|
713
|
+
return next;
|
|
714
|
+
},
|
|
715
|
+
vimActive: () => vimEnabledRef.current,
|
|
716
|
+
};
|
|
717
|
+
return () => {
|
|
718
|
+
controllerRef.current = null;
|
|
719
|
+
};
|
|
720
|
+
});
|
|
721
|
+
const [selectedCommand, setSelectedCommand] = React.useState(0);
|
|
722
|
+
// ctrl+r history fill: replace the input when a new fill arrives, then
|
|
723
|
+
// tell the caller to clear it.
|
|
724
|
+
const lastFill = React.useRef(null);
|
|
725
|
+
React.useLayoutEffect(() => {
|
|
726
|
+
if (fillText && fillText !== lastFill.current) {
|
|
727
|
+
lastFill.current = fillText;
|
|
728
|
+
syncImageGeneration();
|
|
729
|
+
discardDraftImages();
|
|
730
|
+
updateFoldBlock(null);
|
|
731
|
+
setInput(fillText);
|
|
732
|
+
onFillConsumed?.();
|
|
733
|
+
}
|
|
734
|
+
}, [fillText, onFillConsumed]);
|
|
735
|
+
// Double-tap Esc to clear.
|
|
736
|
+
const escPendingRef = React.useRef(false);
|
|
737
|
+
const escTimerRef = React.useRef(null);
|
|
738
|
+
/** True while a clipboard paste read is in flight (ignore repeat keys). */
|
|
739
|
+
const clipboardBusyRef = React.useRef(false);
|
|
740
|
+
/** True while the external editor owns the terminal (editor-key round-trip). */
|
|
741
|
+
const editorBusyRef = React.useRef(false);
|
|
742
|
+
/** Enter dedupe window: cmd pipelines can deliver one Enter as `\r`+`\n`. */
|
|
743
|
+
const lastEnterAtRef = React.useRef(0);
|
|
744
|
+
React.useEffect(() => {
|
|
745
|
+
return () => {
|
|
746
|
+
if (escTimerRef.current)
|
|
747
|
+
clearTimeout(escTimerRef.current);
|
|
748
|
+
if (hoverLeaveTimerRef.current)
|
|
749
|
+
clearTimeout(hoverLeaveTimerRef.current);
|
|
750
|
+
/**
|
|
751
|
+
* Hand the draft to the owner's slot as this component goes away.
|
|
752
|
+
*
|
|
753
|
+
* This is the ONLY write to that slot, and it belongs here: it happens at
|
|
754
|
+
* the instant a screen replaces the composer, so it can neither race the
|
|
755
|
+
* restore effect nor change the transcript's first frame.
|
|
756
|
+
*
|
|
757
|
+
* `advanceDraftRevision` invalidates any image read/stage still in
|
|
758
|
+
* flight, so a late continuation cannot bind a capability into a composer
|
|
759
|
+
* that no longer exists. `clearVimUndo` releases the capabilities that
|
|
760
|
+
* only the undo stack was holding: they are not part of the draft, and
|
|
761
|
+
* nothing will ever restore them once this composer is gone.
|
|
762
|
+
*
|
|
763
|
+
* The stageIds the DRAFT holds are deliberately NOT revoked — they are
|
|
764
|
+
* part of the snapshot the slot now keeps, and the restore filters out
|
|
765
|
+
* whatever the channel revoked in the meantime. Revoking them here is why
|
|
766
|
+
* an image draft used to come back as inert text.
|
|
767
|
+
*/
|
|
768
|
+
advanceDraftRevision();
|
|
769
|
+
clearVimUndo();
|
|
770
|
+
const images = [...draftImagesRef.current.entries()]
|
|
771
|
+
.map(([token, stageId]) => [token, stageId]);
|
|
772
|
+
draftImagesRef.current.clear();
|
|
773
|
+
if (draftCache === undefined) {
|
|
774
|
+
discardUnretainedImages(images.map(image => image[1]));
|
|
775
|
+
return;
|
|
776
|
+
}
|
|
777
|
+
const text = valueRef.current;
|
|
778
|
+
// The edit state rides along even with nothing typed: vim mode and the
|
|
779
|
+
// fullscreen editor are MODES the user turned on, not content, and an
|
|
780
|
+
// empty composer must not drop them (`text === ''` alone used to skip
|
|
781
|
+
// the snapshot entirely, losing the vim badge on a round trip with an
|
|
782
|
+
// empty composer).
|
|
783
|
+
const editState = foldBlockRef.current !== null || expandedRef.current
|
|
784
|
+
|| vimEnabledRef.current;
|
|
785
|
+
if (text === '' && images.length === 0 && !editState) {
|
|
786
|
+
draftCache.current = null;
|
|
787
|
+
return;
|
|
788
|
+
}
|
|
789
|
+
draftCache.current = {
|
|
790
|
+
ownerAgentId: String(channel.agentId),
|
|
791
|
+
bindingGeneration: resolveBindingGeneration(channel),
|
|
792
|
+
value: text,
|
|
793
|
+
cursor: cursorRef.current,
|
|
794
|
+
foldBlock: foldBlockRef.current,
|
|
795
|
+
expanded: expandedRef.current,
|
|
796
|
+
vimEnabled: vimEnabledRef.current,
|
|
797
|
+
vimInsert: vimInsertRef.current,
|
|
798
|
+
images,
|
|
799
|
+
};
|
|
800
|
+
};
|
|
801
|
+
}, []);
|
|
802
|
+
const { columns, rows: terminalRows } = useTerminalSize();
|
|
803
|
+
React.useEffect(() => {
|
|
804
|
+
// PromptInput self-detects double-clicks because its drag target resets
|
|
805
|
+
// App's global chain. Geometry changed across resize, so the same screen
|
|
806
|
+
// cell no longer identifies the same grapheme.
|
|
807
|
+
lastClickAtRef.current = 0;
|
|
808
|
+
lastClickColRef.current = -1;
|
|
809
|
+
lastClickRowRef.current = -1;
|
|
810
|
+
}, [columns, terminalRows]);
|
|
811
|
+
const helpScrollRef = React.useRef(null);
|
|
812
|
+
// Help viewport budget: the overlay anchors at the composer's top edge
|
|
813
|
+
// (OverlayAbove bottom:'100%') and grows UP, so its budget is the space
|
|
814
|
+
// ABOVE that anchor — smallest on an empty session, where the whale
|
|
815
|
+
// splash (~15 rows) sits between the screen top and the composer
|
|
816
|
+
// (terminalRows minus chrome only applies once the transcript fills the
|
|
817
|
+
// viewport). Take the conservative intersection: 15 rows of viewport (16
|
|
818
|
+
// with the hint + margin) fits the empty-session anchor on the default
|
|
819
|
+
// layout at any terminal size, and the renderer's bottom-anchored
|
|
820
|
+
// clipping for absolute overlays (no negative-y clamp) then never has
|
|
821
|
+
// to eat the overlay's FIRST rows — the shortcut-column headers. The
|
|
822
|
+
// command registry scrolls inside the viewport, so a taller terminal
|
|
823
|
+
// loses nothing functional. (PR #446; restored after the picker
|
|
824
|
+
// snapshot's cherry-pick resurrected the old formula.)
|
|
825
|
+
const helpViewportHeight = Math.max(3, Math.min(terminalRows - 7, 15));
|
|
826
|
+
const suggestions = value.startsWith('/') ? channel.commandCompletions(value) : [];
|
|
827
|
+
const overlayOpen = suggestions.length > 0 &&
|
|
828
|
+
!expanded &&
|
|
829
|
+
!helpOpen &&
|
|
830
|
+
!selectionActive &&
|
|
831
|
+
!value.includes('\n');
|
|
832
|
+
// `@` file completion (issue #15): the trigger is the mention token at the
|
|
833
|
+
// CARET, so `@` works mid-message (`看看 @src/a.ts 这个`), not only when it
|
|
834
|
+
// is the input's first character. The cwd listing loads when the trigger
|
|
835
|
+
// appears.
|
|
836
|
+
const [fileMatches, setFileMatches] = React.useState([]);
|
|
837
|
+
const [fileSelected, setFileSelected] = React.useState(0);
|
|
838
|
+
const mention = mentionAtCaret(value, cursor);
|
|
839
|
+
const atTrigger = mention !== undefined;
|
|
840
|
+
const fileRequestId = React.useRef(0);
|
|
841
|
+
const selectedFile = fileMatches[fileSelected];
|
|
842
|
+
React.useEffect(() => {
|
|
843
|
+
const requestId = ++fileRequestId.current;
|
|
844
|
+
if (!mention) {
|
|
845
|
+
setFileMatches([]);
|
|
846
|
+
setFileSelected(0);
|
|
847
|
+
return;
|
|
848
|
+
}
|
|
849
|
+
const previous = selectedFile;
|
|
850
|
+
// Deps key on `mention.query` (and trigger on/off) only: cursor movement
|
|
851
|
+
// within the same token must NOT refetch, and `selectedFile`/`fileSelected`
|
|
852
|
+
// are read as their render-time values only to seed selection preservation.
|
|
853
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
854
|
+
void channel.listFileCandidates(mention.query, { topK: 50 }).then(next => {
|
|
855
|
+
if (requestId !== fileRequestId.current)
|
|
856
|
+
return;
|
|
857
|
+
setFileMatches(next);
|
|
858
|
+
setFileSelected(preserveSelection(previous, next, fileSelected));
|
|
859
|
+
});
|
|
860
|
+
}, [channel, mention?.query, atTrigger]);
|
|
861
|
+
// Esc dismisses the overlay for the token being edited (it reopens once the
|
|
862
|
+
// text changes); it must NOT clear a mid-message input.
|
|
863
|
+
const fileEscRef = React.useRef(-1);
|
|
864
|
+
React.useEffect(() => {
|
|
865
|
+
fileEscRef.current = -1;
|
|
866
|
+
}, [value]);
|
|
867
|
+
const fileOverlayOpen = fileMatches.length > 0 &&
|
|
868
|
+
!expanded &&
|
|
869
|
+
!helpOpen &&
|
|
870
|
+
!selectionActive &&
|
|
871
|
+
fileEscRef.current !== mention?.start;
|
|
872
|
+
/**
|
|
873
|
+
* The `[Image #N]` tokens of `text` that carry a live draft capability.
|
|
874
|
+
* These are atomic: the caret never rests inside one, ←/→ step over the
|
|
875
|
+
* whole token, Backspace at its end / Delete at its start remove it whole,
|
|
876
|
+
* a selection never cuts it, and it renders as a chip (the whole token is
|
|
877
|
+
* the caret cluster when the caret sits at its start). A raw token typed
|
|
878
|
+
* or restored without a capability is ordinary text.
|
|
879
|
+
*/
|
|
880
|
+
const boundImageSpans = (text) => imageTokenSpans(text).filter(span => draftImagesRef.current.has(span.token));
|
|
881
|
+
/** Move the caret to `offset`, snapped off any token interior. */
|
|
882
|
+
const placeCaret = (offset, prefer) => {
|
|
883
|
+
const snapped = snapOffImageToken(boundImageSpans(valueRef.current), offset, prefer);
|
|
884
|
+
cursorRef.current = snapped;
|
|
885
|
+
setCursor(snapped);
|
|
886
|
+
};
|
|
887
|
+
/**
|
|
888
|
+
* The staged image the caret is on: the token whose start is the caret —
|
|
889
|
+
* exactly when the whole token is the caret cluster and inverts. The
|
|
890
|
+
* caret sitting just after a token is not "on" it (the token is plain
|
|
891
|
+
* there), so no preview. Undefined for a raw or stale token.
|
|
892
|
+
*/
|
|
893
|
+
const caretImageAt = (text, offset) => {
|
|
894
|
+
const span = boundImageSpans(text).find(s => s.start === offset);
|
|
895
|
+
if (span === undefined)
|
|
896
|
+
return undefined;
|
|
897
|
+
const stageId = draftImagesRef.current.get(span.token);
|
|
898
|
+
const image = stageId === undefined ? undefined : channel.stagedImage(stageId);
|
|
899
|
+
return image === undefined ? undefined : { image, title: span.token.slice(1, -1) };
|
|
900
|
+
};
|
|
901
|
+
const onCaretImageRef = React.useRef(onCaretImage);
|
|
902
|
+
onCaretImageRef.current = onCaretImage;
|
|
903
|
+
const lastCaretImageRef = React.useRef({ image: undefined, title: undefined });
|
|
904
|
+
/** Tell the caller which staged image the caret is on. `'caret'` reports
|
|
905
|
+
* only changes; `'click'` always reports (see onCaretImage). "Same image"
|
|
906
|
+
* is the attachment id plus the token, not facade identity: this runs
|
|
907
|
+
* after every commit and the caller stores what it reports, so an
|
|
908
|
+
* identity-only difference would re-render the caller forever (#885). */
|
|
909
|
+
const reportCaretImage = (reason) => {
|
|
910
|
+
const report = onCaretImageRef.current;
|
|
911
|
+
if (report === undefined)
|
|
912
|
+
return;
|
|
913
|
+
const found = suspended ? undefined : caretImageAt(valueRef.current, cursorRef.current);
|
|
914
|
+
const last = lastCaretImageRef.current;
|
|
915
|
+
if (reason === 'caret' && last.image?.id === found?.image.id && last.title === found?.title)
|
|
916
|
+
return;
|
|
917
|
+
lastCaretImageRef.current = { image: found?.image, title: found?.title };
|
|
918
|
+
report(found?.image, found?.title, reason);
|
|
919
|
+
};
|
|
920
|
+
// After every commit: the caret, the text and the staged map are all
|
|
921
|
+
// settled by then, and a no-change report is skipped.
|
|
922
|
+
React.useEffect(() => { reportCaretImage('caret'); });
|
|
923
|
+
React.useEffect(() => () => {
|
|
924
|
+
if (lastCaretImageRef.current.image !== undefined) {
|
|
925
|
+
onCaretImageRef.current?.(undefined, undefined, 'caret');
|
|
926
|
+
}
|
|
927
|
+
}, []);
|
|
928
|
+
/** Fold-block state + a synchronous mirror (setInput reads the ref).
|
|
929
|
+
* Creating a block also drags a caret that sits inside it out to the
|
|
930
|
+
* block's end (the block is atomic; typing continues after it). */
|
|
931
|
+
const updateFoldBlock = (block) => {
|
|
932
|
+
foldBlockRef.current = block;
|
|
933
|
+
setFoldBlock(block);
|
|
934
|
+
if (block) {
|
|
935
|
+
const c = cursorRef.current;
|
|
936
|
+
if (c > block.start && c < block.end) {
|
|
937
|
+
cursorRef.current = block.end;
|
|
938
|
+
setCursor(block.end);
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
};
|
|
942
|
+
const setInput = (next, cursorOffset = next.length) => {
|
|
943
|
+
// Apply the same ingress normalization to fills/history/editor results as
|
|
944
|
+
// to paste. Map the requested caret through the sanitized prefix so an
|
|
945
|
+
// ANSI sequence removed before it cannot leave the caret past the text.
|
|
946
|
+
const sanitizedCursor = sanitizeEditableText(next.slice(0, cursorOffset)).length;
|
|
947
|
+
next = sanitizeEditableText(next);
|
|
948
|
+
cursorOffset = sanitizedCursor;
|
|
949
|
+
const prev = valueRef.current;
|
|
950
|
+
const prevCursor = cursorRef.current;
|
|
951
|
+
const block = foldBlockRef.current;
|
|
952
|
+
// Normalize onto a grapheme boundary (also clamps into range): every
|
|
953
|
+
// caller passes a caret they believe is on a character edge — paste
|
|
954
|
+
// merges, history fills, and IME composition can still hand back an
|
|
955
|
+
// offset inside a surrogate pair or combining cluster.
|
|
956
|
+
let offset = normalizeCursorOffset(next, cursorOffset);
|
|
957
|
+
if (block) {
|
|
958
|
+
// Fold block is atomic: the caret never lands INSIDE [start, end).
|
|
959
|
+
if (offset > block.start && offset < block.end) {
|
|
960
|
+
offset = offset - block.start < block.end - offset ? block.start : block.end;
|
|
961
|
+
}
|
|
962
|
+
// Every real edit happens AT the caret, so a block that starts at or
|
|
963
|
+
// after the caret shifts with the value delta; one fully past the
|
|
964
|
+
// caret is untouched. Whole-value replacements (fill/history/editor)
|
|
965
|
+
// clear the block explicitly at their call sites.
|
|
966
|
+
const delta = next.length - prev.length;
|
|
967
|
+
let start = block.start;
|
|
968
|
+
let end = block.end;
|
|
969
|
+
if (prevCursor <= block.start) {
|
|
970
|
+
start += delta;
|
|
971
|
+
end += delta;
|
|
972
|
+
}
|
|
973
|
+
start = Math.max(0, Math.min(start, next.length));
|
|
974
|
+
end = Math.max(start, Math.min(end, next.length));
|
|
975
|
+
if (start >= end)
|
|
976
|
+
updateFoldBlock(null);
|
|
977
|
+
else if (start !== block.start || end !== block.end)
|
|
978
|
+
updateFoldBlock({ start, end });
|
|
979
|
+
}
|
|
980
|
+
// Staged `[Image #N]` tokens are atomic: a caret that would land inside
|
|
981
|
+
// one continues in its direction of travel (←/word-left/↑ → the token's
|
|
982
|
+
// start, →/word-right/↓ → its end); with no travel, the nearer edge.
|
|
983
|
+
offset = snapOffImageToken(boundImageSpans(next), offset, offset < prevCursor ? 'start' : offset > prevCursor ? 'end' : 'nearest');
|
|
984
|
+
// The synchronous mirrors are what batch-dispatched events (one stdin
|
|
985
|
+
// read → several keys, no render in between) read on their next turn.
|
|
986
|
+
if (next !== prev)
|
|
987
|
+
inputEditSequenceRef.current += 1;
|
|
988
|
+
valueRef.current = next;
|
|
989
|
+
cursorRef.current = offset;
|
|
990
|
+
// Deleting a visible token revokes its capability. Re-typing the same
|
|
991
|
+
// label later must stay inert; only a fresh paste may mint a new binding.
|
|
992
|
+
for (const [token, stageId] of draftImagesRef.current) {
|
|
993
|
+
if (next.includes(token))
|
|
994
|
+
continue;
|
|
995
|
+
draftImagesRef.current.delete(token);
|
|
996
|
+
if (!stageIdIsRetained(stageId))
|
|
997
|
+
channel.discardStagedImage(stageId);
|
|
998
|
+
}
|
|
999
|
+
// Every real edit drops the selection: its offsets describe the OLD
|
|
1000
|
+
// text. Selection-consuming callers (delete/replace) read it first.
|
|
1001
|
+
selectionRef.current = null;
|
|
1002
|
+
setSelection(null);
|
|
1003
|
+
setValue(next);
|
|
1004
|
+
setCursor(offset);
|
|
1005
|
+
};
|
|
1006
|
+
const deleteInputRange = (start, end) => {
|
|
1007
|
+
if (start >= end)
|
|
1008
|
+
return;
|
|
1009
|
+
const text = valueRef.current;
|
|
1010
|
+
const range = expandImageTokenRange(boundImageSpans(text), start, end);
|
|
1011
|
+
setInput(text.slice(0, range.start) + text.slice(range.end), range.start);
|
|
1012
|
+
};
|
|
1013
|
+
/**
|
|
1014
|
+
* Write the selection [start, end) (snapped to grapheme boundaries,
|
|
1015
|
+
* start ≤ end); a degenerate range clears it. With a fold block the
|
|
1016
|
+
* range is clamped into the side that holds `start` — the block is
|
|
1017
|
+
* atomic and a selection may never cross the chip row.
|
|
1018
|
+
*/
|
|
1019
|
+
const updateSelection = (start, end) => {
|
|
1020
|
+
const text = valueRef.current;
|
|
1021
|
+
const anchor = normalizeCursorOffset(text, start);
|
|
1022
|
+
let lo = normalizeCursorOffset(text, Math.min(start, end));
|
|
1023
|
+
let hi = normalizeCursorOffset(text, Math.max(start, end));
|
|
1024
|
+
const block = foldBlockRef.current;
|
|
1025
|
+
if (block) {
|
|
1026
|
+
// Clamp by the ORIGINAL anchor side, not sorted `lo`: for a reverse
|
|
1027
|
+
// tail→head drag, `lo` is in the head even though the gesture belongs
|
|
1028
|
+
// to the tail. Both selection and caret must stay on the anchor side.
|
|
1029
|
+
if (anchor <= block.start) {
|
|
1030
|
+
hi = Math.min(hi, block.start);
|
|
1031
|
+
}
|
|
1032
|
+
else {
|
|
1033
|
+
lo = Math.max(lo, block.end);
|
|
1034
|
+
hi = Math.max(hi, block.end);
|
|
1035
|
+
}
|
|
1036
|
+
}
|
|
1037
|
+
// A selection never cuts a staged token: an edge inside one grows
|
|
1038
|
+
// outward to cover the whole token.
|
|
1039
|
+
const range = expandImageTokenRange(boundImageSpans(text), lo, hi);
|
|
1040
|
+
lo = range.start;
|
|
1041
|
+
hi = range.end;
|
|
1042
|
+
if (lo >= hi) {
|
|
1043
|
+
selectionRef.current = null;
|
|
1044
|
+
setSelection(null);
|
|
1045
|
+
}
|
|
1046
|
+
else {
|
|
1047
|
+
selectionRef.current = { start: lo, end: hi };
|
|
1048
|
+
setSelection({ start: lo, end: hi });
|
|
1049
|
+
}
|
|
1050
|
+
};
|
|
1051
|
+
/** Drop the selection without touching text or caret. */
|
|
1052
|
+
const clearSelection = () => {
|
|
1053
|
+
selectionRef.current = null;
|
|
1054
|
+
setSelection(null);
|
|
1055
|
+
};
|
|
1056
|
+
/**
|
|
1057
|
+
* Accept the selected file suggestion: replace ONLY the mention token at
|
|
1058
|
+
* the caret (prefix/suffix text survives), quoting whitespace paths. A
|
|
1059
|
+
* directory inserts `@dir/` without a trailing space so completion
|
|
1060
|
+
* continues into it; a file completes the token with a space. A typed
|
|
1061
|
+
* `#L12-14` suffix is NOT part of the replacement — completion ends at
|
|
1062
|
+
* `pathEnd` so the line range survives acceptance (issue #359).
|
|
1063
|
+
*/
|
|
1064
|
+
const acceptFile = (candidate) => {
|
|
1065
|
+
if (!mention)
|
|
1066
|
+
return;
|
|
1067
|
+
const file = candidate.path;
|
|
1068
|
+
const body = /\s/.test(file) ? `@"${file}"` : `@${file}`;
|
|
1069
|
+
// A typed `#L12-14` suffix rides along AFTER the completed body (before
|
|
1070
|
+
// the trailing space) — quoting a whitespace path must not detach it.
|
|
1071
|
+
const suffix = mention.pathEnd === undefined ? '' : value.slice(mention.pathEnd, mention.end);
|
|
1072
|
+
const insert = candidate.kind === 'directory' ? `${body}${suffix}` : `${body}${suffix} `;
|
|
1073
|
+
const next = value.slice(0, mention.start) + insert + value.slice(mention.end);
|
|
1074
|
+
setInput(next, mention.start + insert.length);
|
|
1075
|
+
setFileSelected(0);
|
|
1076
|
+
};
|
|
1077
|
+
const imageRefsFor = (text) => {
|
|
1078
|
+
syncImageGeneration();
|
|
1079
|
+
return composerImageRefsForText(text, draftImagesRef.current);
|
|
1080
|
+
};
|
|
1081
|
+
/**
|
|
1082
|
+
* Warn once when the text carries an `[Image #N]` placeholder that will
|
|
1083
|
+
* not attach an image: unbound in this draft (history, rewind, typed) or
|
|
1084
|
+
* bound to a staging the channel has since dropped. Channel routes
|
|
1085
|
+
* (submit, steer, registry commands) warn on their own; this covers the
|
|
1086
|
+
* lines the screen consumes locally, where the text is otherwise dropped
|
|
1087
|
+
* without a word.
|
|
1088
|
+
*/
|
|
1089
|
+
const warnStaleImageTokens = (text, images) => {
|
|
1090
|
+
const live = new Set(images
|
|
1091
|
+
.filter(image => channel.stagedImage(image.stageId) !== undefined)
|
|
1092
|
+
.map(image => image.token));
|
|
1093
|
+
for (const match of text.matchAll(COMPOSER_IMAGE_TOKEN)) {
|
|
1094
|
+
if (live.has(match[0]))
|
|
1095
|
+
continue;
|
|
1096
|
+
channel.notify(t('input-image-token-stale', { token: match[0] }), { color: 'warning', timeoutMs: 5000 });
|
|
1097
|
+
return;
|
|
1098
|
+
}
|
|
1099
|
+
};
|
|
1100
|
+
/**
|
|
1101
|
+
* Seed the walk with the persisted history (issue #986). `↑`/`↓` used to
|
|
1102
|
+
* see only what this process submitted, so a restart lost every earlier
|
|
1103
|
+
* entry. Seeding before the first push keeps ONE chronological list —
|
|
1104
|
+
* persisted entries first, this run's submits behind them — instead of two
|
|
1105
|
+
* lists to merge at recall time. Restored text carries no image capability
|
|
1106
|
+
* (the file stores text only), which is also what keeps a recalled entry
|
|
1107
|
+
* from binding to a later staged image by accident.
|
|
1108
|
+
*/
|
|
1109
|
+
const seedHistory = () => {
|
|
1110
|
+
if (historySeeded.current)
|
|
1111
|
+
return;
|
|
1112
|
+
historySeeded.current = true;
|
|
1113
|
+
history.current = loadHistoryOldestFirst().map(entry => ({ text: entry.text, images: [] }));
|
|
1114
|
+
historyIndex.current = -1;
|
|
1115
|
+
};
|
|
1116
|
+
const rememberHistory = (text, images) => {
|
|
1117
|
+
// Both entries into the walk (a submit and ↑) must see the persisted
|
|
1118
|
+
// prefix, so seed here rather than merging two lists later.
|
|
1119
|
+
seedHistory();
|
|
1120
|
+
history.current.push({
|
|
1121
|
+
text,
|
|
1122
|
+
images: images.map(image => ({ ...image })),
|
|
1123
|
+
});
|
|
1124
|
+
if (history.current.length > HISTORY_LIMIT)
|
|
1125
|
+
history.current.shift();
|
|
1126
|
+
historyIndex.current = -1;
|
|
1127
|
+
void appendHistory(text);
|
|
1128
|
+
};
|
|
1129
|
+
const clearDeliveredDraft = () => {
|
|
1130
|
+
syncImageGeneration();
|
|
1131
|
+
// Delivery captured the opaque refs and history retains them; only the
|
|
1132
|
+
// editable-draft binding is ending here.
|
|
1133
|
+
detachDraftImages();
|
|
1134
|
+
setInput('', 0);
|
|
1135
|
+
setSelectedCommand(0);
|
|
1136
|
+
setFileSelected(0);
|
|
1137
|
+
};
|
|
1138
|
+
const sameImageRefs = (left, right) => left.length === right.length && left.every((image, index) => image.token === right[index]?.token && image.stageId === right[index]?.stageId);
|
|
1139
|
+
/** `!`/`!!` are host shell routes, not model messages. They have no image
|
|
1140
|
+
* grammar, so reject before history/clear just like a non-image command. */
|
|
1141
|
+
const shellImagesUnsupported = (text, images) => {
|
|
1142
|
+
if (images.length === 0 || !text.startsWith('!'))
|
|
1143
|
+
return false;
|
|
1144
|
+
channel.notify(t('shell-images-unsupported'), { color: 'warning', timeoutMs: 4000 });
|
|
1145
|
+
return true;
|
|
1146
|
+
};
|
|
1147
|
+
const restoreDraftImages = (entry) => {
|
|
1148
|
+
syncImageGeneration();
|
|
1149
|
+
advanceDraftRevision();
|
|
1150
|
+
replaceDraftImages(entry.images);
|
|
1151
|
+
clearVimUndo();
|
|
1152
|
+
};
|
|
1153
|
+
const submitText = (text, notice) => {
|
|
1154
|
+
const trimmed = text.trim();
|
|
1155
|
+
if (!trimmed)
|
|
1156
|
+
return;
|
|
1157
|
+
const images = imageRefsFor(trimmed);
|
|
1158
|
+
if (shellImagesUnsupported(trimmed, images))
|
|
1159
|
+
return;
|
|
1160
|
+
rememberHistory(trimmed, images);
|
|
1161
|
+
clearDeliveredDraft();
|
|
1162
|
+
channel.submit(trimmed, images);
|
|
1163
|
+
if (notice) {
|
|
1164
|
+
channel.notify(notice, { timeoutMs: 2500 });
|
|
1165
|
+
}
|
|
1166
|
+
else if (channel.working) {
|
|
1167
|
+
// While the model is streaming, the message joins the DSH inbox and is
|
|
1168
|
+
// processed after the current turn — say so, or it looks "lost".
|
|
1169
|
+
channel.notify(t('input-sent-after-turn'), { timeoutMs: 2500 });
|
|
1170
|
+
}
|
|
1171
|
+
};
|
|
1172
|
+
/**
|
|
1173
|
+
* Enter while the model is working = STEER (Codex/pi semantics): the
|
|
1174
|
+
* message is injected at the next step boundary of the RUNNING turn and
|
|
1175
|
+
* the agent continues without aborting — the "immediate" send.
|
|
1176
|
+
*/
|
|
1177
|
+
const steerSend = (text) => {
|
|
1178
|
+
const trimmed = text.trim();
|
|
1179
|
+
if (!trimmed)
|
|
1180
|
+
return;
|
|
1181
|
+
const images = imageRefsFor(trimmed);
|
|
1182
|
+
rememberHistory(trimmed, images);
|
|
1183
|
+
clearDeliveredDraft();
|
|
1184
|
+
channel.steer(trimmed, images);
|
|
1185
|
+
channel.notify(t('input-interrupted-next'), { timeoutMs: 2500 });
|
|
1186
|
+
};
|
|
1187
|
+
/**
|
|
1188
|
+
* Tab while the model is working = plain queue (followup): the message
|
|
1189
|
+
* waits for the running turn to end, then is processed in order.
|
|
1190
|
+
*/
|
|
1191
|
+
const queueSend = (text) => {
|
|
1192
|
+
const trimmed = text.trim();
|
|
1193
|
+
if (!trimmed)
|
|
1194
|
+
return;
|
|
1195
|
+
const images = imageRefsFor(trimmed);
|
|
1196
|
+
if (shellImagesUnsupported(trimmed, images))
|
|
1197
|
+
return;
|
|
1198
|
+
rememberHistory(trimmed, images);
|
|
1199
|
+
clearDeliveredDraft();
|
|
1200
|
+
channel.submit(trimmed, images);
|
|
1201
|
+
channel.notify(t('input-queued-after-turn'), { timeoutMs: 2500 });
|
|
1202
|
+
};
|
|
1203
|
+
/**
|
|
1204
|
+
* Alt+Up: pull the last pending message back into the input for editing
|
|
1205
|
+
* (never interrupts the running turn). Refused when the running dsh-agent
|
|
1206
|
+
* cannot withdraw inbox messages (released package without the inbox API).
|
|
1207
|
+
*/
|
|
1208
|
+
const pullBackLast = () => {
|
|
1209
|
+
const item = channel.pending[channel.pending.length - 1];
|
|
1210
|
+
if (!item)
|
|
1211
|
+
return;
|
|
1212
|
+
if (!channel.removePending(item.id)) {
|
|
1213
|
+
channel.notify(t('input-cannot-retract'), { color: 'warning', timeoutMs: 2500 });
|
|
1214
|
+
return;
|
|
1215
|
+
}
|
|
1216
|
+
restoreDraftImages({
|
|
1217
|
+
text: item.text,
|
|
1218
|
+
images: item.images ?? [],
|
|
1219
|
+
});
|
|
1220
|
+
setInput(item.text);
|
|
1221
|
+
updateFoldBlock(null);
|
|
1222
|
+
setSelectedCommand(0);
|
|
1223
|
+
setFileSelected(0);
|
|
1224
|
+
channel.notify(t('input-retracted'), { timeoutMs: 2000 });
|
|
1225
|
+
};
|
|
1226
|
+
/**
|
|
1227
|
+
* Withdraw the queued copy of the text `↑` just recalled (issue #986): the
|
|
1228
|
+
* message is still parked in the inbox, so editing it and sending again
|
|
1229
|
+
* would run the same text twice. Alt+Up withdraws explicitly; walking the
|
|
1230
|
+
* history to the same text has to land in the same place. The newest match
|
|
1231
|
+
* wins — `↑` walks newest-first and the queue is FIFO. A message the
|
|
1232
|
+
* running turn already claimed cannot be withdrawn, and saying so beats
|
|
1233
|
+
* pretending it was.
|
|
1234
|
+
*/
|
|
1235
|
+
const retractRecalledCopy = (text) => {
|
|
1236
|
+
let target;
|
|
1237
|
+
for (const item of channel.pending) {
|
|
1238
|
+
if (item.text === text)
|
|
1239
|
+
target = item;
|
|
1240
|
+
}
|
|
1241
|
+
if (target === undefined)
|
|
1242
|
+
return;
|
|
1243
|
+
if (channel.removePending(target.id)) {
|
|
1244
|
+
channel.notify(t('input-retracted'), { timeoutMs: 2000 });
|
|
1245
|
+
}
|
|
1246
|
+
else {
|
|
1247
|
+
channel.notify(t('input-cannot-retract'), { color: 'warning', timeoutMs: 2500 });
|
|
1248
|
+
}
|
|
1249
|
+
};
|
|
1250
|
+
/**
|
|
1251
|
+
* Ctrl+Enter: abort the running turn and send the input immediately — the
|
|
1252
|
+
* model stops what it is doing and starts on this message right away.
|
|
1253
|
+
*/
|
|
1254
|
+
const interruptSend = () => {
|
|
1255
|
+
const trimmed = value.trim();
|
|
1256
|
+
if (!trimmed) {
|
|
1257
|
+
channel.notify(t('input-empty'), { color: 'warning' });
|
|
1258
|
+
return;
|
|
1259
|
+
}
|
|
1260
|
+
// Abort the running turn and deliver: previously queued pending
|
|
1261
|
+
// messages first (FIFO), then the current input — all processed
|
|
1262
|
+
// immediately once the abort settles.
|
|
1263
|
+
const images = imageRefsFor(trimmed);
|
|
1264
|
+
const queued = [
|
|
1265
|
+
...channel.pending.map(item => ({ text: item.text, images: item.images ?? [] })),
|
|
1266
|
+
{ text: value, images },
|
|
1267
|
+
];
|
|
1268
|
+
const count = channel.interruptAndDeliver(queued);
|
|
1269
|
+
if (count === 0)
|
|
1270
|
+
return;
|
|
1271
|
+
rememberHistory(trimmed, images);
|
|
1272
|
+
clearDeliveredDraft();
|
|
1273
|
+
channel.notify(t('input-interrupt-immediate'), { timeoutMs: 2500 });
|
|
1274
|
+
};
|
|
1275
|
+
/**
|
|
1276
|
+
* Execute a slash command (built-in, plugin-registered, or hidden) when
|
|
1277
|
+
* the input resolves to one: the name parses as the first token so
|
|
1278
|
+
* `/plan off` dispatches `plan` with its argument text, and the merged
|
|
1279
|
+
* command list (locals + registry) decides whether the line is a command
|
|
1280
|
+
* at all. Hidden commands are recognized even though they are intentionally
|
|
1281
|
+
* absent from the suggestion/help catalogs.
|
|
1282
|
+
*/
|
|
1283
|
+
const tryRunCommand = (text) => {
|
|
1284
|
+
if (!text.startsWith('/'))
|
|
1285
|
+
return false;
|
|
1286
|
+
const parsed = parseCommandName(text);
|
|
1287
|
+
if (parsed === undefined)
|
|
1288
|
+
return false;
|
|
1289
|
+
const command = channel.commandList.find(entry => entry.name === parsed.name);
|
|
1290
|
+
const known = command !== undefined || isHiddenCommandName(parsed.name);
|
|
1291
|
+
if (!known)
|
|
1292
|
+
return false;
|
|
1293
|
+
const generation = syncImageGeneration();
|
|
1294
|
+
const revision = draftRevisionRef.current;
|
|
1295
|
+
const editSequence = inputEditSequenceRef.current;
|
|
1296
|
+
if (pendingCommandRef.current?.generation === generation
|
|
1297
|
+
&& pendingCommandRef.current.revision === revision
|
|
1298
|
+
&& pendingCommandRef.current.editSequence === editSequence) {
|
|
1299
|
+
channel.notify(t('command-running'), { color: 'warning', timeoutMs: 2500 });
|
|
1300
|
+
return true;
|
|
1301
|
+
}
|
|
1302
|
+
if (pendingCommandRef.current !== null)
|
|
1303
|
+
pendingCommandRef.current = null;
|
|
1304
|
+
const images = imageRefsFor(text);
|
|
1305
|
+
// Commands are an explicit non-model route. Unless their registry
|
|
1306
|
+
// descriptor opted into composer images, refuse before dispatch and keep
|
|
1307
|
+
// the exact draft/capabilities so the user can edit or send them normally.
|
|
1308
|
+
// Completion-only filesystem skills still fall through to the model.
|
|
1309
|
+
const modelRoutedSkill = command?.skill === true && command.external !== true;
|
|
1310
|
+
if (images.length > 0 && !modelRoutedSkill && command?.acceptsImages !== true) {
|
|
1311
|
+
channel.notify(t('command-images-unsupported', { name: parsed.name }), {
|
|
1312
|
+
color: 'warning',
|
|
1313
|
+
timeoutMs: 4000,
|
|
1314
|
+
});
|
|
1315
|
+
return true;
|
|
1316
|
+
}
|
|
1317
|
+
const lease = captureDraftImageLease();
|
|
1318
|
+
const draftText = valueRef.current;
|
|
1319
|
+
const draftImages = imageRefsFor(draftText);
|
|
1320
|
+
const handled = onRunCommand(parsed.name, parsed.rawInput, images);
|
|
1321
|
+
if (handled === true) {
|
|
1322
|
+
// A local command consumed the line inside the screen.
|
|
1323
|
+
warnStaleImageTokens(text, images);
|
|
1324
|
+
rememberHistory(text.trim(), images);
|
|
1325
|
+
clearDeliveredDraft();
|
|
1326
|
+
}
|
|
1327
|
+
else if (handled !== false) {
|
|
1328
|
+
// Registry commands settle asynchronously. Keep the exact draft and
|
|
1329
|
+
// capabilities in place until admission succeeds; a handler/admission
|
|
1330
|
+
// error deliberately leaves them editable. A late success may clear
|
|
1331
|
+
// only the snapshot it actually submitted, never text/images typed
|
|
1332
|
+
// while the command was running or a replacement session's draft.
|
|
1333
|
+
const attempt = { token: Symbol('command-attempt'), generation, revision, editSequence };
|
|
1334
|
+
pendingCommandRef.current = attempt;
|
|
1335
|
+
void handled.then((consume) => {
|
|
1336
|
+
if (!consume
|
|
1337
|
+
|| !draftImageLeaseIsCurrent(lease)
|
|
1338
|
+
|| inputEditSequenceRef.current !== editSequence)
|
|
1339
|
+
return;
|
|
1340
|
+
rememberHistory(text.trim(), images);
|
|
1341
|
+
const currentText = valueRef.current;
|
|
1342
|
+
const currentImages = imageRefsFor(currentText);
|
|
1343
|
+
if (currentText === draftText && sameImageRefs(currentImages, draftImages)) {
|
|
1344
|
+
clearDeliveredDraft();
|
|
1345
|
+
}
|
|
1346
|
+
}).catch((error) => {
|
|
1347
|
+
if (!draftImageLeaseIsCurrent(lease))
|
|
1348
|
+
return;
|
|
1349
|
+
channel.notify(error instanceof Error ? error.message : String(error), {
|
|
1350
|
+
color: 'error',
|
|
1351
|
+
timeoutMs: 5000,
|
|
1352
|
+
});
|
|
1353
|
+
}).finally(() => {
|
|
1354
|
+
if (pendingCommandRef.current?.token === attempt.token)
|
|
1355
|
+
pendingCommandRef.current = null;
|
|
1356
|
+
});
|
|
1357
|
+
}
|
|
1358
|
+
return handled !== false;
|
|
1359
|
+
};
|
|
1360
|
+
/**
|
|
1361
|
+
* The Enter main path, shared by the inline prompt, the expanded
|
|
1362
|
+
* editor's Ctrl+Enter, and its Send button:
|
|
1363
|
+
* - command menu open → run the SELECTED command (never send `/mo`);
|
|
1364
|
+
* - model working → STEER into the running turn (next step boundary,
|
|
1365
|
+
* agent continues — the "immediate" send; Codex/pi semantics);
|
|
1366
|
+
* - otherwise → submit directly (or run a unique command).
|
|
1367
|
+
* Reads valueRef so a key batch (typing + Enter in one stdin read)
|
|
1368
|
+
* operates on the text the preceding keys produced.
|
|
1369
|
+
*/
|
|
1370
|
+
const handleEnter = () => {
|
|
1371
|
+
// A single Enter can arrive as two events in cmd pipelines (`\r`
|
|
1372
|
+
// parsed as return + a raw `\n` line): collapse them so one keypress
|
|
1373
|
+
// never sends the message twice.
|
|
1374
|
+
const now = Date.now();
|
|
1375
|
+
if (now - lastEnterAtRef.current < 80)
|
|
1376
|
+
return;
|
|
1377
|
+
lastEnterAtRef.current = now;
|
|
1378
|
+
const value = valueRef.current;
|
|
1379
|
+
if (overlayOpen) {
|
|
1380
|
+
const command = suggestions[selectedCommand];
|
|
1381
|
+
if (command) {
|
|
1382
|
+
tryRunCommand(command.commandLine);
|
|
1383
|
+
return;
|
|
1384
|
+
}
|
|
1385
|
+
}
|
|
1386
|
+
// File-completion overlay open → Enter accepts the selection (same
|
|
1387
|
+
// contract as the command menu: the overlay owns Enter while open).
|
|
1388
|
+
if (fileOverlayOpen) {
|
|
1389
|
+
const file = fileMatches[fileSelected];
|
|
1390
|
+
if (file) {
|
|
1391
|
+
acceptFile(file);
|
|
1392
|
+
return;
|
|
1393
|
+
}
|
|
1394
|
+
}
|
|
1395
|
+
if (channel.working && value.trim() !== '') {
|
|
1396
|
+
// Immediate-command semantics: /btw and /skills are exempt from
|
|
1397
|
+
// steering — neither command interrupts the running turn. Hidden
|
|
1398
|
+
// UI-only easter eggs (e.g. /deepseek) are also safe to run while
|
|
1399
|
+
// streaming. Every other input keeps the steer behavior so /new
|
|
1400
|
+
// /model etc. stay idle-only.
|
|
1401
|
+
const parsed = value.startsWith('/') ? parseCommandName(value) : undefined;
|
|
1402
|
+
if (parsed !== undefined && (((parsed.name === 'btw' || parsed.name === 'skills')
|
|
1403
|
+
&& channel.commandList.some(c => c.name === parsed.name))
|
|
1404
|
+
|| isHiddenCommandName(parsed.name))) {
|
|
1405
|
+
if (tryRunCommand(value))
|
|
1406
|
+
return;
|
|
1407
|
+
}
|
|
1408
|
+
steerSend(value);
|
|
1409
|
+
return;
|
|
1410
|
+
}
|
|
1411
|
+
if (!tryRunCommand(value))
|
|
1412
|
+
submitText(value);
|
|
1413
|
+
};
|
|
1414
|
+
/** Expand/collapse the fullscreen editor (shortcut + ✎ affordance).
|
|
1415
|
+
* Expanding also DROPS any fold block: the fullscreen view shows and
|
|
1416
|
+
* edits the full text, and the block's atomic-clamp semantics (caret
|
|
1417
|
+
* pushed to its edges, selection clamped to one side) would contradict
|
|
1418
|
+
* that — the chip's own click-to-expand already means "unfold". */
|
|
1419
|
+
const toggleExpand = () => {
|
|
1420
|
+
const next = !expandedRef.current;
|
|
1421
|
+
expandedRef.current = next;
|
|
1422
|
+
setExpanded(next);
|
|
1423
|
+
if (next) {
|
|
1424
|
+
expandedScrollRef.current = 0;
|
|
1425
|
+
if (foldBlockRef.current)
|
|
1426
|
+
updateFoldBlock(null);
|
|
1427
|
+
}
|
|
1428
|
+
// Geometry changed wholesale (a fullscreen cover): the double-click
|
|
1429
|
+
// self-detection's screen-cell memory is void, same as on resize.
|
|
1430
|
+
lastClickAtRef.current = 0;
|
|
1431
|
+
lastClickColRef.current = -1;
|
|
1432
|
+
lastClickRowRef.current = -1;
|
|
1433
|
+
};
|
|
1434
|
+
/** Withdraw the fullscreen editor, keeping text/caret/selection intact. */
|
|
1435
|
+
const collapseEditor = () => {
|
|
1436
|
+
expandedRef.current = false;
|
|
1437
|
+
setExpanded(false);
|
|
1438
|
+
lastClickAtRef.current = 0;
|
|
1439
|
+
lastClickColRef.current = -1;
|
|
1440
|
+
lastClickRowRef.current = -1;
|
|
1441
|
+
};
|
|
1442
|
+
/** The editor's explicit send (Ctrl+Enter / Send button): the Enter main
|
|
1443
|
+
* path, then collapse — an empty draft just collapses. */
|
|
1444
|
+
const submitFromEditor = () => {
|
|
1445
|
+
handleEnter();
|
|
1446
|
+
collapseEditor();
|
|
1447
|
+
};
|
|
1448
|
+
/** Clipboard reads are asynchronous; insert against the latest render so
|
|
1449
|
+
* typing while PowerShell owns the clipboard never gets overwritten.
|
|
1450
|
+
* An active selection is replaced by the insert. Returns the resulting
|
|
1451
|
+
* value and the insertion offset so callers can apply the paste fold. */
|
|
1452
|
+
const insertClipboardAtCaret = (text) => {
|
|
1453
|
+
text = sanitizeEditableText(text);
|
|
1454
|
+
const current = valueRef.current;
|
|
1455
|
+
const sel = selectionRef.current;
|
|
1456
|
+
const position = sel ? sel.start : cursorRef.current;
|
|
1457
|
+
const next = sel
|
|
1458
|
+
? current.slice(0, sel.start) + text + current.slice(sel.end)
|
|
1459
|
+
: current.slice(0, position) + text + current.slice(position);
|
|
1460
|
+
setInput(next, position + text.length);
|
|
1461
|
+
setSelectedCommand(0);
|
|
1462
|
+
setFileSelected(0);
|
|
1463
|
+
return { next, at: position };
|
|
1464
|
+
};
|
|
1465
|
+
/** Read one local image file and stage it through the channel, returning
|
|
1466
|
+
* an opaque capability. Shared by every image paste path:
|
|
1467
|
+
* clipboard bitmap export, Finder-copied files, and pasted drop paths.
|
|
1468
|
+
* A Finder/drop path is untrusted input: one bounded descriptor read
|
|
1469
|
+
* applies the profile limit before bytes enter the composer. */
|
|
1470
|
+
const stageImagePath = async (path, lease) => {
|
|
1471
|
+
if (!draftImageLeaseIsCurrent(lease)) {
|
|
1472
|
+
throw new Error('the draft changed while the image was being staged');
|
|
1473
|
+
}
|
|
1474
|
+
const limits = channel.stagedImageLimits?.();
|
|
1475
|
+
if (limits === undefined)
|
|
1476
|
+
throw new Error('image attachments are unavailable in this profile');
|
|
1477
|
+
// One-image paste operations are serialized, so this is the cumulative
|
|
1478
|
+
// draft count (not merely the current Finder batch). Refuse before the
|
|
1479
|
+
// descriptor read/save and never let the channel's 128-capability cache
|
|
1480
|
+
// become an accidental per-command batch size.
|
|
1481
|
+
if (draftImagesRef.current.size >= limits.maxImagesPerMessage) {
|
|
1482
|
+
throw new Error('image count exceeds this profile\'s per-message limit');
|
|
1483
|
+
}
|
|
1484
|
+
const data = await readBoundedRegularFile(path, limits.maxImageBytes);
|
|
1485
|
+
if (!draftImageLeaseIsCurrent(lease)) {
|
|
1486
|
+
throw new Error('the draft changed while the image was being staged');
|
|
1487
|
+
}
|
|
1488
|
+
return channel.stageComposerImage({
|
|
1489
|
+
data,
|
|
1490
|
+
// Callers gate on imagePathMediaType; the assertion is unreachable.
|
|
1491
|
+
mediaType: imagePathMediaType(path) ?? 'image/png',
|
|
1492
|
+
name: basename(path),
|
|
1493
|
+
// The preview card's path row: the file that was read, as an
|
|
1494
|
+
// absolute path (a clipboard bitmap shows its temp export).
|
|
1495
|
+
path: resolve(path),
|
|
1496
|
+
}, lease.generation);
|
|
1497
|
+
};
|
|
1498
|
+
/** Queue one complete image operation (save plus synchronous bind/insert).
|
|
1499
|
+
* Keeping the visible mutation inside the chain makes terminal drop order
|
|
1500
|
+
* independent of attachment-store latency. Rejections settle the chain so
|
|
1501
|
+
* one bad file cannot wedge later drops. */
|
|
1502
|
+
const enqueueImageWork = (work) => {
|
|
1503
|
+
const queued = imageStageChainRef.current.then(work, work);
|
|
1504
|
+
imageStageChainRef.current = queued.then(() => undefined, () => undefined);
|
|
1505
|
+
return queued;
|
|
1506
|
+
};
|
|
1507
|
+
/** Success notice for one staged image. An adapted paste says what the user
|
|
1508
|
+
* actually got (stored dimensions, stored format, filled alpha) — naming it
|
|
1509
|
+
* is the difference between a reported adaptation and a silently rewritten
|
|
1510
|
+
* image. */
|
|
1511
|
+
const stagedImageNotice = (token, handle) => {
|
|
1512
|
+
const adjustment = handle.adjustment;
|
|
1513
|
+
if (adjustment === undefined)
|
|
1514
|
+
return t('input-image-pasted', { token });
|
|
1515
|
+
const details = [];
|
|
1516
|
+
if (adjustment.resized) {
|
|
1517
|
+
details.push(t('input-image-detail-resized', {
|
|
1518
|
+
width: adjustment.width,
|
|
1519
|
+
height: adjustment.height,
|
|
1520
|
+
}));
|
|
1521
|
+
}
|
|
1522
|
+
if (adjustment.mediaType !== adjustment.sourceMediaType) {
|
|
1523
|
+
const from = mediaTypeLabel(adjustment.sourceMediaType);
|
|
1524
|
+
const to = mediaTypeLabel(adjustment.mediaType);
|
|
1525
|
+
details.push(adjustment.flattened
|
|
1526
|
+
? t('input-image-detail-converted-flattened', { from, to })
|
|
1527
|
+
: t('input-image-detail-converted', { from, to }));
|
|
1528
|
+
}
|
|
1529
|
+
// An adjustment always describes a resize or a conversion; anything else
|
|
1530
|
+
// keeps the plain notice rather than an empty parenthetical. ' · ' joins
|
|
1531
|
+
// the clauses in both shipped languages.
|
|
1532
|
+
if (details.length === 0)
|
|
1533
|
+
return t('input-image-pasted', { token });
|
|
1534
|
+
return t('input-image-pasted-adjusted', { token, detail: details.join(' · ') });
|
|
1535
|
+
};
|
|
1536
|
+
const discardStagedHandles = (handles) => {
|
|
1537
|
+
for (const stageId of new Set(handles.map(handle => handle.stageId))) {
|
|
1538
|
+
channel.discardStagedImage(stageId);
|
|
1539
|
+
}
|
|
1540
|
+
};
|
|
1541
|
+
/** Assign presentation numbering only after staging succeeds. Existing raw
|
|
1542
|
+
* tokens (including stale history) reserve their number, so a fresh image
|
|
1543
|
+
* can never visually alias one. The counter never goes backwards within
|
|
1544
|
+
* one session generation, which also keeps delete/undo safe. */
|
|
1545
|
+
const bindStagedImage = (handle, lease) => {
|
|
1546
|
+
if (!draftImageLeaseIsCurrent(lease)) {
|
|
1547
|
+
// The save completed, but its initiating draft was already submitted,
|
|
1548
|
+
// replaced, or unmounted. Reclaim this otherwise-unreachable channel
|
|
1549
|
+
// capability instead of waiting for the session FIFO to evict it.
|
|
1550
|
+
channel.discardStagedImage(handle.stageId);
|
|
1551
|
+
throw new Error('the draft changed while the image was being staged');
|
|
1552
|
+
}
|
|
1553
|
+
if (channel.hasStagedImage?.(handle.stageId) !== true) {
|
|
1554
|
+
throw new Error('the draft changed while the image was being staged');
|
|
1555
|
+
}
|
|
1556
|
+
let token = `[Image #${nextImageNumberRef.current}]`;
|
|
1557
|
+
while (valueRef.current.includes(token) || draftImagesRef.current.has(token)) {
|
|
1558
|
+
nextImageNumberRef.current += 1;
|
|
1559
|
+
token = `[Image #${nextImageNumberRef.current}]`;
|
|
1560
|
+
}
|
|
1561
|
+
nextImageNumberRef.current += 1;
|
|
1562
|
+
draftImagesRef.current.set(token, handle.stageId);
|
|
1563
|
+
return token;
|
|
1564
|
+
};
|
|
1565
|
+
/** Line index of the cursor; -1 when the cursor is at the very end. */
|
|
1566
|
+
const cursorLine = (text, cursorOffset) => {
|
|
1567
|
+
const before = text.slice(0, cursorOffset);
|
|
1568
|
+
return before.split('\n').length - 1;
|
|
1569
|
+
};
|
|
1570
|
+
/** Column of the cursor within its line. */
|
|
1571
|
+
const cursorColumn = (text, cursorOffset) => {
|
|
1572
|
+
const before = text.slice(0, cursorOffset);
|
|
1573
|
+
const line = before.split('\n').pop() ?? '';
|
|
1574
|
+
return line.length;
|
|
1575
|
+
};
|
|
1576
|
+
useInput((input, key, event) => {
|
|
1577
|
+
if (selectionActive)
|
|
1578
|
+
return;
|
|
1579
|
+
// The editor round-trip ends by resuming stdin one microtask before the
|
|
1580
|
+
// outcome lands — drop any key squeezed into that gap so the prompt's
|
|
1581
|
+
// setValue can never overwrite fresh typing (and vice versa).
|
|
1582
|
+
if (editorBusyRef.current)
|
|
1583
|
+
return;
|
|
1584
|
+
// Ctrl+Shift+E (remappable via /settings): toggle the fullscreen draft
|
|
1585
|
+
// editor. Matched before anything else so it works both while editing
|
|
1586
|
+
// and from an idle prompt; selectionActive has already returned above.
|
|
1587
|
+
// Refused while the feature is turned off in /settings.
|
|
1588
|
+
if (expandEnabled && actionMatches('expandEditor', input, key)) {
|
|
1589
|
+
event?.stopImmediatePropagation();
|
|
1590
|
+
toggleExpand();
|
|
1591
|
+
return;
|
|
1592
|
+
}
|
|
1593
|
+
// App deliberately dispatches every parsed key from one stdin read in a
|
|
1594
|
+
// single React update. Read the synchronous mirrors so each event sees
|
|
1595
|
+
// the text/caret produced by the preceding event in that batch.
|
|
1596
|
+
const value = valueRef.current;
|
|
1597
|
+
const cursor = cursorRef.current;
|
|
1598
|
+
const selection = selectionRef.current;
|
|
1599
|
+
// ── caret-driven image preview ──────────────────────────────────────
|
|
1600
|
+
// Esc dismisses the preview the caret opened, before any other Esc
|
|
1601
|
+
// meaning (help stays above it: the help menu is modal over the prompt).
|
|
1602
|
+
if (key.escape && caretPreviewOpen && !helpOpen) {
|
|
1603
|
+
event?.stopImmediatePropagation();
|
|
1604
|
+
onDismissCaretPreview?.();
|
|
1605
|
+
return;
|
|
1606
|
+
}
|
|
1607
|
+
// ── mouse selection (drag / Shift+click / double-click) ─────────────
|
|
1608
|
+
// Layered ahead of the fold-block rules: with an active selection, Esc
|
|
1609
|
+
// ONLY drops the highlight (text untouched), and Backspace/Delete
|
|
1610
|
+
// delete the selected span. Arrows/typing handle the selection at their
|
|
1611
|
+
// own arms below.
|
|
1612
|
+
if (key.escape && selection && !helpOpen && !overlayOpen && !fileOverlayOpen) {
|
|
1613
|
+
event.stopImmediatePropagation();
|
|
1614
|
+
clearSelection();
|
|
1615
|
+
return;
|
|
1616
|
+
}
|
|
1617
|
+
if ((key.backspace || key.delete) && selection) {
|
|
1618
|
+
deleteInputRange(selection.start, selection.end);
|
|
1619
|
+
setSelectedCommand(0);
|
|
1620
|
+
setFileSelected(0);
|
|
1621
|
+
return;
|
|
1622
|
+
}
|
|
1623
|
+
// ── 全屏草稿编辑(expandEditor,默认 Ctrl+Shift+E / 输入行 ✎)─────
|
|
1624
|
+
// 展开态拥有屏幕;Esc 收起(有选区时上面的 selection 分支已先行只清
|
|
1625
|
+
// 选区)。滚轮不经此——编辑区的 onWheel 位置路由直接驱动滚动窗口。
|
|
1626
|
+
if (key.escape && expandedRef.current) {
|
|
1627
|
+
// Collapse runs AHEAD of the fold-block/vim Esc meanings: the
|
|
1628
|
+
// fullscreen cover is the outermost modal layer.
|
|
1629
|
+
event?.stopImmediatePropagation();
|
|
1630
|
+
collapseEditor();
|
|
1631
|
+
return;
|
|
1632
|
+
}
|
|
1633
|
+
// Fold block: Esc expands it — it must NEVER clear text
|
|
1634
|
+
// that LOOKS like one line; Backspace at the block's tail / Delete at
|
|
1635
|
+
// its head deletes the WHOLE block in one key; ←/→ jump over the
|
|
1636
|
+
// atomic block. Typing NEVER expands it — the caret lives outside the
|
|
1637
|
+
// block and edits land in the visible text around it.
|
|
1638
|
+
const block = foldBlockRef.current;
|
|
1639
|
+
// Line-level moves/edits (↑/↓/Home/End/Ctrl+A/E/U/K/W) treat the
|
|
1640
|
+
// block as an atomic row: boundaries that would land inside it are
|
|
1641
|
+
// clamped to its edges.
|
|
1642
|
+
const clampRowStart = (pos) => block && cursor >= block.end ? Math.max(pos, block.end) : pos;
|
|
1643
|
+
const clampRowEnd = (pos) => block && cursor <= block.start ? Math.min(pos, block.start) : pos;
|
|
1644
|
+
if (block) {
|
|
1645
|
+
if (key.escape) {
|
|
1646
|
+
updateFoldBlock(null);
|
|
1647
|
+
return;
|
|
1648
|
+
}
|
|
1649
|
+
if ((key.backspace && cursor === block.end) || (key.delete && cursor === block.start)) {
|
|
1650
|
+
const next = value.slice(0, block.start) + value.slice(block.end);
|
|
1651
|
+
updateFoldBlock(null);
|
|
1652
|
+
setInput(next, block.start);
|
|
1653
|
+
setSelectedCommand(0);
|
|
1654
|
+
setFileSelected(0);
|
|
1655
|
+
return;
|
|
1656
|
+
}
|
|
1657
|
+
if (key.leftArrow && cursor === block.end) {
|
|
1658
|
+
setInput(value, block.start);
|
|
1659
|
+
return;
|
|
1660
|
+
}
|
|
1661
|
+
if (key.rightArrow && cursor === block.start) {
|
|
1662
|
+
setInput(value, block.end);
|
|
1663
|
+
return;
|
|
1664
|
+
}
|
|
1665
|
+
}
|
|
1666
|
+
// Grapheme boundaries of the current text: every caret move / delete
|
|
1667
|
+
// below snaps onto one of these offsets (never mid-cluster).
|
|
1668
|
+
const bounds = graphemeBoundaries(value);
|
|
1669
|
+
/** Insert text at the caret (typing, paste) and dismiss overlays. An
|
|
1670
|
+
* active selection is REPLACED by the insert (standard editor
|
|
1671
|
+
* semantics). Returns the insertion offset so callers can derive the
|
|
1672
|
+
* inserted span (paste fold). */
|
|
1673
|
+
const insertAtCaret = (text) => {
|
|
1674
|
+
if (helpOpen)
|
|
1675
|
+
onToggleHelp();
|
|
1676
|
+
const sel = selectionRef.current;
|
|
1677
|
+
const at = sel ? sel.start : cursor;
|
|
1678
|
+
const next = sel
|
|
1679
|
+
? value.slice(0, sel.start) + text + value.slice(sel.end)
|
|
1680
|
+
: value.slice(0, cursor) + text + value.slice(cursor);
|
|
1681
|
+
setInput(next, at + text.length);
|
|
1682
|
+
setSelectedCommand(0);
|
|
1683
|
+
setFileSelected(0);
|
|
1684
|
+
return at;
|
|
1685
|
+
};
|
|
1686
|
+
// Bracketed paste (terminal paste — Ctrl+Shift+V / right-click): insert
|
|
1687
|
+
// at the caret after removing terminal controls and expanding tabs.
|
|
1688
|
+
// Newlines remain data — they are NOT Enter — so this branch runs before
|
|
1689
|
+
// the whole-line submit rule.
|
|
1690
|
+
if (event?.isPasted && input.length > 0) {
|
|
1691
|
+
const text = sanitizeEditableText(input.replace(/\r\n/g, '\n').replace(/\r/g, '\n'));
|
|
1692
|
+
// Desktop drops reach the TUI as pasted text (Ghostty forwards
|
|
1693
|
+
// Shell.escape(path) through the PTY with no drop boundary). Only a
|
|
1694
|
+
// paste that IS one unambiguous existing local image path stages as
|
|
1695
|
+
// an image; any parse/stat/staging failure inserts the text verbatim.
|
|
1696
|
+
const droppedPath = parsePastedImagePath(text);
|
|
1697
|
+
if (droppedPath !== null) {
|
|
1698
|
+
const lease = captureDraftImageLease();
|
|
1699
|
+
if (helpOpen)
|
|
1700
|
+
onToggleHelp();
|
|
1701
|
+
setSelectedCommand(0);
|
|
1702
|
+
setFileSelected(0);
|
|
1703
|
+
void enqueueImageWork(async () => {
|
|
1704
|
+
const handle = await stageImagePath(droppedPath, lease);
|
|
1705
|
+
const token = bindStagedImage(handle, lease);
|
|
1706
|
+
// Bind and insert share this synchronous continuation: setInput's
|
|
1707
|
+
// sidecar pruning can never observe a bound-but-not-visible token.
|
|
1708
|
+
insertClipboardAtCaret(`${token} `);
|
|
1709
|
+
channel.notify(stagedImageNotice(token, handle), { timeoutMs: 2500 });
|
|
1710
|
+
})
|
|
1711
|
+
.catch(() => {
|
|
1712
|
+
if (!draftImageLeaseIsCurrent(lease))
|
|
1713
|
+
return;
|
|
1714
|
+
// A real path/read/stage failure keeps the terminal paste as text.
|
|
1715
|
+
// A revoked lease is handled above and must not edit a newer draft.
|
|
1716
|
+
insertClipboardAtCaret(text);
|
|
1717
|
+
});
|
|
1718
|
+
return;
|
|
1719
|
+
}
|
|
1720
|
+
const at = insertAtCaret(text);
|
|
1721
|
+
// A big paste becomes a fold block right away (hover peeks
|
|
1722
|
+
// at it); an existing block is replaced by the new paste's span.
|
|
1723
|
+
// The EXPANDED editor never folds — pasting there is plain text
|
|
1724
|
+
// (fold semantics would clamp the caret out of the pasted span).
|
|
1725
|
+
if (!expandedRef.current && isBigInput(text))
|
|
1726
|
+
updateFoldBlock({ start: at, end: at + text.length });
|
|
1727
|
+
return;
|
|
1728
|
+
}
|
|
1729
|
+
// Clipboard paste (default Ctrl+V / Cmd+V, plus the Alt+V alias for
|
|
1730
|
+
// terminals that intercept Ctrl+V — combos are user-remappable via
|
|
1731
|
+
// /settings): raw mode hands the key to the app, so the clipboard is
|
|
1732
|
+
// read here — text, file paths when the file manager copied files, or
|
|
1733
|
+
// an exported temp-file path when the clipboard holds a raw image.
|
|
1734
|
+
if (actionMatches('paste', input, key)) {
|
|
1735
|
+
if (clipboardBusyRef.current)
|
|
1736
|
+
return;
|
|
1737
|
+
const lease = captureDraftImageLease();
|
|
1738
|
+
// Match insertAtCaret's overlay/selection dismissal up front: the
|
|
1739
|
+
// async continuation below only sets value/cursor, so a paste landing
|
|
1740
|
+
// while the help overlay is open would otherwise insert behind it.
|
|
1741
|
+
if (helpOpen)
|
|
1742
|
+
onToggleHelp();
|
|
1743
|
+
setSelectedCommand(0);
|
|
1744
|
+
setFileSelected(0);
|
|
1745
|
+
clipboardBusyRef.current = true;
|
|
1746
|
+
void readClipboard()
|
|
1747
|
+
.then(async (content) => {
|
|
1748
|
+
// Every `kind: image` path is a private temp export owned by this
|
|
1749
|
+
// paste, including TIFF/BMP/SVG formats the attachment profile
|
|
1750
|
+
// cannot stage. Ownership must not depend on format support.
|
|
1751
|
+
const temporaryImagePath = content?.kind === 'image' ? content.path : undefined;
|
|
1752
|
+
try {
|
|
1753
|
+
if (!draftImageLeaseIsCurrent(lease))
|
|
1754
|
+
return;
|
|
1755
|
+
if (content === null) {
|
|
1756
|
+
channel.notify(t('input-clipboard-empty'), { color: 'warning' });
|
|
1757
|
+
return;
|
|
1758
|
+
}
|
|
1759
|
+
if (content.kind === 'unavailable') {
|
|
1760
|
+
channel.notify(t('input-clipboard-unavailable'), { color: 'warning' });
|
|
1761
|
+
return;
|
|
1762
|
+
}
|
|
1763
|
+
if (content.kind === 'image') {
|
|
1764
|
+
if (imagePathMediaType(content.path) === undefined) {
|
|
1765
|
+
channel.notify(t('input-image-format-unsupported'), { color: 'warning', timeoutMs: 5000 });
|
|
1766
|
+
return;
|
|
1767
|
+
}
|
|
1768
|
+
try {
|
|
1769
|
+
await enqueueImageWork(async () => {
|
|
1770
|
+
const handle = await stageImagePath(content.path, lease);
|
|
1771
|
+
const token = bindStagedImage(handle, lease);
|
|
1772
|
+
insertClipboardAtCaret(`${token} `);
|
|
1773
|
+
channel.notify(stagedImageNotice(token, handle), { timeoutMs: 2500 });
|
|
1774
|
+
});
|
|
1775
|
+
}
|
|
1776
|
+
catch (error) {
|
|
1777
|
+
if (!draftImageLeaseIsCurrent(lease))
|
|
1778
|
+
return;
|
|
1779
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1780
|
+
channel.notify(t('input-image-paste-failed', { err: message }), { color: 'warning', timeoutMs: 5000 });
|
|
1781
|
+
}
|
|
1782
|
+
return;
|
|
1783
|
+
}
|
|
1784
|
+
if (content.kind === 'files') {
|
|
1785
|
+
// Finder/Explorer-copied image FILES stage like clipboard
|
|
1786
|
+
// bitmaps (macOS furl offers exactly one). Other files keep the
|
|
1787
|
+
// quoted/@ path insert, and an image that fails to stage falls
|
|
1788
|
+
// back to its `@` reference — the mention pipeline still
|
|
1789
|
+
// attaches it at submit.
|
|
1790
|
+
let insertedStaged = false;
|
|
1791
|
+
try {
|
|
1792
|
+
insertedStaged = await enqueueImageWork(async () => {
|
|
1793
|
+
const maxImages = channel.stagedImageLimits?.()?.maxImagesPerMessage ?? 0;
|
|
1794
|
+
const { parts, staged, failure, failureCode } = await stageClipboardFilePaths(content.paths, path => stageImagePath(path, lease), filePath => formatClipboardInsert({ kind: 'files', paths: [filePath] }), Math.max(0, maxImages - draftImagesRef.current.size));
|
|
1795
|
+
if (!draftImageLeaseIsCurrent(lease)) {
|
|
1796
|
+
discardStagedHandles(staged);
|
|
1797
|
+
return true;
|
|
1798
|
+
}
|
|
1799
|
+
const boundTokens = [];
|
|
1800
|
+
try {
|
|
1801
|
+
const rendered = parts.map(part => {
|
|
1802
|
+
if (part.kind === 'text')
|
|
1803
|
+
return part.value;
|
|
1804
|
+
const token = bindStagedImage(part.value, lease);
|
|
1805
|
+
boundTokens.push(token);
|
|
1806
|
+
return token;
|
|
1807
|
+
});
|
|
1808
|
+
if (failure !== '') {
|
|
1809
|
+
const message = failureCode === 'image-limit' ? t('input-image-paste-limit') : failure;
|
|
1810
|
+
channel.notify(t('input-image-paste-failed', { err: message }), { color: 'warning', timeoutMs: 5000 });
|
|
1811
|
+
}
|
|
1812
|
+
if (boundTokens.length === 0)
|
|
1813
|
+
return false;
|
|
1814
|
+
// All bindings and their visible labels enter together;
|
|
1815
|
+
// typing while an earlier file saves cannot prune one.
|
|
1816
|
+
insertClipboardAtCaret(`${rendered.join(' ')} `);
|
|
1817
|
+
// A batch cannot itemise every image in one line, but it must
|
|
1818
|
+
// still say that some of them were not stored as pasted.
|
|
1819
|
+
const adapted = staged.filter(handle => handle.adjustment !== undefined).length;
|
|
1820
|
+
channel.notify(boundTokens.length === 1
|
|
1821
|
+
? stagedImageNotice(boundTokens[0], staged[0])
|
|
1822
|
+
: adapted > 0
|
|
1823
|
+
? t('input-images-staged-adapted', { count: boundTokens.length, adapted })
|
|
1824
|
+
: t('input-images-staged', { count: boundTokens.length }), { timeoutMs: 2500 });
|
|
1825
|
+
return true;
|
|
1826
|
+
}
|
|
1827
|
+
catch (error) {
|
|
1828
|
+
for (const token of boundTokens)
|
|
1829
|
+
draftImagesRef.current.delete(token);
|
|
1830
|
+
discardStagedHandles(staged);
|
|
1831
|
+
throw error;
|
|
1832
|
+
}
|
|
1833
|
+
});
|
|
1834
|
+
}
|
|
1835
|
+
catch (error) {
|
|
1836
|
+
if (!draftImageLeaseIsCurrent(lease))
|
|
1837
|
+
return;
|
|
1838
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1839
|
+
channel.notify(t('input-image-paste-failed', { err: message }), { color: 'warning', timeoutMs: 5000 });
|
|
1840
|
+
}
|
|
1841
|
+
if (insertedStaged)
|
|
1842
|
+
return;
|
|
1843
|
+
// Nothing staged: fall through to the verbatim files insert.
|
|
1844
|
+
}
|
|
1845
|
+
if (!draftImageLeaseIsCurrent(lease))
|
|
1846
|
+
return;
|
|
1847
|
+
// Insert against the LIVE input state: the read above resolved
|
|
1848
|
+
// asynchronously and the user may have typed while waiting.
|
|
1849
|
+
const text = sanitizeEditableText(formatClipboardInsert(content));
|
|
1850
|
+
const { at } = insertClipboardAtCaret(text);
|
|
1851
|
+
// Same fold as bracketed paste — but never inside the expanded
|
|
1852
|
+
// editor (plain text there, see the isPasted branch).
|
|
1853
|
+
if (!expandedRef.current && isBigInput(text))
|
|
1854
|
+
updateFoldBlock({ start: at, end: at + text.length });
|
|
1855
|
+
}
|
|
1856
|
+
finally {
|
|
1857
|
+
// Clipboard bitmaps are private temp exports owned by this paste,
|
|
1858
|
+
// never durable attachment storage. Clean them on success,
|
|
1859
|
+
// rejection, draft invalidation, and session replacement alike.
|
|
1860
|
+
if (temporaryImagePath !== undefined) {
|
|
1861
|
+
await unlink(temporaryImagePath).catch(() => undefined);
|
|
1862
|
+
}
|
|
1863
|
+
}
|
|
1864
|
+
})
|
|
1865
|
+
.catch(() => {
|
|
1866
|
+
if (!draftImageLeaseIsCurrent(lease))
|
|
1867
|
+
return;
|
|
1868
|
+
channel.notify(t('input-clipboard-read-failed'), { color: 'warning' });
|
|
1869
|
+
})
|
|
1870
|
+
.finally(() => {
|
|
1871
|
+
// A rejected read must never wedge Ctrl+V for the rest of the
|
|
1872
|
+
// session.
|
|
1873
|
+
clipboardBusyRef.current = false;
|
|
1874
|
+
});
|
|
1875
|
+
return;
|
|
1876
|
+
}
|
|
1877
|
+
// Help is modal for modified keys and every Enter variant. The paste
|
|
1878
|
+
// branch above is the intentional exception: paste closes Help and
|
|
1879
|
+
// inserts visibly.
|
|
1880
|
+
// Swallow here before editor/submit/interrupt branches can mutate hidden
|
|
1881
|
+
// composer or working-turn state; plain typing still dismisses Help below.
|
|
1882
|
+
if (helpOpen && !key.escape && (key.ctrl || key.meta || key.super || key.return || input.includes('\n') || input.includes('\r'))) {
|
|
1883
|
+
event.stopImmediatePropagation();
|
|
1884
|
+
return;
|
|
1885
|
+
}
|
|
1886
|
+
// Ctrl+G (remappable via /settings): edit the current draft in
|
|
1887
|
+
// $VISUAL/$EDITOR (issue #123, readline's edit-and-execute-command). The
|
|
1888
|
+
// draft is written to a temp file, the terminal is handed to the editor
|
|
1889
|
+
// (Ink's alt-screen handoff), and the saved text replaces the input when
|
|
1890
|
+
// it differs. The util maps every failure to an outcome, but the
|
|
1891
|
+
// catch/finally here is the hard guarantee: a rejected promise must
|
|
1892
|
+
// never kill the process, and the busy flag must always clear or the
|
|
1893
|
+
// editor key stays locked forever.
|
|
1894
|
+
if (actionMatches('editor', input, key)) {
|
|
1895
|
+
// Opening the editor starts a new async draft lifecycle immediately:
|
|
1896
|
+
// fence an older paste before the terminal handoff, but retain already
|
|
1897
|
+
// bound image capabilities. setInput below prunes only tokens the user
|
|
1898
|
+
// actually removed in the editor.
|
|
1899
|
+
syncImageGeneration();
|
|
1900
|
+
advanceDraftRevision();
|
|
1901
|
+
const editorLease = captureDraftImageLease();
|
|
1902
|
+
editorBusyRef.current = true;
|
|
1903
|
+
void (async () => {
|
|
1904
|
+
try {
|
|
1905
|
+
const outcome = await editInExternalEditor(value);
|
|
1906
|
+
// Session switches, clears, history restores, and unmount all
|
|
1907
|
+
// revoke this editor round-trip. Never write its old draft into a
|
|
1908
|
+
// newer composer after the terminal handoff returns.
|
|
1909
|
+
if (!draftImageLeaseIsCurrent(editorLease))
|
|
1910
|
+
return;
|
|
1911
|
+
if (outcome.kind === 'edited') {
|
|
1912
|
+
updateFoldBlock(null);
|
|
1913
|
+
setInput(outcome.text);
|
|
1914
|
+
setSelectedCommand(0);
|
|
1915
|
+
setFileSelected(0);
|
|
1916
|
+
}
|
|
1917
|
+
else if (outcome.kind === 'unavailable') {
|
|
1918
|
+
channel.notify(t('input-editor-unavailable'), { color: 'warning' });
|
|
1919
|
+
}
|
|
1920
|
+
else if (outcome.kind === 'failed') {
|
|
1921
|
+
channel.notify(t('input-editor-failed', { name: outcome.message }), {
|
|
1922
|
+
color: 'warning',
|
|
1923
|
+
});
|
|
1924
|
+
}
|
|
1925
|
+
}
|
|
1926
|
+
catch {
|
|
1927
|
+
if (!draftImageLeaseIsCurrent(editorLease))
|
|
1928
|
+
return;
|
|
1929
|
+
channel.notify(t('input-editor-failed', { name: 'unknown' }), {
|
|
1930
|
+
color: 'warning',
|
|
1931
|
+
});
|
|
1932
|
+
}
|
|
1933
|
+
finally {
|
|
1934
|
+
editorBusyRef.current = false;
|
|
1935
|
+
}
|
|
1936
|
+
})();
|
|
1937
|
+
return;
|
|
1938
|
+
}
|
|
1939
|
+
// Ctrl+J is the portable multiline fallback when a terminal cannot
|
|
1940
|
+
// report modifiers on Enter. Legacy terminals deliver bare LF (`enter`),
|
|
1941
|
+
// while kitty/modifyOtherKeys encode it as an exact Ctrl+J key.
|
|
1942
|
+
const isCtrlJ = input === 'j' && key.ctrl && !key.shift && !key.meta && !key.super;
|
|
1943
|
+
if ((input === '\n' && event?.keypress.name === 'enter') || isCtrlJ) {
|
|
1944
|
+
insertAtCaret('\n');
|
|
1945
|
+
return;
|
|
1946
|
+
}
|
|
1947
|
+
// Whole-line input from Windows ConPTY pipelines (cmd batch -> node):
|
|
1948
|
+
// the trailing CR/LF marks a complete line to submit. A bare CR/CRLF is
|
|
1949
|
+
// Enter, while real multi-char piped lines keep the legacy direct-submit
|
|
1950
|
+
// path. The expanded editor treats piped lines as DATA — CR/LF
|
|
1951
|
+
// normalizes to '\n' and inserts (sending needs Ctrl+Enter there), so a
|
|
1952
|
+
// fast batch never dumps raw '\r' into the draft.
|
|
1953
|
+
if (input.includes('\n') || input.includes('\r')) {
|
|
1954
|
+
if (expandedRef.current) {
|
|
1955
|
+
insertAtCaret(sanitizeEditableText(input.replace(/\r\n/g, '\n').replace(/\r/g, '\n')));
|
|
1956
|
+
return;
|
|
1957
|
+
}
|
|
1958
|
+
if (/^[\r\n]+$/.test(input)) {
|
|
1959
|
+
handleEnter();
|
|
1960
|
+
return;
|
|
1961
|
+
}
|
|
1962
|
+
const line = (value + input).trim();
|
|
1963
|
+
if (line.startsWith('/')) {
|
|
1964
|
+
const matches = channel.commandCompletions(line);
|
|
1965
|
+
if (matches.length === 1) {
|
|
1966
|
+
tryRunCommand(matches[0].commandLine);
|
|
1967
|
+
return;
|
|
1968
|
+
}
|
|
1969
|
+
}
|
|
1970
|
+
if (!tryRunCommand(line))
|
|
1971
|
+
submitText(line);
|
|
1972
|
+
return;
|
|
1973
|
+
}
|
|
1974
|
+
if (key.return && isMod(key)) {
|
|
1975
|
+
// Ctrl+Enter / Cmd+Enter: in the EXPANDED editor this is the explicit
|
|
1976
|
+
// send (Enter inserts newlines there, so sending needs its own key);
|
|
1977
|
+
// inline it interrupts the running turn and processes this message
|
|
1978
|
+
// immediately (Windows Terminal sends CSI 13;5u / 13;1;5u).
|
|
1979
|
+
if (expandedRef.current) {
|
|
1980
|
+
submitFromEditor();
|
|
1981
|
+
}
|
|
1982
|
+
else {
|
|
1983
|
+
interruptSend();
|
|
1984
|
+
}
|
|
1985
|
+
return;
|
|
1986
|
+
}
|
|
1987
|
+
if (key.return && (key.shift || key.meta)) {
|
|
1988
|
+
// Shift+Enter / Option+Enter: insert a newline at the caret
|
|
1989
|
+
// (multi-line input). Shift+Enter only arrives when the terminal
|
|
1990
|
+
// supports extended key reporting (kitty/modifyOtherKeys allowlist in
|
|
1991
|
+
// ink/terminal.ts); Option+Enter (ESC CR) is the fallback on terminals
|
|
1992
|
+
// that can't report shift — e.g. macOS Terminal.app (issue #110).
|
|
1993
|
+
insertAtCaret('\n');
|
|
1994
|
+
return;
|
|
1995
|
+
}
|
|
1996
|
+
if (key.return && expandedRef.current) {
|
|
1997
|
+
// Expanded editor: plain Enter inserts a newline (text-editor
|
|
1998
|
+
// semantics — long-draft editing must never misfire a send). The
|
|
1999
|
+
// shift/meta variants above already newline; Ctrl+Enter sends.
|
|
2000
|
+
insertAtCaret('\n');
|
|
2001
|
+
return;
|
|
2002
|
+
}
|
|
2003
|
+
if (key.return) {
|
|
2004
|
+
handleEnter();
|
|
2005
|
+
return;
|
|
2006
|
+
}
|
|
2007
|
+
// Help is modal over the composer. Backtab must not cycle the session
|
|
2008
|
+
// mode invisibly behind it, and plain Tab has no Help action.
|
|
2009
|
+
if (helpOpen && key.tab) {
|
|
2010
|
+
event.stopImmediatePropagation();
|
|
2011
|
+
return;
|
|
2012
|
+
}
|
|
2013
|
+
// Completion menus own Backtab just like plain Tab; do not mutate the
|
|
2014
|
+
// background session mode while the user is choosing a candidate.
|
|
2015
|
+
if (key.tab && key.shift && (fileOverlayOpen || overlayOpen)) {
|
|
2016
|
+
event.stopImmediatePropagation();
|
|
2017
|
+
return;
|
|
2018
|
+
}
|
|
2019
|
+
// Shift+Tab cycles the configured session modes (default: 默认 →
|
|
2020
|
+
// 计划模式 → 完全访问; each mode bundles plan/sandbox/approval atoms —
|
|
2021
|
+
// see the `modes` config). Must precede the fullscreen editor's Tab
|
|
2022
|
+
// indentation arm so the expanded editor participates in the cycle too —
|
|
2023
|
+
// the parser reports backtab as key.tab + key.shift.
|
|
2024
|
+
if (key.tab && key.shift) {
|
|
2025
|
+
void channel.cycleMode();
|
|
2026
|
+
return;
|
|
2027
|
+
}
|
|
2028
|
+
// Expanded editor: plain Tab inserts indentation; Shift+Tab was handled
|
|
2029
|
+
// above and therefore never disappears behind the fullscreen cover.
|
|
2030
|
+
if (expandedRef.current && key.tab) {
|
|
2031
|
+
if (!key.shift)
|
|
2032
|
+
insertAtCaret(' ');
|
|
2033
|
+
return;
|
|
2034
|
+
}
|
|
2035
|
+
if (key.tab && fileOverlayOpen) {
|
|
2036
|
+
const file = fileMatches[fileSelected];
|
|
2037
|
+
if (file)
|
|
2038
|
+
acceptFile(file);
|
|
2039
|
+
return;
|
|
2040
|
+
}
|
|
2041
|
+
if (key.tab && overlayOpen) {
|
|
2042
|
+
const command = suggestions[selectedCommand];
|
|
2043
|
+
if (command) {
|
|
2044
|
+
updateFoldBlock(null);
|
|
2045
|
+
setInput(command.replacement);
|
|
2046
|
+
}
|
|
2047
|
+
return;
|
|
2048
|
+
}
|
|
2049
|
+
// Tab while the model is working = queue for AFTER the turn (followup),
|
|
2050
|
+
// distinct from Enter's steer (Codex's "tab to queue message").
|
|
2051
|
+
if (key.tab && channel.working && value.trim() !== '') {
|
|
2052
|
+
queueSend(value);
|
|
2053
|
+
return;
|
|
2054
|
+
}
|
|
2055
|
+
// Help is a viewport, not prompt history. It deliberately owns every
|
|
2056
|
+
// vertical navigation event while visible; otherwise Up/Down silently
|
|
2057
|
+
// walk the input history and the clipped command rows remain unreachable.
|
|
2058
|
+
if (helpOpen) {
|
|
2059
|
+
const page = Math.max(1, helpViewportHeight - 2);
|
|
2060
|
+
if (key.upArrow || key.wheelUp) {
|
|
2061
|
+
helpScrollRef.current?.scrollBy(key.wheelUp ? -3 : -1);
|
|
2062
|
+
event.stopImmediatePropagation();
|
|
2063
|
+
return;
|
|
2064
|
+
}
|
|
2065
|
+
if (key.downArrow || key.wheelDown) {
|
|
2066
|
+
helpScrollRef.current?.scrollBy(key.wheelDown ? 3 : 1);
|
|
2067
|
+
event.stopImmediatePropagation();
|
|
2068
|
+
return;
|
|
2069
|
+
}
|
|
2070
|
+
if (key.pageUp || key.pageDown) {
|
|
2071
|
+
helpScrollRef.current?.scrollBy(key.pageUp ? -page : page);
|
|
2072
|
+
event.stopImmediatePropagation();
|
|
2073
|
+
return;
|
|
2074
|
+
}
|
|
2075
|
+
if (key.home) {
|
|
2076
|
+
helpScrollRef.current?.scrollTo(0);
|
|
2077
|
+
event.stopImmediatePropagation();
|
|
2078
|
+
return;
|
|
2079
|
+
}
|
|
2080
|
+
if (key.end) {
|
|
2081
|
+
// Use a deliberately oversized absolute target rather than the
|
|
2082
|
+
// sticky-bottom path: compact Help may still be measuring nested
|
|
2083
|
+
// sections in this commit, while ScrollBox's render clamp resolves
|
|
2084
|
+
// the target to the exact current maximum without a follow-up frame.
|
|
2085
|
+
helpScrollRef.current?.scrollTo(Number.MAX_SAFE_INTEGER);
|
|
2086
|
+
event.stopImmediatePropagation();
|
|
2087
|
+
return;
|
|
2088
|
+
}
|
|
2089
|
+
}
|
|
2090
|
+
if (key.meta && key.upArrow) {
|
|
2091
|
+
// Alt+Up: pull the last pending message back for editing (pi/Codex).
|
|
2092
|
+
pullBackLast();
|
|
2093
|
+
return;
|
|
2094
|
+
}
|
|
2095
|
+
if (key.upArrow) {
|
|
2096
|
+
// A history walk owns the arrows until it returns to the draft: a
|
|
2097
|
+
// recalled entry can itself open the @ menu or the slash menu (e.g.
|
|
2098
|
+
// `/model`), and letting the overlay navigate here strands the stashed
|
|
2099
|
+
// draft — Down would cycle menu rows instead of walking back.
|
|
2100
|
+
if (fileOverlayOpen && historyIndex.current < 0) {
|
|
2101
|
+
setFileSelected(index => index <= 0 ? fileMatches.length - 1 : index - 1);
|
|
2102
|
+
return;
|
|
2103
|
+
}
|
|
2104
|
+
// With a fold block the caret in the tail walks the TAIL's lines
|
|
2105
|
+
// (the block is one atomic row); the first tail line steps over the
|
|
2106
|
+
// block to its head.
|
|
2107
|
+
if (block && cursor >= block.end) {
|
|
2108
|
+
const tailCursor = cursor - block.end;
|
|
2109
|
+
const line = cursorLine(tail, tailCursor);
|
|
2110
|
+
if (line > 0) {
|
|
2111
|
+
const upToLineStart = tail.lastIndexOf('\n', tailCursor - 1);
|
|
2112
|
+
const prevLineStart = upToLineStart === -1 ? 0 : tail.lastIndexOf('\n', upToLineStart - 1) + 1;
|
|
2113
|
+
const prevLine = tail.slice(prevLineStart, upToLineStart);
|
|
2114
|
+
setInput(value, block.end + prevLineStart + Math.min(cursorColumn(tail, tailCursor), prevLine.length));
|
|
2115
|
+
return;
|
|
2116
|
+
}
|
|
2117
|
+
setInput(value, block.start);
|
|
2118
|
+
return;
|
|
2119
|
+
}
|
|
2120
|
+
const line = cursorLine(value, cursor);
|
|
2121
|
+
if (line > 0) {
|
|
2122
|
+
// Move to the previous line, clamping to its length.
|
|
2123
|
+
const upToLineStart = value.lastIndexOf('\n', cursor - 1);
|
|
2124
|
+
const prevLineStart = upToLineStart === -1 ? 0 : value.lastIndexOf('\n', upToLineStart - 1) + 1;
|
|
2125
|
+
const prevLine = value.slice(prevLineStart, upToLineStart);
|
|
2126
|
+
setInput(value, prevLineStart + Math.min(cursorColumn(value, cursor), prevLine.length));
|
|
2127
|
+
return;
|
|
2128
|
+
}
|
|
2129
|
+
if (overlayOpen && historyIndex.current < 0) {
|
|
2130
|
+
setSelectedCommand(index => index <= 0 ? suggestions.length - 1 : index - 1);
|
|
2131
|
+
return;
|
|
2132
|
+
}
|
|
2133
|
+
seedHistory();
|
|
2134
|
+
if (history.current.length === 0)
|
|
2135
|
+
return;
|
|
2136
|
+
if (historyIndex.current < 0) {
|
|
2137
|
+
historyDraft.current = {
|
|
2138
|
+
text: value,
|
|
2139
|
+
images: imageRefsFor(value),
|
|
2140
|
+
};
|
|
2141
|
+
historyIndex.current = history.current.length - 1;
|
|
2142
|
+
}
|
|
2143
|
+
else {
|
|
2144
|
+
historyIndex.current = Math.max(0, historyIndex.current - 1);
|
|
2145
|
+
}
|
|
2146
|
+
const entry = history.current[historyIndex.current];
|
|
2147
|
+
if (entry === undefined)
|
|
2148
|
+
return;
|
|
2149
|
+
retractRecalledCopy(entry.text);
|
|
2150
|
+
updateFoldBlock(null);
|
|
2151
|
+
restoreDraftImages(entry);
|
|
2152
|
+
setInput(entry.text);
|
|
2153
|
+
return;
|
|
2154
|
+
}
|
|
2155
|
+
if (key.downArrow) {
|
|
2156
|
+
// Same history-walk ownership as ↑ above.
|
|
2157
|
+
if (fileOverlayOpen && historyIndex.current < 0) {
|
|
2158
|
+
setFileSelected(index => index >= fileMatches.length - 1 ? 0 : index + 1);
|
|
2159
|
+
return;
|
|
2160
|
+
}
|
|
2161
|
+
// Mirror of the tail-side ↑ walk: with a fold block the caret walks
|
|
2162
|
+
// the TAIL's lines; past its last line it falls through to the
|
|
2163
|
+
// overlay/history handling below.
|
|
2164
|
+
if (block && cursor >= block.end) {
|
|
2165
|
+
const tailCursor = cursor - block.end;
|
|
2166
|
+
const line = cursorLine(tail, tailCursor);
|
|
2167
|
+
const lines = tail.split('\n');
|
|
2168
|
+
if (line < lines.length - 1) {
|
|
2169
|
+
const nextLineStart = tail.indexOf('\n', tailCursor) + 1;
|
|
2170
|
+
const nextLineEnd = tail.indexOf('\n', nextLineStart);
|
|
2171
|
+
const nextLine = tail.slice(nextLineStart, nextLineEnd === -1 ? tail.length : nextLineEnd);
|
|
2172
|
+
setInput(value, block.end + nextLineStart + Math.min(cursorColumn(tail, tailCursor), nextLine.length));
|
|
2173
|
+
return;
|
|
2174
|
+
}
|
|
2175
|
+
}
|
|
2176
|
+
else if (block && cursor <= block.start) {
|
|
2177
|
+
// Head side: walk the HEAD's lines; its last line steps over the
|
|
2178
|
+
// block to the tail (never into history for a multi-row input).
|
|
2179
|
+
const line = cursorLine(head, cursor);
|
|
2180
|
+
const lines = head.split('\n');
|
|
2181
|
+
if (line < lines.length - 1) {
|
|
2182
|
+
const nextLineStart = head.indexOf('\n', cursor) + 1;
|
|
2183
|
+
const nextLineEnd = head.indexOf('\n', nextLineStart);
|
|
2184
|
+
const nextLine = head.slice(nextLineStart, nextLineEnd === -1 ? head.length : nextLineEnd);
|
|
2185
|
+
setInput(value, nextLineStart + Math.min(cursorColumn(head, cursor), nextLine.length));
|
|
2186
|
+
return;
|
|
2187
|
+
}
|
|
2188
|
+
setInput(value, block.end);
|
|
2189
|
+
return;
|
|
2190
|
+
}
|
|
2191
|
+
const line = cursorLine(value, cursor);
|
|
2192
|
+
const lines = value.split('\n');
|
|
2193
|
+
if (line < lines.length - 1) {
|
|
2194
|
+
const nextLineStart = value.indexOf('\n', cursor) + 1;
|
|
2195
|
+
const nextLineEnd = value.indexOf('\n', nextLineStart);
|
|
2196
|
+
const nextLine = value.slice(nextLineStart, nextLineEnd === -1 ? value.length : nextLineEnd);
|
|
2197
|
+
setInput(value, nextLineStart + Math.min(cursorColumn(value, cursor), nextLine.length));
|
|
2198
|
+
return;
|
|
2199
|
+
}
|
|
2200
|
+
if (overlayOpen && historyIndex.current < 0) {
|
|
2201
|
+
setSelectedCommand(index => index >= suggestions.length - 1 ? 0 : index + 1);
|
|
2202
|
+
return;
|
|
2203
|
+
}
|
|
2204
|
+
if (historyIndex.current < 0)
|
|
2205
|
+
return;
|
|
2206
|
+
if (historyIndex.current >= history.current.length - 1) {
|
|
2207
|
+
historyIndex.current = -1;
|
|
2208
|
+
updateFoldBlock(null);
|
|
2209
|
+
restoreDraftImages(historyDraft.current);
|
|
2210
|
+
setInput(historyDraft.current.text);
|
|
2211
|
+
}
|
|
2212
|
+
else {
|
|
2213
|
+
historyIndex.current += 1;
|
|
2214
|
+
const entry = history.current[historyIndex.current];
|
|
2215
|
+
if (entry !== undefined) {
|
|
2216
|
+
restoreDraftImages(entry);
|
|
2217
|
+
setInput(entry.text);
|
|
2218
|
+
}
|
|
2219
|
+
}
|
|
2220
|
+
return;
|
|
2221
|
+
}
|
|
2222
|
+
if (isMod(key) && key.leftArrow) {
|
|
2223
|
+
// Jump to the previous word boundary (readline alt+b). Must precede the
|
|
2224
|
+
// bare-arrow arms: Ctrl+Left arrives as leftArrow + ctrl. An active
|
|
2225
|
+
// selection collapses to its START edge first (editor semantics).
|
|
2226
|
+
const sel = selectionRef.current;
|
|
2227
|
+
setInput(value, sel ? sel.start : wordBoundaryLeft(value, cursor));
|
|
2228
|
+
return;
|
|
2229
|
+
}
|
|
2230
|
+
if (isMod(key) && key.rightArrow) {
|
|
2231
|
+
// Jump to the next word boundary (readline alt+f).
|
|
2232
|
+
const sel = selectionRef.current;
|
|
2233
|
+
setInput(value, sel ? sel.end : wordBoundaryRight(value, cursor));
|
|
2234
|
+
return;
|
|
2235
|
+
}
|
|
2236
|
+
if (key.leftArrow) {
|
|
2237
|
+
// ← on an EMPTY prompt backgrounds this session
|
|
2238
|
+
// and opens the agent view; with text it moves the caret as usual.
|
|
2239
|
+
// (The command/file overlays both imply non-empty text, so no extra
|
|
2240
|
+
// gate beyond the help menu is needed.)
|
|
2241
|
+
if (value.length === 0 && !helpOpen) {
|
|
2242
|
+
onBackgroundRequest?.();
|
|
2243
|
+
return;
|
|
2244
|
+
}
|
|
2245
|
+
// Grapheme-step: skip the whole cluster (surrogate pair, ZWJ emoji,
|
|
2246
|
+
// combining mark) so the caret never sits inside one. With a
|
|
2247
|
+
// selection, collapse to its start edge instead.
|
|
2248
|
+
const sel = selectionRef.current;
|
|
2249
|
+
setInput(value, sel ? sel.start : previousGraphemeBoundary(bounds, cursor));
|
|
2250
|
+
return;
|
|
2251
|
+
}
|
|
2252
|
+
if (key.rightArrow) {
|
|
2253
|
+
const sel = selectionRef.current;
|
|
2254
|
+
setInput(value, sel ? sel.end : nextGraphemeBoundary(bounds, cursor));
|
|
2255
|
+
return;
|
|
2256
|
+
}
|
|
2257
|
+
if (key.backspace) {
|
|
2258
|
+
if (cursor === 0)
|
|
2259
|
+
return;
|
|
2260
|
+
const start = previousGraphemeBoundary(bounds, cursor);
|
|
2261
|
+
deleteInputRange(start, cursor);
|
|
2262
|
+
return;
|
|
2263
|
+
}
|
|
2264
|
+
if (key.delete) {
|
|
2265
|
+
const end = nextGraphemeBoundary(bounds, cursor);
|
|
2266
|
+
if (end === cursor)
|
|
2267
|
+
return;
|
|
2268
|
+
deleteInputRange(cursor, end);
|
|
2269
|
+
return;
|
|
2270
|
+
}
|
|
2271
|
+
if (key.home) {
|
|
2272
|
+
// Start of the current line (the block is one atomic row).
|
|
2273
|
+
const lineStart = clampRowStart(value.lastIndexOf('\n', cursor - 1) + 1);
|
|
2274
|
+
setInput(value, lineStart);
|
|
2275
|
+
return;
|
|
2276
|
+
}
|
|
2277
|
+
if (key.end) {
|
|
2278
|
+
// End of the current line.
|
|
2279
|
+
const nextLine = value.indexOf('\n', cursor);
|
|
2280
|
+
setInput(value, nextLine === -1 ? value.length : clampRowEnd(nextLine));
|
|
2281
|
+
return;
|
|
2282
|
+
}
|
|
2283
|
+
if (isMod(key) && input === 'a') {
|
|
2284
|
+
const lineStart = clampRowStart(value.lastIndexOf('\n', cursor - 1) + 1);
|
|
2285
|
+
setInput(value, lineStart);
|
|
2286
|
+
return;
|
|
2287
|
+
}
|
|
2288
|
+
if (isMod(key) && input === 'e') {
|
|
2289
|
+
const nextLine = value.indexOf('\n', cursor);
|
|
2290
|
+
setInput(value, nextLine === -1 ? value.length : clampRowEnd(nextLine));
|
|
2291
|
+
return;
|
|
2292
|
+
}
|
|
2293
|
+
if (isMod(key) && input === 'u') {
|
|
2294
|
+
// Delete to start of line (never into the block).
|
|
2295
|
+
const lineStart = clampRowStart(value.lastIndexOf('\n', cursor - 1) + 1);
|
|
2296
|
+
deleteInputRange(lineStart, cursor);
|
|
2297
|
+
return;
|
|
2298
|
+
}
|
|
2299
|
+
if (isMod(key) && input === 'k') {
|
|
2300
|
+
// Delete to end of line (never into the block).
|
|
2301
|
+
const nextLine = value.indexOf('\n', cursor);
|
|
2302
|
+
const end = nextLine === -1 ? value.length : clampRowEnd(nextLine);
|
|
2303
|
+
deleteInputRange(cursor, end);
|
|
2304
|
+
return;
|
|
2305
|
+
}
|
|
2306
|
+
if (isMod(key) && input === 'w') {
|
|
2307
|
+
// Delete the word before the cursor: skip
|
|
2308
|
+
// trailing whitespace, then the whitespace-delimited word. The
|
|
2309
|
+
// deletion start never crosses into the block.
|
|
2310
|
+
const before = value.slice(0, cursor);
|
|
2311
|
+
let end = before.length;
|
|
2312
|
+
while (end > 0 && /\s/.test(before[end - 1]))
|
|
2313
|
+
end--;
|
|
2314
|
+
let start = end;
|
|
2315
|
+
while (start > 0 && !/\s/.test(before[start - 1]))
|
|
2316
|
+
start--;
|
|
2317
|
+
deleteInputRange(clampRowStart(start), cursor);
|
|
2318
|
+
return;
|
|
2319
|
+
}
|
|
2320
|
+
// ── vim mode (`/vim`) ──────────────────────────────────────────────
|
|
2321
|
+
// INSERT submode: only Esc is intercepted (back to NORMAL — the usual
|
|
2322
|
+
// clear/rewind Esc semantics stay disabled while vim is on).
|
|
2323
|
+
// NORMAL submode: bare characters and Esc are vim keys; help/command/
|
|
2324
|
+
// file overlays, modified combos, Tab, Enter, arrows and other
|
|
2325
|
+
// structured keys keep their existing handlers.
|
|
2326
|
+
const vimNormalEdit = (next, cursorOffset) => {
|
|
2327
|
+
updateFoldBlock(null);
|
|
2328
|
+
setInput(next, cursorOffset);
|
|
2329
|
+
setSelectedCommand(0);
|
|
2330
|
+
setFileSelected(0);
|
|
2331
|
+
};
|
|
2332
|
+
const vimPushUndo = () => {
|
|
2333
|
+
const images = imageRefsFor(valueRef.current);
|
|
2334
|
+
const evicted = vimUndoRef.current.length >= 100 ? vimUndoRef.current.shift() : undefined;
|
|
2335
|
+
vimUndoRef.current.push({ text: valueRef.current, cursor: cursorRef.current, images });
|
|
2336
|
+
if (evicted !== undefined)
|
|
2337
|
+
discardUnretainedImages(evicted.images.map(image => image.stageId));
|
|
2338
|
+
};
|
|
2339
|
+
const vimDeleteRange = (start, end) => {
|
|
2340
|
+
if (start >= end)
|
|
2341
|
+
return;
|
|
2342
|
+
vimPushUndo();
|
|
2343
|
+
updateFoldBlock(null);
|
|
2344
|
+
deleteInputRange(start, end);
|
|
2345
|
+
setSelectedCommand(0);
|
|
2346
|
+
setFileSelected(0);
|
|
2347
|
+
};
|
|
2348
|
+
const handleVimNormal = (input) => {
|
|
2349
|
+
// One stdin batch can merge several bare keys into a single event
|
|
2350
|
+
// (fast typing), so re-read the synchronous mirrors and recompute
|
|
2351
|
+
// the grapheme boundaries: every key in the batch operates on the
|
|
2352
|
+
// text/caret its predecessor produced (the outer `value`/`cursor`/
|
|
2353
|
+
// `bounds` reflect only the batch's first key).
|
|
2354
|
+
const value = valueRef.current;
|
|
2355
|
+
const cursor = cursorRef.current;
|
|
2356
|
+
const bounds = graphemeBoundaries(value);
|
|
2357
|
+
const pending = vimPendingRef.current;
|
|
2358
|
+
vimPendingRef.current = '';
|
|
2359
|
+
// Operator pending (`d`): the second key picks the target.
|
|
2360
|
+
if (pending === 'd') {
|
|
2361
|
+
switch (input) {
|
|
2362
|
+
case 'd': { // delete the whole line, newline included (vim `dd`);
|
|
2363
|
+
// the last line has no newline — its content is cleared
|
|
2364
|
+
const lineStart = vimLineStart(value, cursor);
|
|
2365
|
+
const lineEnd = vimLineEnd(value, cursor);
|
|
2366
|
+
const end = lineEnd < value.length ? lineEnd + 1 : lineEnd;
|
|
2367
|
+
vimDeleteRange(lineStart, end);
|
|
2368
|
+
return;
|
|
2369
|
+
}
|
|
2370
|
+
case '$': { // delete to end of line
|
|
2371
|
+
const end = vimLineEnd(value, cursor);
|
|
2372
|
+
vimDeleteRange(cursor, end);
|
|
2373
|
+
return;
|
|
2374
|
+
}
|
|
2375
|
+
case '0':
|
|
2376
|
+
case '^': { // delete to start of line
|
|
2377
|
+
const start = input === '^' ? vimLineFirstNonBlank(value, cursor) : vimLineStart(value, cursor);
|
|
2378
|
+
vimDeleteRange(start, cursor);
|
|
2379
|
+
return;
|
|
2380
|
+
}
|
|
2381
|
+
case 'w': { // delete to end of word
|
|
2382
|
+
const end = vimWordEnd(value, cursor);
|
|
2383
|
+
vimDeleteRange(cursor, end);
|
|
2384
|
+
return;
|
|
2385
|
+
}
|
|
2386
|
+
default:
|
|
2387
|
+
return; // unrecognized second key: cancel the operator, drop it
|
|
2388
|
+
}
|
|
2389
|
+
}
|
|
2390
|
+
switch (input) {
|
|
2391
|
+
case 'h':
|
|
2392
|
+
setInput(value, previousGraphemeBoundary(bounds, cursor));
|
|
2393
|
+
return;
|
|
2394
|
+
case 'l':
|
|
2395
|
+
setInput(value, nextGraphemeBoundary(bounds, cursor));
|
|
2396
|
+
return;
|
|
2397
|
+
case 'j': { // down one line (single-line input: no-op)
|
|
2398
|
+
const line = cursorLine(value, cursor);
|
|
2399
|
+
const lines = value.split('\n');
|
|
2400
|
+
if (line >= lines.length - 1)
|
|
2401
|
+
return;
|
|
2402
|
+
const col = Math.min(cursorColumn(value, cursor), (lines[line + 1] ?? '').length);
|
|
2403
|
+
setInput(value, vimLineEnd(value, cursor) + 1 + col);
|
|
2404
|
+
return;
|
|
2405
|
+
}
|
|
2406
|
+
case 'k': { // up one line
|
|
2407
|
+
const line = cursorLine(value, cursor);
|
|
2408
|
+
if (line <= 0)
|
|
2409
|
+
return;
|
|
2410
|
+
const lines = value.split('\n');
|
|
2411
|
+
const col = Math.min(cursorColumn(value, cursor), (lines[line - 1] ?? '').length);
|
|
2412
|
+
const prevStart = vimLineStart(value, vimLineStart(value, cursor) - 1);
|
|
2413
|
+
setInput(value, prevStart + col);
|
|
2414
|
+
return;
|
|
2415
|
+
}
|
|
2416
|
+
case '0': {
|
|
2417
|
+
setInput(value, vimLineStart(value, cursor));
|
|
2418
|
+
return;
|
|
2419
|
+
}
|
|
2420
|
+
case '^': {
|
|
2421
|
+
setInput(value, vimLineFirstNonBlank(value, cursor));
|
|
2422
|
+
return;
|
|
2423
|
+
}
|
|
2424
|
+
case '$': {
|
|
2425
|
+
setInput(value, vimLineEnd(value, cursor));
|
|
2426
|
+
return;
|
|
2427
|
+
}
|
|
2428
|
+
case 'w':
|
|
2429
|
+
setInput(value, vimWordForward(value, cursor));
|
|
2430
|
+
return;
|
|
2431
|
+
case 'b':
|
|
2432
|
+
setInput(value, vimWordBackward(value, cursor));
|
|
2433
|
+
return;
|
|
2434
|
+
case 'x': { // delete the character at the caret; at the very end of
|
|
2435
|
+
// the text delete the last character (vim: the caret sits ON the
|
|
2436
|
+
// last char after `$`, so `x` must still delete it). Same rule
|
|
2437
|
+
// when the caret sits right before a '\n' mid-draft — after `$`
|
|
2438
|
+
// the caret is on the line's last char, `x` must delete THAT
|
|
2439
|
+
// char, not the newline (which would join the lines).
|
|
2440
|
+
if (cursor > 0 && (cursor === value.length || value[cursor] === '\n')) {
|
|
2441
|
+
const start = previousGraphemeBoundary(bounds, cursor);
|
|
2442
|
+
vimDeleteRange(start, cursor);
|
|
2443
|
+
return;
|
|
2444
|
+
}
|
|
2445
|
+
const end = nextGraphemeBoundary(bounds, cursor);
|
|
2446
|
+
if (end === cursor)
|
|
2447
|
+
return;
|
|
2448
|
+
vimDeleteRange(cursor, end);
|
|
2449
|
+
return;
|
|
2450
|
+
}
|
|
2451
|
+
case 'X': { // delete the character before the caret
|
|
2452
|
+
if (cursor === 0)
|
|
2453
|
+
return;
|
|
2454
|
+
const start = previousGraphemeBoundary(bounds, cursor);
|
|
2455
|
+
vimDeleteRange(start, cursor);
|
|
2456
|
+
return;
|
|
2457
|
+
}
|
|
2458
|
+
case 'd':
|
|
2459
|
+
vimPendingRef.current = 'd';
|
|
2460
|
+
return;
|
|
2461
|
+
case 'u': { // undo the last vim edit
|
|
2462
|
+
syncImageGeneration();
|
|
2463
|
+
const prev = vimUndoRef.current.pop();
|
|
2464
|
+
if (prev === undefined)
|
|
2465
|
+
return;
|
|
2466
|
+
advanceDraftRevision();
|
|
2467
|
+
replaceDraftImages(prev.images);
|
|
2468
|
+
updateFoldBlock(null);
|
|
2469
|
+
setInput(prev.text, prev.cursor);
|
|
2470
|
+
setSelectedCommand(0);
|
|
2471
|
+
setFileSelected(0);
|
|
2472
|
+
return;
|
|
2473
|
+
}
|
|
2474
|
+
case 'i': // insert at the caret
|
|
2475
|
+
vimInsertRef.current = true;
|
|
2476
|
+
setVimInsert(true);
|
|
2477
|
+
return;
|
|
2478
|
+
case 'I': { // insert at the line's first non-blank (vim `I`)
|
|
2479
|
+
setInput(value, vimLineFirstNonBlank(value, cursor));
|
|
2480
|
+
vimInsertRef.current = true;
|
|
2481
|
+
setVimInsert(true);
|
|
2482
|
+
return;
|
|
2483
|
+
}
|
|
2484
|
+
case 'a': { // insert after the caret
|
|
2485
|
+
setInput(value, nextGraphemeBoundary(bounds, cursor));
|
|
2486
|
+
vimInsertRef.current = true;
|
|
2487
|
+
setVimInsert(true);
|
|
2488
|
+
return;
|
|
2489
|
+
}
|
|
2490
|
+
case 'A': { // insert at the line end
|
|
2491
|
+
setInput(value, vimLineEnd(value, cursor));
|
|
2492
|
+
vimInsertRef.current = true;
|
|
2493
|
+
setVimInsert(true);
|
|
2494
|
+
return;
|
|
2495
|
+
}
|
|
2496
|
+
case 'o': { // new line below, then insert
|
|
2497
|
+
vimPushUndo();
|
|
2498
|
+
const end = vimLineEnd(value, cursor);
|
|
2499
|
+
vimNormalEdit(value.slice(0, end) + '\n' + value.slice(end), end + 1);
|
|
2500
|
+
vimInsertRef.current = true;
|
|
2501
|
+
setVimInsert(true);
|
|
2502
|
+
return;
|
|
2503
|
+
}
|
|
2504
|
+
case 'O': { // new line above, then insert
|
|
2505
|
+
vimPushUndo();
|
|
2506
|
+
const start = vimLineStart(value, cursor);
|
|
2507
|
+
vimNormalEdit(value.slice(0, start) + '\n' + value.slice(start), start);
|
|
2508
|
+
vimInsertRef.current = true;
|
|
2509
|
+
setVimInsert(true);
|
|
2510
|
+
return;
|
|
2511
|
+
}
|
|
2512
|
+
default:
|
|
2513
|
+
return; // unrecognized key: ignored (never inserts in NORMAL)
|
|
2514
|
+
}
|
|
2515
|
+
};
|
|
2516
|
+
if (vimEnabledRef.current) {
|
|
2517
|
+
if (vimInsertRef.current) {
|
|
2518
|
+
// INSERT: Esc returns to NORMAL instead of the clear/rewind path.
|
|
2519
|
+
if (key.escape && !helpOpen && !overlayOpen && !fileOverlayOpen) {
|
|
2520
|
+
event.stopImmediatePropagation();
|
|
2521
|
+
vimInsertRef.current = false;
|
|
2522
|
+
setVimInsert(false);
|
|
2523
|
+
return;
|
|
2524
|
+
}
|
|
2525
|
+
}
|
|
2526
|
+
else if (!helpOpen && !overlayOpen && !fileOverlayOpen) {
|
|
2527
|
+
if (key.escape) {
|
|
2528
|
+
// NORMAL: Esc cancels a pending operator and otherwise no-ops
|
|
2529
|
+
// (the clear/rewind double-Esc semantics belong to non-vim mode).
|
|
2530
|
+
vimPendingRef.current = '';
|
|
2531
|
+
event.stopImmediatePropagation();
|
|
2532
|
+
return;
|
|
2533
|
+
}
|
|
2534
|
+
const plainChar = input.length > 0 &&
|
|
2535
|
+
!key.ctrl && !key.meta && !key.super && !key.tab && !key.return;
|
|
2536
|
+
if (plainChar) {
|
|
2537
|
+
// `?` on an empty input falls through to the help shortcut below.
|
|
2538
|
+
if (input === '?' && value.length === 0)
|
|
2539
|
+
return;
|
|
2540
|
+
// `/` opens the slash-command menu even in NORMAL: insert it and
|
|
2541
|
+
// switch to INSERT so the rest of the command types normally
|
|
2542
|
+
// (the menu then owns the keys while it is open).
|
|
2543
|
+
if (input === '/') {
|
|
2544
|
+
const text = valueRef.current;
|
|
2545
|
+
const pos = cursorRef.current;
|
|
2546
|
+
const next = text.slice(0, pos) + '/' + text.slice(pos);
|
|
2547
|
+
setInput(next, pos + 1);
|
|
2548
|
+
setSelectedCommand(0);
|
|
2549
|
+
setFileSelected(0);
|
|
2550
|
+
vimInsertRef.current = true;
|
|
2551
|
+
setVimInsert(true);
|
|
2552
|
+
return;
|
|
2553
|
+
}
|
|
2554
|
+
event.stopImmediatePropagation();
|
|
2555
|
+
// A merged multi-char event (fast typing) is one vim key per
|
|
2556
|
+
// character — handleVimNormal re-reads the refs each call, so a
|
|
2557
|
+
// batch like `dd` works exactly like two separate keypresses.
|
|
2558
|
+
// Once a key switches back to INSERT (i/a/o/…), the remaining
|
|
2559
|
+
// characters of the batch are ordinary typing and insert as text.
|
|
2560
|
+
for (let i = 0; i < input.length; i++) {
|
|
2561
|
+
if (vimInsertRef.current) {
|
|
2562
|
+
const rest = input.slice(i);
|
|
2563
|
+
const text = valueRef.current;
|
|
2564
|
+
const pos = cursorRef.current;
|
|
2565
|
+
const next = text.slice(0, pos) + rest + text.slice(pos);
|
|
2566
|
+
setInput(next, pos + rest.length);
|
|
2567
|
+
setSelectedCommand(0);
|
|
2568
|
+
setFileSelected(0);
|
|
2569
|
+
break;
|
|
2570
|
+
}
|
|
2571
|
+
handleVimNormal(input[i]);
|
|
2572
|
+
}
|
|
2573
|
+
return;
|
|
2574
|
+
}
|
|
2575
|
+
}
|
|
2576
|
+
}
|
|
2577
|
+
if (key.escape) {
|
|
2578
|
+
if (helpOpen) {
|
|
2579
|
+
onToggleHelp();
|
|
2580
|
+
return;
|
|
2581
|
+
}
|
|
2582
|
+
// A single Esc closes the open command menu first;
|
|
2583
|
+
// the double-tap-clear semantics only apply to ordinary input.
|
|
2584
|
+
if (overlayOpen) {
|
|
2585
|
+
syncImageGeneration();
|
|
2586
|
+
discardDraftImages();
|
|
2587
|
+
setInput('', 0);
|
|
2588
|
+
setSelectedCommand(0);
|
|
2589
|
+
setFileSelected(0);
|
|
2590
|
+
return;
|
|
2591
|
+
}
|
|
2592
|
+
// File overlay: Esc dismisses the menu for THIS token only — clearing
|
|
2593
|
+
// the input would nuke a mid-message `@` mention's surrounding text.
|
|
2594
|
+
if (fileOverlayOpen) {
|
|
2595
|
+
fileEscRef.current = mention?.start ?? -1;
|
|
2596
|
+
return;
|
|
2597
|
+
}
|
|
2598
|
+
// With pending messages while working, Esc = interrupt and deliver
|
|
2599
|
+
// them right away (Codex's "interrupt and send immediately"): the
|
|
2600
|
+
// turn is aborted and each message is re-queued once it settles.
|
|
2601
|
+
if (channel.working && channel.pending.length > 0) {
|
|
2602
|
+
const count = channel.interruptAndDeliver(channel.pending.map(item => ({
|
|
2603
|
+
text: item.text,
|
|
2604
|
+
images: item.images ?? [],
|
|
2605
|
+
})));
|
|
2606
|
+
channel.notify(t('interrupt-delivered', { n: count }), {
|
|
2607
|
+
timeoutMs: 2500,
|
|
2608
|
+
});
|
|
2609
|
+
return;
|
|
2610
|
+
}
|
|
2611
|
+
// A single Esc clears the current input (if any); the double-tap
|
|
2612
|
+
// path below handles rewind/clear on an already-empty input. A BIG
|
|
2613
|
+
// input folds into a block instead (Esc = the fold toggle; the
|
|
2614
|
+
// expand → fold → expand cycle is lossless, and clearing a big draft
|
|
2615
|
+
// stays reachable via the block-delete Backspace or Ctrl+C).
|
|
2616
|
+
if (value.length > 0) {
|
|
2617
|
+
if (isBigInput(value)) {
|
|
2618
|
+
updateFoldBlock({ start: 0, end: value.length });
|
|
2619
|
+
setSelectedCommand(0);
|
|
2620
|
+
setFileSelected(0);
|
|
2621
|
+
return;
|
|
2622
|
+
}
|
|
2623
|
+
syncImageGeneration();
|
|
2624
|
+
discardDraftImages();
|
|
2625
|
+
setInput('', 0);
|
|
2626
|
+
setSelectedCommand(0);
|
|
2627
|
+
setFileSelected(0);
|
|
2628
|
+
return;
|
|
2629
|
+
}
|
|
2630
|
+
// Double-tap Esc: clear the input when it has content; when empty,
|
|
2631
|
+
// open the rewind picker (double-tap Esc rewinds the selected message
|
|
2632
|
+
// and/or conversation to a previous point in time).
|
|
2633
|
+
if (escPendingRef.current) {
|
|
2634
|
+
escPendingRef.current = false;
|
|
2635
|
+
if (escTimerRef.current)
|
|
2636
|
+
clearTimeout(escTimerRef.current);
|
|
2637
|
+
if (value.length === 0) {
|
|
2638
|
+
onRewindRequest?.();
|
|
2639
|
+
}
|
|
2640
|
+
else {
|
|
2641
|
+
syncImageGeneration();
|
|
2642
|
+
discardDraftImages();
|
|
2643
|
+
setInput('', 0);
|
|
2644
|
+
}
|
|
2645
|
+
return;
|
|
2646
|
+
}
|
|
2647
|
+
escPendingRef.current = true;
|
|
2648
|
+
channel.notify(value.length === 0 ? t('esc-again-rewind') : t('esc-again-clear'));
|
|
2649
|
+
escTimerRef.current = setTimeout(() => {
|
|
2650
|
+
escPendingRef.current = false;
|
|
2651
|
+
}, 3000);
|
|
2652
|
+
return;
|
|
2653
|
+
}
|
|
2654
|
+
if (input === '?' && value.length === 0 && !expandedRef.current) {
|
|
2655
|
+
onToggleHelp();
|
|
2656
|
+
return;
|
|
2657
|
+
}
|
|
2658
|
+
if (input && !key.ctrl && !key.meta && !key.super && !key.tab && !key.escape) {
|
|
2659
|
+
// Typing anything else dismisses the help menu.
|
|
2660
|
+
if (helpOpen)
|
|
2661
|
+
onToggleHelp();
|
|
2662
|
+
// An active selection is REPLACED by the typed text, caret after it.
|
|
2663
|
+
const sel = selectionRef.current;
|
|
2664
|
+
const at = sel ? sel.start : cursor;
|
|
2665
|
+
const next = sel
|
|
2666
|
+
? value.slice(0, sel.start) + input + value.slice(sel.end)
|
|
2667
|
+
: value.slice(0, cursor) + input + value.slice(cursor);
|
|
2668
|
+
setInput(next, at + input.length);
|
|
2669
|
+
setSelectedCommand(0);
|
|
2670
|
+
setFileSelected(0);
|
|
2671
|
+
}
|
|
2672
|
+
}, { isActive: !suspended });
|
|
2673
|
+
// === Render: hard-wrap every logical line at the input width, then show
|
|
2674
|
+
// the window of visual lines with the caret row always visible.
|
|
2675
|
+
// Narrow terminals: the usable width follows the real terminal down to a
|
|
2676
|
+
// single column — a fixed floor of 10 would wrap far too early and park
|
|
2677
|
+
// the declared cursor past the value box's actual width.
|
|
2678
|
+
// The vim badge ('INSERT '/'NORMAL ', 7 cols) sits BEFORE the value box in
|
|
2679
|
+
// the same row, so the wrap budget must shrink by its width or long lines
|
|
2680
|
+
// would be clipped at the value box's right edge.
|
|
2681
|
+
const vimBadgeCols = vimEnabled ? 7 : 0;
|
|
2682
|
+
// 行首会话入口占列:未传 onOpenSessions 时按钮整体不渲染,预算同步归还
|
|
2683
|
+
// (既有调用方的可用列数逐列不变)。用实测常量而不是 stringWidth:该字符
|
|
2684
|
+
// 是 U+1F3E0+VS15,stringWidth 按码点算 2(含 VS15 仍是 2),终端只给 1,
|
|
2685
|
+
// 见 HOME_BUTTON_COLS 的说明。
|
|
2686
|
+
const homeButtonCols = onOpenSessions === undefined ? 0 : HOME_BUTTON_COLS;
|
|
2687
|
+
// 补全卡片边框与输入框 idle 边框同色(plan 模式下整套面板一起变 sage
|
|
2688
|
+
// 绿)。`/color` 会话强调色优先于主题 promptBorder(plan 模式仍整体走
|
|
2689
|
+
// sage 绿)。`?? ''` 防御最小 mock channel(只声明用到的字段的回归脚
|
|
2690
|
+
// 本)。声明在渲染派生区之前:展开编辑器的行号高亮与边框同用此色。
|
|
2691
|
+
const sessionAccent = sessionColorHex(channel.sessionColor ?? '');
|
|
2692
|
+
const promptAccent = channel.mode.plan === true ? 'planMode' : (sessionAccent ?? 'promptBorder');
|
|
2693
|
+
// 展开态布局参数:编辑器独占整屏 —— 行号槽(宽度随逻辑行数伸缩)+
|
|
2694
|
+
// 圆角边框 2 + 两侧 padding 各 1 占列,⌸ 入口 / ❯ 前缀 / vim 徽标 /
|
|
2695
|
+
// ⛶ 按钮全部让位;收起态额外扣掉行首 ⌸ 入口(未渲染时 0 列)与行尾
|
|
2696
|
+
// ⛶ 按钮的 2 列。
|
|
2697
|
+
const editorLogicalLines = expanded ? value.split('\n').length : 1;
|
|
2698
|
+
const editorNoWidth = Math.max(2, String(editorLogicalLines).length);
|
|
2699
|
+
const editorGutterCols = editorNoWidth + 3;
|
|
2700
|
+
const inputWidth = expanded
|
|
2701
|
+
? Math.max(1, columns - 4 - editorGutterCols)
|
|
2702
|
+
: Math.max(1, columns - 3 - vimBadgeCols - homeButtonCols - (expandEnabled ? 2 : 0));
|
|
2703
|
+
// 展开态无视折叠块:全屏编辑就是为了看全文(foldBlock 状态保留,
|
|
2704
|
+
// 收起后折叠显示恢复)。
|
|
2705
|
+
const block = expanded ? null : foldBlock;
|
|
2706
|
+
// Fold-block model: the value renders as [head rows][chip row][tail rows]
|
|
2707
|
+
// — the block is ONE atomic visual row regardless of its text size; text
|
|
2708
|
+
// before/after it stays fully editable and never unfolds it.
|
|
2709
|
+
const foldText = block ? value.slice(block.start, block.end) : value;
|
|
2710
|
+
const head = block ? value.slice(0, block.start) : '';
|
|
2711
|
+
const tail = block ? value.slice(block.end) : '';
|
|
2712
|
+
const headRows = block ? wrapToWidth(head, inputWidth) : [];
|
|
2713
|
+
const tailRows = block ? wrapToWidth(tail, inputWidth) : [];
|
|
2714
|
+
const chipRow = block ? headRows.length : -1;
|
|
2715
|
+
const visualLines = block
|
|
2716
|
+
? [...headRows, '', ...tailRows]
|
|
2717
|
+
: wrapToWidth(value, inputWidth);
|
|
2718
|
+
// Caret geometry uses the FULL wrap (not `value.slice(0, cursor)`):
|
|
2719
|
+
// word-wrap carry moves already-placed clusters, so a prefix wrap would
|
|
2720
|
+
// disagree with the displayed rows whenever the cursor sits inside a
|
|
2721
|
+
// carried word.
|
|
2722
|
+
const caretPlaced = block
|
|
2723
|
+
? cursor <= block.start
|
|
2724
|
+
? caretInText(head, inputWidth, cursor)
|
|
2725
|
+
: caretInText(tail, inputWidth, Math.max(0, cursor - block.end))
|
|
2726
|
+
: caretInText(value, inputWidth, cursor);
|
|
2727
|
+
const caretVisualLine = block && cursor > block.start
|
|
2728
|
+
? headRows.length + 1 + caretPlaced.line
|
|
2729
|
+
: caretPlaced.line;
|
|
2730
|
+
const caretCharCol = caretPlaced.charCol;
|
|
2731
|
+
const caretVisualCol = caretPlaced.visualCol;
|
|
2732
|
+
// Fold stats describe the BLOCK (or the whole input when expanded and
|
|
2733
|
+
// the ▾ prefix offers a manual whole-input fold). The chip shows the
|
|
2734
|
+
// block's own line/char count + first-line preview — the preview text
|
|
2735
|
+
// around it is unaffected.
|
|
2736
|
+
const big = isBigInput(foldText);
|
|
2737
|
+
const stats = big
|
|
2738
|
+
? t('input-fold-stats', { lines: foldText.split('\n').length, chars: foldText.length })
|
|
2739
|
+
: '';
|
|
2740
|
+
const peekOpen = block !== null && hovered && !selectionActive;
|
|
2741
|
+
// 展开态的编辑区行高预算:圆角边框 2 + 标题行 1 + 状态行 1 + 按钮行 1。
|
|
2742
|
+
const editorMaxRows = Math.max(1, terminalRows - EDITOR_CHROME_ROWS);
|
|
2743
|
+
if (expanded && caretVisualLine !== prevCaretLineRef.current) {
|
|
2744
|
+
// A real caret move re-engages following after wheel browsing.
|
|
2745
|
+
editorFreeScrollRef.current = false;
|
|
2746
|
+
prevCaretLineRef.current = caretVisualLine;
|
|
2747
|
+
}
|
|
2748
|
+
let windowStart;
|
|
2749
|
+
let visibleCount;
|
|
2750
|
+
if (expanded) {
|
|
2751
|
+
// 滚轮推动的窗口与 caret 跟随合并:自由滚动(滚轮浏览)期间不强制
|
|
2752
|
+
// caret 可见;caret 移动后恢复跟随。写回 ref,滚轮分支读到的就是
|
|
2753
|
+
// 合并后的基线。
|
|
2754
|
+
let win = expandedScrollRef.current;
|
|
2755
|
+
if (!editorFreeScrollRef.current) {
|
|
2756
|
+
if (caretVisualLine < win)
|
|
2757
|
+
win = caretVisualLine;
|
|
2758
|
+
if (caretVisualLine >= win + editorMaxRows)
|
|
2759
|
+
win = caretVisualLine - editorMaxRows + 1;
|
|
2760
|
+
}
|
|
2761
|
+
win = Math.max(0, Math.min(win, Math.max(0, visualLines.length - editorMaxRows)));
|
|
2762
|
+
expandedScrollRef.current = win;
|
|
2763
|
+
windowStart = win;
|
|
2764
|
+
visibleCount = editorMaxRows;
|
|
2765
|
+
}
|
|
2766
|
+
else {
|
|
2767
|
+
windowStart = Math.max(0, Math.min(caretVisualLine - MAX_VISIBLE_LINES + 1, visualLines.length - MAX_VISIBLE_LINES));
|
|
2768
|
+
visibleCount = MAX_VISIBLE_LINES;
|
|
2769
|
+
}
|
|
2770
|
+
const visibleLines = visualLines.slice(windowStart, windowStart + visibleCount);
|
|
2771
|
+
// Caret-window jump: when the caret leaves MAX_VISIBLE_LINES (typing past
|
|
2772
|
+
// it, arrow-key walks through a long prompt) the whole visible band is
|
|
2773
|
+
// replaced in place — same-height rows, completely different text. The
|
|
2774
|
+
// per-cell diff that repaints that band is exactly the path that goes
|
|
2775
|
+
// haywire when the inline viewport is even one row out of sync with the
|
|
2776
|
+
// terminal's scrollback ("重叠变花": old rows bleeding through the new
|
|
2777
|
+
// ones). Same one-shot reanchor family as the shrink/floaters/editor
|
|
2778
|
+
// patches above — the window jump is a viewport-level discontinuity, not
|
|
2779
|
+
// an ordinary content edit.
|
|
2780
|
+
const prevWindowStartRef = React.useRef(windowStart);
|
|
2781
|
+
React.useLayoutEffect(() => {
|
|
2782
|
+
if (windowStart !== prevWindowStartRef.current) {
|
|
2783
|
+
prevWindowStartRef.current = windowStart;
|
|
2784
|
+
const ink = instances.get(process.stdout) ?? instances.values().next().value;
|
|
2785
|
+
ink?.invalidatePrevFrame();
|
|
2786
|
+
ink?.reanchorViewport();
|
|
2787
|
+
}
|
|
2788
|
+
}, [windowStart]);
|
|
2789
|
+
// useInput 的滚轮分支需要这份几何(它在这些派生之前注册)。
|
|
2790
|
+
if (expanded) {
|
|
2791
|
+
editorViewportRef.current = { maxRows: editorMaxRows, total: visualLines.length };
|
|
2792
|
+
}
|
|
2793
|
+
// Folded chip content: block stats + first-line preview + hover hint,
|
|
2794
|
+
// all pre-truncated to the input width (the row is one line, always).
|
|
2795
|
+
// `·` separators are U+30FB KATAKANA MIDDLE DOT, NOT U+00B7: the latter
|
|
2796
|
+
// is East-Asian-ambiguous (model 1 cell, CJK terminal fonts paint 2) and
|
|
2797
|
+
// this row's stats→preview→hint truncation arithmetic all runs through
|
|
2798
|
+
// stringWidth — a painted-wide separator shifts every segment right by
|
|
2799
|
+
// one column per separator and the row reads as overlapping text.
|
|
2800
|
+
// U+30FB is officially Ambiguous too, but get-east-asian-width hardcodes
|
|
2801
|
+
// it Wide and mainstream terminals (Western included) paint it 2 cells —
|
|
2802
|
+
// the model and the painted width agree in practice. A wcwidth-strict
|
|
2803
|
+
// Western terminal painting it 1 cell would leave a spare column, which
|
|
2804
|
+
// the `- 6` slack below absorbs.
|
|
2805
|
+
const foldBadge = `▸ ${stats}`;
|
|
2806
|
+
const foldHint = t('input-fold-hover');
|
|
2807
|
+
const foldPreviewWidth = inputWidth - stringWidth(foldBadge) - stringWidth(`・${foldHint}`) - 6;
|
|
2808
|
+
const foldPreview = foldPreviewWidth >= 8
|
|
2809
|
+
? truncateToWidth(foldText.split('\n')[0] ?? '', foldPreviewWidth)
|
|
2810
|
+
: '';
|
|
2811
|
+
// Expanded-state fold affordance: a `▾` prefix at the start of the FIRST
|
|
2812
|
+
// row (only while the window is at the top and no block exists); its
|
|
2813
|
+
// cells fold the whole input into a block again on click.
|
|
2814
|
+
const prefixLabel = `▾ ${stats}・`;
|
|
2815
|
+
const prefixCols = !block && !expanded && big && windowStart === 0 ? stringWidth(prefixLabel) : 0;
|
|
2816
|
+
// Offset range [start, end) of every visual row — the SAME break rule as
|
|
2817
|
+
// wrapToWidth — so the selection highlight can intersect each row. With a
|
|
2818
|
+
// fold block, the chip row maps to the block's own span (never selected:
|
|
2819
|
+
// updateSelection clamps the selection into the head or the tail side).
|
|
2820
|
+
const lineRanges = block
|
|
2821
|
+
? [
|
|
2822
|
+
...visualLineRanges(head, inputWidth),
|
|
2823
|
+
[block.start, block.end],
|
|
2824
|
+
...visualLineRanges(tail, inputWidth).map(([s, e]) => [s + block.end, e + block.end]),
|
|
2825
|
+
]
|
|
2826
|
+
: visualLineRanges(value, inputWidth);
|
|
2827
|
+
/**
|
|
2828
|
+
* Inverse runs for one rendered row, shared by the inline prompt and the
|
|
2829
|
+
* expanded editor: the selection's intersection (if any) and the caret
|
|
2830
|
+
* cluster on the caret's row. Both render <Text inverse>; overlapping
|
|
2831
|
+
* intervals merge so a caret inside the selection stays one continuous
|
|
2832
|
+
* highlight. The caret row inverts the WHOLE cluster at the caret column
|
|
2833
|
+
* (solid block) — [col, next boundary) covers a surrogate pair or ZWJ
|
|
2834
|
+
* emoji as one glyph; at the text end it shows a blank inverse cell like
|
|
2835
|
+
* the empty-input caret (appended after everything, so a selection
|
|
2836
|
+
* ending there cannot swallow it).
|
|
2837
|
+
*/
|
|
2838
|
+
const rowHighlightPieces = (text, absoluteLine) => {
|
|
2839
|
+
const [rowStart] = lineRanges[absoluteLine] ?? [0, 0];
|
|
2840
|
+
// Per-character style: 0 plain, 1 chip (a staged token), 2 inverse
|
|
2841
|
+
// (selection or caret cluster). Inverse wins over chip.
|
|
2842
|
+
const PLAIN = 0;
|
|
2843
|
+
const CHIP = 1;
|
|
2844
|
+
const INVERSE = 2;
|
|
2845
|
+
const kinds = new Uint8Array(text.length);
|
|
2846
|
+
const fill = (lo, hi, kind) => {
|
|
2847
|
+
for (let i = Math.max(lo, 0); i < Math.min(hi, text.length); i++)
|
|
2848
|
+
kinds[i] = kind;
|
|
2849
|
+
};
|
|
2850
|
+
const spans = boundImageSpans(value);
|
|
2851
|
+
for (const span of spans)
|
|
2852
|
+
fill(span.start - rowStart, span.end - rowStart, CHIP);
|
|
2853
|
+
const sel = selection;
|
|
2854
|
+
if (sel)
|
|
2855
|
+
fill(sel.start - rowStart, sel.end - rowStart, INVERSE);
|
|
2856
|
+
let endBlankCaret = false;
|
|
2857
|
+
if (absoluteLine === caretVisualLine) {
|
|
2858
|
+
const col = Math.min(caretCharCol, text.length);
|
|
2859
|
+
// A staged token is one caret cluster: with the caret at its start the
|
|
2860
|
+
// whole token inverts (the selected-chip look), and Delete removes it.
|
|
2861
|
+
const tokenAtCaret = spans.find(span => span.start === rowStart + col);
|
|
2862
|
+
const clusterEnd = tokenAtCaret !== undefined
|
|
2863
|
+
? Math.min(tokenAtCaret.end - rowStart, text.length)
|
|
2864
|
+
: nextGraphemeBoundary(graphemeBoundaries(text), col);
|
|
2865
|
+
if (clusterEnd > col)
|
|
2866
|
+
fill(col, clusterEnd, INVERSE);
|
|
2867
|
+
else
|
|
2868
|
+
endBlankCaret = col === text.length;
|
|
2869
|
+
}
|
|
2870
|
+
const pieces = [];
|
|
2871
|
+
let pos = 0;
|
|
2872
|
+
while (pos < text.length) {
|
|
2873
|
+
const kind = kinds[pos];
|
|
2874
|
+
let end = pos + 1;
|
|
2875
|
+
while (end < text.length && kinds[end] === kind)
|
|
2876
|
+
end++;
|
|
2877
|
+
pieces.push({ text: text.slice(pos, end), inverse: kind === INVERSE, chip: kind === CHIP });
|
|
2878
|
+
pos = end;
|
|
2879
|
+
}
|
|
2880
|
+
if (endBlankCaret)
|
|
2881
|
+
pieces.push({ text: ' ', inverse: true, chip: false });
|
|
2882
|
+
return pieces;
|
|
2883
|
+
};
|
|
2884
|
+
const rendered = visibleLines.map((line, index) => {
|
|
2885
|
+
const absoluteLine = windowStart + index;
|
|
2886
|
+
if (block && absoluteLine === chipRow) {
|
|
2887
|
+
// The fold block's atomic chip row: click expands it; hover pops the
|
|
2888
|
+
// peek card. The row is one line no matter how big the block is.
|
|
2889
|
+
return (_jsxs(Box, { flexDirection: "row", onClick: (event) => {
|
|
2890
|
+
event.stopImmediatePropagation();
|
|
2891
|
+
updateFoldBlock(null);
|
|
2892
|
+
}, onMouseEnter: hoverEnter, onMouseLeave: hoverLeave, children: [_jsx(Text, { dimColor: true, children: foldBadge }), foldPreview !== '' && _jsx(Text, { dimColor: true, children: "\u30FB" }), foldPreview !== '' && _jsx(Text, { wrap: "truncate-end", children: foldPreview }), _jsx(Text, { dimColor: true, children: `・${foldHint}` })] }, `fold-${absoluteLine}`));
|
|
2893
|
+
}
|
|
2894
|
+
const withPrefix = absoluteLine === 0 && prefixCols > 0;
|
|
2895
|
+
const text = withPrefix ? truncateToWidth(line, inputWidth - prefixCols) : line;
|
|
2896
|
+
const prefix = withPrefix ? _jsx(Text, { dimColor: true, children: prefixLabel }) : null;
|
|
2897
|
+
const pieces = rowHighlightPieces(text, absoluteLine);
|
|
2898
|
+
return (_jsxs(Text, { wrap: "truncate-end", children: [prefix, pieces.length === 0 ? ' ' : pieces.map((piece, pieceIndex) => piece.inverse ? (_jsx(Text, { inverse: true, children: piece.text }, pieceIndex)) : piece.chip ? (_jsx(Text, { color: "suggestion", children: piece.text }, pieceIndex)) : (piece.text))] }, absoluteLine));
|
|
2899
|
+
});
|
|
2900
|
+
// ── 展开态编辑行 ────────────────────────────────────────────────────
|
|
2901
|
+
// Logical line number per visual row (a wrapped continuation keeps its
|
|
2902
|
+
// line's number): the row's range end sitting on a '\n' starts a new
|
|
2903
|
+
// logical line from the NEXT row.
|
|
2904
|
+
const editorRowLogical = [];
|
|
2905
|
+
if (expanded) {
|
|
2906
|
+
let count = 0;
|
|
2907
|
+
for (let i = 0; i < lineRanges.length; i++) {
|
|
2908
|
+
editorRowLogical.push(count);
|
|
2909
|
+
const rangeEnd = lineRanges[i][1];
|
|
2910
|
+
if (value[rangeEnd] === '\n')
|
|
2911
|
+
count++;
|
|
2912
|
+
}
|
|
2913
|
+
}
|
|
2914
|
+
const editorRows = expanded
|
|
2915
|
+
? visibleLines.map((line, index) => {
|
|
2916
|
+
const absoluteLine = windowStart + index;
|
|
2917
|
+
const isCaretRow = absoluteLine === caretVisualLine;
|
|
2918
|
+
const logicalNo = editorRowLogical[absoluteLine];
|
|
2919
|
+
const gutterLabel = logicalNo === undefined
|
|
2920
|
+
? ' '.repeat(editorNoWidth)
|
|
2921
|
+
: String(logicalNo + 1).padStart(editorNoWidth, ' ');
|
|
2922
|
+
const pieces = rowHighlightPieces(line, absoluteLine);
|
|
2923
|
+
return (_jsxs(Text, { wrap: "truncate-end", backgroundColor: isCaretRow ? 'toolCardBackgroundDim' : undefined, children: [_jsx(Text, { dimColor: !isCaretRow, bold: isCaretRow, color: isCaretRow ? promptAccent : undefined, children: `${gutterLabel} │ ` }), pieces.map((piece, pieceIndex) => piece.inverse ? (_jsx(Text, { inverse: true, children: piece.text }, pieceIndex)) : piece.chip ? (_jsx(Text, { color: "suggestion", children: piece.text }, pieceIndex)) : (piece.text))] }, absoluteLine));
|
|
2924
|
+
})
|
|
2925
|
+
: null;
|
|
2926
|
+
// Peek card content: the BLOCK's text wrapped to the card's inner width,
|
|
2927
|
+
// capped at PEEK_MAX_ROWS visual rows (a preview — click to expand and
|
|
2928
|
+
// edit the real input). The footer reports the clipped remainder.
|
|
2929
|
+
const PEEK_MAX_ROWS = 10;
|
|
2930
|
+
const peekVisualLines = [];
|
|
2931
|
+
for (const line of foldText.split('\n')) {
|
|
2932
|
+
for (const row of wrapToWidth(line, cardContentWidth(columns))) {
|
|
2933
|
+
if (peekVisualLines.length >= PEEK_MAX_ROWS)
|
|
2934
|
+
break;
|
|
2935
|
+
peekVisualLines.push(row);
|
|
2936
|
+
}
|
|
2937
|
+
if (peekVisualLines.length >= PEEK_MAX_ROWS)
|
|
2938
|
+
break;
|
|
2939
|
+
}
|
|
2940
|
+
const peekClipped = peekVisualLines.length >= PEEK_MAX_ROWS;
|
|
2941
|
+
// Composer height shrink: clearing multi-line text (Enter/Esc/Ctrl+C/
|
|
2942
|
+
// Backspace) collapses the input area within one commit, shifting the
|
|
2943
|
+
// status line up and the whole chrome with it. The renderer's
|
|
2944
|
+
// full-damage pass (didLayoutShift) repaints the shifted siblings, but
|
|
2945
|
+
// inline mode's virtual↔scrollback correspondence needs the stronger
|
|
2946
|
+
// in-place viewport repaint — same treatment as Ctrl+O and the
|
|
2947
|
+
// loaded-context toggle (see Chat.tsx). One-shot, only on SHRINK:
|
|
2948
|
+
// growth scrolls the terminal naturally and needs no recovery.
|
|
2949
|
+
const contentRows = value.length === 0 ? 1 : visibleLines.length;
|
|
2950
|
+
noteAuxNumber('promptContentRows', contentRows);
|
|
2951
|
+
const prevContentRowsRef = React.useRef(contentRows);
|
|
2952
|
+
React.useLayoutEffect(() => {
|
|
2953
|
+
if (contentRows < prevContentRowsRef.current) {
|
|
2954
|
+
const ink = instances.get(process.stdout) ?? instances.values().next().value;
|
|
2955
|
+
ink?.invalidatePrevFrame();
|
|
2956
|
+
ink?.reanchorViewport();
|
|
2957
|
+
}
|
|
2958
|
+
prevContentRowsRef.current = contentRows;
|
|
2959
|
+
}, [contentRows]);
|
|
2960
|
+
const lastNotification = channel.notifications[channel.notifications.length - 1];
|
|
2961
|
+
// Park the native terminal cursor at the input caret (via the renderer's
|
|
2962
|
+
// cursor-declaration mechanism). Terminal emulators render IME preedit
|
|
2963
|
+
// text and screen-reader focus at the physical cursor, so parking it at
|
|
2964
|
+
// the caret makes CJK/IME composition appear inline at the input instead
|
|
2965
|
+
// of at the screen's bottom row; in line-mode terminals the console echo
|
|
2966
|
+
// of typed characters lands at the same spot. `line`/`column` are
|
|
2967
|
+
// relative to the value box the ref attaches to.
|
|
2968
|
+
const valueBoxRef = useDeclaredCursor({
|
|
2969
|
+
line: caretVisualLine - windowStart,
|
|
2970
|
+
// Clamp the declared column to the wrap width: a grapheme wider than
|
|
2971
|
+
// the last remaining column (emoji at width 1) can push the visual
|
|
2972
|
+
// column past inputWidth, and the park must stay inside the value box.
|
|
2973
|
+
// In the expanded editor the declared box starts at its outer edge
|
|
2974
|
+
// (padding + gutter included), so those columns ride along and the
|
|
2975
|
+
// clamp grows with them.
|
|
2976
|
+
//
|
|
2977
|
+
// The declared column is relative to the VALUE BOX, and `caretVisualCol`
|
|
2978
|
+
// is already measured in that box's cell space: the text box IS the text
|
|
2979
|
+
// run, so nothing that renders before it — the session entry, the `❯ `
|
|
2980
|
+
// glyph, the fold prefix — shifts a cell inside it. Adding any of those
|
|
2981
|
+
// back double-counts them and parks the hardware cursor to the RIGHT of
|
|
2982
|
+
// the inverted caret cell (a 2-column miss shows up as a displaced IME
|
|
2983
|
+
// preedit target). Only the expanded editor declares against a wider box
|
|
2984
|
+
// (gutter + padding), so only that branch adds columns.
|
|
2985
|
+
column: Math.min(caretVisualCol + (expanded ? editorGutterCols + 1 : 0), expanded ? editorGutterCols + 1 + inputWidth : inputWidth),
|
|
2986
|
+
active: !suspended && !selectionActive,
|
|
2987
|
+
});
|
|
2988
|
+
/**
|
|
2989
|
+
* Map a pointer position relative to the value box to a UTF-16 offset
|
|
2990
|
+
* via the same grapheme walk the renderer wraps with — exact under CJK
|
|
2991
|
+
* widths, wrapped rows and multi-codepoint clusters. Rows are clamped to
|
|
2992
|
+
* the VISIBLE window (drag moves never auto-scroll past it in v1); the
|
|
2993
|
+
* fold chip row maps to null (its cells belong to the expand affordance,
|
|
2994
|
+
* not to text).
|
|
2995
|
+
*/
|
|
2996
|
+
const localToOffset = (localCol, localRow) => {
|
|
2997
|
+
const lastVisible = Math.min(visualLines.length - 1, windowStart + visibleCount - 1);
|
|
2998
|
+
const clamped = Math.max(windowStart, Math.min(windowStart + localRow, lastVisible));
|
|
2999
|
+
// Fold prefix (▾) row: the rendered first row is truncated by prefixCols,
|
|
3000
|
+
// so both the column and the wrap budget shift — without the correction
|
|
3001
|
+
// a drag starting on the first row lands prefixCols to the right of the
|
|
3002
|
+
// pointer (consistent with handleValueClick's click mapping). Presses ON the
|
|
3003
|
+
// prefix cells clamp to the row start (drag-from-0, like selecting the
|
|
3004
|
+
// whole first row backwards).
|
|
3005
|
+
const isPrefixRow = !block && clamped === 0 && prefixCols > 0;
|
|
3006
|
+
const col = isPrefixRow ? Math.max(0, localCol - prefixCols) : localCol;
|
|
3007
|
+
const width = isPrefixRow ? inputWidth - prefixCols : inputWidth;
|
|
3008
|
+
if (block) {
|
|
3009
|
+
if (clamped === chipRow)
|
|
3010
|
+
return null;
|
|
3011
|
+
return clamped < chipRow
|
|
3012
|
+
? clickToCursorOffset(head, width, clamped, col)
|
|
3013
|
+
: block.end + clickToCursorOffset(tail, width, clamped - chipRow - 1, col);
|
|
3014
|
+
}
|
|
3015
|
+
return clickToCursorOffset(value, width, clamped, col);
|
|
3016
|
+
};
|
|
3017
|
+
/**
|
|
3018
|
+
* Click-to-position the caret: map a click inside the value box (local
|
|
3019
|
+
* row/column relative to the box) to a UTF-16 cursor offset via the same
|
|
3020
|
+
* grapheme walk the renderer wraps with — exact under CJK widths, wrapped
|
|
3021
|
+
* rows and multi-codepoint clusters. Clicks land on the boundary nearest
|
|
3022
|
+
* the clicked cell (mid-grapheme snaps to its start). With a fold block,
|
|
3023
|
+
* clicks map into the head/tail text (the chip row has its own expand
|
|
3024
|
+
* onClick); without one, the fold prefix's cells fold the whole input.
|
|
3025
|
+
*
|
|
3026
|
+
* The drag protocol resets ink's multi-click chain on every press inside
|
|
3027
|
+
* the value box (it carries onDragStart), so double-click word selection
|
|
3028
|
+
* is self-detected here: two clicks within DOUBLE_CLICK_MS and one cell.
|
|
3029
|
+
* Shift+click extends the selection from its start edge (or the caret)
|
|
3030
|
+
* to the clicked offset; a plain click clears the selection and moves
|
|
3031
|
+
* the caret.
|
|
3032
|
+
*
|
|
3033
|
+
* `colOffset` shifts the local column origin: the expanded editor's
|
|
3034
|
+
* value box starts at the line-number gutter, so its callers subtract
|
|
3035
|
+
* the gutter width (0 for the inline prompt).
|
|
3036
|
+
*/
|
|
3037
|
+
/** A plain click on a `[Image #N]` token: a staged one is reported as the
|
|
3038
|
+
* caret image (the caret was just placed at its start, so the caller
|
|
3039
|
+
* shows the preview even if it was dismissed); a stale one warns.
|
|
3040
|
+
* Offsets come from the input's own click-to-cursor mapping, never from
|
|
3041
|
+
* screen coordinates. */
|
|
3042
|
+
const openStagedImageAt = (offset) => {
|
|
3043
|
+
syncImageGeneration();
|
|
3044
|
+
for (const match of valueRef.current.matchAll(COMPOSER_IMAGE_TOKEN)) {
|
|
3045
|
+
const start = match.index ?? 0;
|
|
3046
|
+
if (start > offset)
|
|
3047
|
+
break;
|
|
3048
|
+
if (offset < start + match[0].length) {
|
|
3049
|
+
const stageId = draftImagesRef.current.get(match[0]);
|
|
3050
|
+
const image = stageId === undefined ? undefined : channel.stagedImage(stageId);
|
|
3051
|
+
if (image === undefined) {
|
|
3052
|
+
// Evicted by the FIFO cap or cleared by a session switch: the
|
|
3053
|
+
// placeholder text will NOT attach an image on submit. A raw
|
|
3054
|
+
// token (never staged) is plain text and gets no notice.
|
|
3055
|
+
if (stageId !== undefined) {
|
|
3056
|
+
channel.notify(t('input-image-token-stale', { token: match[0] }), { color: 'warning', timeoutMs: 5000 });
|
|
3057
|
+
}
|
|
3058
|
+
}
|
|
3059
|
+
else {
|
|
3060
|
+
reportCaretImage('click');
|
|
3061
|
+
}
|
|
3062
|
+
return;
|
|
3063
|
+
}
|
|
3064
|
+
}
|
|
3065
|
+
};
|
|
3066
|
+
const handleValueClick = (e, colOffset = 0) => {
|
|
3067
|
+
const now = Date.now();
|
|
3068
|
+
const modified = e.shift || e.alt || e.ctrl;
|
|
3069
|
+
const isDouble = !modified &&
|
|
3070
|
+
now - lastClickAtRef.current < DOUBLE_CLICK_MS &&
|
|
3071
|
+
Math.abs(e.col - lastClickColRef.current) <= 1 &&
|
|
3072
|
+
Math.abs(e.row - lastClickRowRef.current) <= 1;
|
|
3073
|
+
if (modified) {
|
|
3074
|
+
// Shift+click is range extension, never a word-select click. It also
|
|
3075
|
+
// breaks the local chain so a following plain click cannot complete a
|
|
3076
|
+
// double-click that started under a modifier.
|
|
3077
|
+
lastClickAtRef.current = 0;
|
|
3078
|
+
lastClickColRef.current = -1;
|
|
3079
|
+
lastClickRowRef.current = -1;
|
|
3080
|
+
}
|
|
3081
|
+
else {
|
|
3082
|
+
lastClickAtRef.current = now;
|
|
3083
|
+
lastClickColRef.current = e.col;
|
|
3084
|
+
lastClickRowRef.current = e.row;
|
|
3085
|
+
}
|
|
3086
|
+
const localCol = e.localCol - colOffset;
|
|
3087
|
+
const clickedVisual = windowStart + e.localRow;
|
|
3088
|
+
const clamped = Math.max(0, Math.min(clickedVisual, visualLines.length - 1));
|
|
3089
|
+
if (block) {
|
|
3090
|
+
if (clamped === chipRow)
|
|
3091
|
+
return;
|
|
3092
|
+
const offset = clamped < chipRow
|
|
3093
|
+
? clickToCursorOffset(head, inputWidth, clamped, localCol, isDouble ? 'grapheme-start' : 'nearest')
|
|
3094
|
+
: block.end + clickToCursorOffset(tail, inputWidth, clamped - chipRow - 1, localCol, isDouble ? 'grapheme-start' : 'nearest');
|
|
3095
|
+
if (isDouble) {
|
|
3096
|
+
// Word select stays inside the clicked side: the block is atomic.
|
|
3097
|
+
const side = offset <= block.start;
|
|
3098
|
+
const w = wordSelectionAt(value, offset, side ? 0 : block.end, side ? block.start : value.length);
|
|
3099
|
+
if (w) {
|
|
3100
|
+
updateSelection(w.start, w.end);
|
|
3101
|
+
placeCaret(selectionRef.current?.end ?? w.end, 'end');
|
|
3102
|
+
}
|
|
3103
|
+
return;
|
|
3104
|
+
}
|
|
3105
|
+
if (e.shift) {
|
|
3106
|
+
const base = selectionRef.current ? selectionRef.current.start : cursorRef.current;
|
|
3107
|
+
updateSelection(base, offset);
|
|
3108
|
+
placeCaret(offset, 'nearest');
|
|
3109
|
+
return;
|
|
3110
|
+
}
|
|
3111
|
+
clearSelection();
|
|
3112
|
+
// A click on a staged token puts the caret at its start: the token is
|
|
3113
|
+
// the caret cluster, so it highlights whole.
|
|
3114
|
+
placeCaret(offset, 'start');
|
|
3115
|
+
openStagedImageAt(offset);
|
|
3116
|
+
return;
|
|
3117
|
+
}
|
|
3118
|
+
if (clamped === 0 && prefixCols > 0 && localCol < prefixCols) {
|
|
3119
|
+
// Folding hides the entire editable projection, so no selection may
|
|
3120
|
+
// survive invisibly inside the chip and keep owning Ctrl+C/Delete.
|
|
3121
|
+
clearSelection();
|
|
3122
|
+
dragAnchorRef.current = null;
|
|
3123
|
+
setCursor(value.length);
|
|
3124
|
+
updateFoldBlock({ start: 0, end: value.length });
|
|
3125
|
+
return;
|
|
3126
|
+
}
|
|
3127
|
+
const col = clamped === 0 && prefixCols > 0 ? Math.max(0, localCol - prefixCols) : localCol;
|
|
3128
|
+
const offset = clickToCursorOffset(value, clamped === 0 && prefixCols > 0 ? inputWidth - prefixCols : inputWidth, clamped, col, isDouble ? 'grapheme-start' : 'nearest');
|
|
3129
|
+
if (isDouble) {
|
|
3130
|
+
const w = wordSelectionAt(value, offset, 0, value.length);
|
|
3131
|
+
if (w) {
|
|
3132
|
+
updateSelection(w.start, w.end);
|
|
3133
|
+
placeCaret(selectionRef.current?.end ?? w.end, 'end');
|
|
3134
|
+
}
|
|
3135
|
+
return;
|
|
3136
|
+
}
|
|
3137
|
+
if (e.shift) {
|
|
3138
|
+
const base = selectionRef.current ? selectionRef.current.start : cursorRef.current;
|
|
3139
|
+
updateSelection(base, offset);
|
|
3140
|
+
placeCaret(offset, 'nearest');
|
|
3141
|
+
return;
|
|
3142
|
+
}
|
|
3143
|
+
clearSelection();
|
|
3144
|
+
placeCaret(offset, 'start');
|
|
3145
|
+
openStagedImageAt(offset);
|
|
3146
|
+
};
|
|
3147
|
+
/**
|
|
3148
|
+
* Drag selection (component-level drag protocol): the press origin is
|
|
3149
|
+
* derived from the event's start/current delta (dragstart fires on the
|
|
3150
|
+
* FIRST motion), the focus follows every dragmove. The caret rides the
|
|
3151
|
+
* focus edge; updateSelection clamps both ends into one fold side, so a
|
|
3152
|
+
* drag can never cross the chip row. `colOffset` as in handleValueClick.
|
|
3153
|
+
*/
|
|
3154
|
+
const handleDragStart = (e, colOffset = 0) => {
|
|
3155
|
+
const anchorLocalCol = e.localCol - (e.col - e.startCol) - colOffset;
|
|
3156
|
+
const anchorLocalRow = e.localRow - (e.row - e.startRow);
|
|
3157
|
+
const anchor = localToOffset(anchorLocalCol, anchorLocalRow);
|
|
3158
|
+
if (anchor === null) {
|
|
3159
|
+
dragAnchorRef.current = null;
|
|
3160
|
+
return;
|
|
3161
|
+
}
|
|
3162
|
+
dragAnchorRef.current = anchor;
|
|
3163
|
+
placeCaret(anchor, 'nearest');
|
|
3164
|
+
};
|
|
3165
|
+
const handleDragMove = (e, colOffset = 0) => {
|
|
3166
|
+
const anchor = dragAnchorRef.current;
|
|
3167
|
+
if (anchor === null)
|
|
3168
|
+
return;
|
|
3169
|
+
const focus = localToOffset(e.localCol - colOffset, e.localRow);
|
|
3170
|
+
if (focus === null)
|
|
3171
|
+
return;
|
|
3172
|
+
// The caret rides the focus edge, clamped into the anchor's fold side
|
|
3173
|
+
// exactly like updateSelection clamps the range — the caret must never
|
|
3174
|
+
// jump across the chip row while the selection stays behind.
|
|
3175
|
+
let caret = focus;
|
|
3176
|
+
const block = foldBlockRef.current;
|
|
3177
|
+
if (block) {
|
|
3178
|
+
caret = anchor <= block.start ? Math.min(focus, block.start) : Math.max(focus, block.end);
|
|
3179
|
+
}
|
|
3180
|
+
updateSelection(anchor, focus);
|
|
3181
|
+
placeCaret(normalizeCursorOffset(valueRef.current, caret), 'nearest');
|
|
3182
|
+
};
|
|
3183
|
+
const handleDragEnd = () => {
|
|
3184
|
+
dragAnchorRef.current = null;
|
|
3185
|
+
};
|
|
3186
|
+
// 浮层整体挂载条件:与内部面板可见条件精确同值。关闭时必须把整个
|
|
3187
|
+
// absolute 浮层移除——渲染器的 absolute-removed 检测只看被移除节点自身
|
|
3188
|
+
// 的 style.position,常驻浮层 + 移除普通子节点不会触发 blit 解毒,被
|
|
3189
|
+
// 覆盖的转录行会留空(见 Chat.tsx dialogOverlayOpen 注释)。展开态由
|
|
3190
|
+
// 全屏编辑器接管,内联浮层全部撤下。
|
|
3191
|
+
const floatersOpen = !suspended &&
|
|
3192
|
+
!expanded &&
|
|
3193
|
+
(helpOpen || channel.pending.length > 0 || fileOverlayOpen || overlayOpen || peekOpen);
|
|
3194
|
+
// 顶边框右侧的会话名标签 chip:色随强调色;超宽截断,宽度
|
|
3195
|
+
// 随终端列数伸缩但不超过 28 显示单元。默认关闭——`/settings` 的
|
|
3196
|
+
// 「会话名标签」开关(dsh-tui.promptSessionLabel)开启后显示。
|
|
3197
|
+
const sessionTitle = channel.sessionTitle ?? '';
|
|
3198
|
+
const topRightLabel = channel.promptSessionLabel === true && sessionTitle !== ''
|
|
3199
|
+
? {
|
|
3200
|
+
text: truncateToWidth(sessionTitle, Math.max(8, Math.min(28, columns - 8))),
|
|
3201
|
+
color: channel.mode.plan === true ? 'planMode' : (sessionAccent ?? 'accent'),
|
|
3202
|
+
ink: 'inverseText',
|
|
3203
|
+
}
|
|
3204
|
+
: undefined;
|
|
3205
|
+
// 浮层最佳路径(/ 命令卡、@ 文件卡、帮助/队列)经渲染器 absolute-overlay
|
|
3206
|
+
// 机制覆盖转录尾部与状态行。若覆盖期间上方兄弟重绘(spinner 滴答、流式
|
|
3207
|
+
// 文本),覆盖单元格会混入 prevScreen 的旧转录内容——"重叠变花"的根因
|
|
3208
|
+
// (渲染器注释描述的同族问题)。Chat.tsx 对其浮层/高度切换都做视口重锚
|
|
3209
|
+
// (Ctrl+O、loaded-context);此处为斜杠/文件浮层的开关补上同样的恢复:
|
|
3210
|
+
// 打开时保证卡片整块重绘、关闭时保证被覆盖行回到干净的转录内容,而非与
|
|
3211
|
+
// scrollback 失同步后残留花屏。
|
|
3212
|
+
const prevFloatersOpenRef = React.useRef(floatersOpen);
|
|
3213
|
+
React.useLayoutEffect(() => {
|
|
3214
|
+
if (floatersOpen === prevFloatersOpenRef.current)
|
|
3215
|
+
return;
|
|
3216
|
+
prevFloatersOpenRef.current = floatersOpen;
|
|
3217
|
+
const ink = instances.get(process.stdout) ?? instances.values().next().value;
|
|
3218
|
+
ink?.invalidatePrevFrame();
|
|
3219
|
+
ink?.reanchorViewport();
|
|
3220
|
+
}, [floatersOpen]);
|
|
3221
|
+
// 全屏编辑器的真实可见性同时受 expanded 与 prompt-slot suspension
|
|
3222
|
+
// 控制。对话框临时接管时撤下、关闭后恢复,和手动展开/收起一样都必须
|
|
3223
|
+
// 重锚 inline 视口;只在一次真正的 false→true 展开时归零滚动位置,
|
|
3224
|
+
// suspension 往返保留用户浏览到的行。
|
|
3225
|
+
const editorVisible = expanded && !suspended;
|
|
3226
|
+
const prevEditorVisibleRef = React.useRef(editorVisible);
|
|
3227
|
+
React.useLayoutEffect(() => {
|
|
3228
|
+
if (expanded && !prevExpandedRef.current)
|
|
3229
|
+
expandedScrollRef.current = 0;
|
|
3230
|
+
prevExpandedRef.current = expanded;
|
|
3231
|
+
if (editorVisible === prevEditorVisibleRef.current)
|
|
3232
|
+
return;
|
|
3233
|
+
prevEditorVisibleRef.current = editorVisible;
|
|
3234
|
+
const ink = instances.get(process.stdout) ?? instances.values().next().value;
|
|
3235
|
+
ink?.invalidatePrevFrame();
|
|
3236
|
+
ink?.reanchorViewport();
|
|
3237
|
+
}, [editorVisible, expanded]);
|
|
3238
|
+
// Feature turned off mid-session while the editor is up: withdraw the
|
|
3239
|
+
// cover (the draft and every other editing state survive).
|
|
3240
|
+
React.useEffect(() => {
|
|
3241
|
+
if (expanded && !expandEnabled)
|
|
3242
|
+
collapseEditor();
|
|
3243
|
+
}, [expanded, expandEnabled]);
|
|
3244
|
+
// ── 全屏草稿编辑节点 ────────────────────────────────────────────────
|
|
3245
|
+
// 每次渲染构造新鲜闭包(value/caret/handlers),经 module store 发布给
|
|
3246
|
+
// Chat 根部末尾的 PromptEditorLayer(树序最后 → 盖住全部后绘兄弟)。
|
|
3247
|
+
// useInsertionEffect 发布:sink 的同步重渲染发生在 layout 阶段之前,
|
|
3248
|
+
// useDeclaredCursor(layout effect)读到的新 ref 已指向编辑区 Box。
|
|
3249
|
+
// 点击/拖拽坐标以内容区为原点(localCol 去掉行号槽宽度)。
|
|
3250
|
+
const editorNode = editorVisible ? (_jsxs(Box, { flexDirection: "column", width: "100%", height: "100%", borderStyle: "round", borderColor: promptAccent, backgroundColor: "toolCardBackground", children: [_jsxs(Box, { flexDirection: "row", flexShrink: 0, paddingLeft: 1, paddingRight: 1, children: [_jsx(Text, { bold: true, color: promptAccent, children: `✎ ${t('input-expand-editor-title')}` }), _jsx(Box, { flexGrow: 1 }), _jsx(Text, { dimColor: true, children: t('input-fold-stats', {
|
|
3251
|
+
lines: value.split('\n').length,
|
|
3252
|
+
chars: value.length,
|
|
3253
|
+
}) })] }), _jsx(Box, { ref: valueBoxRef, flexDirection: "column", flexGrow: 1, flexShrink: 1, paddingLeft: 1, paddingRight: 1, onClick: (event) => {
|
|
3254
|
+
handleValueClick(event, editorGutterCols + 1);
|
|
3255
|
+
}, onDragStart: (event) => {
|
|
3256
|
+
handleDragStart(event, editorGutterCols + 1);
|
|
3257
|
+
}, onDragMove: (event) => {
|
|
3258
|
+
handleDragMove(event, editorGutterCols + 1);
|
|
3259
|
+
}, onDragEnd: handleDragEnd, onWheel: (event) => {
|
|
3260
|
+
const viewport = editorViewportRef.current;
|
|
3261
|
+
if (!viewport)
|
|
3262
|
+
return;
|
|
3263
|
+
editorFreeScrollRef.current = true;
|
|
3264
|
+
const max = Math.max(0, viewport.total - viewport.maxRows);
|
|
3265
|
+
expandedScrollRef.current = Math.max(0, Math.min(expandedScrollRef.current + Math.round(event.deltaY), max));
|
|
3266
|
+
setExpandedTick(tick => tick + 1);
|
|
3267
|
+
}, children: editorRows }), _jsxs(Box, { flexDirection: "row", flexShrink: 0, paddingLeft: 1, paddingRight: 1, children: [vimEnabled && (_jsxs(Text, { bold: true, color: vimInsert ? 'success' : 'warning', children: [vimInsert ? 'INSERT' : 'NORMAL', ' '] })), _jsx(Text, { dimColor: true, children: t('input-expand-editor-position', {
|
|
3268
|
+
line: cursorLine(value, cursor) + 1,
|
|
3269
|
+
col: cursorColumn(value, cursor) + 1,
|
|
3270
|
+
}) }), _jsx(Box, { flexGrow: 1 }), _jsx(Text, { dimColor: true, children: `${t('input-expand-editor-hint-send')} · ${t('input-expand-editor-hint-collapse')}` })] }), _jsxs(Box, { flexDirection: "row", flexShrink: 0, paddingLeft: 1, paddingRight: 1, columnGap: 1, children: [_jsx(EditorButton, { label: `⏎ ${t('input-expand-editor-send')}`, hint: "Ctrl+Enter", primary: true, accent: promptAccent, onClick: submitFromEditor }), _jsx(EditorButton, { label: t('input-expand-editor-collapse'), hint: "Esc", onClick: collapseEditor }), _jsx(Box, { flexGrow: 1 }), _jsx(Text, { dimColor: true, children: t('input-expand-editor-scroll') })] })] })) : null;
|
|
3271
|
+
React.useInsertionEffect(() => {
|
|
3272
|
+
setPromptEditorNode(editorNode);
|
|
3273
|
+
});
|
|
3274
|
+
React.useEffect(() => {
|
|
3275
|
+
// 卸载保险:PromptInput 被替换(问卷/面板接管)时撤下全屏浮层。
|
|
3276
|
+
return () => {
|
|
3277
|
+
setPromptEditorNode(null);
|
|
3278
|
+
};
|
|
3279
|
+
}, []);
|
|
3280
|
+
// Approval, question and managed-dialog panels temporarily own Chat's
|
|
3281
|
+
// prompt slot. Stay mounted so an async command can settle without losing
|
|
3282
|
+
// its exact text/image draft, while contributing no layout, input, cursor,
|
|
3283
|
+
// editor layer, or external injection controller.
|
|
3284
|
+
if (suspended)
|
|
3285
|
+
return null;
|
|
3286
|
+
return (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [floatersOpen && (_jsxs(OverlayAbove, { maxHeight: Math.max(terminalRows - 6, 1), children: [helpOpen && (_jsx(Box, { marginBottom: 1, children: _jsx(HelpMenu, { commands: channel.commandList, viewportHeight: helpViewportHeight, viewportWidth: columns, scrollRef: helpScrollRef, onCommandPick: (name) => {
|
|
3287
|
+
// 点击命令行 = 填入 /name 并关闭帮助(Tab 补全的鼠标等价)
|
|
3288
|
+
updateFoldBlock(null);
|
|
3289
|
+
setInput(`/${name} `);
|
|
3290
|
+
onToggleHelp();
|
|
3291
|
+
} }) })), !helpOpen && channel.pending.length > 0 && (_jsxs(Box, { flexDirection: "column", paddingLeft: 2, paddingBottom: 1, children: [channel.pending.some(item => item.placement === 'steer') && (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { dimColor: true, children: ["\u26A1 ", t('input-pending-steer-label')] }), channel.pending
|
|
3292
|
+
.filter(item => item.placement === 'steer')
|
|
3293
|
+
.map(item => (_jsxs(Text, { dimColor: true, wrap: "truncate", children: [' ', "\u21B3 ", item.text] }, item.id)))] })), channel.pending.some(item => item.placement === 'followup') && (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { dimColor: true, children: ["\u23F3 ", t('input-pending-queue-label')] }), channel.pending
|
|
3294
|
+
.filter(item => item.placement === 'followup')
|
|
3295
|
+
.map(item => (_jsxs(Text, { dimColor: true, wrap: "truncate", children: [' ', "\u21B3 ", item.text] }, item.id)))] })), _jsxs(Text, { dimColor: true, children: ["Alt+\u2191 ", t('input-pending-actions-hint')] })] })), fileOverlayOpen && (_jsx(FileSuggestions, { files: fileMatches, selectedIndex: fileSelected, columns: columns, query: mention?.query ?? '', accent: promptAccent,
|
|
3296
|
+
// 点击行 = 接受该项(与 Enter 同路径)
|
|
3297
|
+
onPick: (index) => {
|
|
3298
|
+
const file = fileMatches[index];
|
|
3299
|
+
if (file)
|
|
3300
|
+
acceptFile(file);
|
|
3301
|
+
},
|
|
3302
|
+
// 滚轮 = 移动选中行(与 ↑/↓ 同路径,窗口跟随)
|
|
3303
|
+
onWheelStep: (step) => {
|
|
3304
|
+
setFileSelected(i => Math.max(0, Math.min(fileMatches.length - 1, i + step)));
|
|
3305
|
+
} })), overlayOpen && (_jsx(CommandSuggestions, { commands: suggestions, selectedIndex: selectedCommand, columns: columns, query: value, accent: promptAccent,
|
|
3306
|
+
// 点击行 = 运行该命令(与 Enter 同路径)
|
|
3307
|
+
onPick: (index) => {
|
|
3308
|
+
const command = suggestions[index];
|
|
3309
|
+
if (command)
|
|
3310
|
+
tryRunCommand(command.commandLine);
|
|
3311
|
+
},
|
|
3312
|
+
// 滚轮 = 移动选中行(与 ↑/↓ 同路径,窗口跟随)
|
|
3313
|
+
onWheelStep: (step) => {
|
|
3314
|
+
setSelectedCommand(i => Math.max(0, Math.min(suggestions.length - 1, i + step)));
|
|
3315
|
+
} })), peekOpen && (_jsx(Box, { onMouseEnter: hoverEnter, onMouseLeave: hoverLeave, children: _jsx(SuggestionCard, { title: stats, columns: columns, accent: promptAccent, footer: peekClipped
|
|
3316
|
+
? t('input-fold-peek-footer', { lines: foldText.split('\n').length })
|
|
3317
|
+
: undefined, rows: peekVisualLines.map((row, index) => (_jsx(Text, { wrap: "truncate-end", children: row }, index))), onRowPick: () => {
|
|
3318
|
+
updateFoldBlock(null);
|
|
3319
|
+
setHovered(false);
|
|
3320
|
+
} }) }))] })), lastNotification && (_jsx(Box, { position: "absolute", marginTop: -1, height: 1, width: "100%", paddingLeft: 2, paddingRight: 1, flexDirection: "column", justifyContent: "flex-end", overflow: "hidden", children: _jsx(Box, { justifyContent: "flex-end", children: _jsx(Text, { color: lastNotification.color, dimColor: !lastNotification.color, wrap: "truncate", children: lastNotification.text }) }) })), _jsx(EffortInputBorder, { effort: channel.reasoningEffort, levels: channel.effortLevels, columns: columns, onLight: isLightThemeActive(themeName), idleColor: promptAccent, topRightLabel: topRightLabel, children: _jsxs(Box, { flexDirection: "row", alignItems: "flex-start", width: "100%", children: [onOpenSessions !== undefined && (_jsx(Box, { flexShrink: 0, onClick: (event) => {
|
|
3321
|
+
event.stopImmediatePropagation();
|
|
3322
|
+
onOpenSessions?.();
|
|
3323
|
+
}, onMouseEnter: () => {
|
|
3324
|
+
setHomeHovered(true);
|
|
3325
|
+
}, onMouseLeave: () => {
|
|
3326
|
+
setHomeHovered(false);
|
|
3327
|
+
}, children: _jsx(Text, { dimColor: !homeHovered, bold: homeHovered, color: homeHovered ? promptAccent : undefined, children: HOME_BUTTON_TEXT }) })), _jsx(EffortChargeGlyph, { effort: channel.reasoningEffort, levels: channel.effortLevels, working: channel.working }), vimEnabled && (_jsxs(Text, { bold: true, color: vimInsert ? 'success' : 'warning', children: [vimInsert ? 'INSERT' : 'NORMAL', " "] })), _jsx(Box, { ref: expanded ? undefined : valueBoxRef, flexGrow: 1, flexShrink: 1, onClick: handleValueClick, onDragStart: handleDragStart, onDragMove: handleDragMove, onDragEnd: handleDragEnd, children: value.length === 0 ? (_jsxs(_Fragment, { children: [_jsx(Text, { inverse: true, children: " " }), _jsx(EffortTierBadge, { effort: channel.reasoningEffort, levels: channel.effortLevels, onLight: isLightThemeActive(themeName), columns: columns, leadingColumns: 3 + homeButtonCols })] })) : (_jsx(Box, { flexDirection: "column", children: rendered })) }), expandEnabled && (_jsx(Box, { flexShrink: 0, onClick: (event) => {
|
|
3328
|
+
event.stopImmediatePropagation();
|
|
3329
|
+
toggleExpand();
|
|
3330
|
+
}, onMouseEnter: () => {
|
|
3331
|
+
setExpandHovered(true);
|
|
3332
|
+
}, onMouseLeave: () => {
|
|
3333
|
+
setExpandHovered(false);
|
|
3334
|
+
}, children: _jsx(Text, { dimColor: !expandHovered, bold: expandHovered, color: expandHovered ? promptAccent : undefined, children: "\u26F6" }) }))] }) }), backgroundAgentsNeedingInput !== undefined && (_jsx(Box, { flexDirection: "row", justifyContent: "flex-end", paddingRight: 2, children: _jsx(Text, { dimColor: true, children: backgroundAgentsNeedingInput > 0
|
|
3335
|
+
? t('input-background-hint-count', { n: backgroundAgentsNeedingInput })
|
|
3336
|
+
: t('input-background-hint-idle') }) }))] }));
|
|
3337
|
+
}
|
|
3338
|
+
/**
|
|
3339
|
+
* Grapheme word-wrap for one logical line: break at the last space when
|
|
3340
|
+
* the row overflows; hard-wrap a word that cannot fit; never split a
|
|
3341
|
+
* cluster (ZWJ emoji, combining sequences, CJK wide cells stay whole).
|
|
3342
|
+
*/
|
|
3343
|
+
function wrapLineRows(line, width) {
|
|
3344
|
+
if (line === '')
|
|
3345
|
+
return [{ start: 0, end: 0 }];
|
|
3346
|
+
const rows = [];
|
|
3347
|
+
const segmenter = getGraphemeSegmenter();
|
|
3348
|
+
// The space inside `[Image #N]` is not a break opportunity: the token is
|
|
3349
|
+
// one unit on screen (its caret cluster and chip styling span it), so it
|
|
3350
|
+
// wraps whole, like a word.
|
|
3351
|
+
const unbreakable = imageTokenSpans(line);
|
|
3352
|
+
let rowStart = 0;
|
|
3353
|
+
let currentWidth = 0;
|
|
3354
|
+
let offset = 0;
|
|
3355
|
+
let lastBreak = -1;
|
|
3356
|
+
for (const { segment } of segmenter.segment(line)) {
|
|
3357
|
+
const w = stringWidth(segment);
|
|
3358
|
+
while (currentWidth + w > width && offset > rowStart) {
|
|
3359
|
+
if (lastBreak > rowStart) {
|
|
3360
|
+
rows.push({ start: rowStart, end: lastBreak });
|
|
3361
|
+
rowStart = lastBreak;
|
|
3362
|
+
currentWidth = stringWidth(line.slice(rowStart, offset));
|
|
3363
|
+
lastBreak = -1;
|
|
3364
|
+
}
|
|
3365
|
+
else {
|
|
3366
|
+
rows.push({ start: rowStart, end: offset });
|
|
3367
|
+
rowStart = offset;
|
|
3368
|
+
currentWidth = 0;
|
|
3369
|
+
lastBreak = -1;
|
|
3370
|
+
}
|
|
3371
|
+
}
|
|
3372
|
+
currentWidth += w;
|
|
3373
|
+
offset += segment.length;
|
|
3374
|
+
if (segment === ' ' && imageTokenAround(unbreakable, offset) === undefined)
|
|
3375
|
+
lastBreak = offset;
|
|
3376
|
+
}
|
|
3377
|
+
rows.push({ start: rowStart, end: offset });
|
|
3378
|
+
return rows;
|
|
3379
|
+
}
|
|
3380
|
+
/** Wrap text to `width` columns via {@link wrapLineRows} (newlines honoured). */
|
|
3381
|
+
function wrapToWidth(text, width) {
|
|
3382
|
+
return text.split('\n').flatMap(line => wrapLineRows(line, width).map(r => line.slice(r.start, r.end)));
|
|
3383
|
+
}
|
|
3384
|
+
/**
|
|
3385
|
+
* Locate `offset` in the wrapped layout of `text`. Uses the full wrap —
|
|
3386
|
+
* not a prefix — so a cursor inside a carried word maps to the displayed
|
|
3387
|
+
* row. At a wrap join the caret stays on the earlier row (after the space).
|
|
3388
|
+
*/
|
|
3389
|
+
function caretInText(text, width, offset) {
|
|
3390
|
+
let line = 0;
|
|
3391
|
+
let lineBase = 0;
|
|
3392
|
+
let last = { line: 0, charCol: 0, visualCol: 0 };
|
|
3393
|
+
for (const logical of text.split('\n')) {
|
|
3394
|
+
for (const r of wrapLineRows(logical, width)) {
|
|
3395
|
+
const start = lineBase + r.start;
|
|
3396
|
+
const charCol = Math.max(0, offset - start);
|
|
3397
|
+
last = {
|
|
3398
|
+
line,
|
|
3399
|
+
charCol,
|
|
3400
|
+
visualCol: stringWidth(logical.slice(r.start, r.start + charCol)),
|
|
3401
|
+
};
|
|
3402
|
+
if (offset <= lineBase + r.end)
|
|
3403
|
+
return last;
|
|
3404
|
+
line++;
|
|
3405
|
+
}
|
|
3406
|
+
lineBase += logical.length + 1;
|
|
3407
|
+
}
|
|
3408
|
+
return last;
|
|
3409
|
+
}
|
|
3410
|
+
/**
|
|
3411
|
+
* Inverse of {@link wrapToWidth}: map a click position (visual row index +
|
|
3412
|
+
* visual column) back to a UTF-16 offset in the original text. Uses the
|
|
3413
|
+
* same {@link wrapLineRows} row boundaries, so every visual row's start
|
|
3414
|
+
* offset is known exactly. Within the clicked row, the caret snaps to the
|
|
3415
|
+
* boundary nearest the click: a grapheme whose midpoint lies past the
|
|
3416
|
+
* click column takes the caret before it, otherwise after.
|
|
3417
|
+
*/
|
|
3418
|
+
function clickToCursorOffset(text, width, visualLine, visualCol, snapWithin = 'nearest') {
|
|
3419
|
+
const segmenter = getGraphemeSegmenter();
|
|
3420
|
+
let row = 0;
|
|
3421
|
+
let lineBase = 0;
|
|
3422
|
+
for (const line of text.split('\n')) {
|
|
3423
|
+
for (const r of wrapLineRows(line, width)) {
|
|
3424
|
+
if (row === visualLine) {
|
|
3425
|
+
let currentWidth = 0;
|
|
3426
|
+
let local = 0;
|
|
3427
|
+
for (const { segment } of segmenter.segment(line.slice(r.start, r.end))) {
|
|
3428
|
+
const w = stringWidth(segment);
|
|
3429
|
+
// Caret positioning uses the nearest edge. Double-click selection
|
|
3430
|
+
// instead needs the grapheme UNDER the cell: on the second cell of a
|
|
3431
|
+
// CJK/emoji glyph, nearest-edge would point after the glyph (and the
|
|
3432
|
+
// final glyph would select nothing).
|
|
3433
|
+
if (snapWithin === 'grapheme-start' && currentWidth + w > visualCol) {
|
|
3434
|
+
return lineBase + r.start + local;
|
|
3435
|
+
}
|
|
3436
|
+
if (currentWidth + w / 2 > visualCol)
|
|
3437
|
+
return lineBase + r.start + local;
|
|
3438
|
+
if (currentWidth + w > visualCol)
|
|
3439
|
+
return lineBase + r.start + local + segment.length;
|
|
3440
|
+
currentWidth += w;
|
|
3441
|
+
local += segment.length;
|
|
3442
|
+
}
|
|
3443
|
+
return lineBase + r.end;
|
|
3444
|
+
}
|
|
3445
|
+
row++;
|
|
3446
|
+
}
|
|
3447
|
+
lineBase += line.length + 1;
|
|
3448
|
+
}
|
|
3449
|
+
return lineBase;
|
|
3450
|
+
}
|
|
3451
|
+
/**
|
|
3452
|
+
* Offset range [start, end) of every visual row, using the same {@link
|
|
3453
|
+
* wrapLineRows} boundaries as `wrapToWidth`. The selection highlight
|
|
3454
|
+
* intersects each row with these ranges so offsets map 1:1 onto the
|
|
3455
|
+
* rendered row strings.
|
|
3456
|
+
*/
|
|
3457
|
+
function visualLineRanges(text, width) {
|
|
3458
|
+
const ranges = [];
|
|
3459
|
+
let lineBase = 0;
|
|
3460
|
+
for (const line of text.split('\n')) {
|
|
3461
|
+
for (const r of wrapLineRows(line, width))
|
|
3462
|
+
ranges.push([lineBase + r.start, lineBase + r.end]);
|
|
3463
|
+
lineBase += line.length + 1;
|
|
3464
|
+
}
|
|
3465
|
+
return ranges;
|
|
3466
|
+
}
|
|
3467
|
+
/** Word characters for double-click selection: letters (any script),
|
|
3468
|
+
* digits and the punctuation set terminal emulators treat as word-part by
|
|
3469
|
+
* default (`/usr/bin/bash` selects whole — iTerm2 defaults). */
|
|
3470
|
+
const WORD_CHAR = /[\p{L}\p{N}_/.\-+~\\]/u;
|
|
3471
|
+
/** Character class for double-click word expansion: whitespace, word
|
|
3472
|
+
* char, everything else (punctuation/symbols). A same-class grapheme
|
|
3473
|
+
* run is one word. */
|
|
3474
|
+
function selectionCharClass(c) {
|
|
3475
|
+
if (c === '' || /\s/.test(c))
|
|
3476
|
+
return 0;
|
|
3477
|
+
if (WORD_CHAR.test(c))
|
|
3478
|
+
return 1;
|
|
3479
|
+
return 2;
|
|
3480
|
+
}
|
|
3481
|
+
/**
|
|
3482
|
+
* Word selection around `offset` within [lo, hi): expand left/right over
|
|
3483
|
+
* grapheme clusters of the same class as the clicked one (terminal
|
|
3484
|
+
* double-click semantics — punctuation runs select as one). Returns null
|
|
3485
|
+
* when the offset sits at the range's end (nothing to select).
|
|
3486
|
+
*/
|
|
3487
|
+
function wordSelectionAt(text, offset, lo, hi) {
|
|
3488
|
+
if (offset < lo || offset >= hi)
|
|
3489
|
+
return null;
|
|
3490
|
+
const bounds = graphemeBoundaries(text.slice(lo, hi)).map(b => b + lo);
|
|
3491
|
+
let i = 0;
|
|
3492
|
+
while (i < bounds.length - 1 && bounds[i + 1] <= offset)
|
|
3493
|
+
i++;
|
|
3494
|
+
const clusterAt = (index) => text.slice(bounds[index], bounds[index + 1]);
|
|
3495
|
+
const cls = selectionCharClass(clusterAt(i));
|
|
3496
|
+
let a = i;
|
|
3497
|
+
while (a > 0 && selectionCharClass(clusterAt(a - 1)) === cls)
|
|
3498
|
+
a--;
|
|
3499
|
+
let b = i;
|
|
3500
|
+
while (b < bounds.length - 2 && selectionCharClass(clusterAt(b + 1)) === cls)
|
|
3501
|
+
b++;
|
|
3502
|
+
return { start: bounds[a], end: bounds[b + 1] };
|
|
3503
|
+
}
|