@code-yeongyu/senpi 2026.6.4 → 2026.6.10-2
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/CHANGELOG.md +69 -1
- package/README.md +116 -99
- package/dist/bun/cli.d.ts.map +1 -1
- package/dist/bun/cli.js.map +1 -1
- package/dist/bun/register-bedrock.d.ts.map +1 -1
- package/dist/bun/restore-sandbox-env.d.ts.map +1 -1
- package/dist/bun/restore-sandbox-env.js.map +1 -1
- package/dist/cli/args.d.ts +1 -0
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +13 -4
- package/dist/cli/args.js.map +1 -1
- package/dist/cli/config-selector.d.ts.map +1 -1
- package/dist/cli/config-selector.js.map +1 -1
- package/dist/cli/file-processor.d.ts.map +1 -1
- package/dist/cli/file-processor.js.map +1 -1
- package/dist/cli/initial-message.d.ts +1 -1
- package/dist/cli/initial-message.d.ts.map +1 -1
- package/dist/cli/initial-message.js.map +1 -1
- package/dist/cli/list-models.d.ts.map +1 -1
- package/dist/cli/list-models.js.map +1 -1
- package/dist/cli/project-trust.d.ts +10 -0
- package/dist/cli/project-trust.d.ts.map +1 -0
- package/dist/cli/project-trust.js +48 -0
- package/dist/cli/project-trust.js.map +1 -0
- package/dist/cli/session-picker.d.ts.map +1 -1
- package/dist/cli/session-picker.js.map +1 -1
- package/dist/cli/startup-ui.d.ts +7 -0
- package/dist/cli/startup-ui.d.ts.map +1 -0
- package/dist/cli/startup-ui.js +59 -0
- package/dist/cli/startup-ui.js.map +1 -0
- package/dist/cli-main.d.ts.map +1 -1
- package/dist/cli-main.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +10 -1
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js.map +1 -1
- package/dist/core/agent-session-runtime.d.ts +3 -1
- package/dist/core/agent-session-runtime.d.ts.map +1 -1
- package/dist/core/agent-session-runtime.js +4 -9
- package/dist/core/agent-session-runtime.js.map +1 -1
- package/dist/core/agent-session-services.d.ts +2 -1
- package/dist/core/agent-session-services.d.ts.map +1 -1
- package/dist/core/agent-session-services.js +2 -2
- package/dist/core/agent-session-services.js.map +1 -1
- package/dist/core/agent-session.d.ts +33 -0
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +57 -79
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/auth-guidance.d.ts.map +1 -1
- package/dist/core/auth-guidance.js.map +1 -1
- package/dist/core/auth-storage.d.ts +4 -0
- package/dist/core/auth-storage.d.ts.map +1 -1
- package/dist/core/auth-storage.js +4 -8
- package/dist/core/auth-storage.js.map +1 -1
- package/dist/core/bash-executor.d.ts.map +1 -1
- package/dist/core/bash-executor.js.map +1 -1
- package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
- package/dist/core/compaction/branch-summarization.js.map +1 -1
- package/dist/core/compaction/compaction.d.ts.map +1 -1
- package/dist/core/compaction/compaction.js.map +1 -1
- package/dist/core/compaction/index.d.ts.map +1 -1
- package/dist/core/compaction/utils.d.ts +1 -1
- package/dist/core/compaction/utils.d.ts.map +1 -1
- package/dist/core/compaction/utils.js +1 -1
- package/dist/core/compaction/utils.js.map +1 -1
- package/dist/core/defaults.d.ts.map +1 -1
- package/dist/core/diagnostics.d.ts.map +1 -1
- package/dist/core/dynamic-prompt/build.d.ts.map +1 -1
- package/dist/core/dynamic-prompt/build.js.map +1 -1
- package/dist/core/dynamic-prompt/exploration.d.ts.map +1 -1
- package/dist/core/dynamic-prompt/exploration.js.map +1 -1
- package/dist/core/dynamic-prompt/identity.d.ts.map +1 -1
- package/dist/core/dynamic-prompt/identity.js.map +1 -1
- package/dist/core/dynamic-prompt/index.d.ts.map +1 -1
- package/dist/core/dynamic-prompt/intent-gate.d.ts.map +1 -1
- package/dist/core/dynamic-prompt/intent-gate.js.map +1 -1
- package/dist/core/dynamic-prompt/parallel-tools.d.ts.map +1 -1
- package/dist/core/dynamic-prompt/parallel-tools.js.map +1 -1
- package/dist/core/dynamic-prompt/policies.d.ts.map +1 -1
- package/dist/core/dynamic-prompt/policies.js.map +1 -1
- package/dist/core/dynamic-prompt/style.d.ts.map +1 -1
- package/dist/core/dynamic-prompt/style.js.map +1 -1
- package/dist/core/dynamic-prompt/tool-categorization.d.ts.map +1 -1
- package/dist/core/dynamic-prompt/tool-categorization.js.map +1 -1
- package/dist/core/dynamic-prompt/tool-section.d.ts.map +1 -1
- package/dist/core/dynamic-prompt/tool-section.js.map +1 -1
- package/dist/core/dynamic-prompt/types.d.ts.map +1 -1
- package/dist/core/dynamic-prompt/verification.d.ts.map +1 -1
- package/dist/core/dynamic-prompt/verification.js.map +1 -1
- package/dist/core/event-bus.d.ts.map +1 -1
- package/dist/core/event-bus.js.map +1 -1
- package/dist/core/exec.d.ts.map +1 -1
- package/dist/core/exec.js.map +1 -1
- package/dist/core/export-html/ansi-to-html.d.ts.map +1 -1
- package/dist/core/export-html/ansi-to-html.js.map +1 -1
- package/dist/core/export-html/index.d.ts.map +1 -1
- package/dist/core/export-html/index.js.map +1 -1
- package/dist/core/export-html/tool-renderer.d.ts.map +1 -1
- package/dist/core/export-html/tool-renderer.js.map +1 -1
- package/dist/core/extensions/builtin/anthropic-bash/index.d.ts.map +1 -1
- package/dist/core/extensions/builtin/anthropic-bash/index.js.map +1 -1
- package/dist/core/extensions/builtin/anthropic-web-search/index.d.ts.map +1 -1
- package/dist/core/extensions/builtin/anthropic-web-search/index.js.map +1 -1
- package/dist/core/extensions/builtin/bash-timeout/index.d.ts.map +1 -1
- package/dist/core/extensions/builtin/bash-timeout/index.js.map +1 -1
- package/dist/core/extensions/builtin/bash-timeout/timeout.d.ts.map +1 -1
- package/dist/core/extensions/builtin/bash-timeout/timeout.js.map +1 -1
- package/dist/core/extensions/builtin/compaction/checkpoint-state.d.ts.map +1 -1
- package/dist/core/extensions/builtin/compaction/checkpoint-state.js.map +1 -1
- package/dist/core/extensions/builtin/compaction/circuit-breaker.d.ts.map +1 -1
- package/dist/core/extensions/builtin/compaction/circuit-breaker.js.map +1 -1
- package/dist/core/extensions/builtin/compaction/context-reduction.d.ts.map +1 -1
- package/dist/core/extensions/builtin/compaction/context-reduction.js +1 -1
- package/dist/core/extensions/builtin/compaction/context-reduction.js.map +1 -1
- package/dist/core/extensions/builtin/compaction/degradation-monitor.d.ts +0 -1
- package/dist/core/extensions/builtin/compaction/degradation-monitor.d.ts.map +1 -1
- package/dist/core/extensions/builtin/compaction/degradation-monitor.js +0 -1
- package/dist/core/extensions/builtin/compaction/degradation-monitor.js.map +1 -1
- package/dist/core/extensions/builtin/compaction/index.d.ts.map +1 -1
- package/dist/core/extensions/builtin/compaction/index.js +4 -4
- package/dist/core/extensions/builtin/compaction/index.js.map +1 -1
- package/dist/core/extensions/builtin/compaction/openai-remote.d.ts.map +1 -1
- package/dist/core/extensions/builtin/compaction/openai-remote.js.map +1 -1
- package/dist/core/extensions/builtin/compaction/per-turn-cap.d.ts.map +1 -1
- package/dist/core/extensions/builtin/compaction/per-turn-cap.js.map +1 -1
- package/dist/core/extensions/builtin/compaction/policy.d.ts +0 -5
- package/dist/core/extensions/builtin/compaction/policy.d.ts.map +1 -1
- package/dist/core/extensions/builtin/compaction/policy.js +0 -4
- package/dist/core/extensions/builtin/compaction/policy.js.map +1 -1
- package/dist/core/extensions/builtin/compaction/prompts.d.ts.map +1 -1
- package/dist/core/extensions/builtin/compaction/prompts.js.map +1 -1
- package/dist/core/extensions/builtin/compaction/repair-tool-pairs.d.ts.map +1 -1
- package/dist/core/extensions/builtin/compaction/repair-tool-pairs.js.map +1 -1
- package/dist/core/extensions/builtin/compaction/restoration-tracker.d.ts.map +1 -1
- package/dist/core/extensions/builtin/compaction/restoration-tracker.js.map +1 -1
- package/dist/core/extensions/builtin/compaction/speculative.d.ts +0 -6
- package/dist/core/extensions/builtin/compaction/speculative.d.ts.map +1 -1
- package/dist/core/extensions/builtin/compaction/speculative.js +0 -3
- package/dist/core/extensions/builtin/compaction/speculative.js.map +1 -1
- package/dist/core/extensions/builtin/compaction/state.d.ts.map +1 -1
- package/dist/core/extensions/builtin/compaction/state.js.map +1 -1
- package/dist/core/extensions/builtin/compaction/todo-bridge.d.ts.map +1 -1
- package/dist/core/extensions/builtin/compaction/todo-bridge.js.map +1 -1
- package/dist/core/extensions/builtin/compaction/tool-truncation.d.ts.map +1 -1
- package/dist/core/extensions/builtin/compaction/tool-truncation.js.map +1 -1
- package/dist/core/extensions/builtin/diff.d.ts.map +1 -1
- package/dist/core/extensions/builtin/diff.js.map +1 -1
- package/dist/core/extensions/builtin/files.d.ts.map +1 -1
- package/dist/core/extensions/builtin/files.js.map +1 -1
- package/dist/core/extensions/builtin/gpt-apply-patch/apply.d.ts.map +1 -1
- package/dist/core/extensions/builtin/gpt-apply-patch/apply.js.map +1 -1
- package/dist/core/extensions/builtin/gpt-apply-patch/constants.d.ts.map +1 -1
- package/dist/core/extensions/builtin/gpt-apply-patch/constants.js.map +1 -1
- package/dist/core/extensions/builtin/gpt-apply-patch/errors.d.ts.map +1 -1
- package/dist/core/extensions/builtin/gpt-apply-patch/errors.js +0 -2
- package/dist/core/extensions/builtin/gpt-apply-patch/errors.js.map +1 -1
- package/dist/core/extensions/builtin/gpt-apply-patch/extension.d.ts +1 -1
- package/dist/core/extensions/builtin/gpt-apply-patch/extension.d.ts.map +1 -1
- package/dist/core/extensions/builtin/gpt-apply-patch/extension.js +10 -2
- package/dist/core/extensions/builtin/gpt-apply-patch/extension.js.map +1 -1
- package/dist/core/extensions/builtin/gpt-apply-patch/index.d.ts.map +1 -1
- package/dist/core/extensions/builtin/gpt-apply-patch/params.d.ts.map +1 -1
- package/dist/core/extensions/builtin/gpt-apply-patch/params.js.map +1 -1
- package/dist/core/extensions/builtin/gpt-apply-patch/parser.d.ts.map +1 -1
- package/dist/core/extensions/builtin/gpt-apply-patch/parser.js.map +1 -1
- package/dist/core/extensions/builtin/gpt-apply-patch/patch-diff.d.ts.map +1 -1
- package/dist/core/extensions/builtin/gpt-apply-patch/patch-diff.js.map +1 -1
- package/dist/core/extensions/builtin/gpt-apply-patch/patch-replace.d.ts.map +1 -1
- package/dist/core/extensions/builtin/gpt-apply-patch/patch-replace.js.map +1 -1
- package/dist/core/extensions/builtin/gpt-apply-patch/preview-format.d.ts.map +1 -1
- package/dist/core/extensions/builtin/gpt-apply-patch/preview-format.js.map +1 -1
- package/dist/core/extensions/builtin/gpt-apply-patch/preview.d.ts.map +1 -1
- package/dist/core/extensions/builtin/gpt-apply-patch/preview.js.map +1 -1
- package/dist/core/extensions/builtin/gpt-apply-patch/seek-sequence.d.ts.map +1 -1
- package/dist/core/extensions/builtin/gpt-apply-patch/seek-sequence.js.map +1 -1
- package/dist/core/extensions/builtin/gpt-apply-patch/streaming-parser.d.ts.map +1 -1
- package/dist/core/extensions/builtin/gpt-apply-patch/streaming-parser.js +7 -5
- package/dist/core/extensions/builtin/gpt-apply-patch/streaming-parser.js.map +1 -1
- package/dist/core/extensions/builtin/gpt-apply-patch/streaming-render.d.ts.map +1 -1
- package/dist/core/extensions/builtin/gpt-apply-patch/streaming-render.js.map +1 -1
- package/dist/core/extensions/builtin/gpt-apply-patch/text.d.ts.map +1 -1
- package/dist/core/extensions/builtin/gpt-apply-patch/text.js.map +1 -1
- package/dist/core/extensions/builtin/gpt-apply-patch/tool.d.ts.map +1 -1
- package/dist/core/extensions/builtin/gpt-apply-patch/tool.js.map +1 -1
- package/dist/core/extensions/builtin/gpt-apply-patch/types.d.ts.map +1 -1
- package/dist/core/extensions/builtin/gpt-apply-patch/workspace.d.ts.map +1 -1
- package/dist/core/extensions/builtin/gpt-apply-patch/workspace.js.map +1 -1
- package/dist/core/extensions/builtin/history-search/filter.d.ts.map +1 -1
- package/dist/core/extensions/builtin/history-search/filter.js.map +1 -1
- package/dist/core/extensions/builtin/history-search/index.d.ts.map +1 -1
- package/dist/core/extensions/builtin/history-search/index.js.map +1 -1
- package/dist/core/extensions/builtin/history-search/indexer.d.ts.map +1 -1
- package/dist/core/extensions/builtin/history-search/indexer.js.map +1 -1
- package/dist/core/extensions/builtin/history-search/overlay.d.ts.map +1 -1
- package/dist/core/extensions/builtin/history-search/overlay.js +3 -6
- package/dist/core/extensions/builtin/history-search/overlay.js.map +1 -1
- package/dist/core/extensions/builtin/history-search/types.d.ts.map +1 -1
- package/dist/core/extensions/builtin/index.d.ts.map +1 -1
- package/dist/core/extensions/builtin/kimi-web-search/index.d.ts.map +1 -1
- package/dist/core/extensions/builtin/kimi-web-search/index.js.map +1 -1
- package/dist/core/extensions/builtin/openai-web-search/index.d.ts.map +1 -1
- package/dist/core/extensions/builtin/openai-web-search/index.js.map +1 -1
- package/dist/core/extensions/builtin/permission-system/arity.d.ts.map +1 -1
- package/dist/core/extensions/builtin/permission-system/arity.js.map +1 -1
- package/dist/core/extensions/builtin/permission-system/cli.d.ts +0 -3
- package/dist/core/extensions/builtin/permission-system/cli.d.ts.map +1 -1
- package/dist/core/extensions/builtin/permission-system/cli.js +0 -13
- package/dist/core/extensions/builtin/permission-system/cli.js.map +1 -1
- package/dist/core/extensions/builtin/permission-system/config.d.ts.map +1 -1
- package/dist/core/extensions/builtin/permission-system/config.js.map +1 -1
- package/dist/core/extensions/builtin/permission-system/evaluate.d.ts.map +1 -1
- package/dist/core/extensions/builtin/permission-system/evaluate.js.map +1 -1
- package/dist/core/extensions/builtin/permission-system/events.d.ts.map +1 -1
- package/dist/core/extensions/builtin/permission-system/events.js.map +1 -1
- package/dist/core/extensions/builtin/permission-system/external-dir.d.ts.map +1 -1
- package/dist/core/extensions/builtin/permission-system/external-dir.js.map +1 -1
- package/dist/core/extensions/builtin/permission-system/index.d.ts.map +1 -1
- package/dist/core/extensions/builtin/permission-system/index.js.map +1 -1
- package/dist/core/extensions/builtin/permission-system/non-interactive.d.ts.map +1 -1
- package/dist/core/extensions/builtin/permission-system/non-interactive.js.map +1 -1
- package/dist/core/extensions/builtin/permission-system/parsers.d.ts.map +1 -1
- package/dist/core/extensions/builtin/permission-system/parsers.js +3 -1
- package/dist/core/extensions/builtin/permission-system/parsers.js.map +1 -1
- package/dist/core/extensions/builtin/permission-system/prompt.d.ts.map +1 -1
- package/dist/core/extensions/builtin/permission-system/prompt.js.map +1 -1
- package/dist/core/extensions/builtin/permission-system/service.d.ts.map +1 -1
- package/dist/core/extensions/builtin/permission-system/service.js +2 -5
- package/dist/core/extensions/builtin/permission-system/service.js.map +1 -1
- package/dist/core/extensions/builtin/permission-system/settings.d.ts.map +1 -1
- package/dist/core/extensions/builtin/permission-system/settings.js.map +1 -1
- package/dist/core/extensions/builtin/permission-system/storage.d.ts.map +1 -1
- package/dist/core/extensions/builtin/permission-system/storage.js.map +1 -1
- package/dist/core/extensions/builtin/permission-system/types.d.ts.map +1 -1
- package/dist/core/extensions/builtin/permission-system/types.js +3 -5
- package/dist/core/extensions/builtin/permission-system/types.js.map +1 -1
- package/dist/core/extensions/builtin/permission-system/wildcard.d.ts.map +1 -1
- package/dist/core/extensions/builtin/permission-system/wildcard.js.map +1 -1
- package/dist/core/extensions/builtin/prompt-preset/claude-opus-4-5.d.ts.map +1 -1
- package/dist/core/extensions/builtin/prompt-preset/claude-opus-4-5.js.map +1 -1
- package/dist/core/extensions/builtin/prompt-preset/claude-opus-4-6.d.ts.map +1 -1
- package/dist/core/extensions/builtin/prompt-preset/claude-opus-4-6.js.map +1 -1
- package/dist/core/extensions/builtin/prompt-preset/claude-opus-4-7.d.ts.map +1 -1
- package/dist/core/extensions/builtin/prompt-preset/claude-opus-4-7.js.map +1 -1
- package/dist/core/extensions/builtin/prompt-preset/file-operations.d.ts.map +1 -1
- package/dist/core/extensions/builtin/prompt-preset/file-operations.js.map +1 -1
- package/dist/core/extensions/builtin/prompt-preset/gpt-5.2.d.ts.map +1 -1
- package/dist/core/extensions/builtin/prompt-preset/gpt-5.2.js.map +1 -1
- package/dist/core/extensions/builtin/prompt-preset/gpt-5.3-codex.d.ts.map +1 -1
- package/dist/core/extensions/builtin/prompt-preset/gpt-5.3-codex.js.map +1 -1
- package/dist/core/extensions/builtin/prompt-preset/gpt-5.4.d.ts.map +1 -1
- package/dist/core/extensions/builtin/prompt-preset/gpt-5.4.js.map +1 -1
- package/dist/core/extensions/builtin/prompt-preset/gpt-5.5.d.ts.map +1 -1
- package/dist/core/extensions/builtin/prompt-preset/gpt-5.5.js.map +1 -1
- package/dist/core/extensions/builtin/prompt-preset/gpt-5.d.ts.map +1 -1
- package/dist/core/extensions/builtin/prompt-preset/gpt-5.js.map +1 -1
- package/dist/core/extensions/builtin/prompt-preset/index.d.ts.map +1 -1
- package/dist/core/extensions/builtin/prompt-preset/index.js.map +1 -1
- package/dist/core/extensions/builtin/prompt-preset/kimi-k2-6.d.ts.map +1 -1
- package/dist/core/extensions/builtin/prompt-preset/kimi-k2-6.js.map +1 -1
- package/dist/core/extensions/builtin/prompt-preset/presets.d.ts.map +1 -1
- package/dist/core/extensions/builtin/prompt-preset/presets.js.map +1 -1
- package/dist/core/extensions/builtin/prompt-preset/settings.d.ts.map +1 -1
- package/dist/core/extensions/builtin/prompt-preset/settings.js.map +1 -1
- package/dist/core/extensions/builtin/prompt-url-widget.d.ts.map +1 -1
- package/dist/core/extensions/builtin/prompt-url-widget.js.map +1 -1
- package/dist/core/extensions/builtin/redraws.d.ts.map +1 -1
- package/dist/core/extensions/builtin/redraws.js.map +1 -1
- package/dist/core/extensions/builtin/service-tier.d.ts.map +1 -1
- package/dist/core/extensions/builtin/service-tier.js.map +1 -1
- package/dist/core/extensions/builtin/session-observer/index.d.ts.map +1 -1
- package/dist/core/extensions/builtin/session-observer/index.js.map +1 -1
- package/dist/core/extensions/builtin/session-observer/loader.d.ts.map +1 -1
- package/dist/core/extensions/builtin/session-observer/loader.js.map +1 -1
- package/dist/core/extensions/builtin/session-observer/overlay-format.d.ts.map +1 -1
- package/dist/core/extensions/builtin/session-observer/overlay-format.js.map +1 -1
- package/dist/core/extensions/builtin/session-observer/overlay.d.ts.map +1 -1
- package/dist/core/extensions/builtin/session-observer/overlay.js +13 -18
- package/dist/core/extensions/builtin/session-observer/overlay.js.map +1 -1
- package/dist/core/extensions/builtin/session-observer/scanner.d.ts.map +1 -1
- package/dist/core/extensions/builtin/session-observer/scanner.js.map +1 -1
- package/dist/core/extensions/builtin/session-observer/text.d.ts.map +1 -1
- package/dist/core/extensions/builtin/session-observer/text.js.map +1 -1
- package/dist/core/extensions/builtin/session-observer/transcript-entries.d.ts.map +1 -1
- package/dist/core/extensions/builtin/session-observer/transcript-entries.js.map +1 -1
- package/dist/core/extensions/builtin/session-observer/transcript-format.d.ts.map +1 -1
- package/dist/core/extensions/builtin/session-observer/transcript-format.js.map +1 -1
- package/dist/core/extensions/builtin/session-observer/transcript.d.ts.map +1 -1
- package/dist/core/extensions/builtin/session-observer/transcript.js.map +1 -1
- package/dist/core/extensions/builtin/session-observer/types.d.ts.map +1 -1
- package/dist/core/extensions/builtin/todotools/index.d.ts.map +1 -1
- package/dist/core/extensions/builtin/todotools/index.js +0 -2
- package/dist/core/extensions/builtin/todotools/index.js.map +1 -1
- package/dist/core/extensions/builtin/todotools/prompt.d.ts +1 -1
- package/dist/core/extensions/builtin/todotools/prompt.d.ts.map +1 -1
- package/dist/core/extensions/builtin/todotools/prompt.js +0 -2
- package/dist/core/extensions/builtin/todotools/prompt.js.map +1 -1
- package/dist/core/extensions/builtin/todotools/state.d.ts.map +1 -1
- package/dist/core/extensions/builtin/todotools/state.js.map +1 -1
- package/dist/core/extensions/builtin/todotools/tools/todoread.d.ts.map +1 -1
- package/dist/core/extensions/builtin/todotools/tools/todoread.js.map +1 -1
- package/dist/core/extensions/builtin/todotools/tools/todowrite.d.ts.map +1 -1
- package/dist/core/extensions/builtin/todotools/tools/todowrite.js.map +1 -1
- package/dist/core/extensions/builtin/tool-pair-guard/index.d.ts.map +1 -1
- package/dist/core/extensions/builtin/tool-pair-guard/index.js.map +1 -1
- package/dist/core/extensions/builtin/tool-pair-guard/sanitize-anthropic-payload.d.ts.map +1 -1
- package/dist/core/extensions/builtin/tool-pair-guard/sanitize-anthropic-payload.js.map +1 -1
- package/dist/core/extensions/builtin/tool-pair-guard/sanitize-openai-chat-completions-payload.d.ts.map +1 -1
- package/dist/core/extensions/builtin/tool-pair-guard/sanitize-openai-chat-completions-payload.js.map +1 -1
- package/dist/core/extensions/builtin/tool-pair-guard/sanitize-openai-responses-payload.d.ts.map +1 -1
- package/dist/core/extensions/builtin/tool-pair-guard/sanitize-openai-responses-payload.js.map +1 -1
- package/dist/core/extensions/builtin/tps.d.ts.map +1 -1
- package/dist/core/extensions/builtin/tps.js.map +1 -1
- package/dist/core/extensions/index.d.ts +1 -1
- package/dist/core/extensions/index.d.ts.map +1 -1
- package/dist/core/extensions/index.js.map +1 -1
- package/dist/core/extensions/loader.d.ts +1 -1
- package/dist/core/extensions/loader.d.ts.map +1 -1
- package/dist/core/extensions/loader.js +4 -4
- package/dist/core/extensions/loader.js.map +1 -1
- package/dist/core/extensions/runner.d.ts +7 -2
- package/dist/core/extensions/runner.d.ts.map +1 -1
- package/dist/core/extensions/runner.js +69 -43
- package/dist/core/extensions/runner.js.map +1 -1
- package/dist/core/extensions/types.d.ts +22 -2
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/extensions/wrapper.d.ts.map +1 -1
- package/dist/core/extensions/wrapper.js.map +1 -1
- package/dist/core/footer-data-provider.d.ts.map +1 -1
- package/dist/core/footer-data-provider.js +17 -18
- package/dist/core/footer-data-provider.js.map +1 -1
- package/dist/core/http-dispatcher.d.ts.map +1 -1
- package/dist/core/http-dispatcher.js.map +1 -1
- package/dist/core/keybindings.d.ts +1 -1
- package/dist/core/keybindings.d.ts.map +1 -1
- package/dist/core/keybindings.js +0 -1
- package/dist/core/keybindings.js.map +1 -1
- package/dist/core/messages.d.ts.map +1 -1
- package/dist/core/messages.js.map +1 -1
- package/dist/core/model-registry.d.ts +3 -4
- package/dist/core/model-registry.d.ts.map +1 -1
- package/dist/core/model-registry.js +31 -16
- package/dist/core/model-registry.js.map +1 -1
- package/dist/core/model-resolver.d.ts.map +1 -1
- package/dist/core/model-resolver.js.map +1 -1
- package/dist/core/output-guard.d.ts.map +1 -1
- package/dist/core/output-guard.js.map +1 -1
- package/dist/core/package-manager.d.ts +1 -0
- package/dist/core/package-manager.d.ts.map +1 -1
- package/dist/core/package-manager.js +25 -13
- package/dist/core/package-manager.js.map +1 -1
- package/dist/core/project-trust.d.ts +15 -0
- package/dist/core/project-trust.d.ts.map +1 -0
- package/dist/core/project-trust.js +58 -0
- package/dist/core/project-trust.js.map +1 -0
- package/dist/core/prompt-templates.d.ts +2 -1
- package/dist/core/prompt-templates.d.ts.map +1 -1
- package/dist/core/prompt-templates.js +24 -26
- package/dist/core/prompt-templates.js.map +1 -1
- package/dist/core/provider-attribution.d.ts.map +1 -1
- package/dist/core/provider-attribution.js.map +1 -1
- package/dist/core/provider-display-names.d.ts.map +1 -1
- package/dist/core/resolve-config-value.d.ts +0 -4
- package/dist/core/resolve-config-value.d.ts.map +1 -1
- package/dist/core/resolve-config-value.js +0 -15
- package/dist/core/resolve-config-value.js.map +1 -1
- package/dist/core/resource-loader.d.ts +14 -2
- package/dist/core/resource-loader.d.ts.map +1 -1
- package/dist/core/resource-loader.js +117 -74
- package/dist/core/resource-loader.js.map +1 -1
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/session-cwd.d.ts.map +1 -1
- package/dist/core/session-cwd.js +0 -1
- package/dist/core/session-cwd.js.map +1 -1
- package/dist/core/session-manager.d.ts +3 -0
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +41 -28
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/session-resident-store.d.ts +16 -0
- package/dist/core/session-resident-store.d.ts.map +1 -0
- package/dist/core/session-resident-store.js +48 -0
- package/dist/core/session-resident-store.js.map +1 -0
- package/dist/core/session-work-barrier.d.ts.map +1 -1
- package/dist/core/session-work-barrier.js +5 -3
- package/dist/core/session-work-barrier.js.map +1 -1
- package/dist/core/settings-manager.d.ts +14 -2
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +86 -46
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/skills.d.ts.map +1 -1
- package/dist/core/skills.js.map +1 -1
- package/dist/core/slash-commands.d.ts.map +1 -1
- package/dist/core/slash-commands.js +1 -0
- package/dist/core/slash-commands.js.map +1 -1
- package/dist/core/source-info.d.ts.map +1 -1
- package/dist/core/source-info.js.map +1 -1
- package/dist/core/system-prompt.d.ts.map +1 -1
- package/dist/core/system-prompt.js.map +1 -1
- package/dist/core/telemetry.d.ts.map +1 -1
- package/dist/core/telemetry.js.map +1 -1
- package/dist/core/thinking-levels.d.ts.map +1 -1
- package/dist/core/thinking-levels.js.map +1 -1
- package/dist/core/timings.d.ts.map +1 -1
- package/dist/core/timings.js.map +1 -1
- package/dist/core/tools/bash.d.ts.map +1 -1
- package/dist/core/tools/bash.js +9 -6
- package/dist/core/tools/bash.js.map +1 -1
- package/dist/core/tools/diff-render.d.ts.map +1 -1
- package/dist/core/tools/diff-render.js.map +1 -1
- package/dist/core/tools/edit-diff.d.ts +0 -5
- package/dist/core/tools/edit-diff.d.ts.map +1 -1
- package/dist/core/tools/edit-diff.js +0 -7
- package/dist/core/tools/edit-diff.js.map +1 -1
- package/dist/core/tools/edit.d.ts.map +1 -1
- package/dist/core/tools/edit.js.map +1 -1
- package/dist/core/tools/file-mutation-queue.d.ts.map +1 -1
- package/dist/core/tools/file-mutation-queue.js.map +1 -1
- package/dist/core/tools/find.d.ts.map +1 -1
- package/dist/core/tools/find.js +1 -1
- package/dist/core/tools/find.js.map +1 -1
- package/dist/core/tools/grep.d.ts.map +1 -1
- package/dist/core/tools/grep.js +1 -1
- package/dist/core/tools/grep.js.map +1 -1
- package/dist/core/tools/index.d.ts +0 -5
- package/dist/core/tools/index.d.ts.map +1 -1
- package/dist/core/tools/index.js +0 -67
- package/dist/core/tools/index.js.map +1 -1
- package/dist/core/tools/ls.d.ts.map +1 -1
- package/dist/core/tools/ls.js +1 -1
- package/dist/core/tools/ls.js.map +1 -1
- package/dist/core/tools/output-accumulator.d.ts.map +1 -1
- package/dist/core/tools/output-accumulator.js +12 -18
- package/dist/core/tools/output-accumulator.js.map +1 -1
- package/dist/core/tools/path-utils.d.ts.map +1 -1
- package/dist/core/tools/path-utils.js.map +1 -1
- package/dist/core/tools/read.d.ts.map +1 -1
- package/dist/core/tools/read.js +1 -1
- package/dist/core/tools/read.js.map +1 -1
- package/dist/core/tools/render-utils.d.ts.map +1 -1
- package/dist/core/tools/render-utils.js.map +1 -1
- package/dist/core/tools/tool-definition-wrapper.d.ts.map +1 -1
- package/dist/core/tools/tool-definition-wrapper.js.map +1 -1
- package/dist/core/tools/truncate.d.ts.map +1 -1
- package/dist/core/tools/truncate.js.map +1 -1
- package/dist/core/tools/write.d.ts.map +1 -1
- package/dist/core/tools/write.js +1 -2
- package/dist/core/tools/write.js.map +1 -1
- package/dist/core/trust-manager.d.ts +31 -0
- package/dist/core/trust-manager.d.ts.map +1 -0
- package/dist/core/trust-manager.js +186 -0
- package/dist/core/trust-manager.js.map +1 -0
- package/dist/index.d.ts +5 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +101 -86
- package/dist/main.js.map +1 -1
- package/dist/migrations.d.ts.map +1 -1
- package/dist/migrations.js +39 -34
- package/dist/migrations.js.map +1 -1
- package/dist/modes/index.d.ts +1 -1
- package/dist/modes/index.d.ts.map +1 -1
- package/dist/modes/index.js.map +1 -1
- package/dist/modes/interactive/components/armin.d.ts.map +1 -1
- package/dist/modes/interactive/components/armin.js +6 -10
- package/dist/modes/interactive/components/armin.js.map +1 -1
- package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/assistant-message.js +2 -11
- package/dist/modes/interactive/components/assistant-message.js.map +1 -1
- package/dist/modes/interactive/components/bash-execution.d.ts.map +1 -1
- package/dist/modes/interactive/components/bash-execution.js +6 -11
- package/dist/modes/interactive/components/bash-execution.js.map +1 -1
- package/dist/modes/interactive/components/bordered-loader.d.ts.map +1 -1
- package/dist/modes/interactive/components/bordered-loader.js +0 -3
- package/dist/modes/interactive/components/bordered-loader.js.map +1 -1
- package/dist/modes/interactive/components/branch-summary-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/branch-summary-message.js +1 -3
- package/dist/modes/interactive/components/branch-summary-message.js.map +1 -1
- package/dist/modes/interactive/components/compaction-summary-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/compaction-summary-message.js +1 -3
- package/dist/modes/interactive/components/compaction-summary-message.js.map +1 -1
- package/dist/modes/interactive/components/config-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/config-selector.js +5 -15
- package/dist/modes/interactive/components/config-selector.js.map +1 -1
- package/dist/modes/interactive/components/countdown-timer.d.ts.map +1 -1
- package/dist/modes/interactive/components/countdown-timer.js +0 -5
- package/dist/modes/interactive/components/countdown-timer.js.map +1 -1
- package/dist/modes/interactive/components/custom-editor.d.ts.map +1 -1
- package/dist/modes/interactive/components/custom-editor.js +1 -8
- package/dist/modes/interactive/components/custom-editor.js.map +1 -1
- package/dist/modes/interactive/components/custom-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/custom-message.js +1 -6
- package/dist/modes/interactive/components/custom-message.js.map +1 -1
- package/dist/modes/interactive/components/daxnuts.d.ts.map +1 -1
- package/dist/modes/interactive/components/daxnuts.js +6 -8
- package/dist/modes/interactive/components/daxnuts.js.map +1 -1
- package/dist/modes/interactive/components/diff.d.ts.map +1 -1
- package/dist/modes/interactive/components/diff.js +2 -2
- package/dist/modes/interactive/components/diff.js.map +1 -1
- package/dist/modes/interactive/components/dynamic-border.d.ts.map +1 -1
- package/dist/modes/interactive/components/dynamic-border.js +0 -1
- package/dist/modes/interactive/components/dynamic-border.js.map +1 -1
- package/dist/modes/interactive/components/earendil-announcement.d.ts.map +1 -1
- package/dist/modes/interactive/components/earendil-announcement.js.map +1 -1
- package/dist/modes/interactive/components/extension-editor.d.ts.map +1 -1
- package/dist/modes/interactive/components/extension-editor.js +1 -6
- package/dist/modes/interactive/components/extension-editor.js.map +1 -1
- package/dist/modes/interactive/components/extension-input.d.ts.map +1 -1
- package/dist/modes/interactive/components/extension-input.js +2 -8
- package/dist/modes/interactive/components/extension-input.js.map +1 -1
- package/dist/modes/interactive/components/extension-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/extension-selector.js +1 -9
- package/dist/modes/interactive/components/extension-selector.js.map +1 -1
- package/dist/modes/interactive/components/favorite-models-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/favorite-models-selector.js +9 -15
- package/dist/modes/interactive/components/favorite-models-selector.js.map +1 -1
- package/dist/modes/interactive/components/footer.d.ts.map +1 -1
- package/dist/modes/interactive/components/footer.js +10 -3
- package/dist/modes/interactive/components/footer.js.map +1 -1
- package/dist/modes/interactive/components/index.d.ts +1 -0
- package/dist/modes/interactive/components/index.d.ts.map +1 -1
- package/dist/modes/interactive/components/index.js +1 -0
- package/dist/modes/interactive/components/index.js.map +1 -1
- package/dist/modes/interactive/components/keybinding-hints.d.ts.map +1 -1
- package/dist/modes/interactive/components/keybinding-hints.js.map +1 -1
- package/dist/modes/interactive/components/login-dialog.d.ts +1 -0
- package/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
- package/dist/modes/interactive/components/login-dialog.js +10 -10
- package/dist/modes/interactive/components/login-dialog.js.map +1 -1
- package/dist/modes/interactive/components/model-favorites.d.ts.map +1 -1
- package/dist/modes/interactive/components/model-favorites.js.map +1 -1
- package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/model-selector.js +9 -22
- package/dist/modes/interactive/components/model-selector.js.map +1 -1
- package/dist/modes/interactive/components/oauth-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/oauth-selector.js +3 -12
- package/dist/modes/interactive/components/oauth-selector.js.map +1 -1
- package/dist/modes/interactive/components/session-selector-search.d.ts.map +1 -1
- package/dist/modes/interactive/components/session-selector-search.js.map +1 -1
- package/dist/modes/interactive/components/session-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/session-selector.js +33 -58
- package/dist/modes/interactive/components/session-selector.js.map +1 -1
- package/dist/modes/interactive/components/settings-selector.d.ts +3 -1
- package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/settings-selector.js +20 -4
- package/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/dist/modes/interactive/components/show-images-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/show-images-selector.js +0 -1
- package/dist/modes/interactive/components/show-images-selector.js.map +1 -1
- package/dist/modes/interactive/components/skill-invocation-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/skill-invocation-message.js +1 -3
- package/dist/modes/interactive/components/skill-invocation-message.js.map +1 -1
- package/dist/modes/interactive/components/theme-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/theme-selector.js +0 -2
- package/dist/modes/interactive/components/theme-selector.js.map +1 -1
- package/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/thinking-selector.js +0 -1
- package/dist/modes/interactive/components/thinking-selector.js.map +1 -1
- package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/dist/modes/interactive/components/tool-execution.js +35 -32
- package/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/dist/modes/interactive/components/tree-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/tree-selector.js +18 -32
- package/dist/modes/interactive/components/tree-selector.js.map +1 -1
- package/dist/modes/interactive/components/trust-selector.d.ts +23 -0
- package/dist/modes/interactive/components/trust-selector.d.ts.map +1 -0
- package/dist/modes/interactive/components/trust-selector.js +85 -0
- package/dist/modes/interactive/components/trust-selector.js.map +1 -0
- package/dist/modes/interactive/components/user-message-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/user-message-selector.js +3 -6
- package/dist/modes/interactive/components/user-message-selector.js.map +1 -1
- package/dist/modes/interactive/components/user-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/user-message.js +0 -1
- package/dist/modes/interactive/components/user-message.js.map +1 -1
- package/dist/modes/interactive/components/visual-truncate.d.ts.map +1 -1
- package/dist/modes/interactive/components/visual-truncate.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +39 -0
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +264 -114
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/session-info-format.d.ts.map +1 -1
- package/dist/modes/interactive/session-info-format.js.map +1 -1
- package/dist/modes/interactive/startup-tools.d.ts.map +1 -1
- package/dist/modes/interactive/startup-tools.js.map +1 -1
- package/dist/modes/interactive/theme/theme.d.ts +0 -4
- package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
- package/dist/modes/interactive/theme/theme.js +0 -13
- package/dist/modes/interactive/theme/theme.js.map +1 -1
- package/dist/modes/interactive/working-status.d.ts +2 -0
- package/dist/modes/interactive/working-status.d.ts.map +1 -1
- package/dist/modes/interactive/working-status.js +34 -0
- package/dist/modes/interactive/working-status.js.map +1 -1
- package/dist/modes/print-mode.d.ts.map +1 -1
- package/dist/modes/print-mode.js.map +1 -1
- package/dist/modes/provider-native-rendering.d.ts.map +1 -1
- package/dist/modes/provider-native-rendering.js.map +1 -1
- package/dist/modes/rpc/jsonl.d.ts.map +1 -1
- package/dist/modes/rpc/jsonl.js.map +1 -1
- package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-client.js +7 -8
- package/dist/modes/rpc/rpc-client.js.map +1 -1
- package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/dist/modes/rpc/rpc-types.d.ts +0 -1
- package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-types.js.map +1 -1
- package/dist/package-manager-cli.d.ts +6 -2
- package/dist/package-manager-cli.d.ts.map +1 -1
- package/dist/package-manager-cli.js +105 -11
- package/dist/package-manager-cli.js.map +1 -1
- package/dist/self-update-bootstrap.d.ts.map +1 -1
- package/dist/self-update-bootstrap.js.map +1 -1
- package/dist/senpi +10 -1
- package/dist/utils/ansi.d.ts.map +1 -1
- package/dist/utils/ansi.js.map +1 -1
- package/dist/utils/changelog.d.ts +1 -0
- package/dist/utils/changelog.d.ts.map +1 -1
- package/dist/utils/changelog.js +78 -0
- package/dist/utils/changelog.js.map +1 -1
- package/dist/utils/child-process.d.ts.map +1 -1
- package/dist/utils/child-process.js.map +1 -1
- package/dist/utils/clipboard-image.d.ts.map +1 -1
- package/dist/utils/clipboard-image.js.map +1 -1
- package/dist/utils/clipboard-native.d.ts.map +1 -1
- package/dist/utils/clipboard-native.js.map +1 -1
- package/dist/utils/clipboard.d.ts.map +1 -1
- package/dist/utils/clipboard.js.map +1 -1
- package/dist/utils/deprecation.d.ts.map +1 -1
- package/dist/utils/deprecation.js.map +1 -1
- package/dist/utils/exif-orientation.d.ts.map +1 -1
- package/dist/utils/exif-orientation.js.map +1 -1
- package/dist/utils/frontmatter.d.ts.map +1 -1
- package/dist/utils/frontmatter.js.map +1 -1
- package/dist/utils/fs-watch.d.ts.map +1 -1
- package/dist/utils/fs-watch.js.map +1 -1
- package/dist/utils/git.d.ts.map +1 -1
- package/dist/utils/git.js.map +1 -1
- package/dist/utils/html.d.ts.map +1 -1
- package/dist/utils/html.js.map +1 -1
- package/dist/utils/image-convert.d.ts.map +1 -1
- package/dist/utils/image-convert.js.map +1 -1
- package/dist/utils/image-resize-core.d.ts.map +1 -1
- package/dist/utils/image-resize-core.js.map +1 -1
- package/dist/utils/image-resize-worker.d.ts.map +1 -1
- package/dist/utils/image-resize-worker.js.map +1 -1
- package/dist/utils/image-resize.d.ts.map +1 -1
- package/dist/utils/image-resize.js.map +1 -1
- package/dist/utils/json.d.ts.map +1 -1
- package/dist/utils/json.js.map +1 -1
- package/dist/utils/mime.d.ts.map +1 -1
- package/dist/utils/mime.js.map +1 -1
- package/dist/utils/open-browser.d.ts.map +1 -1
- package/dist/utils/open-browser.js.map +1 -1
- package/dist/utils/paths.d.ts.map +1 -1
- package/dist/utils/paths.js.map +1 -1
- package/dist/utils/photon.d.ts.map +1 -1
- package/dist/utils/photon.js.map +1 -1
- package/dist/utils/pi-user-agent.d.ts.map +1 -1
- package/dist/utils/pi-user-agent.js.map +1 -1
- package/dist/utils/shell.d.ts.map +1 -1
- package/dist/utils/shell.js.map +1 -1
- package/dist/utils/sleep.d.ts.map +1 -1
- package/dist/utils/sleep.js.map +1 -1
- package/dist/utils/syntax-highlight.d.ts.map +1 -1
- package/dist/utils/syntax-highlight.js.map +1 -1
- package/dist/utils/tools-manager.d.ts.map +1 -1
- package/dist/utils/tools-manager.js.map +1 -1
- package/dist/utils/version-check.d.ts.map +1 -1
- package/dist/utils/version-check.js.map +1 -1
- package/dist/utils/windows-self-update.d.ts.map +1 -1
- package/dist/utils/windows-self-update.js.map +1 -1
- package/docs/compaction-guide.md +9 -9
- package/docs/compaction.md +2 -2
- package/docs/containerization.md +22 -22
- package/docs/custom-provider.md +13 -13
- package/docs/development.md +4 -2
- package/docs/docs.json +4 -0
- package/docs/extensions.md +46 -18
- package/docs/index.md +5 -4
- package/docs/json.md +21 -15
- package/docs/keybindings.md +6 -3
- package/docs/models.md +10 -43
- package/docs/packages.md +12 -12
- package/docs/prompt-templates.md +11 -4
- package/docs/providers.md +9 -9
- package/docs/rpc.md +14 -13
- package/docs/sdk.md +18 -9
- package/docs/security.md +55 -0
- package/docs/session-format.md +4 -4
- package/docs/sessions.md +14 -14
- package/docs/settings.md +20 -7
- package/docs/skills.md +11 -11
- package/docs/terminal-setup.md +38 -4
- package/docs/termux.md +3 -3
- package/docs/themes.md +1 -1
- package/docs/tmux.md +4 -2
- package/docs/tui.md +4 -4
- package/docs/usage.md +60 -50
- package/examples/extensions/README.md +1 -0
- package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/gondolin/package-lock.json +2 -2
- package/examples/extensions/gondolin/package.json +1 -1
- package/examples/extensions/minimal-mode.ts +6 -6
- package/examples/extensions/project-trust.ts +64 -0
- package/examples/extensions/sandbox/package-lock.json +2 -2
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/node_modules/@earendil-works/pi-agent-core/README.md +4 -3
- package/node_modules/@earendil-works/pi-agent-core/dist/agent-loop.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/agent-loop.js +125 -77
- package/node_modules/@earendil-works/pi-agent-core/dist/agent-loop.js.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/agent.d.ts +7 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/agent.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/agent.js +2 -27
- package/node_modules/@earendil-works/pi-agent-core/dist/agent.js.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/agent-harness.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/agent-harness.js +7 -20
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/agent-harness.js.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/compaction/branch-summarization.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/compaction/branch-summarization.js.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/compaction/compaction.d.ts +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/compaction/compaction.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/compaction/compaction.js +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/compaction/compaction.js.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/compaction/utils.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/compaction/utils.js.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/env/nodejs.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/env/nodejs.js +0 -3
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/env/nodejs.js.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/messages.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/messages.js.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/prompt-templates.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/prompt-templates.js.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/jsonl-repo.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/jsonl-repo.js +0 -3
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/jsonl-repo.js.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/jsonl-storage.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/jsonl-storage.js +0 -7
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/jsonl-storage.js.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/memory-repo.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/memory-repo.js +3 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/memory-repo.js.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/memory-storage.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/memory-storage.js +0 -5
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/memory-storage.js.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/repo-utils.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/repo-utils.js.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/session.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/session.js +0 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/session.js.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/uuid.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/uuid.js.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/skills.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/skills.js.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/system-prompt.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/system-prompt.js.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/types.d.ts +0 -2
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/types.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/types.js +0 -20
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/types.js.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/shell-output.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/shell-output.js.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/truncate.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/truncate.js.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/index.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/node.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/proxy.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/proxy.js.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/types.d.ts +7 -4
- package/node_modules/@earendil-works/pi-agent-core/dist/types.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/types.js.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/package.json +6 -6
- package/node_modules/@earendil-works/pi-ai/README.md +4 -5
- package/node_modules/@earendil-works/pi-ai/dist/api-registry.d.ts +0 -1
- package/node_modules/@earendil-works/pi-ai/dist/api-registry.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api-registry.js +0 -3
- package/node_modules/@earendil-works/pi-ai/dist/api-registry.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/bedrock-provider.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/cli.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/cli.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/env-api-keys.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/env-api-keys.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/image-models.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/image-models.generated.d.ts +30 -0
- package/node_modules/@earendil-works/pi-ai/dist/image-models.generated.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/image-models.generated.js +30 -0
- package/node_modules/@earendil-works/pi-ai/dist/image-models.generated.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/image-models.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/images-api-registry.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/images-api-registry.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/images.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/images.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/index.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/models.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/models.generated.d.ts +584 -397
- package/node_modules/@earendil-works/pi-ai/dist/models.generated.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/models.generated.js +578 -509
- package/node_modules/@earendil-works/pi-ai/dist/models.generated.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/models.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/oauth.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/amazon-bedrock.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/amazon-bedrock.js +15 -7
- package/node_modules/@earendil-works/pi-ai/dist/providers/amazon-bedrock.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/anthropic.d.ts +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/anthropic.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/anthropic.js +17 -7
- package/node_modules/@earendil-works/pi-ai/dist/providers/anthropic.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/azure-openai-responses.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/azure-openai-responses.js +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/providers/azure-openai-responses.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/cloudflare.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/cloudflare.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/faux.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/faux.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/github-copilot-headers.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/github-copilot-headers.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/google-shared.d.ts +0 -4
- package/node_modules/@earendil-works/pi-ai/dist/providers/google-shared.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/google-shared.js +0 -13
- package/node_modules/@earendil-works/pi-ai/dist/providers/google-shared.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/google-vertex.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/google-vertex.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/google.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/google.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/images/openrouter.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/images/openrouter.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/images/register-builtins.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/images/register-builtins.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/mistral.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/mistral.js +3 -3
- package/node_modules/@earendil-works/pi-ai/dist/providers/mistral.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/openai-codex-responses.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/openai-codex-responses.js +0 -7
- package/node_modules/@earendil-works/pi-ai/dist/providers/openai-codex-responses.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/openai-completions.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/openai-completions.js +5 -4
- package/node_modules/@earendil-works/pi-ai/dist/providers/openai-completions.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/openai-prompt-cache.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/openai-prompt-cache.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/openai-responses-shared.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/openai-responses-shared.js +2 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/openai-responses-shared.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/openai-responses.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/openai-responses.js +3 -2
- package/node_modules/@earendil-works/pi-ai/dist/providers/openai-responses.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/register-builtins.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/register-builtins.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/simple-options.d.ts +1 -7
- package/node_modules/@earendil-works/pi-ai/dist/providers/simple-options.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/simple-options.js +2 -16
- package/node_modules/@earendil-works/pi-ai/dist/providers/simple-options.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/transform-messages.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/transform-messages.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/session-resources.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/session-resources.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/stream.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/stream.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/tool-call-middleware/context-transformer.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/tool-call-middleware/context-transformer.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/tool-call-middleware/index.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/tool-call-middleware/index.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/tool-call-middleware/protocols/gemma4.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/tool-call-middleware/protocols/gemma4.js +10 -8
- package/node_modules/@earendil-works/pi-ai/dist/tool-call-middleware/protocols/gemma4.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/tool-call-middleware/protocols/hermes.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/tool-call-middleware/protocols/hermes.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/tool-call-middleware/protocols/json-mix.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/tool-call-middleware/protocols/json-mix.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/tool-call-middleware/protocols/morph-xml.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/tool-call-middleware/protocols/morph-xml.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/tool-call-middleware/protocols/xml-tool-tag-scanner.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/tool-call-middleware/protocols/xml-tool-tag-scanner.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/tool-call-middleware/protocols/yaml-xml.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/tool-call-middleware/protocols/yaml-xml.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/tool-call-middleware/stream-wrapper.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/tool-call-middleware/stream-wrapper.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/tool-call-middleware/types.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/types.d.ts +6 -2
- package/node_modules/@earendil-works/pi-ai/dist/types.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/types.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/abort-signals.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/abort-signals.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/diagnostics.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/diagnostics.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/event-stream.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/event-stream.js +3 -7
- package/node_modules/@earendil-works/pi-ai/dist/utils/event-stream.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/hash.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/hash.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/headers.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/headers.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/json-parse.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/json-parse.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/node-http-proxy.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/node-http-proxy.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/oauth/anthropic.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/oauth/anthropic.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/oauth/device-code.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/oauth/device-code.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/oauth/github-copilot.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/oauth/github-copilot.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/oauth/index.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/oauth/index.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/oauth/oauth-page.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/oauth/oauth-page.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/oauth/openai-codex.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/oauth/openai-codex.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/oauth/pkce.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/oauth/pkce.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/oauth/types.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/overflow.d.ts +0 -4
- package/node_modules/@earendil-works/pi-ai/dist/utils/overflow.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/overflow.js +0 -6
- package/node_modules/@earendil-works/pi-ai/dist/utils/overflow.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/sanitize-unicode.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/sanitize-unicode.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/tool-pair-repair.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/tool-pair-repair.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/typebox-helpers.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/typebox-helpers.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/validation.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/validation.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/package.json +5 -5
- package/node_modules/@earendil-works/pi-tui/README.md +1 -2
- package/node_modules/@earendil-works/pi-tui/dist/autocomplete.d.ts +3 -1
- package/node_modules/@earendil-works/pi-tui/dist/autocomplete.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/autocomplete.js +0 -3
- package/node_modules/@earendil-works/pi-tui/dist/autocomplete.js.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/box.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/box.js +1 -6
- package/node_modules/@earendil-works/pi-tui/dist/components/box.js.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/cancellable-loader.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/cancellable-loader.js +4 -3
- package/node_modules/@earendil-works/pi-tui/dist/components/cancellable-loader.js.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/editor.d.ts +6 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/editor.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/editor.js +132 -93
- package/node_modules/@earendil-works/pi-tui/dist/components/editor.js.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/image.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/image.js +0 -8
- package/node_modules/@earendil-works/pi-tui/dist/components/image.js.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/input.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/input.js +14 -14
- package/node_modules/@earendil-works/pi-tui/dist/components/input.js.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/loader.d.ts +5 -0
- package/node_modules/@earendil-works/pi-tui/dist/components/loader.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/loader.js +29 -17
- package/node_modules/@earendil-works/pi-tui/dist/components/loader.js.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/markdown.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/markdown.js +0 -11
- package/node_modules/@earendil-works/pi-tui/dist/components/markdown.js.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/select-list.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/select-list.js +4 -9
- package/node_modules/@earendil-works/pi-tui/dist/components/select-list.js.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/settings-list.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/settings-list.js +4 -12
- package/node_modules/@earendil-works/pi-tui/dist/components/settings-list.js.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/spacer.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/spacer.js +0 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/spacer.js.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/text.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/text.js +0 -8
- package/node_modules/@earendil-works/pi-tui/dist/components/text.js.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/truncated-text.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/truncated-text.js +0 -3
- package/node_modules/@earendil-works/pi-tui/dist/components/truncated-text.js.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/editor-component.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/fuzzy.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/fuzzy.js +131 -61
- package/node_modules/@earendil-works/pi-tui/dist/fuzzy.js.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/index.d.ts +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/index.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/index.js +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/index.js.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/keybindings.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/keybindings.js +2 -4
- package/node_modules/@earendil-works/pi-tui/dist/keybindings.js.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/keys.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/keys.js.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/kill-ring.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/kill-ring.js +3 -1
- package/node_modules/@earendil-works/pi-tui/dist/kill-ring.js.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/native-modifiers.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/native-modifiers.js.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/slash-command-autocomplete.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/slash-command-autocomplete.js.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/stdin-buffer.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/stdin-buffer.js +4 -6
- package/node_modules/@earendil-works/pi-tui/dist/stdin-buffer.js.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/terminal-image.d.ts +0 -1
- package/node_modules/@earendil-works/pi-tui/dist/terminal-image.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/terminal-image.js +4 -7
- package/node_modules/@earendil-works/pi-tui/dist/terminal-image.js.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/terminal.d.ts +4 -7
- package/node_modules/@earendil-works/pi-tui/dist/terminal.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/terminal.js +60 -103
- package/node_modules/@earendil-works/pi-tui/dist/terminal.js.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/tui.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/tui.js +39 -31
- package/node_modules/@earendil-works/pi-tui/dist/tui.js.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/undo-stack.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/undo-stack.js +3 -1
- package/node_modules/@earendil-works/pi-tui/dist/undo-stack.js.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/utils.d.ts +0 -4
- package/node_modules/@earendil-works/pi-tui/dist/utils.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/utils.js +57 -33
- package/node_modules/@earendil-works/pi-tui/dist/utils.js.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/word-navigation.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/word-navigation.js.map +1 -1
- package/node_modules/@earendil-works/pi-tui/package.json +1 -1
- package/node_modules/@types/node/README.md +3 -3
- package/node_modules/@types/node/assert/strict.d.ts +3 -55
- package/node_modules/@types/node/assert.d.ts +49 -177
- package/node_modules/@types/node/async_hooks.d.ts +151 -43
- package/node_modules/@types/node/buffer.buffer.d.ts +2 -8
- package/node_modules/@types/node/buffer.d.ts +43 -212
- package/node_modules/@types/node/child_process.d.ts +23 -133
- package/node_modules/@types/node/cluster.d.ts +238 -384
- package/node_modules/@types/node/compatibility/iterators.d.ts +1 -0
- package/node_modules/@types/node/console.d.ts +45 -404
- package/node_modules/@types/node/constants.d.ts +3 -10
- package/node_modules/@types/node/crypto.d.ts +640 -1127
- package/node_modules/@types/node/dgram.d.ts +14 -77
- package/node_modules/@types/node/diagnostics_channel.d.ts +3 -29
- package/node_modules/@types/node/dns/promises.d.ts +4 -10
- package/node_modules/@types/node/dns.d.ts +130 -177
- package/node_modules/@types/node/domain.d.ts +12 -32
- package/node_modules/@types/node/events.d.ts +872 -840
- package/node_modules/@types/node/fs/promises.d.ts +193 -20
- package/node_modules/@types/node/fs.d.ts +754 -435
- package/node_modules/@types/node/globals.d.ts +8 -30
- package/node_modules/@types/node/globals.typedarray.d.ts +63 -0
- package/node_modules/@types/node/http.d.ts +363 -305
- package/node_modules/@types/node/http2.d.ts +545 -781
- package/node_modules/@types/node/https.d.ts +64 -243
- package/node_modules/@types/node/index.d.ts +26 -6
- package/node_modules/@types/node/inspector/promises.d.ts +35 -0
- package/node_modules/@types/node/inspector.d.ts +68 -57
- package/node_modules/@types/node/inspector.generated.d.ts +764 -410
- package/node_modules/@types/node/module.d.ts +53 -189
- package/node_modules/@types/node/net.d.ts +101 -207
- package/node_modules/@types/node/os.d.ts +11 -19
- package/node_modules/@types/node/package.json +13 -3
- package/node_modules/@types/node/path/posix.d.ts +8 -0
- package/node_modules/@types/node/path/win32.d.ts +8 -0
- package/node_modules/@types/node/path.d.ts +119 -141
- package/node_modules/@types/node/perf_hooks.d.ts +317 -673
- package/node_modules/@types/node/process.d.ts +277 -157
- package/node_modules/@types/node/punycode.d.ts +3 -31
- package/node_modules/@types/node/querystring.d.ts +3 -16
- package/node_modules/@types/node/quic.d.ts +897 -0
- package/node_modules/@types/node/readline/promises.d.ts +3 -6
- package/node_modules/@types/node/readline.d.ts +65 -152
- package/node_modules/@types/node/repl.d.ts +101 -109
- package/node_modules/@types/node/sea.d.ts +9 -115
- package/node_modules/@types/node/sqlite.d.ts +415 -68
- package/node_modules/@types/node/stream/consumers.d.ts +91 -15
- package/node_modules/@types/node/stream/iter.d.ts +301 -0
- package/node_modules/@types/node/stream/promises.d.ts +136 -15
- package/node_modules/@types/node/stream/web.d.ts +179 -501
- package/node_modules/@types/node/stream.d.ts +577 -490
- package/node_modules/@types/node/string_decoder.d.ts +3 -43
- package/node_modules/@types/node/test/reporters.d.ts +59 -0
- package/node_modules/@types/node/test.d.ts +402 -286
- package/node_modules/@types/node/timers/promises.d.ts +3 -18
- package/node_modules/@types/node/timers.d.ts +3 -141
- package/node_modules/@types/node/tls.d.ts +115 -241
- package/node_modules/@types/node/trace_events.d.ts +3 -97
- package/node_modules/@types/node/ts5.6/buffer.buffer.d.ts +2 -8
- package/node_modules/@types/node/ts5.6/compatibility/float16array.d.ts +71 -0
- package/node_modules/@types/node/ts5.6/globals.typedarray.d.ts +2 -0
- package/node_modules/@types/node/ts5.6/index.d.ts +28 -6
- package/node_modules/@types/node/ts5.7/compatibility/float16array.d.ts +72 -0
- package/node_modules/@types/node/ts5.7/index.d.ts +119 -0
- package/node_modules/@types/node/tty.d.ts +57 -40
- package/node_modules/@types/node/url.d.ts +156 -584
- package/node_modules/@types/node/util/types.d.ts +558 -0
- package/node_modules/@types/node/util.d.ts +167 -1096
- package/node_modules/@types/node/v8.d.ts +75 -15
- package/node_modules/@types/node/vm.d.ts +334 -198
- package/node_modules/@types/node/wasi.d.ts +24 -74
- package/node_modules/@types/node/web-globals/abortcontroller.d.ts +27 -2
- package/node_modules/@types/node/web-globals/blob.d.ts +23 -0
- package/node_modules/@types/node/web-globals/console.d.ts +9 -0
- package/node_modules/@types/node/web-globals/crypto.d.ts +39 -0
- package/node_modules/@types/node/web-globals/encoding.d.ts +11 -0
- package/node_modules/@types/node/web-globals/events.d.ts +9 -0
- package/node_modules/@types/node/web-globals/fetch.d.ts +14 -0
- package/node_modules/@types/node/web-globals/importmeta.d.ts +13 -0
- package/node_modules/@types/node/web-globals/messaging.d.ts +23 -0
- package/node_modules/@types/node/web-globals/navigator.d.ts +3 -0
- package/node_modules/@types/node/web-globals/performance.d.ts +45 -0
- package/node_modules/@types/node/web-globals/streams.d.ts +115 -0
- package/node_modules/@types/node/web-globals/timers.d.ts +44 -0
- package/node_modules/@types/node/web-globals/url.d.ts +24 -0
- package/node_modules/@types/node/worker_threads.d.ts +280 -393
- package/node_modules/@types/node/zlib/iter.d.ts +131 -0
- package/node_modules/@types/node/zlib.d.ts +7 -165
- package/node_modules/undici-types/agent.d.ts +13 -12
- package/node_modules/undici-types/api.d.ts +26 -26
- package/node_modules/undici-types/balanced-pool.d.ts +13 -12
- package/node_modules/undici-types/cache-interceptor.d.ts +179 -0
- package/node_modules/undici-types/client-stats.d.ts +15 -0
- package/node_modules/undici-types/client.d.ts +34 -19
- package/node_modules/undici-types/connector.d.ts +4 -2
- package/node_modules/undici-types/cookies.d.ts +2 -0
- package/node_modules/undici-types/diagnostics-channel.d.ts +18 -10
- package/node_modules/undici-types/dispatcher.d.ts +127 -104
- package/node_modules/undici-types/env-http-proxy-agent.d.ts +4 -3
- package/node_modules/undici-types/errors.d.ts +82 -54
- package/node_modules/undici-types/eventsource.d.ts +9 -4
- package/node_modules/undici-types/fetch.d.ts +22 -20
- package/node_modules/undici-types/formdata.d.ts +7 -7
- package/node_modules/undici-types/global-dispatcher.d.ts +4 -4
- package/node_modules/undici-types/global-origin.d.ts +5 -5
- package/node_modules/undici-types/h2c-client.d.ts +73 -0
- package/node_modules/undici-types/handlers.d.ts +8 -8
- package/node_modules/undici-types/header.d.ts +157 -1
- package/node_modules/undici-types/index.d.ts +67 -47
- package/node_modules/undici-types/interceptors.d.ts +71 -8
- package/node_modules/undici-types/mock-agent.d.ts +36 -18
- package/node_modules/undici-types/mock-call-history.d.ts +111 -0
- package/node_modules/undici-types/mock-client.d.ts +6 -4
- package/node_modules/undici-types/mock-errors.d.ts +3 -3
- package/node_modules/undici-types/mock-interceptor.d.ts +21 -20
- package/node_modules/undici-types/mock-pool.d.ts +6 -4
- package/node_modules/undici-types/package.json +1 -1
- package/node_modules/undici-types/patch.d.ts +0 -4
- package/node_modules/undici-types/pool-stats.d.ts +8 -8
- package/node_modules/undici-types/pool.d.ts +15 -13
- package/node_modules/undici-types/proxy-agent.d.ts +5 -4
- package/node_modules/undici-types/readable.d.ts +19 -16
- package/node_modules/undici-types/retry-agent.d.ts +1 -1
- package/node_modules/undici-types/retry-handler.d.ts +19 -10
- package/node_modules/undici-types/round-robin-pool.d.ts +41 -0
- package/node_modules/undici-types/snapshot-agent.d.ts +109 -0
- package/node_modules/undici-types/socks5-proxy-agent.d.ts +25 -0
- package/node_modules/undici-types/util.d.ts +3 -3
- package/node_modules/undici-types/utility.d.ts +7 -0
- package/node_modules/undici-types/webidl.d.ts +148 -29
- package/node_modules/undici-types/websocket.d.ts +48 -10
- package/npm-shrinkwrap.json +21 -21
- package/package.json +13 -12
- package/dist/core/extensions/builtin/compaction/overflow-detection.d.ts +0 -11
- package/dist/core/extensions/builtin/compaction/overflow-detection.d.ts.map +0 -1
- package/dist/core/extensions/builtin/compaction/overflow-detection.js +0 -40
- package/dist/core/extensions/builtin/compaction/overflow-detection.js.map +0 -1
- package/dist/core/extensions/builtin/system-messages.d.ts +0 -47
- package/dist/core/extensions/builtin/system-messages.d.ts.map +0 -1
- package/dist/core/extensions/builtin/system-messages.js +0 -117
- package/dist/core/extensions/builtin/system-messages.js.map +0 -1
- package/dist/core/extensions/builtin/todotools/continuation/config.d.ts +0 -10
- package/dist/core/extensions/builtin/todotools/continuation/config.d.ts.map +0 -1
- package/dist/core/extensions/builtin/todotools/continuation/config.js +0 -33
- package/dist/core/extensions/builtin/todotools/continuation/config.js.map +0 -1
- package/dist/core/extensions/builtin/todotools/continuation/index.d.ts +0 -2
- package/dist/core/extensions/builtin/todotools/continuation/index.d.ts.map +0 -1
- package/dist/core/extensions/builtin/todotools/continuation/index.js +0 -2
- package/dist/core/extensions/builtin/todotools/continuation/index.js.map +0 -1
- package/dist/core/extensions/builtin/todotools/continuation/prompt.d.ts +0 -5
- package/dist/core/extensions/builtin/todotools/continuation/prompt.d.ts.map +0 -1
- package/dist/core/extensions/builtin/todotools/continuation/prompt.js +0 -34
- package/dist/core/extensions/builtin/todotools/continuation/prompt.js.map +0 -1
- package/dist/core/extensions/builtin/todotools/continuation/runtime.d.ts +0 -11
- package/dist/core/extensions/builtin/todotools/continuation/runtime.d.ts.map +0 -1
- package/dist/core/extensions/builtin/todotools/continuation/runtime.js +0 -201
- package/dist/core/extensions/builtin/todotools/continuation/runtime.js.map +0 -1
- package/dist/core/extensions/builtin/todotools/settings.d.ts +0 -6
- package/dist/core/extensions/builtin/todotools/settings.d.ts.map +0 -1
- package/dist/core/extensions/builtin/todotools/settings.js +0 -58
- package/dist/core/extensions/builtin/todotools/settings.js.map +0 -1
- package/dist/core/extensions/builtin/todotools/system-messages.d.ts +0 -34
- package/dist/core/extensions/builtin/todotools/system-messages.d.ts.map +0 -1
- package/dist/core/extensions/builtin/todotools/system-messages.js +0 -82
- package/dist/core/extensions/builtin/todotools/system-messages.js.map +0 -1
- package/dist/core/index.d.ts +0 -12
- package/dist/core/index.d.ts.map +0 -1
- package/dist/core/index.js +0 -12
- package/dist/core/index.js.map +0 -1
- package/node_modules/@types/node/compatibility/disposable.d.ts +0 -14
- package/node_modules/@types/node/compatibility/index.d.ts +0 -9
- package/node_modules/@types/node/compatibility/indexable.d.ts +0 -20
- package/node_modules/undici-types/file.d.ts +0 -39
- package/node_modules/undici-types/filereader.d.ts +0 -54
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"editor.d.ts","sourceRoot":"","sources":["../../src/components/editor.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AutocompleteProvider, AutocompleteSuggestions } from \"../autocomplete.ts\";\nimport { getKeybindings } from \"../keybindings.ts\";\nimport { decodePrintableKey, matchesKey } from \"../keys.ts\";\nimport { KillRing } from \"../kill-ring.ts\";\nimport { type Component, CURSOR_MARKER, type Focusable, type TUI } from \"../tui.ts\";\nimport { UndoStack } from \"../undo-stack.ts\";\nimport { getGraphemeSegmenter, getWordSegmenter, isWhitespaceChar, truncateToWidth, visibleWidth } from \"../utils.ts\";\nimport { findWordBackward, findWordForward } from \"../word-navigation.ts\";\nimport { SelectList, type SelectListLayoutOptions, type SelectListTheme } from \"./select-list.ts\";\n\nconst graphemeSegmenter = getGraphemeSegmenter();\nconst wordSegmenter = getWordSegmenter();\n\n/** Regex matching paste markers like `[paste #1 +123 lines]` or `[paste #2 1234 chars]`. */\nconst PASTE_MARKER_REGEX = /\\[paste #(\\d+)( (\\+\\d+ lines|\\d+ chars))?\\]/g;\n\n/** Non-global version for single-segment testing. */\nconst PASTE_MARKER_SINGLE = /^\\[paste #(\\d+)( (\\+\\d+ lines|\\d+ chars))?\\]$/;\n\n/** Check if a segment is a paste marker (i.e. was merged by segmentWithMarkers). */\nfunction isPasteMarker(segment: string): boolean {\n\treturn segment.length >= 10 && PASTE_MARKER_SINGLE.test(segment);\n}\n\n/**\n * A segmenter that wraps Intl.Segmenter and merges graphemes that fall\n * within paste markers into single atomic segments. This makes cursor\n * movement, deletion, word-wrap, etc. treat paste markers as single units.\n *\n * Only markers whose numeric ID exists in `validIds` are merged.\n */\nfunction segmentWithMarkers(\n\ttext: string,\n\tbaseSegmenter: Intl.Segmenter,\n\tvalidIds: Set<number>,\n): Iterable<Intl.SegmentData> {\n\t// Fast path: no paste markers in the text or no valid IDs.\n\tif (validIds.size === 0 || !text.includes(\"[paste #\")) {\n\t\treturn baseSegmenter.segment(text);\n\t}\n\n\t// Find all marker spans with valid IDs.\n\tconst markers: Array<{ start: number; end: number }> = [];\n\tfor (const m of text.matchAll(PASTE_MARKER_REGEX)) {\n\t\tconst id = Number.parseInt(m[1]!, 10);\n\t\tif (!validIds.has(id)) continue;\n\t\tmarkers.push({ start: m.index, end: m.index + m[0].length });\n\t}\n\tif (markers.length === 0) {\n\t\treturn baseSegmenter.segment(text);\n\t}\n\n\t// Build merged segment list.\n\tconst baseSegments = baseSegmenter.segment(text);\n\tconst result: Intl.SegmentData[] = [];\n\tlet markerIdx = 0;\n\n\tfor (const seg of baseSegments) {\n\t\t// Skip past markers that are entirely before this segment.\n\t\twhile (markerIdx < markers.length && markers[markerIdx]!.end <= seg.index) {\n\t\t\tmarkerIdx++;\n\t\t}\n\n\t\tconst marker = markerIdx < markers.length ? markers[markerIdx]! : null;\n\n\t\tif (marker && seg.index >= marker.start && seg.index < marker.end) {\n\t\t\t// This segment falls inside a marker.\n\t\t\t// If this is the first segment of the marker, emit a merged segment.\n\t\t\tif (seg.index === marker.start) {\n\t\t\t\tconst markerText = text.slice(marker.start, marker.end);\n\t\t\t\tresult.push({\n\t\t\t\t\tsegment: markerText,\n\t\t\t\t\tindex: marker.start,\n\t\t\t\t\tinput: text,\n\t\t\t\t});\n\t\t\t}\n\t\t\t// Otherwise skip (already merged into the first segment).\n\t\t} else {\n\t\t\tresult.push(seg);\n\t\t}\n\t}\n\n\treturn result;\n}\n\n/**\n * Represents a chunk of text for word-wrap layout.\n * Tracks both the text content and its position in the original line.\n */\nexport interface TextChunk {\n\ttext: string;\n\tstartIndex: number;\n\tendIndex: number;\n}\n\n/**\n * Split a line into word-wrapped chunks.\n * Wraps at word boundaries when possible, falling back to character-level\n * wrapping for words longer than the available width.\n *\n * @param line - The text line to wrap\n * @param maxWidth - Maximum visible width per chunk\n * @param preSegmented - Optional pre-segmented graphemes (e.g. with paste-marker awareness).\n * When omitted the default Intl.Segmenter is used.\n * @returns Array of chunks with text and position information\n */\nexport function wordWrapLine(line: string, maxWidth: number, preSegmented?: Intl.SegmentData[]): TextChunk[] {\n\tif (!line || maxWidth <= 0) {\n\t\treturn [{ text: \"\", startIndex: 0, endIndex: 0 }];\n\t}\n\n\tconst lineWidth = visibleWidth(line);\n\tif (lineWidth <= maxWidth) {\n\t\treturn [{ text: line, startIndex: 0, endIndex: line.length }];\n\t}\n\n\tconst chunks: TextChunk[] = [];\n\tconst segments = preSegmented ?? [...graphemeSegmenter.segment(line)];\n\n\tlet currentWidth = 0;\n\tlet chunkStart = 0;\n\n\t// Wrap opportunity: the position after the last whitespace before a non-whitespace\n\t// grapheme, i.e. where a line break is allowed.\n\tlet wrapOppIndex = -1;\n\tlet wrapOppWidth = 0;\n\n\tfor (let i = 0; i < segments.length; i++) {\n\t\tconst seg = segments[i]!;\n\t\tconst grapheme = seg.segment;\n\t\tconst gWidth = visibleWidth(grapheme);\n\t\tconst charIndex = seg.index;\n\t\tconst isWs = !isPasteMarker(grapheme) && isWhitespaceChar(grapheme);\n\n\t\t// Overflow check before advancing.\n\t\tif (currentWidth + gWidth > maxWidth) {\n\t\t\tif (wrapOppIndex >= 0 && currentWidth - wrapOppWidth + gWidth <= maxWidth) {\n\t\t\t\t// Backtrack to last wrap opportunity (the remaining content\n\t\t\t\t// plus the current grapheme still fits within maxWidth).\n\t\t\t\tchunks.push({ text: line.slice(chunkStart, wrapOppIndex), startIndex: chunkStart, endIndex: wrapOppIndex });\n\t\t\t\tchunkStart = wrapOppIndex;\n\t\t\t\tcurrentWidth -= wrapOppWidth;\n\t\t\t} else if (chunkStart < charIndex) {\n\t\t\t\t// No viable wrap opportunity: force-break at current position.\n\t\t\t\t// This also handles the case where backtracking to a word\n\t\t\t\t// boundary wouldn't help because the remaining content plus\n\t\t\t\t// the current grapheme (e.g. a wide character) still exceeds\n\t\t\t\t// maxWidth.\n\t\t\t\tchunks.push({ text: line.slice(chunkStart, charIndex), startIndex: chunkStart, endIndex: charIndex });\n\t\t\t\tchunkStart = charIndex;\n\t\t\t\tcurrentWidth = 0;\n\t\t\t}\n\t\t\twrapOppIndex = -1;\n\t\t}\n\n\t\tif (gWidth > maxWidth) {\n\t\t\t// Single atomic segment wider than maxWidth (e.g. paste marker\n\t\t\t// in a narrow terminal). Re-wrap it at grapheme granularity.\n\n\t\t\t// The segment remains logically atomic for cursor\n\t\t\t// movement / editing — the split is purely visual for word-wrap layout.\n\t\t\tconst subChunks = wordWrapLine(grapheme, maxWidth);\n\t\t\tfor (let j = 0; j < subChunks.length - 1; j++) {\n\t\t\t\tconst sc = subChunks[j]!;\n\t\t\t\tchunks.push({ text: sc.text, startIndex: charIndex + sc.startIndex, endIndex: charIndex + sc.endIndex });\n\t\t\t}\n\t\t\tconst last = subChunks[subChunks.length - 1]!;\n\t\t\tchunkStart = charIndex + last.startIndex;\n\t\t\tcurrentWidth = visibleWidth(last.text);\n\t\t\twrapOppIndex = -1;\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Advance.\n\t\tcurrentWidth += gWidth;\n\n\t\t// Record wrap opportunity: whitespace followed by non-whitespace.\n\t\t// Multiple spaces join (no break between them); the break point is\n\t\t// after the last space before the next word.\n\t\tconst next = segments[i + 1];\n\t\tif (isWs && next && (isPasteMarker(next.segment) || !isWhitespaceChar(next.segment))) {\n\t\t\twrapOppIndex = next.index;\n\t\t\twrapOppWidth = currentWidth;\n\t\t}\n\t}\n\n\t// Push final chunk.\n\tchunks.push({ text: line.slice(chunkStart), startIndex: chunkStart, endIndex: line.length });\n\n\treturn chunks;\n}\n\n// Kitty CSI-u sequences for printable keys, including optional shifted/base codepoints.\ninterface EditorState {\n\tlines: string[];\n\tcursorLine: number;\n\tcursorCol: number;\n}\n\ninterface LayoutLine {\n\ttext: string;\n\thasCursor: boolean;\n\tcursorPos?: number;\n}\n\nexport interface EditorTheme {\n\tborderColor: (str: string) => string;\n\tselectList: SelectListTheme;\n}\n\nexport interface EditorOptions {\n\tpaddingX?: number;\n\tautocompleteMaxVisible?: number;\n}\n\nconst SLASH_COMMAND_SELECT_LIST_LAYOUT: SelectListLayoutOptions = {\n\tminPrimaryColumnWidth: 12,\n\tmaxPrimaryColumnWidth: 32,\n};\n\nconst ATTACHMENT_AUTOCOMPLETE_DEBOUNCE_MS = 20;\n\nexport class Editor implements Component, Focusable {\n\tprivate state: EditorState = {\n\t\tlines: [\"\"],\n\t\tcursorLine: 0,\n\t\tcursorCol: 0,\n\t};\n\n\t/** Focusable interface - set by TUI when focus changes */\n\tfocused: boolean = false;\n\n\tprotected tui: TUI;\n\tprivate theme: EditorTheme;\n\tprivate paddingX: number = 0;\n\n\t// Store last render width for cursor navigation\n\tprivate lastWidth: number = 80;\n\n\t// Vertical scrolling support\n\tprivate scrollOffset: number = 0;\n\n\t// Border color (can be changed dynamically)\n\tpublic borderColor: (str: string) => string;\n\n\t// Autocomplete support\n\tprivate autocompleteProvider?: AutocompleteProvider;\n\tprivate autocompleteList?: SelectList;\n\tprivate autocompleteState: \"regular\" | \"force\" | null = null;\n\tprivate autocompletePrefix: string = \"\";\n\tprivate autocompleteMaxVisible: number = 5;\n\tprivate autocompleteAbort?: AbortController;\n\tprivate autocompleteDebounceTimer?: ReturnType<typeof setTimeout>;\n\tprivate autocompleteRequestTask: Promise<void> = Promise.resolve();\n\tprivate autocompleteStartToken: number = 0;\n\tprivate autocompleteRequestId: number = 0;\n\n\t// Paste tracking for large pastes\n\tprivate pastes: Map<number, string> = new Map();\n\tprivate pasteCounter: number = 0;\n\n\t// Bracketed paste mode buffering\n\tprivate pasteBuffer: string = \"\";\n\tprivate isInPaste: boolean = false;\n\n\t// Prompt history for up/down navigation\n\tprivate history: string[] = [];\n\tprivate historyIndex: number = -1; // -1 = not browsing, 0 = most recent, 1 = older, etc.\n\n\t// Kill ring for Emacs-style kill/yank operations\n\tprivate killRing = new KillRing();\n\tprivate lastAction: \"kill\" | \"yank\" | \"type-word\" | null = null;\n\n\t// Character jump mode\n\tprivate jumpMode: \"forward\" | \"backward\" | null = null;\n\n\t// Preferred visual column for vertical cursor movement (sticky column)\n\tprivate preferredVisualCol: number | null = null;\n\n\t// When the cursor is snapped to the start of an atomic segment, e.g. a\n\t// paste marker, cursorCol no longer reflects where the cursor would have\n\t// landed. This field stores the pre-snap cursorCol so that the next\n\t// vertical move can resolve it to a visual column on whatever VL it belongs\n\t// to.\n\tprivate snappedFromCursorCol: number | null = null;\n\n\t// Undo support\n\tprivate undoStack = new UndoStack<EditorState>();\n\n\tpublic onSubmit?: (text: string) => void;\n\tpublic onChange?: (text: string) => void;\n\tpublic disableSubmit: boolean = false;\n\n\tconstructor(tui: TUI, theme: EditorTheme, options: EditorOptions = {}) {\n\t\tthis.tui = tui;\n\t\tthis.theme = theme;\n\t\tthis.borderColor = theme.borderColor;\n\t\tconst paddingX = options.paddingX ?? 0;\n\t\tthis.paddingX = Number.isFinite(paddingX) ? Math.max(0, Math.floor(paddingX)) : 0;\n\t\tconst maxVisible = options.autocompleteMaxVisible ?? 5;\n\t\tthis.autocompleteMaxVisible = Number.isFinite(maxVisible) ? Math.max(3, Math.min(20, Math.floor(maxVisible))) : 5;\n\t}\n\n\t/** Set of currently valid paste IDs, for marker-aware segmentation. */\n\tprivate validPasteIds(): Set<number> {\n\t\treturn new Set(this.pastes.keys());\n\t}\n\n\t/** Segment text with paste-marker awareness, only merging markers with valid IDs. */\n\tprivate segment(text: string, mode: \"word\" | \"grapheme\"): Iterable<Intl.SegmentData> {\n\t\treturn segmentWithMarkers(text, mode === \"word\" ? wordSegmenter : graphemeSegmenter, this.validPasteIds());\n\t}\n\n\tgetPaddingX(): number {\n\t\treturn this.paddingX;\n\t}\n\n\tsetPaddingX(padding: number): void {\n\t\tconst newPadding = Number.isFinite(padding) ? Math.max(0, Math.floor(padding)) : 0;\n\t\tif (this.paddingX !== newPadding) {\n\t\t\tthis.paddingX = newPadding;\n\t\t\tthis.tui.requestRender();\n\t\t}\n\t}\n\n\tgetAutocompleteMaxVisible(): number {\n\t\treturn this.autocompleteMaxVisible;\n\t}\n\n\tsetAutocompleteMaxVisible(maxVisible: number): void {\n\t\tconst newMaxVisible = Number.isFinite(maxVisible) ? Math.max(3, Math.min(20, Math.floor(maxVisible))) : 5;\n\t\tif (this.autocompleteMaxVisible !== newMaxVisible) {\n\t\t\tthis.autocompleteMaxVisible = newMaxVisible;\n\t\t\tthis.tui.requestRender();\n\t\t}\n\t}\n\n\tsetAutocompleteProvider(provider: AutocompleteProvider): void {\n\t\tthis.cancelAutocomplete();\n\t\tthis.autocompleteProvider = provider;\n\t}\n\n\t/**\n\t * Add a prompt to history for up/down arrow navigation.\n\t * Called after successful submission.\n\t */\n\taddToHistory(text: string): void {\n\t\tconst trimmed = text.trim();\n\t\tif (!trimmed) return;\n\t\t// Don't add consecutive duplicates\n\t\tif (this.history.length > 0 && this.history[0] === trimmed) return;\n\t\tthis.history.unshift(trimmed);\n\t\t// Limit history size\n\t\tif (this.history.length > 100) {\n\t\t\tthis.history.pop();\n\t\t}\n\t}\n\n\tprivate isEditorEmpty(): boolean {\n\t\treturn this.state.lines.length === 1 && this.state.lines[0] === \"\";\n\t}\n\n\tprivate isOnFirstVisualLine(): boolean {\n\t\tconst visualLines = this.buildVisualLineMap(this.lastWidth);\n\t\tconst currentVisualLine = this.findCurrentVisualLine(visualLines);\n\t\treturn currentVisualLine === 0;\n\t}\n\n\tprivate isOnLastVisualLine(): boolean {\n\t\tconst visualLines = this.buildVisualLineMap(this.lastWidth);\n\t\tconst currentVisualLine = this.findCurrentVisualLine(visualLines);\n\t\treturn currentVisualLine === visualLines.length - 1;\n\t}\n\n\tprivate navigateHistory(direction: 1 | -1): void {\n\t\tthis.lastAction = null;\n\t\tif (this.history.length === 0) return;\n\n\t\tconst newIndex = this.historyIndex - direction; // Up(-1) increases index, Down(1) decreases\n\t\tif (newIndex < -1 || newIndex >= this.history.length) return;\n\n\t\t// Capture state when first entering history browsing mode\n\t\tif (this.historyIndex === -1 && newIndex >= 0) {\n\t\t\tthis.pushUndoSnapshot();\n\t\t}\n\n\t\tthis.historyIndex = newIndex;\n\n\t\tif (this.historyIndex === -1) {\n\t\t\t// Returned to \"current\" state - clear editor\n\t\t\tthis.setTextInternal(\"\");\n\t\t} else {\n\t\t\tthis.setTextInternal(this.history[this.historyIndex] || \"\");\n\t\t}\n\t}\n\n\t/** Internal setText that doesn't reset history state - used by navigateHistory */\n\tprivate setTextInternal(text: string): void {\n\t\tconst lines = text.split(\"\\n\");\n\t\tthis.state.lines = lines.length === 0 ? [\"\"] : lines;\n\t\tthis.state.cursorLine = this.state.lines.length - 1;\n\t\tthis.setCursorCol(this.state.lines[this.state.cursorLine]?.length || 0);\n\t\t// Reset scroll - render() will adjust to show cursor\n\t\tthis.scrollOffset = 0;\n\n\t\tif (this.onChange) {\n\t\t\tthis.onChange(this.getText());\n\t\t}\n\t}\n\n\tinvalidate(): void {\n\t\t// No cached state to invalidate currently\n\t}\n\n\trender(width: number): string[] {\n\t\tconst maxPadding = Math.max(0, Math.floor((width - 1) / 2));\n\t\tconst paddingX = Math.min(this.paddingX, maxPadding);\n\t\tconst contentWidth = Math.max(1, width - paddingX * 2);\n\n\t\t// Layout width: with padding the cursor can overflow into it,\n\t\t// without padding we reserve 1 column for the cursor.\n\t\tconst layoutWidth = Math.max(1, contentWidth - (paddingX ? 0 : 1));\n\n\t\t// Store for cursor navigation (must match wrapping width)\n\t\tthis.lastWidth = layoutWidth;\n\n\t\tconst horizontal = this.borderColor(\"─\");\n\n\t\t// Layout the text\n\t\tconst layoutLines = this.layoutText(layoutWidth);\n\n\t\t// Calculate max visible lines: 30% of terminal height, minimum 5 lines\n\t\tconst terminalRows = this.tui.terminal.rows;\n\t\tconst maxVisibleLines = Math.max(5, Math.floor(terminalRows * 0.3));\n\n\t\t// Find the cursor line index in layoutLines\n\t\tlet cursorLineIndex = layoutLines.findIndex((line) => line.hasCursor);\n\t\tif (cursorLineIndex === -1) cursorLineIndex = 0;\n\n\t\t// Adjust scroll offset to keep cursor visible\n\t\tif (cursorLineIndex < this.scrollOffset) {\n\t\t\tthis.scrollOffset = cursorLineIndex;\n\t\t} else if (cursorLineIndex >= this.scrollOffset + maxVisibleLines) {\n\t\t\tthis.scrollOffset = cursorLineIndex - maxVisibleLines + 1;\n\t\t}\n\n\t\t// Clamp scroll offset to valid range\n\t\tconst maxScrollOffset = Math.max(0, layoutLines.length - maxVisibleLines);\n\t\tthis.scrollOffset = Math.max(0, Math.min(this.scrollOffset, maxScrollOffset));\n\n\t\t// Get visible lines slice\n\t\tconst visibleLines = layoutLines.slice(this.scrollOffset, this.scrollOffset + maxVisibleLines);\n\n\t\tconst result: string[] = [];\n\t\tconst leftPadding = \" \".repeat(paddingX);\n\t\tconst rightPadding = leftPadding;\n\n\t\t// Render top border (with scroll indicator if scrolled down)\n\t\tif (this.scrollOffset > 0) {\n\t\t\tconst indicator = `─── ↑ ${this.scrollOffset} more `;\n\t\t\tconst remaining = width - visibleWidth(indicator);\n\t\t\tif (remaining >= 0) {\n\t\t\t\tresult.push(this.borderColor(indicator + \"─\".repeat(remaining)));\n\t\t\t} else {\n\t\t\t\tresult.push(this.borderColor(truncateToWidth(indicator, width)));\n\t\t\t}\n\t\t} else {\n\t\t\tresult.push(horizontal.repeat(width));\n\t\t}\n\n\t\t// Render each visible layout line\n\t\t// Emit hardware cursor marker only when focused and not showing autocomplete\n\t\tconst emitCursorMarker = this.focused && !this.autocompleteState;\n\n\t\tfor (const layoutLine of visibleLines) {\n\t\t\tlet displayText = layoutLine.text;\n\t\t\tlet lineVisibleWidth = visibleWidth(layoutLine.text);\n\t\t\tlet cursorInPadding = false;\n\n\t\t\t// Add cursor if this line has it\n\t\t\tif (layoutLine.hasCursor && layoutLine.cursorPos !== undefined) {\n\t\t\t\tconst before = displayText.slice(0, layoutLine.cursorPos);\n\t\t\t\tconst after = displayText.slice(layoutLine.cursorPos);\n\n\t\t\t\t// Hardware cursor marker (zero-width, emitted before fake cursor for IME positioning)\n\t\t\t\tconst marker = emitCursorMarker ? CURSOR_MARKER : \"\";\n\n\t\t\t\tif (after.length > 0) {\n\t\t\t\t\t// Cursor is on a character (grapheme) - replace it with highlighted version\n\t\t\t\t\t// Get the first grapheme from 'after'\n\t\t\t\t\tconst afterGraphemes = [...this.segment(after, \"grapheme\")];\n\t\t\t\t\tconst firstGrapheme = afterGraphemes[0]?.segment || \"\";\n\t\t\t\t\tconst restAfter = after.slice(firstGrapheme.length);\n\t\t\t\t\tconst cursor = `\\x1b[7m${firstGrapheme}\\x1b[0m`;\n\t\t\t\t\tdisplayText = before + marker + cursor + restAfter;\n\t\t\t\t\t// lineVisibleWidth stays the same - we're replacing, not adding\n\t\t\t\t} else {\n\t\t\t\t\t// Cursor is at the end - add highlighted space\n\t\t\t\t\tconst cursor = \"\\x1b[7m \\x1b[0m\";\n\t\t\t\t\tdisplayText = before + marker + cursor;\n\t\t\t\t\tlineVisibleWidth = lineVisibleWidth + 1;\n\t\t\t\t\t// If cursor overflows content width into the padding, flag it\n\t\t\t\t\tif (lineVisibleWidth > contentWidth && paddingX > 0) {\n\t\t\t\t\t\tcursorInPadding = true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Calculate padding based on actual visible width\n\t\t\tconst padding = \" \".repeat(Math.max(0, contentWidth - lineVisibleWidth));\n\t\t\tconst lineRightPadding = cursorInPadding ? rightPadding.slice(1) : rightPadding;\n\n\t\t\t// Render the line (no side borders, just horizontal lines above and below)\n\t\t\tresult.push(`${leftPadding}${displayText}${padding}${lineRightPadding}`);\n\t\t}\n\n\t\t// Render bottom border (with scroll indicator if more content below)\n\t\tconst linesBelow = layoutLines.length - (this.scrollOffset + visibleLines.length);\n\t\tif (linesBelow > 0) {\n\t\t\tconst indicator = `─── ↓ ${linesBelow} more `;\n\t\t\tconst remaining = width - visibleWidth(indicator);\n\t\t\tresult.push(this.borderColor(indicator + \"─\".repeat(Math.max(0, remaining))));\n\t\t} else {\n\t\t\tresult.push(horizontal.repeat(width));\n\t\t}\n\n\t\t// Add autocomplete list if active\n\t\tif (this.autocompleteState && this.autocompleteList) {\n\t\t\tconst autocompleteResult = this.autocompleteList.render(contentWidth);\n\t\t\tfor (const line of autocompleteResult) {\n\t\t\t\tconst lineWidth = visibleWidth(line);\n\t\t\t\tconst linePadding = \" \".repeat(Math.max(0, contentWidth - lineWidth));\n\t\t\t\tresult.push(`${leftPadding}${line}${linePadding}${rightPadding}`);\n\t\t\t}\n\t\t}\n\n\t\treturn result;\n\t}\n\n\thandleInput(data: string): void {\n\t\tconst kb = getKeybindings();\n\n\t\t// Handle character jump mode (awaiting next character to jump to)\n\t\tif (this.jumpMode !== null) {\n\t\t\t// Cancel if the hotkey is pressed again\n\t\t\tif (kb.matches(data, \"tui.editor.jumpForward\") || kb.matches(data, \"tui.editor.jumpBackward\")) {\n\t\t\t\tthis.jumpMode = null;\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst printable = decodePrintableKey(data) ?? (data.charCodeAt(0) >= 32 ? data : undefined);\n\t\t\tif (printable !== undefined) {\n\t\t\t\t// Printable character - perform the jump\n\t\t\t\tconst direction = this.jumpMode;\n\t\t\t\tthis.jumpMode = null;\n\t\t\t\tthis.jumpToChar(printable, direction);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Control character - cancel and fall through to normal handling\n\t\t\tthis.jumpMode = null;\n\t\t}\n\n\t\t// Handle bracketed paste mode\n\t\tif (data.includes(\"\\x1b[200~\")) {\n\t\t\tthis.isInPaste = true;\n\t\t\tthis.pasteBuffer = \"\";\n\t\t\tdata = data.replace(\"\\x1b[200~\", \"\");\n\t\t}\n\n\t\tif (this.isInPaste) {\n\t\t\tthis.pasteBuffer += data;\n\t\t\tconst endIndex = this.pasteBuffer.indexOf(\"\\x1b[201~\");\n\t\t\tif (endIndex !== -1) {\n\t\t\t\tconst pasteContent = this.pasteBuffer.substring(0, endIndex);\n\t\t\t\tif (pasteContent.length > 0) {\n\t\t\t\t\tthis.handlePaste(pasteContent);\n\t\t\t\t}\n\t\t\t\tthis.isInPaste = false;\n\t\t\t\tconst remaining = this.pasteBuffer.substring(endIndex + 6);\n\t\t\t\tthis.pasteBuffer = \"\";\n\t\t\t\tif (remaining.length > 0) {\n\t\t\t\t\tthis.handleInput(remaining);\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// Ctrl+C - let parent handle (exit/clear)\n\t\tif (kb.matches(data, \"tui.input.copy\")) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Undo\n\t\tif (kb.matches(data, \"tui.editor.undo\")) {\n\t\t\tthis.undo();\n\t\t\treturn;\n\t\t}\n\n\t\t// Handle autocomplete mode\n\t\tif (this.autocompleteState && this.autocompleteList) {\n\t\t\tif (kb.matches(data, \"tui.select.cancel\")) {\n\t\t\t\tthis.cancelAutocomplete();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (kb.matches(data, \"tui.select.up\") || kb.matches(data, \"tui.select.down\")) {\n\t\t\t\tthis.autocompleteList.handleInput(data);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (kb.matches(data, \"tui.input.tab\")) {\n\t\t\t\tconst selected = this.autocompleteList.getSelectedItem();\n\t\t\t\tif (selected && this.autocompleteProvider) {\n\t\t\t\t\tthis.pushUndoSnapshot();\n\t\t\t\t\tthis.lastAction = null;\n\t\t\t\t\tconst result = this.autocompleteProvider.applyCompletion(\n\t\t\t\t\t\tthis.state.lines,\n\t\t\t\t\t\tthis.state.cursorLine,\n\t\t\t\t\t\tthis.state.cursorCol,\n\t\t\t\t\t\tselected,\n\t\t\t\t\t\tthis.autocompletePrefix,\n\t\t\t\t\t);\n\t\t\t\t\tthis.state.lines = result.lines;\n\t\t\t\t\tthis.state.cursorLine = result.cursorLine;\n\t\t\t\t\tthis.setCursorCol(result.cursorCol);\n\t\t\t\t\tthis.cancelAutocomplete();\n\t\t\t\t\tif (this.onChange) this.onChange(this.getText());\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (kb.matches(data, \"tui.select.confirm\")) {\n\t\t\t\tconst selected = this.autocompleteList.getSelectedItem();\n\t\t\t\tif (selected && this.autocompleteProvider) {\n\t\t\t\t\tthis.pushUndoSnapshot();\n\t\t\t\t\tthis.lastAction = null;\n\t\t\t\t\tconst result = this.autocompleteProvider.applyCompletion(\n\t\t\t\t\t\tthis.state.lines,\n\t\t\t\t\t\tthis.state.cursorLine,\n\t\t\t\t\t\tthis.state.cursorCol,\n\t\t\t\t\t\tselected,\n\t\t\t\t\t\tthis.autocompletePrefix,\n\t\t\t\t\t);\n\t\t\t\t\tthis.state.lines = result.lines;\n\t\t\t\t\tthis.state.cursorLine = result.cursorLine;\n\t\t\t\t\tthis.setCursorCol(result.cursorCol);\n\n\t\t\t\t\tif (this.autocompletePrefix.startsWith(\"/\")) {\n\t\t\t\t\t\tthis.cancelAutocomplete();\n\t\t\t\t\t\t// Fall through to submit\n\t\t\t\t\t} else {\n\t\t\t\t\t\tthis.cancelAutocomplete();\n\t\t\t\t\t\tif (this.onChange) this.onChange(this.getText());\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Tab - trigger completion\n\t\tif (kb.matches(data, \"tui.input.tab\") && !this.autocompleteState) {\n\t\t\tthis.handleTabCompletion();\n\t\t\treturn;\n\t\t}\n\n\t\t// Deletion actions\n\t\tif (kb.matches(data, \"tui.editor.deleteToLineEnd\")) {\n\t\t\tthis.deleteToEndOfLine();\n\t\t\treturn;\n\t\t}\n\t\tif (kb.matches(data, \"tui.editor.deleteToLineStart\")) {\n\t\t\tthis.deleteToStartOfLine();\n\t\t\treturn;\n\t\t}\n\t\tif (kb.matches(data, \"tui.editor.deleteWordBackward\")) {\n\t\t\tthis.deleteWordBackwards();\n\t\t\treturn;\n\t\t}\n\t\tif (kb.matches(data, \"tui.editor.deleteWordForward\")) {\n\t\t\tthis.deleteWordForward();\n\t\t\treturn;\n\t\t}\n\t\tif (kb.matches(data, \"tui.editor.deleteCharBackward\") || matchesKey(data, \"shift+backspace\")) {\n\t\t\tthis.handleBackspace();\n\t\t\treturn;\n\t\t}\n\t\tif (kb.matches(data, \"tui.editor.deleteCharForward\") || matchesKey(data, \"shift+delete\")) {\n\t\t\tthis.handleForwardDelete();\n\t\t\treturn;\n\t\t}\n\n\t\t// Kill ring actions\n\t\tif (kb.matches(data, \"tui.editor.yank\")) {\n\t\t\tthis.yank();\n\t\t\treturn;\n\t\t}\n\t\tif (kb.matches(data, \"tui.editor.yankPop\")) {\n\t\t\tthis.yankPop();\n\t\t\treturn;\n\t\t}\n\n\t\t// Cursor movement actions\n\t\tif (kb.matches(data, \"tui.editor.cursorLineStart\")) {\n\t\t\tthis.moveToLineStart();\n\t\t\treturn;\n\t\t}\n\t\tif (kb.matches(data, \"tui.editor.cursorLineEnd\")) {\n\t\t\tthis.moveToLineEnd();\n\t\t\treturn;\n\t\t}\n\t\tif (kb.matches(data, \"tui.editor.cursorWordLeft\")) {\n\t\t\tthis.moveWordBackwards();\n\t\t\treturn;\n\t\t}\n\t\tif (kb.matches(data, \"tui.editor.cursorWordRight\")) {\n\t\t\tthis.moveWordForwards();\n\t\t\treturn;\n\t\t}\n\n\t\t// New line\n\t\tif (\n\t\t\tkb.matches(data, \"tui.input.newLine\") ||\n\t\t\t(data.charCodeAt(0) === 10 && data.length > 1) ||\n\t\t\tdata === \"\\x1b\\r\" ||\n\t\t\tdata === \"\\x1b[13;2~\" ||\n\t\t\t(data.length > 1 && data.includes(\"\\x1b\") && data.includes(\"\\r\")) ||\n\t\t\t(data === \"\\n\" && data.length === 1)\n\t\t) {\n\t\t\tif (this.shouldSubmitOnBackslashEnter(data, kb)) {\n\t\t\t\tthis.handleBackspace();\n\t\t\t\tthis.submitValue();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tthis.addNewLine();\n\t\t\treturn;\n\t\t}\n\n\t\t// Submit (Enter)\n\t\tif (kb.matches(data, \"tui.input.submit\")) {\n\t\t\tif (this.disableSubmit) return;\n\n\t\t\t// Workaround for terminals without Shift+Enter support:\n\t\t\t// If char before cursor is \\, delete it and insert newline instead of submitting.\n\t\t\tconst currentLine = this.state.lines[this.state.cursorLine] || \"\";\n\t\t\tif (this.state.cursorCol > 0 && currentLine[this.state.cursorCol - 1] === \"\\\\\") {\n\t\t\t\tthis.handleBackspace();\n\t\t\t\tthis.addNewLine();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tthis.submitValue();\n\t\t\treturn;\n\t\t}\n\n\t\t// Arrow key navigation (with history support)\n\t\tif (kb.matches(data, \"tui.editor.cursorUp\")) {\n\t\t\tif (this.isEditorEmpty()) {\n\t\t\t\tthis.navigateHistory(-1);\n\t\t\t} else if (this.historyIndex > -1 && this.isOnFirstVisualLine()) {\n\t\t\t\tthis.navigateHistory(-1);\n\t\t\t} else if (this.isOnFirstVisualLine()) {\n\t\t\t\t// Already at top - jump to start of line\n\t\t\t\tthis.moveToLineStart();\n\t\t\t} else {\n\t\t\t\tthis.moveCursor(-1, 0);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tif (kb.matches(data, \"tui.editor.cursorDown\")) {\n\t\t\tif (this.historyIndex > -1 && this.isOnLastVisualLine()) {\n\t\t\t\tthis.navigateHistory(1);\n\t\t\t} else if (this.isOnLastVisualLine()) {\n\t\t\t\t// Already at bottom - jump to end of line\n\t\t\t\tthis.moveToLineEnd();\n\t\t\t} else {\n\t\t\t\tthis.moveCursor(1, 0);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tif (kb.matches(data, \"tui.editor.cursorRight\")) {\n\t\t\tthis.moveCursor(0, 1);\n\t\t\treturn;\n\t\t}\n\t\tif (kb.matches(data, \"tui.editor.cursorLeft\")) {\n\t\t\tthis.moveCursor(0, -1);\n\t\t\treturn;\n\t\t}\n\n\t\t// Page up/down - scroll by page and move cursor\n\t\tif (kb.matches(data, \"tui.editor.pageUp\")) {\n\t\t\tthis.pageScroll(-1);\n\t\t\treturn;\n\t\t}\n\t\tif (kb.matches(data, \"tui.editor.pageDown\")) {\n\t\t\tthis.pageScroll(1);\n\t\t\treturn;\n\t\t}\n\n\t\t// Character jump mode triggers\n\t\tif (kb.matches(data, \"tui.editor.jumpForward\")) {\n\t\t\tthis.jumpMode = \"forward\";\n\t\t\treturn;\n\t\t}\n\t\tif (kb.matches(data, \"tui.editor.jumpBackward\")) {\n\t\t\tthis.jumpMode = \"backward\";\n\t\t\treturn;\n\t\t}\n\n\t\t// Shift+Space - insert regular space\n\t\tif (matchesKey(data, \"shift+space\")) {\n\t\t\tthis.insertCharacter(\" \");\n\t\t\treturn;\n\t\t}\n\n\t\tconst printable = decodePrintableKey(data);\n\t\tif (printable !== undefined) {\n\t\t\tthis.insertCharacter(printable);\n\t\t\treturn;\n\t\t}\n\n\t\t// Regular characters\n\t\tif (data.charCodeAt(0) >= 32) {\n\t\t\tthis.insertCharacter(data);\n\t\t}\n\t}\n\n\tprivate layoutText(contentWidth: number): LayoutLine[] {\n\t\tconst layoutLines: LayoutLine[] = [];\n\n\t\tif (this.state.lines.length === 0 || (this.state.lines.length === 1 && this.state.lines[0] === \"\")) {\n\t\t\t// Empty editor\n\t\t\tlayoutLines.push({\n\t\t\t\ttext: \"\",\n\t\t\t\thasCursor: true,\n\t\t\t\tcursorPos: 0,\n\t\t\t});\n\t\t\treturn layoutLines;\n\t\t}\n\n\t\t// Process each logical line\n\t\tfor (let i = 0; i < this.state.lines.length; i++) {\n\t\t\tconst line = this.state.lines[i] || \"\";\n\t\t\tconst isCurrentLine = i === this.state.cursorLine;\n\t\t\tconst lineVisibleWidth = visibleWidth(line);\n\n\t\t\tif (lineVisibleWidth <= contentWidth) {\n\t\t\t\t// Line fits in one layout line\n\t\t\t\tif (isCurrentLine) {\n\t\t\t\t\tlayoutLines.push({\n\t\t\t\t\t\ttext: line,\n\t\t\t\t\t\thasCursor: true,\n\t\t\t\t\t\tcursorPos: this.state.cursorCol,\n\t\t\t\t\t});\n\t\t\t\t} else {\n\t\t\t\t\tlayoutLines.push({\n\t\t\t\t\t\ttext: line,\n\t\t\t\t\t\thasCursor: false,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// Line needs wrapping - use word-aware wrapping\n\t\t\t\tconst chunks = wordWrapLine(line, contentWidth, [...this.segment(line, \"grapheme\")]);\n\n\t\t\t\tfor (let chunkIndex = 0; chunkIndex < chunks.length; chunkIndex++) {\n\t\t\t\t\tconst chunk = chunks[chunkIndex];\n\t\t\t\t\tif (!chunk) continue;\n\n\t\t\t\t\tconst cursorPos = this.state.cursorCol;\n\t\t\t\t\tconst isLastChunk = chunkIndex === chunks.length - 1;\n\n\t\t\t\t\t// Determine if cursor is in this chunk\n\t\t\t\t\t// For word-wrapped chunks, we need to handle the case where\n\t\t\t\t\t// cursor might be in trimmed whitespace at end of chunk\n\t\t\t\t\tlet hasCursorInChunk = false;\n\t\t\t\t\tlet adjustedCursorPos = 0;\n\n\t\t\t\t\tif (isCurrentLine) {\n\t\t\t\t\t\tif (isLastChunk) {\n\t\t\t\t\t\t\t// Last chunk: cursor belongs here if >= startIndex\n\t\t\t\t\t\t\thasCursorInChunk = cursorPos >= chunk.startIndex;\n\t\t\t\t\t\t\tadjustedCursorPos = cursorPos - chunk.startIndex;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t// Non-last chunk: cursor belongs here if in range [startIndex, endIndex)\n\t\t\t\t\t\t\t// But we need to handle the visual position in the trimmed text\n\t\t\t\t\t\t\thasCursorInChunk = cursorPos >= chunk.startIndex && cursorPos < chunk.endIndex;\n\t\t\t\t\t\t\tif (hasCursorInChunk) {\n\t\t\t\t\t\t\t\tadjustedCursorPos = cursorPos - chunk.startIndex;\n\t\t\t\t\t\t\t\t// Clamp to text length (in case cursor was in trimmed whitespace)\n\t\t\t\t\t\t\t\tif (adjustedCursorPos > chunk.text.length) {\n\t\t\t\t\t\t\t\t\tadjustedCursorPos = chunk.text.length;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif (hasCursorInChunk) {\n\t\t\t\t\t\tlayoutLines.push({\n\t\t\t\t\t\t\ttext: chunk.text,\n\t\t\t\t\t\t\thasCursor: true,\n\t\t\t\t\t\t\tcursorPos: adjustedCursorPos,\n\t\t\t\t\t\t});\n\t\t\t\t\t} else {\n\t\t\t\t\t\tlayoutLines.push({\n\t\t\t\t\t\t\ttext: chunk.text,\n\t\t\t\t\t\t\thasCursor: false,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn layoutLines;\n\t}\n\n\tgetText(): string {\n\t\treturn this.state.lines.join(\"\\n\");\n\t}\n\n\tprivate expandPasteMarkers(text: string): string {\n\t\tlet result = text;\n\t\tfor (const [pasteId, pasteContent] of this.pastes) {\n\t\t\tconst markerRegex = new RegExp(`\\\\[paste #${pasteId}( (\\\\+\\\\d+ lines|\\\\d+ chars))?\\\\]`, \"g\");\n\t\t\tresult = result.replace(markerRegex, () => pasteContent);\n\t\t}\n\t\treturn result;\n\t}\n\n\t/**\n\t * Get text with paste markers expanded to their actual content.\n\t * Use this when you need the full content (e.g., for external editor).\n\t */\n\tgetExpandedText(): string {\n\t\treturn this.expandPasteMarkers(this.state.lines.join(\"\\n\"));\n\t}\n\n\tgetLines(): string[] {\n\t\treturn [...this.state.lines];\n\t}\n\n\tgetCursor(): { line: number; col: number } {\n\t\treturn { line: this.state.cursorLine, col: this.state.cursorCol };\n\t}\n\n\tsetText(text: string): void {\n\t\tthis.cancelAutocomplete();\n\t\tthis.lastAction = null;\n\t\tthis.historyIndex = -1; // Exit history browsing mode\n\t\tconst normalized = this.normalizeText(text);\n\t\t// Push undo snapshot if content differs (makes programmatic changes undoable)\n\t\tif (this.getText() !== normalized) {\n\t\t\tthis.pushUndoSnapshot();\n\t\t}\n\t\tthis.setTextInternal(normalized);\n\t}\n\n\t/**\n\t * Insert text at the current cursor position.\n\t * Used for programmatic insertion (e.g., clipboard image markers).\n\t * This is atomic for undo - single undo restores entire pre-insert state.\n\t */\n\tinsertTextAtCursor(text: string): void {\n\t\tif (!text) return;\n\t\tthis.cancelAutocomplete();\n\t\tthis.pushUndoSnapshot();\n\t\tthis.lastAction = null;\n\t\tthis.historyIndex = -1;\n\t\tthis.insertTextAtCursorInternal(text);\n\t}\n\n\t/**\n\t * Normalize text for editor storage:\n\t * - Normalize line endings (\\r\\n and \\r -> \\n)\n\t * - Expand tabs to 4 spaces\n\t */\n\tprivate normalizeText(text: string): string {\n\t\treturn text.replace(/\\r\\n/g, \"\\n\").replace(/\\r/g, \"\\n\").replace(/\\t/g, \" \");\n\t}\n\n\t/**\n\t * Internal text insertion at cursor. Handles single and multi-line text.\n\t * Does not push undo snapshots or trigger autocomplete - caller is responsible.\n\t * Normalizes line endings and calls onChange once at the end.\n\t */\n\tprivate insertTextAtCursorInternal(text: string): void {\n\t\tif (!text) return;\n\n\t\t// Normalize line endings and tabs\n\t\tconst normalized = this.normalizeText(text);\n\t\tconst insertedLines = normalized.split(\"\\n\");\n\n\t\tconst currentLine = this.state.lines[this.state.cursorLine] || \"\";\n\t\tconst beforeCursor = currentLine.slice(0, this.state.cursorCol);\n\t\tconst afterCursor = currentLine.slice(this.state.cursorCol);\n\n\t\tif (insertedLines.length === 1) {\n\t\t\t// Single line - insert at cursor position\n\t\t\tthis.state.lines[this.state.cursorLine] = beforeCursor + normalized + afterCursor;\n\t\t\tthis.setCursorCol(this.state.cursorCol + normalized.length);\n\t\t} else {\n\t\t\t// Multi-line insertion\n\t\t\tthis.state.lines = [\n\t\t\t\t// All lines before current line\n\t\t\t\t...this.state.lines.slice(0, this.state.cursorLine),\n\n\t\t\t\t// The first inserted line merged with text before cursor\n\t\t\t\tbeforeCursor + insertedLines[0],\n\n\t\t\t\t// All middle inserted lines\n\t\t\t\t...insertedLines.slice(1, -1),\n\n\t\t\t\t// The last inserted line with text after cursor\n\t\t\t\tinsertedLines[insertedLines.length - 1] + afterCursor,\n\n\t\t\t\t// All lines after current line\n\t\t\t\t...this.state.lines.slice(this.state.cursorLine + 1),\n\t\t\t];\n\n\t\t\tthis.state.cursorLine += insertedLines.length - 1;\n\t\t\tthis.setCursorCol((insertedLines[insertedLines.length - 1] || \"\").length);\n\t\t}\n\n\t\tif (this.onChange) {\n\t\t\tthis.onChange(this.getText());\n\t\t}\n\t}\n\n\t// All the editor methods from before...\n\tprivate insertCharacter(char: string, skipUndoCoalescing?: boolean): void {\n\t\tthis.historyIndex = -1; // Exit history browsing mode\n\n\t\t// Undo coalescing (fish-style):\n\t\t// - Consecutive word chars coalesce into one undo unit\n\t\t// - Space captures state before itself (so undo removes space+following word together)\n\t\t// - Each space is separately undoable\n\t\t// Skip coalescing when called from atomic operations (e.g., handlePaste)\n\t\tif (!skipUndoCoalescing) {\n\t\t\tif (isWhitespaceChar(char) || this.lastAction !== \"type-word\") {\n\t\t\t\tthis.pushUndoSnapshot();\n\t\t\t}\n\t\t\tthis.lastAction = \"type-word\";\n\t\t}\n\n\t\tconst line = this.state.lines[this.state.cursorLine] || \"\";\n\n\t\tconst before = line.slice(0, this.state.cursorCol);\n\t\tconst after = line.slice(this.state.cursorCol);\n\n\t\tthis.state.lines[this.state.cursorLine] = before + char + after;\n\t\tthis.setCursorCol(this.state.cursorCol + char.length);\n\n\t\tif (this.onChange) {\n\t\t\tthis.onChange(this.getText());\n\t\t}\n\n\t\t// Check if we should trigger or update autocomplete\n\t\tif (!this.autocompleteState) {\n\t\t\t// Auto-trigger for \"/\" at the start of a line (slash commands)\n\t\t\tif (char === \"/\" && this.isAtStartOfMessage()) {\n\t\t\t\tthis.tryTriggerAutocomplete();\n\t\t\t}\n\t\t\t// Auto-trigger for symbol-based completion like @ or # at token boundaries\n\t\t\telse if (char === \"@\" || char === \"#\") {\n\t\t\t\tconst currentLine = this.state.lines[this.state.cursorLine] || \"\";\n\t\t\t\tconst textBeforeCursor = currentLine.slice(0, this.state.cursorCol);\n\t\t\t\tconst charBeforeSymbol = textBeforeCursor[textBeforeCursor.length - 2];\n\t\t\t\tif (textBeforeCursor.length === 1 || charBeforeSymbol === \" \" || charBeforeSymbol === \"\\t\") {\n\t\t\t\t\tthis.tryTriggerAutocomplete();\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Also auto-trigger when typing letters in a slash command or symbol completion context\n\t\t\telse if (/[a-zA-Z0-9.\\-_]/.test(char)) {\n\t\t\t\tconst currentLine = this.state.lines[this.state.cursorLine] || \"\";\n\t\t\t\tconst textBeforeCursor = currentLine.slice(0, this.state.cursorCol);\n\t\t\t\t// Check if we're in a slash command (with or without space for arguments)\n\t\t\t\tif (this.isInSlashCommandContext(textBeforeCursor)) {\n\t\t\t\t\tthis.tryTriggerAutocomplete();\n\t\t\t\t}\n\t\t\t\t// Check if we're in a symbol-based completion context like @ or #\n\t\t\t\telse if (textBeforeCursor.match(/(?:^|[\\s])[@#][^\\s]*$/)) {\n\t\t\t\t\tthis.tryTriggerAutocomplete();\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tthis.updateAutocomplete();\n\t\t}\n\t}\n\n\tprivate handlePaste(pastedText: string): void {\n\t\tthis.cancelAutocomplete();\n\t\tthis.historyIndex = -1; // Exit history browsing mode\n\t\tthis.lastAction = null;\n\n\t\tthis.pushUndoSnapshot();\n\n\t\t// Some terminals (e.g. tmux popups with extended-keys-format=csi-u) re-encode\n\t\t// control bytes inside bracketed paste as CSI-u Ctrl+<letter> sequences\n\t\t// (ESC [ <codepoint> ; 5 u). Decode those back to their literal byte so the\n\t\t// per-char filter below preserves newlines instead of stripping ESC and\n\t\t// leaking the printable tail (e.g. \"[106;5u\") into the editor.\n\t\tconst decodedText = pastedText.replace(/\\x1b\\[(\\d+);5u/g, (match, code) => {\n\t\t\tconst cp = Number(code);\n\t\t\tif (cp >= 97 && cp <= 122) return String.fromCharCode(cp - 96);\n\t\t\tif (cp >= 65 && cp <= 90) return String.fromCharCode(cp - 64);\n\t\t\treturn match;\n\t\t});\n\n\t\t// Clean the pasted text: normalize line endings, expand tabs\n\t\tconst cleanText = this.normalizeText(decodedText);\n\n\t\t// Filter out non-printable characters except newlines\n\t\tlet filteredText = cleanText\n\t\t\t.split(\"\")\n\t\t\t.filter((char) => char === \"\\n\" || char.charCodeAt(0) >= 32)\n\t\t\t.join(\"\");\n\n\t\t// If pasting a file path (starts with /, ~, or .) and the character before\n\t\t// the cursor is a word character, prepend a space for better readability\n\t\tif (/^[/~.]/.test(filteredText)) {\n\t\t\tconst currentLine = this.state.lines[this.state.cursorLine] || \"\";\n\t\t\tconst charBeforeCursor = this.state.cursorCol > 0 ? currentLine[this.state.cursorCol - 1] : \"\";\n\t\t\tif (charBeforeCursor && /\\w/.test(charBeforeCursor)) {\n\t\t\t\tfilteredText = ` ${filteredText}`;\n\t\t\t}\n\t\t}\n\n\t\t// Split into lines to check for large paste\n\t\tconst pastedLines = filteredText.split(\"\\n\");\n\n\t\t// Check if this is a large paste (> 10 lines or > 1000 characters)\n\t\tconst totalChars = filteredText.length;\n\t\tif (pastedLines.length > 10 || totalChars > 1000) {\n\t\t\t// Store the paste and insert a marker\n\t\t\tthis.pasteCounter++;\n\t\t\tconst pasteId = this.pasteCounter;\n\t\t\tthis.pastes.set(pasteId, filteredText);\n\n\t\t\t// Insert marker like \"[paste #1 +123 lines]\" or \"[paste #1 1234 chars]\"\n\t\t\tconst marker =\n\t\t\t\tpastedLines.length > 10\n\t\t\t\t\t? `[paste #${pasteId} +${pastedLines.length} lines]`\n\t\t\t\t\t: `[paste #${pasteId} ${totalChars} chars]`;\n\t\t\tthis.insertTextAtCursorInternal(marker);\n\t\t\treturn;\n\t\t}\n\n\t\tif (pastedLines.length === 1) {\n\t\t\t// Single line - insert atomically (do not trigger autocomplete during paste)\n\t\t\tthis.insertTextAtCursorInternal(filteredText);\n\t\t\treturn;\n\t\t}\n\n\t\t// Multi-line paste - use direct state manipulation\n\t\tthis.insertTextAtCursorInternal(filteredText);\n\t}\n\n\tprivate addNewLine(): void {\n\t\tthis.cancelAutocomplete();\n\t\tthis.historyIndex = -1; // Exit history browsing mode\n\t\tthis.lastAction = null;\n\n\t\tthis.pushUndoSnapshot();\n\n\t\tconst currentLine = this.state.lines[this.state.cursorLine] || \"\";\n\n\t\tconst before = currentLine.slice(0, this.state.cursorCol);\n\t\tconst after = currentLine.slice(this.state.cursorCol);\n\n\t\t// Split current line\n\t\tthis.state.lines[this.state.cursorLine] = before;\n\t\tthis.state.lines.splice(this.state.cursorLine + 1, 0, after);\n\n\t\t// Move cursor to start of new line\n\t\tthis.state.cursorLine++;\n\t\tthis.setCursorCol(0);\n\n\t\tif (this.onChange) {\n\t\t\tthis.onChange(this.getText());\n\t\t}\n\t}\n\n\tprivate shouldSubmitOnBackslashEnter(data: string, kb: ReturnType<typeof getKeybindings>): boolean {\n\t\tif (this.disableSubmit) return false;\n\t\tif (!matchesKey(data, \"enter\")) return false;\n\t\tconst submitKeys = kb.getKeys(\"tui.input.submit\");\n\t\tconst hasShiftEnter = submitKeys.includes(\"shift+enter\") || submitKeys.includes(\"shift+return\");\n\t\tif (!hasShiftEnter) return false;\n\n\t\tconst currentLine = this.state.lines[this.state.cursorLine] || \"\";\n\t\treturn this.state.cursorCol > 0 && currentLine[this.state.cursorCol - 1] === \"\\\\\";\n\t}\n\n\tprivate submitValue(): void {\n\t\tthis.cancelAutocomplete();\n\t\tconst result = this.expandPasteMarkers(this.state.lines.join(\"\\n\")).trim();\n\n\t\tthis.state = { lines: [\"\"], cursorLine: 0, cursorCol: 0 };\n\t\tthis.pastes.clear();\n\t\tthis.pasteCounter = 0;\n\t\tthis.historyIndex = -1;\n\t\tthis.scrollOffset = 0;\n\t\tthis.undoStack.clear();\n\t\tthis.lastAction = null;\n\n\t\tif (this.onChange) this.onChange(\"\");\n\t\tif (this.onSubmit) this.onSubmit(result);\n\t}\n\n\tprivate handleBackspace(): void {\n\t\tthis.historyIndex = -1; // Exit history browsing mode\n\t\tthis.lastAction = null;\n\n\t\tif (this.state.cursorCol > 0) {\n\t\t\tthis.pushUndoSnapshot();\n\n\t\t\t// Delete grapheme before cursor (handles emojis, combining characters, etc.)\n\t\t\tconst line = this.state.lines[this.state.cursorLine] || \"\";\n\t\t\tconst beforeCursor = line.slice(0, this.state.cursorCol);\n\n\t\t\t// Find the last grapheme in the text before cursor\n\t\t\tconst graphemes = [...this.segment(beforeCursor, \"grapheme\")];\n\t\t\tconst lastGrapheme = graphemes[graphemes.length - 1];\n\t\t\tconst graphemeLength = lastGrapheme ? lastGrapheme.segment.length : 1;\n\n\t\t\tconst before = line.slice(0, this.state.cursorCol - graphemeLength);\n\t\t\tconst after = line.slice(this.state.cursorCol);\n\n\t\t\tthis.state.lines[this.state.cursorLine] = before + after;\n\t\t\tthis.setCursorCol(this.state.cursorCol - graphemeLength);\n\t\t} else if (this.state.cursorLine > 0) {\n\t\t\tthis.pushUndoSnapshot();\n\n\t\t\t// Merge with previous line\n\t\t\tconst currentLine = this.state.lines[this.state.cursorLine] || \"\";\n\t\t\tconst previousLine = this.state.lines[this.state.cursorLine - 1] || \"\";\n\n\t\t\tthis.state.lines[this.state.cursorLine - 1] = previousLine + currentLine;\n\t\t\tthis.state.lines.splice(this.state.cursorLine, 1);\n\n\t\t\tthis.state.cursorLine--;\n\t\t\tthis.setCursorCol(previousLine.length);\n\t\t}\n\n\t\tif (this.onChange) {\n\t\t\tthis.onChange(this.getText());\n\t\t}\n\n\t\t// Update or re-trigger autocomplete after backspace\n\t\tif (this.autocompleteState) {\n\t\t\tthis.updateAutocomplete();\n\t\t} else {\n\t\t\t// If autocomplete was cancelled (no matches), re-trigger if we're in a completable context\n\t\t\tconst currentLine = this.state.lines[this.state.cursorLine] || \"\";\n\t\t\tconst textBeforeCursor = currentLine.slice(0, this.state.cursorCol);\n\t\t\t// Slash command context\n\t\t\tif (this.isInSlashCommandContext(textBeforeCursor)) {\n\t\t\t\tthis.tryTriggerAutocomplete();\n\t\t\t}\n\t\t\t// Symbol-based completion context like @ or #\n\t\t\telse if (textBeforeCursor.match(/(?:^|[\\s])[@#][^\\s]*$/)) {\n\t\t\t\tthis.tryTriggerAutocomplete();\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Set cursor column and clear preferredVisualCol.\n\t * Use this for all non-vertical cursor movements to reset sticky column behavior.\n\t */\n\tprivate setCursorCol(col: number): void {\n\t\tthis.state.cursorCol = col;\n\t\tthis.preferredVisualCol = null;\n\t\tthis.snappedFromCursorCol = null;\n\t}\n\n\t/**\n\t * Move cursor to a target visual line, applying sticky column logic.\n\t * Shared by moveCursor() and pageScroll().\n\t */\n\tprivate moveToVisualLine(\n\t\tvisualLines: Array<{ logicalLine: number; startCol: number; length: number }>,\n\t\tcurrentVisualLine: number,\n\t\ttargetVisualLine: number,\n\t): void {\n\t\tconst currentVL = visualLines[currentVisualLine];\n\t\tconst targetVL = visualLines[targetVisualLine];\n\t\tif (!(currentVL && targetVL)) return;\n\n\t\t// When the cursor was snapped to a segment start, resolve the pre-snap\n\t\t// position against the VL it belongs to. This gives the correct visual\n\t\t// column even after a resize reshuffles VLs.\n\t\tlet currentVisualCol: number;\n\t\tif (this.snappedFromCursorCol !== null) {\n\t\t\tconst vlIndex = this.findVisualLineAt(visualLines, currentVL.logicalLine, this.snappedFromCursorCol);\n\t\t\tcurrentVisualCol = this.snappedFromCursorCol - visualLines[vlIndex].startCol;\n\t\t} else {\n\t\t\tcurrentVisualCol = this.state.cursorCol - currentVL.startCol;\n\t\t}\n\n\t\t// For non-last segments, clamp to length-1 to stay within the segment\n\t\tconst isLastSourceSegment =\n\t\t\tcurrentVisualLine === visualLines.length - 1 ||\n\t\t\tvisualLines[currentVisualLine + 1]?.logicalLine !== currentVL.logicalLine;\n\t\tconst sourceMaxVisualCol = isLastSourceSegment ? currentVL.length : Math.max(0, currentVL.length - 1);\n\n\t\tconst isLastTargetSegment =\n\t\t\ttargetVisualLine === visualLines.length - 1 ||\n\t\t\tvisualLines[targetVisualLine + 1]?.logicalLine !== targetVL.logicalLine;\n\t\tconst targetMaxVisualCol = isLastTargetSegment ? targetVL.length : Math.max(0, targetVL.length - 1);\n\n\t\tconst moveToVisualCol = this.computeVerticalMoveColumn(currentVisualCol, sourceMaxVisualCol, targetMaxVisualCol);\n\n\t\t// Set cursor position\n\t\tthis.state.cursorLine = targetVL.logicalLine;\n\t\tconst targetCol = targetVL.startCol + moveToVisualCol;\n\t\tconst logicalLine = this.state.lines[targetVL.logicalLine] || \"\";\n\t\tthis.state.cursorCol = Math.min(targetCol, logicalLine.length);\n\n\t\t// Snap cursor to atomic segment boundary (e.g. paste markers)\n\t\t// so the cursor never lands in the middle of a multi-grapheme unit.\n\t\t// Single-grapheme segments don't need snapping.\n\t\tconst segments = [...this.segment(logicalLine, \"grapheme\")];\n\t\tfor (const seg of segments) {\n\t\t\tif (seg.index > this.state.cursorCol) break;\n\t\t\tif (seg.segment.length <= 1) continue;\n\t\t\tif (this.state.cursorCol < seg.index + seg.segment.length) {\n\t\t\t\tconst isContinuation = seg.index < targetVL.startCol;\n\t\t\t\tconst isMovingDown = targetVisualLine > currentVisualLine;\n\n\t\t\t\tif (isContinuation && isMovingDown) {\n\t\t\t\t\t// The segment started on a previous visual line, and we\n\t\t\t\t\t// already visited it on the way down. Skip all remaining\n\t\t\t\t\t// continuation VLs and land on the first VL past it.\n\t\t\t\t\tconst segEnd = seg.index + seg.segment.length;\n\t\t\t\t\tlet next = targetVisualLine + 1;\n\t\t\t\t\twhile (\n\t\t\t\t\t\tnext < visualLines.length &&\n\t\t\t\t\t\tvisualLines[next].logicalLine === targetVL.logicalLine &&\n\t\t\t\t\t\tvisualLines[next].startCol < segEnd\n\t\t\t\t\t) {\n\t\t\t\t\t\tnext++;\n\t\t\t\t\t}\n\t\t\t\t\tif (next < visualLines.length) {\n\t\t\t\t\t\tthis.moveToVisualLine(visualLines, currentVisualLine, next);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Snap to the start of the segment so it gets highlighted.\n\t\t\t\t// Store the pre-snap position so the next vertical move can\n\t\t\t\t// resolve it to the correct visual column.\n\t\t\t\tthis.snappedFromCursorCol = this.state.cursorCol;\n\t\t\t\tthis.state.cursorCol = seg.index;\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\t// No snap occurred – we moved out of the atomic segment.\n\t\tthis.snappedFromCursorCol = null;\n\t}\n\n\t/**\n\t * Compute the target visual column for vertical cursor movement.\n\t * Implements the sticky column decision table:\n\t *\n\t * | P | S | T | U | Scenario | Set Preferred | Move To |\n\t * |---|---|---|---| ---------------------------------------------------- |---------------|-------------|\n\t * | 0 | * | 0 | - | Start nav, target fits | null | current |\n\t * | 0 | * | 1 | - | Start nav, target shorter | current | target end |\n\t * | 1 | 0 | 0 | 0 | Clamped, target fits preferred | null | preferred |\n\t * | 1 | 0 | 0 | 1 | Clamped, target longer but still can't fit preferred | keep | target end |\n\t * | 1 | 0 | 1 | - | Clamped, target even shorter | keep | target end |\n\t * | 1 | 1 | 0 | - | Rewrapped, target fits current | null | current |\n\t * | 1 | 1 | 1 | - | Rewrapped, target shorter than current | current | target end |\n\t *\n\t * Where:\n\t * - P = preferred col is set\n\t * - S = cursor in middle of source line (not clamped to end)\n\t * - T = target line shorter than current visual col\n\t * - U = target line shorter than preferred col\n\t */\n\tprivate computeVerticalMoveColumn(\n\t\tcurrentVisualCol: number,\n\t\tsourceMaxVisualCol: number,\n\t\ttargetMaxVisualCol: number,\n\t): number {\n\t\tconst hasPreferred = this.preferredVisualCol !== null; // P\n\t\tconst cursorInMiddle = currentVisualCol < sourceMaxVisualCol; // S\n\t\tconst targetTooShort = targetMaxVisualCol < currentVisualCol; // T\n\n\t\tif (!hasPreferred || cursorInMiddle) {\n\t\t\tif (targetTooShort) {\n\t\t\t\t// Cases 2 and 7\n\t\t\t\tthis.preferredVisualCol = currentVisualCol;\n\t\t\t\treturn targetMaxVisualCol;\n\t\t\t}\n\n\t\t\t// Cases 1 and 6\n\t\t\tthis.preferredVisualCol = null;\n\t\t\treturn currentVisualCol;\n\t\t}\n\n\t\tconst targetCantFitPreferred = targetMaxVisualCol < this.preferredVisualCol!; // U\n\t\tif (targetTooShort || targetCantFitPreferred) {\n\t\t\t// Cases 4 and 5\n\t\t\treturn targetMaxVisualCol;\n\t\t}\n\n\t\t// Case 3\n\t\tconst result = this.preferredVisualCol!;\n\t\tthis.preferredVisualCol = null;\n\t\treturn result;\n\t}\n\n\tprivate moveToLineStart(): void {\n\t\tthis.lastAction = null;\n\t\tthis.setCursorCol(0);\n\t}\n\n\tprivate moveToLineEnd(): void {\n\t\tthis.lastAction = null;\n\t\tconst currentLine = this.state.lines[this.state.cursorLine] || \"\";\n\t\tthis.setCursorCol(currentLine.length);\n\t}\n\n\tprivate deleteToStartOfLine(): void {\n\t\tthis.historyIndex = -1; // Exit history browsing mode\n\n\t\tconst currentLine = this.state.lines[this.state.cursorLine] || \"\";\n\n\t\tif (this.state.cursorCol > 0) {\n\t\t\tthis.pushUndoSnapshot();\n\n\t\t\t// Calculate text to be deleted and save to kill ring (backward deletion = prepend)\n\t\t\tconst deletedText = currentLine.slice(0, this.state.cursorCol);\n\t\t\tthis.killRing.push(deletedText, { prepend: true, accumulate: this.lastAction === \"kill\" });\n\t\t\tthis.lastAction = \"kill\";\n\n\t\t\t// Delete from start of line up to cursor\n\t\t\tthis.state.lines[this.state.cursorLine] = currentLine.slice(this.state.cursorCol);\n\t\t\tthis.setCursorCol(0);\n\t\t} else if (this.state.cursorLine > 0) {\n\t\t\tthis.pushUndoSnapshot();\n\n\t\t\t// At start of line - merge with previous line, treating newline as deleted text\n\t\t\tthis.killRing.push(\"\\n\", { prepend: true, accumulate: this.lastAction === \"kill\" });\n\t\t\tthis.lastAction = \"kill\";\n\n\t\t\tconst previousLine = this.state.lines[this.state.cursorLine - 1] || \"\";\n\t\t\tthis.state.lines[this.state.cursorLine - 1] = previousLine + currentLine;\n\t\t\tthis.state.lines.splice(this.state.cursorLine, 1);\n\t\t\tthis.state.cursorLine--;\n\t\t\tthis.setCursorCol(previousLine.length);\n\t\t}\n\n\t\tif (this.onChange) {\n\t\t\tthis.onChange(this.getText());\n\t\t}\n\t}\n\n\tprivate deleteToEndOfLine(): void {\n\t\tthis.historyIndex = -1; // Exit history browsing mode\n\n\t\tconst currentLine = this.state.lines[this.state.cursorLine] || \"\";\n\n\t\tif (this.state.cursorCol < currentLine.length) {\n\t\t\tthis.pushUndoSnapshot();\n\n\t\t\t// Calculate text to be deleted and save to kill ring (forward deletion = append)\n\t\t\tconst deletedText = currentLine.slice(this.state.cursorCol);\n\t\t\tthis.killRing.push(deletedText, { prepend: false, accumulate: this.lastAction === \"kill\" });\n\t\t\tthis.lastAction = \"kill\";\n\n\t\t\t// Delete from cursor to end of line\n\t\t\tthis.state.lines[this.state.cursorLine] = currentLine.slice(0, this.state.cursorCol);\n\t\t} else if (this.state.cursorLine < this.state.lines.length - 1) {\n\t\t\tthis.pushUndoSnapshot();\n\n\t\t\t// At end of line - merge with next line, treating newline as deleted text\n\t\t\tthis.killRing.push(\"\\n\", { prepend: false, accumulate: this.lastAction === \"kill\" });\n\t\t\tthis.lastAction = \"kill\";\n\n\t\t\tconst nextLine = this.state.lines[this.state.cursorLine + 1] || \"\";\n\t\t\tthis.state.lines[this.state.cursorLine] = currentLine + nextLine;\n\t\t\tthis.state.lines.splice(this.state.cursorLine + 1, 1);\n\t\t}\n\n\t\tif (this.onChange) {\n\t\t\tthis.onChange(this.getText());\n\t\t}\n\t}\n\n\tprivate deleteWordBackwards(): void {\n\t\tthis.historyIndex = -1; // Exit history browsing mode\n\n\t\tconst currentLine = this.state.lines[this.state.cursorLine] || \"\";\n\n\t\t// If at start of line, behave like backspace at column 0 (merge with previous line)\n\t\tif (this.state.cursorCol === 0) {\n\t\t\tif (this.state.cursorLine > 0) {\n\t\t\t\tthis.pushUndoSnapshot();\n\n\t\t\t\t// Treat newline as deleted text (backward deletion = prepend)\n\t\t\t\tthis.killRing.push(\"\\n\", { prepend: true, accumulate: this.lastAction === \"kill\" });\n\t\t\t\tthis.lastAction = \"kill\";\n\n\t\t\t\tconst previousLine = this.state.lines[this.state.cursorLine - 1] || \"\";\n\t\t\t\tthis.state.lines[this.state.cursorLine - 1] = previousLine + currentLine;\n\t\t\t\tthis.state.lines.splice(this.state.cursorLine, 1);\n\t\t\t\tthis.state.cursorLine--;\n\t\t\t\tthis.setCursorCol(previousLine.length);\n\t\t\t}\n\t\t} else {\n\t\t\tthis.pushUndoSnapshot();\n\n\t\t\t// Save lastAction before cursor movement (moveWordBackwards resets it)\n\t\t\tconst wasKill = this.lastAction === \"kill\";\n\n\t\t\tconst oldCursorCol = this.state.cursorCol;\n\t\t\tthis.moveWordBackwards();\n\t\t\tconst deleteFrom = this.state.cursorCol;\n\t\t\tthis.setCursorCol(oldCursorCol);\n\n\t\t\tconst deletedText = currentLine.slice(deleteFrom, this.state.cursorCol);\n\t\t\tthis.killRing.push(deletedText, { prepend: true, accumulate: wasKill });\n\t\t\tthis.lastAction = \"kill\";\n\n\t\t\tthis.state.lines[this.state.cursorLine] =\n\t\t\t\tcurrentLine.slice(0, deleteFrom) + currentLine.slice(this.state.cursorCol);\n\t\t\tthis.setCursorCol(deleteFrom);\n\t\t}\n\n\t\tif (this.onChange) {\n\t\t\tthis.onChange(this.getText());\n\t\t}\n\t}\n\n\tprivate deleteWordForward(): void {\n\t\tthis.historyIndex = -1; // Exit history browsing mode\n\n\t\tconst currentLine = this.state.lines[this.state.cursorLine] || \"\";\n\n\t\t// If at end of line, merge with next line (delete the newline)\n\t\tif (this.state.cursorCol >= currentLine.length) {\n\t\t\tif (this.state.cursorLine < this.state.lines.length - 1) {\n\t\t\t\tthis.pushUndoSnapshot();\n\n\t\t\t\t// Treat newline as deleted text (forward deletion = append)\n\t\t\t\tthis.killRing.push(\"\\n\", { prepend: false, accumulate: this.lastAction === \"kill\" });\n\t\t\t\tthis.lastAction = \"kill\";\n\n\t\t\t\tconst nextLine = this.state.lines[this.state.cursorLine + 1] || \"\";\n\t\t\t\tthis.state.lines[this.state.cursorLine] = currentLine + nextLine;\n\t\t\t\tthis.state.lines.splice(this.state.cursorLine + 1, 1);\n\t\t\t}\n\t\t} else {\n\t\t\tthis.pushUndoSnapshot();\n\n\t\t\t// Save lastAction before cursor movement (moveWordForwards resets it)\n\t\t\tconst wasKill = this.lastAction === \"kill\";\n\n\t\t\tconst oldCursorCol = this.state.cursorCol;\n\t\t\tthis.moveWordForwards();\n\t\t\tconst deleteTo = this.state.cursorCol;\n\t\t\tthis.setCursorCol(oldCursorCol);\n\n\t\t\tconst deletedText = currentLine.slice(this.state.cursorCol, deleteTo);\n\t\t\tthis.killRing.push(deletedText, { prepend: false, accumulate: wasKill });\n\t\t\tthis.lastAction = \"kill\";\n\n\t\t\tthis.state.lines[this.state.cursorLine] =\n\t\t\t\tcurrentLine.slice(0, this.state.cursorCol) + currentLine.slice(deleteTo);\n\t\t}\n\n\t\tif (this.onChange) {\n\t\t\tthis.onChange(this.getText());\n\t\t}\n\t}\n\n\tprivate handleForwardDelete(): void {\n\t\tthis.historyIndex = -1; // Exit history browsing mode\n\t\tthis.lastAction = null;\n\n\t\tconst currentLine = this.state.lines[this.state.cursorLine] || \"\";\n\n\t\tif (this.state.cursorCol < currentLine.length) {\n\t\t\tthis.pushUndoSnapshot();\n\n\t\t\t// Delete grapheme at cursor position (handles emojis, combining characters, etc.)\n\t\t\tconst afterCursor = currentLine.slice(this.state.cursorCol);\n\n\t\t\t// Find the first grapheme at cursor\n\t\t\tconst graphemes = [...this.segment(afterCursor, \"grapheme\")];\n\t\t\tconst firstGrapheme = graphemes[0];\n\t\t\tconst graphemeLength = firstGrapheme ? firstGrapheme.segment.length : 1;\n\n\t\t\tconst before = currentLine.slice(0, this.state.cursorCol);\n\t\t\tconst after = currentLine.slice(this.state.cursorCol + graphemeLength);\n\t\t\tthis.state.lines[this.state.cursorLine] = before + after;\n\t\t} else if (this.state.cursorLine < this.state.lines.length - 1) {\n\t\t\tthis.pushUndoSnapshot();\n\n\t\t\t// At end of line - merge with next line\n\t\t\tconst nextLine = this.state.lines[this.state.cursorLine + 1] || \"\";\n\t\t\tthis.state.lines[this.state.cursorLine] = currentLine + nextLine;\n\t\t\tthis.state.lines.splice(this.state.cursorLine + 1, 1);\n\t\t}\n\n\t\tif (this.onChange) {\n\t\t\tthis.onChange(this.getText());\n\t\t}\n\n\t\t// Update or re-trigger autocomplete after forward delete\n\t\tif (this.autocompleteState) {\n\t\t\tthis.updateAutocomplete();\n\t\t} else {\n\t\t\tconst currentLine = this.state.lines[this.state.cursorLine] || \"\";\n\t\t\tconst textBeforeCursor = currentLine.slice(0, this.state.cursorCol);\n\t\t\t// Slash command context\n\t\t\tif (this.isInSlashCommandContext(textBeforeCursor)) {\n\t\t\t\tthis.tryTriggerAutocomplete();\n\t\t\t}\n\t\t\t// Symbol-based completion context like @ or #\n\t\t\telse if (textBeforeCursor.match(/(?:^|[\\s])[@#][^\\s]*$/)) {\n\t\t\t\tthis.tryTriggerAutocomplete();\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Build a mapping from visual lines to logical positions.\n\t * Returns an array where each element represents a visual line with:\n\t * - logicalLine: index into this.state.lines\n\t * - startCol: starting column in the logical line\n\t * - length: length of this visual line segment\n\t */\n\tprivate buildVisualLineMap(width: number): Array<{ logicalLine: number; startCol: number; length: number }> {\n\t\tconst visualLines: Array<{ logicalLine: number; startCol: number; length: number }> = [];\n\n\t\tfor (let i = 0; i < this.state.lines.length; i++) {\n\t\t\tconst line = this.state.lines[i] || \"\";\n\t\t\tconst lineVisWidth = visibleWidth(line);\n\t\t\tif (line.length === 0) {\n\t\t\t\t// Empty line still takes one visual line\n\t\t\t\tvisualLines.push({ logicalLine: i, startCol: 0, length: 0 });\n\t\t\t} else if (lineVisWidth <= width) {\n\t\t\t\tvisualLines.push({ logicalLine: i, startCol: 0, length: line.length });\n\t\t\t} else {\n\t\t\t\t// Line needs wrapping - use word-aware wrapping\n\t\t\t\tconst chunks = wordWrapLine(line, width, [...this.segment(line, \"grapheme\")]);\n\t\t\t\tfor (const chunk of chunks) {\n\t\t\t\t\tvisualLines.push({\n\t\t\t\t\t\tlogicalLine: i,\n\t\t\t\t\t\tstartCol: chunk.startIndex,\n\t\t\t\t\t\tlength: chunk.endIndex - chunk.startIndex,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn visualLines;\n\t}\n\n\t/**\n\t * Find the visual line index that contains the given logical position.\n\t */\n\tprivate findVisualLineAt(\n\t\tvisualLines: Array<{ logicalLine: number; startCol: number; length: number }>,\n\t\tline: number,\n\t\tcol: number,\n\t): number {\n\t\tfor (let i = 0; i < visualLines.length; i++) {\n\t\t\tconst vl = visualLines[i];\n\t\t\tif (!vl || vl.logicalLine !== line) continue;\n\t\t\tconst offset = col - vl.startCol;\n\t\t\t// Cursor is in this segment if it's within range. For the last\n\t\t\t// segment of a logical line, cursor can be at length (end position)\n\t\t\tconst isLastSegmentOfLine = i === visualLines.length - 1 || visualLines[i + 1]?.logicalLine !== vl.logicalLine;\n\t\t\tif (offset >= 0 && (offset < vl.length || (isLastSegmentOfLine && offset === vl.length))) {\n\t\t\t\treturn i;\n\t\t\t}\n\t\t}\n\t\treturn visualLines.length - 1;\n\t}\n\n\t/**\n\t * Find the visual line index for the current cursor position.\n\t */\n\tprivate findCurrentVisualLine(\n\t\tvisualLines: Array<{ logicalLine: number; startCol: number; length: number }>,\n\t): number {\n\t\treturn this.findVisualLineAt(visualLines, this.state.cursorLine, this.state.cursorCol);\n\t}\n\n\tprivate moveCursor(deltaLine: number, deltaCol: number): void {\n\t\tthis.lastAction = null;\n\t\tconst visualLines = this.buildVisualLineMap(this.lastWidth);\n\t\tconst currentVisualLine = this.findCurrentVisualLine(visualLines);\n\n\t\tif (deltaLine !== 0) {\n\t\t\tconst targetVisualLine = currentVisualLine + deltaLine;\n\n\t\t\tif (targetVisualLine >= 0 && targetVisualLine < visualLines.length) {\n\t\t\t\tthis.moveToVisualLine(visualLines, currentVisualLine, targetVisualLine);\n\t\t\t}\n\t\t}\n\n\t\tif (deltaCol !== 0) {\n\t\t\tconst currentLine = this.state.lines[this.state.cursorLine] || \"\";\n\n\t\t\tif (deltaCol > 0) {\n\t\t\t\t// Moving right - move by one grapheme (handles emojis, combining characters, etc.)\n\t\t\t\tif (this.state.cursorCol < currentLine.length) {\n\t\t\t\t\tconst afterCursor = currentLine.slice(this.state.cursorCol);\n\t\t\t\t\tconst graphemes = [...this.segment(afterCursor, \"grapheme\")];\n\t\t\t\t\tconst firstGrapheme = graphemes[0];\n\t\t\t\t\tthis.setCursorCol(this.state.cursorCol + (firstGrapheme ? firstGrapheme.segment.length : 1));\n\t\t\t\t} else if (this.state.cursorLine < this.state.lines.length - 1) {\n\t\t\t\t\t// Wrap to start of next logical line\n\t\t\t\t\tthis.state.cursorLine++;\n\t\t\t\t\tthis.setCursorCol(0);\n\t\t\t\t} else {\n\t\t\t\t\t// At end of last line - can't move, but set preferredVisualCol for up/down navigation\n\t\t\t\t\tconst currentVL = visualLines[currentVisualLine];\n\t\t\t\t\tif (currentVL) {\n\t\t\t\t\t\tthis.preferredVisualCol = this.state.cursorCol - currentVL.startCol;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// Moving left - move by one grapheme (handles emojis, combining characters, etc.)\n\t\t\t\tif (this.state.cursorCol > 0) {\n\t\t\t\t\tconst beforeCursor = currentLine.slice(0, this.state.cursorCol);\n\t\t\t\t\tconst graphemes = [...this.segment(beforeCursor, \"grapheme\")];\n\t\t\t\t\tconst lastGrapheme = graphemes[graphemes.length - 1];\n\t\t\t\t\tthis.setCursorCol(this.state.cursorCol - (lastGrapheme ? lastGrapheme.segment.length : 1));\n\t\t\t\t} else if (this.state.cursorLine > 0) {\n\t\t\t\t\t// Wrap to end of previous logical line\n\t\t\t\t\tthis.state.cursorLine--;\n\t\t\t\t\tconst prevLine = this.state.lines[this.state.cursorLine] || \"\";\n\t\t\t\t\tthis.setCursorCol(prevLine.length);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Scroll by a page (direction: -1 for up, 1 for down).\n\t * Moves cursor by the page size while keeping it in bounds.\n\t */\n\tprivate pageScroll(direction: -1 | 1): void {\n\t\tthis.lastAction = null;\n\t\tconst terminalRows = this.tui.terminal.rows;\n\t\tconst pageSize = Math.max(5, Math.floor(terminalRows * 0.3));\n\n\t\tconst visualLines = this.buildVisualLineMap(this.lastWidth);\n\t\tconst currentVisualLine = this.findCurrentVisualLine(visualLines);\n\t\tconst targetVisualLine = Math.max(0, Math.min(visualLines.length - 1, currentVisualLine + direction * pageSize));\n\n\t\tthis.moveToVisualLine(visualLines, currentVisualLine, targetVisualLine);\n\t}\n\n\tprivate moveWordBackwards(): void {\n\t\tthis.lastAction = null;\n\t\tconst currentLine = this.state.lines[this.state.cursorLine] || \"\";\n\n\t\t// If at start of line, move to end of previous line\n\t\tif (this.state.cursorCol === 0) {\n\t\t\tif (this.state.cursorLine > 0) {\n\t\t\t\tthis.state.cursorLine--;\n\t\t\t\tconst prevLine = this.state.lines[this.state.cursorLine] || \"\";\n\t\t\t\tthis.setCursorCol(prevLine.length);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tthis.setCursorCol(\n\t\t\tfindWordBackward(currentLine, this.state.cursorCol, {\n\t\t\t\tsegment: (text) => this.segment(text, \"word\"),\n\t\t\t\tisAtomicSegment: isPasteMarker,\n\t\t\t}),\n\t\t);\n\t}\n\n\t/**\n\t * Yank (paste) the most recent kill ring entry at cursor position.\n\t */\n\tprivate yank(): void {\n\t\tif (this.killRing.length === 0) return;\n\n\t\tthis.pushUndoSnapshot();\n\n\t\tconst text = this.killRing.peek()!;\n\t\tthis.insertYankedText(text);\n\n\t\tthis.lastAction = \"yank\";\n\t}\n\n\t/**\n\t * Cycle through kill ring (only works immediately after yank or yank-pop).\n\t * Replaces the last yanked text with the previous entry in the ring.\n\t */\n\tprivate yankPop(): void {\n\t\t// Only works if we just yanked and have more than one entry\n\t\tif (this.lastAction !== \"yank\" || this.killRing.length <= 1) return;\n\n\t\tthis.pushUndoSnapshot();\n\n\t\t// Delete the previously yanked text (still at end of ring before rotation)\n\t\tthis.deleteYankedText();\n\n\t\t// Rotate the ring: move end to front\n\t\tthis.killRing.rotate();\n\n\t\t// Insert the new most recent entry (now at end after rotation)\n\t\tconst text = this.killRing.peek()!;\n\t\tthis.insertYankedText(text);\n\n\t\tthis.lastAction = \"yank\";\n\t}\n\n\t/**\n\t * Insert text at cursor position (used by yank operations).\n\t */\n\tprivate insertYankedText(text: string): void {\n\t\tthis.historyIndex = -1; // Exit history browsing mode\n\t\tconst lines = text.split(\"\\n\");\n\n\t\tif (lines.length === 1) {\n\t\t\t// Single line - insert at cursor\n\t\t\tconst currentLine = this.state.lines[this.state.cursorLine] || \"\";\n\t\t\tconst before = currentLine.slice(0, this.state.cursorCol);\n\t\t\tconst after = currentLine.slice(this.state.cursorCol);\n\t\t\tthis.state.lines[this.state.cursorLine] = before + text + after;\n\t\t\tthis.setCursorCol(this.state.cursorCol + text.length);\n\t\t} else {\n\t\t\t// Multi-line insert\n\t\t\tconst currentLine = this.state.lines[this.state.cursorLine] || \"\";\n\t\t\tconst before = currentLine.slice(0, this.state.cursorCol);\n\t\t\tconst after = currentLine.slice(this.state.cursorCol);\n\n\t\t\t// First line merges with text before cursor\n\t\t\tthis.state.lines[this.state.cursorLine] = before + (lines[0] || \"\");\n\n\t\t\t// Insert middle lines\n\t\t\tfor (let i = 1; i < lines.length - 1; i++) {\n\t\t\t\tthis.state.lines.splice(this.state.cursorLine + i, 0, lines[i] || \"\");\n\t\t\t}\n\n\t\t\t// Last line merges with text after cursor\n\t\t\tconst lastLineIndex = this.state.cursorLine + lines.length - 1;\n\t\t\tthis.state.lines.splice(lastLineIndex, 0, (lines[lines.length - 1] || \"\") + after);\n\n\t\t\t// Update cursor position\n\t\t\tthis.state.cursorLine = lastLineIndex;\n\t\t\tthis.setCursorCol((lines[lines.length - 1] || \"\").length);\n\t\t}\n\n\t\tif (this.onChange) {\n\t\t\tthis.onChange(this.getText());\n\t\t}\n\t}\n\n\t/**\n\t * Delete the previously yanked text (used by yank-pop).\n\t * The yanked text is derived from killRing[end] since it hasn't been rotated yet.\n\t */\n\tprivate deleteYankedText(): void {\n\t\tconst yankedText = this.killRing.peek();\n\t\tif (!yankedText) return;\n\n\t\tconst yankLines = yankedText.split(\"\\n\");\n\n\t\tif (yankLines.length === 1) {\n\t\t\t// Single line - delete backward from cursor\n\t\t\tconst currentLine = this.state.lines[this.state.cursorLine] || \"\";\n\t\t\tconst deleteLen = yankedText.length;\n\t\t\tconst before = currentLine.slice(0, this.state.cursorCol - deleteLen);\n\t\t\tconst after = currentLine.slice(this.state.cursorCol);\n\t\t\tthis.state.lines[this.state.cursorLine] = before + after;\n\t\t\tthis.setCursorCol(this.state.cursorCol - deleteLen);\n\t\t} else {\n\t\t\t// Multi-line delete - cursor is at end of last yanked line\n\t\t\tconst startLine = this.state.cursorLine - (yankLines.length - 1);\n\t\t\tconst startCol = (this.state.lines[startLine] || \"\").length - (yankLines[0] || \"\").length;\n\n\t\t\t// Get text after cursor on current line\n\t\t\tconst afterCursor = (this.state.lines[this.state.cursorLine] || \"\").slice(this.state.cursorCol);\n\n\t\t\t// Get text before yank start position\n\t\t\tconst beforeYank = (this.state.lines[startLine] || \"\").slice(0, startCol);\n\n\t\t\t// Remove all lines from startLine to cursorLine and replace with merged line\n\t\t\tthis.state.lines.splice(startLine, yankLines.length, beforeYank + afterCursor);\n\n\t\t\t// Update cursor\n\t\t\tthis.state.cursorLine = startLine;\n\t\t\tthis.setCursorCol(startCol);\n\t\t}\n\n\t\tif (this.onChange) {\n\t\t\tthis.onChange(this.getText());\n\t\t}\n\t}\n\n\tprivate pushUndoSnapshot(): void {\n\t\tthis.undoStack.push(this.state);\n\t}\n\n\tprivate undo(): void {\n\t\tthis.historyIndex = -1; // Exit history browsing mode\n\t\tconst snapshot = this.undoStack.pop();\n\t\tif (!snapshot) return;\n\t\tObject.assign(this.state, snapshot);\n\t\tthis.lastAction = null;\n\t\tthis.preferredVisualCol = null;\n\t\tif (this.onChange) {\n\t\t\tthis.onChange(this.getText());\n\t\t}\n\t}\n\n\t/**\n\t * Jump to the first occurrence of a character in the specified direction.\n\t * Multi-line search. Case-sensitive. Skips the current cursor position.\n\t */\n\tprivate jumpToChar(char: string, direction: \"forward\" | \"backward\"): void {\n\t\tthis.lastAction = null;\n\t\tconst isForward = direction === \"forward\";\n\t\tconst lines = this.state.lines;\n\n\t\tconst end = isForward ? lines.length : -1;\n\t\tconst step = isForward ? 1 : -1;\n\n\t\tfor (let lineIdx = this.state.cursorLine; lineIdx !== end; lineIdx += step) {\n\t\t\tconst line = lines[lineIdx] || \"\";\n\t\t\tconst isCurrentLine = lineIdx === this.state.cursorLine;\n\n\t\t\t// Current line: start after/before cursor; other lines: search full line\n\t\t\tconst searchFrom = isCurrentLine\n\t\t\t\t? isForward\n\t\t\t\t\t? this.state.cursorCol + 1\n\t\t\t\t\t: this.state.cursorCol - 1\n\t\t\t\t: undefined;\n\n\t\t\tconst idx = isForward ? line.indexOf(char, searchFrom) : line.lastIndexOf(char, searchFrom);\n\n\t\t\tif (idx !== -1) {\n\t\t\t\tthis.state.cursorLine = lineIdx;\n\t\t\t\tthis.setCursorCol(idx);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\t// No match found - cursor stays in place\n\t}\n\n\tprivate moveWordForwards(): void {\n\t\tthis.lastAction = null;\n\t\tconst currentLine = this.state.lines[this.state.cursorLine] || \"\";\n\n\t\t// If at end of line, move to start of next line\n\t\tif (this.state.cursorCol >= currentLine.length) {\n\t\t\tif (this.state.cursorLine < this.state.lines.length - 1) {\n\t\t\t\tthis.state.cursorLine++;\n\t\t\t\tthis.setCursorCol(0);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tthis.setCursorCol(\n\t\t\tfindWordForward(currentLine, this.state.cursorCol, {\n\t\t\t\tsegment: (text) => this.segment(text, \"word\"),\n\t\t\t\tisAtomicSegment: isPasteMarker,\n\t\t\t}),\n\t\t);\n\t}\n\n\t// Slash menu only allowed on the first line of the editor\n\tprivate isSlashMenuAllowed(): boolean {\n\t\treturn this.state.cursorLine === 0;\n\t}\n\n\t// Helper method to check if cursor is at start of message (for slash command detection)\n\tprivate isAtStartOfMessage(): boolean {\n\t\tif (!this.isSlashMenuAllowed()) return false;\n\t\tconst currentLine = this.state.lines[this.state.cursorLine] || \"\";\n\t\tconst beforeCursor = currentLine.slice(0, this.state.cursorCol);\n\t\treturn beforeCursor.trim() === \"\" || beforeCursor.trim() === \"/\";\n\t}\n\n\tprivate isInSlashCommandContext(textBeforeCursor: string): boolean {\n\t\treturn this.isSlashMenuAllowed() && textBeforeCursor.trimStart().startsWith(\"/\");\n\t}\n\n\t// Autocomplete methods\n\t/**\n\t * Find the best autocomplete item index for the given prefix.\n\t * Returns -1 if no match is found.\n\t *\n\t * Match priority:\n\t * 1. Exact match (prefix === item.value) -> always selected\n\t * 2. Prefix match -> first item whose value starts with prefix\n\t * 3. No match -> -1 (keep default highlight)\n\t *\n\t * Matching is case-sensitive and checks item.value only.\n\t */\n\tprivate getBestAutocompleteMatchIndex(items: Array<{ value: string; label: string }>, prefix: string): number {\n\t\tif (!prefix) return -1;\n\n\t\tlet firstPrefixIndex = -1;\n\n\t\tfor (let i = 0; i < items.length; i++) {\n\t\t\tconst value = items[i]!.value;\n\t\t\tif (value === prefix) {\n\t\t\t\treturn i; // Exact match always wins\n\t\t\t}\n\t\t\tif (firstPrefixIndex === -1 && value.startsWith(prefix)) {\n\t\t\t\tfirstPrefixIndex = i;\n\t\t\t}\n\t\t}\n\n\t\treturn firstPrefixIndex;\n\t}\n\n\tprivate createAutocompleteList(\n\t\tprefix: string,\n\t\titems: Array<{ value: string; label: string; description?: string }>,\n\t): SelectList {\n\t\tconst layout = prefix.startsWith(\"/\") ? SLASH_COMMAND_SELECT_LIST_LAYOUT : undefined;\n\t\treturn new SelectList(items, this.autocompleteMaxVisible, this.theme.selectList, layout);\n\t}\n\n\tprivate tryTriggerAutocomplete(explicitTab: boolean = false): void {\n\t\tthis.requestAutocomplete({ force: false, explicitTab });\n\t}\n\n\tprivate handleTabCompletion(): void {\n\t\tif (!this.autocompleteProvider) return;\n\n\t\tconst currentLine = this.state.lines[this.state.cursorLine] || \"\";\n\t\tconst beforeCursor = currentLine.slice(0, this.state.cursorCol);\n\n\t\tif (this.isInSlashCommandContext(beforeCursor) && !beforeCursor.trimStart().includes(\" \")) {\n\t\t\tthis.handleSlashCommandCompletion();\n\t\t} else {\n\t\t\tthis.forceFileAutocomplete(true);\n\t\t}\n\t}\n\n\tprivate handleSlashCommandCompletion(): void {\n\t\tthis.requestAutocomplete({ force: false, explicitTab: true });\n\t}\n\n\tprivate forceFileAutocomplete(explicitTab: boolean = false): void {\n\t\tthis.requestAutocomplete({ force: true, explicitTab });\n\t}\n\n\tprivate requestAutocomplete(options: { force: boolean; explicitTab: boolean }): void {\n\t\tif (!this.autocompleteProvider) return;\n\n\t\tif (options.force) {\n\t\t\tconst shouldTrigger =\n\t\t\t\t!this.autocompleteProvider.shouldTriggerFileCompletion ||\n\t\t\t\tthis.autocompleteProvider.shouldTriggerFileCompletion(\n\t\t\t\t\tthis.state.lines,\n\t\t\t\t\tthis.state.cursorLine,\n\t\t\t\t\tthis.state.cursorCol,\n\t\t\t\t);\n\t\t\tif (!shouldTrigger) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\tthis.cancelAutocompleteRequest();\n\t\tconst startToken = ++this.autocompleteStartToken;\n\n\t\tconst debounceMs = this.getAutocompleteDebounceMs(options);\n\t\tif (debounceMs > 0) {\n\t\t\tthis.autocompleteDebounceTimer = setTimeout(() => {\n\t\t\t\tthis.autocompleteDebounceTimer = undefined;\n\t\t\t\tvoid this.startAutocompleteRequest(startToken, options);\n\t\t\t}, debounceMs);\n\t\t\treturn;\n\t\t}\n\n\t\tvoid this.startAutocompleteRequest(startToken, options);\n\t}\n\n\tprivate async startAutocompleteRequest(\n\t\tstartToken: number,\n\t\toptions: { force: boolean; explicitTab: boolean },\n\t): Promise<void> {\n\t\tconst previousTask = this.autocompleteRequestTask;\n\t\tthis.autocompleteRequestTask = (async () => {\n\t\t\tawait previousTask;\n\t\t\tif (startToken !== this.autocompleteStartToken || !this.autocompleteProvider) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst controller = new AbortController();\n\t\t\tthis.autocompleteAbort = controller;\n\t\t\tconst requestId = ++this.autocompleteRequestId;\n\t\t\tconst snapshotText = this.getText();\n\t\t\tconst snapshotLine = this.state.cursorLine;\n\t\t\tconst snapshotCol = this.state.cursorCol;\n\n\t\t\tawait this.runAutocompleteRequest(requestId, controller, snapshotText, snapshotLine, snapshotCol, options);\n\t\t})();\n\t\tawait this.autocompleteRequestTask;\n\t}\n\n\tprivate getAutocompleteDebounceMs(options: { force: boolean; explicitTab: boolean }): number {\n\t\tif (options.explicitTab || options.force) {\n\t\t\treturn 0;\n\t\t}\n\n\t\tconst currentLine = this.state.lines[this.state.cursorLine] || \"\";\n\t\tconst textBeforeCursor = currentLine.slice(0, this.state.cursorCol);\n\t\tconst isSymbolAutocompleteContext = /(?:^|[ \\t])(?:@(?:\"[^\"]*|[^\\s]*)|#[^\\s]*)$/.test(textBeforeCursor);\n\t\treturn isSymbolAutocompleteContext ? ATTACHMENT_AUTOCOMPLETE_DEBOUNCE_MS : 0;\n\t}\n\n\tprivate async runAutocompleteRequest(\n\t\trequestId: number,\n\t\tcontroller: AbortController,\n\t\tsnapshotText: string,\n\t\tsnapshotLine: number,\n\t\tsnapshotCol: number,\n\t\toptions: { force: boolean; explicitTab: boolean },\n\t): Promise<void> {\n\t\tif (!this.autocompleteProvider) return;\n\n\t\tconst suggestions = await this.autocompleteProvider.getSuggestions(\n\t\t\tthis.state.lines,\n\t\t\tthis.state.cursorLine,\n\t\t\tthis.state.cursorCol,\n\t\t\t{ signal: controller.signal, force: options.force },\n\t\t);\n\n\t\tif (!this.isAutocompleteRequestCurrent(requestId, controller, snapshotText, snapshotLine, snapshotCol)) {\n\t\t\treturn;\n\t\t}\n\n\t\tthis.autocompleteAbort = undefined;\n\n\t\tif (!suggestions || !Array.isArray(suggestions.items) || suggestions.items.length === 0) {\n\t\t\tthis.cancelAutocomplete();\n\t\t\tthis.tui.requestRender();\n\t\t\treturn;\n\t\t}\n\n\t\tif (options.force && options.explicitTab && suggestions.items.length === 1) {\n\t\t\tconst item = suggestions.items[0]!;\n\t\t\tthis.pushUndoSnapshot();\n\t\t\tthis.lastAction = null;\n\t\t\tconst result = this.autocompleteProvider.applyCompletion(\n\t\t\t\tthis.state.lines,\n\t\t\t\tthis.state.cursorLine,\n\t\t\t\tthis.state.cursorCol,\n\t\t\t\titem,\n\t\t\t\tsuggestions.prefix,\n\t\t\t);\n\t\t\tthis.state.lines = result.lines;\n\t\t\tthis.state.cursorLine = result.cursorLine;\n\t\t\tthis.setCursorCol(result.cursorCol);\n\t\t\tif (this.onChange) this.onChange(this.getText());\n\t\t\tthis.tui.requestRender();\n\t\t\treturn;\n\t\t}\n\n\t\tthis.applyAutocompleteSuggestions(suggestions, options.force ? \"force\" : \"regular\");\n\t\tthis.tui.requestRender();\n\t}\n\n\tprivate isAutocompleteRequestCurrent(\n\t\trequestId: number,\n\t\tcontroller: AbortController,\n\t\tsnapshotText: string,\n\t\tsnapshotLine: number,\n\t\tsnapshotCol: number,\n\t): boolean {\n\t\treturn (\n\t\t\t!controller.signal.aborted &&\n\t\t\trequestId === this.autocompleteRequestId &&\n\t\t\tthis.getText() === snapshotText &&\n\t\t\tthis.state.cursorLine === snapshotLine &&\n\t\t\tthis.state.cursorCol === snapshotCol\n\t\t);\n\t}\n\n\tprivate applyAutocompleteSuggestions(suggestions: AutocompleteSuggestions, state: \"regular\" | \"force\"): void {\n\t\tthis.autocompletePrefix = suggestions.prefix;\n\t\tthis.autocompleteList = this.createAutocompleteList(suggestions.prefix, suggestions.items);\n\n\t\tconst bestMatchIndex = this.getBestAutocompleteMatchIndex(suggestions.items, suggestions.prefix);\n\t\tif (bestMatchIndex >= 0) {\n\t\t\tthis.autocompleteList.setSelectedIndex(bestMatchIndex);\n\t\t}\n\n\t\tthis.autocompleteState = state;\n\t}\n\n\tprivate cancelAutocompleteRequest(): void {\n\t\tthis.autocompleteStartToken += 1;\n\t\tif (this.autocompleteDebounceTimer) {\n\t\t\tclearTimeout(this.autocompleteDebounceTimer);\n\t\t\tthis.autocompleteDebounceTimer = undefined;\n\t\t}\n\t\tthis.autocompleteAbort?.abort();\n\t\tthis.autocompleteAbort = undefined;\n\t}\n\n\tprivate clearAutocompleteUi(): void {\n\t\tthis.autocompleteState = null;\n\t\tthis.autocompleteList = undefined;\n\t\tthis.autocompletePrefix = \"\";\n\t}\n\n\tprivate cancelAutocomplete(): void {\n\t\tthis.cancelAutocompleteRequest();\n\t\tthis.clearAutocompleteUi();\n\t}\n\n\tpublic isShowingAutocomplete(): boolean {\n\t\treturn this.autocompleteState !== null;\n\t}\n\n\tprivate updateAutocomplete(): void {\n\t\tif (!this.autocompleteState || !this.autocompleteProvider) return;\n\t\tthis.requestAutocomplete({ force: this.autocompleteState === \"force\", explicitTab: false });\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"editor.d.ts","sourceRoot":"","sources":["../../src/components/editor.ts"],"names":[],"mappings":"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"}
|
|
@@ -158,62 +158,66 @@ const SLASH_COMMAND_SELECT_LIST_LAYOUT = {
|
|
|
158
158
|
maxPrimaryColumnWidth: 32,
|
|
159
159
|
};
|
|
160
160
|
const ATTACHMENT_AUTOCOMPLETE_DEBOUNCE_MS = 20;
|
|
161
|
+
const DEFAULT_AUTOCOMPLETE_TRIGGER_CHARACTERS = ["@", "#"];
|
|
162
|
+
function escapeCharacterClass(value) {
|
|
163
|
+
return value.replace(/[\\^$.*+?()[\]{}|-]/g, "\\$&");
|
|
164
|
+
}
|
|
165
|
+
function buildTriggerPattern(triggerCharacters) {
|
|
166
|
+
return new RegExp(`(?:^|[\\s])[${triggerCharacters.map(escapeCharacterClass).join("")}][^\\s]*$`);
|
|
167
|
+
}
|
|
168
|
+
function buildDebouncePattern(triggerCharacters) {
|
|
169
|
+
const escapedWithoutAt = triggerCharacters.filter((character) => character !== "@").map(escapeCharacterClass);
|
|
170
|
+
return new RegExp(`(?:^|[ \\t])(?:@(?:"[^"]*|[^\\s]*)|[${escapedWithoutAt.join("")}][^\\s]*)$`);
|
|
171
|
+
}
|
|
161
172
|
export class Editor {
|
|
162
|
-
state = {
|
|
163
|
-
lines: [""],
|
|
164
|
-
cursorLine: 0,
|
|
165
|
-
cursorCol: 0,
|
|
166
|
-
};
|
|
167
|
-
/** Focusable interface - set by TUI when focus changes */
|
|
168
|
-
focused = false;
|
|
169
|
-
tui;
|
|
170
|
-
theme;
|
|
171
|
-
paddingX = 0;
|
|
172
|
-
// Store last render width for cursor navigation
|
|
173
|
-
lastWidth = 80;
|
|
174
|
-
// Vertical scrolling support
|
|
175
|
-
scrollOffset = 0;
|
|
176
|
-
// Border color (can be changed dynamically)
|
|
177
|
-
borderColor;
|
|
178
|
-
// Autocomplete support
|
|
179
|
-
autocompleteProvider;
|
|
180
|
-
autocompleteList;
|
|
181
|
-
autocompleteState = null;
|
|
182
|
-
autocompletePrefix = "";
|
|
183
|
-
autocompleteMaxVisible = 5;
|
|
184
|
-
autocompleteAbort;
|
|
185
|
-
autocompleteDebounceTimer;
|
|
186
|
-
autocompleteRequestTask = Promise.resolve();
|
|
187
|
-
autocompleteStartToken = 0;
|
|
188
|
-
autocompleteRequestId = 0;
|
|
189
|
-
// Paste tracking for large pastes
|
|
190
|
-
pastes = new Map();
|
|
191
|
-
pasteCounter = 0;
|
|
192
|
-
// Bracketed paste mode buffering
|
|
193
|
-
pasteBuffer = "";
|
|
194
|
-
isInPaste = false;
|
|
195
|
-
// Prompt history for up/down navigation
|
|
196
|
-
history = [];
|
|
197
|
-
historyIndex = -1; // -1 = not browsing, 0 = most recent, 1 = older, etc.
|
|
198
|
-
// Kill ring for Emacs-style kill/yank operations
|
|
199
|
-
killRing = new KillRing();
|
|
200
|
-
lastAction = null;
|
|
201
|
-
// Character jump mode
|
|
202
|
-
jumpMode = null;
|
|
203
|
-
// Preferred visual column for vertical cursor movement (sticky column)
|
|
204
|
-
preferredVisualCol = null;
|
|
205
|
-
// When the cursor is snapped to the start of an atomic segment, e.g. a
|
|
206
|
-
// paste marker, cursorCol no longer reflects where the cursor would have
|
|
207
|
-
// landed. This field stores the pre-snap cursorCol so that the next
|
|
208
|
-
// vertical move can resolve it to a visual column on whatever VL it belongs
|
|
209
|
-
// to.
|
|
210
|
-
snappedFromCursorCol = null;
|
|
211
|
-
// Undo support
|
|
212
|
-
undoStack = new UndoStack();
|
|
213
|
-
onSubmit;
|
|
214
|
-
onChange;
|
|
215
|
-
disableSubmit = false;
|
|
216
173
|
constructor(tui, theme, options = {}) {
|
|
174
|
+
this.state = {
|
|
175
|
+
lines: [""],
|
|
176
|
+
cursorLine: 0,
|
|
177
|
+
cursorCol: 0,
|
|
178
|
+
};
|
|
179
|
+
/** Focusable interface - set by TUI when focus changes */
|
|
180
|
+
this.focused = false;
|
|
181
|
+
this.paddingX = 0;
|
|
182
|
+
// Store last render width for cursor navigation
|
|
183
|
+
this.lastWidth = 80;
|
|
184
|
+
// Vertical scrolling support
|
|
185
|
+
this.scrollOffset = 0;
|
|
186
|
+
this.autocompleteTriggerCharacters = [...DEFAULT_AUTOCOMPLETE_TRIGGER_CHARACTERS];
|
|
187
|
+
this.autocompleteTriggerPattern = buildTriggerPattern(this.autocompleteTriggerCharacters);
|
|
188
|
+
this.autocompleteDebouncePattern = buildDebouncePattern(this.autocompleteTriggerCharacters);
|
|
189
|
+
this.autocompleteState = null;
|
|
190
|
+
this.autocompletePrefix = "";
|
|
191
|
+
this.autocompleteMaxVisible = 5;
|
|
192
|
+
this.autocompleteRequestTask = Promise.resolve();
|
|
193
|
+
this.autocompleteStartToken = 0;
|
|
194
|
+
this.autocompleteRequestId = 0;
|
|
195
|
+
// Paste tracking for large pastes
|
|
196
|
+
this.pastes = new Map();
|
|
197
|
+
this.pasteCounter = 0;
|
|
198
|
+
// Bracketed paste mode buffering
|
|
199
|
+
this.pasteBuffer = "";
|
|
200
|
+
this.isInPaste = false;
|
|
201
|
+
// Prompt history for up/down navigation
|
|
202
|
+
this.history = [];
|
|
203
|
+
this.historyIndex = -1; // -1 = not browsing, 0 = most recent, 1 = older, etc.
|
|
204
|
+
this.historyDraft = null;
|
|
205
|
+
// Kill ring for Emacs-style kill/yank operations
|
|
206
|
+
this.killRing = new KillRing();
|
|
207
|
+
this.lastAction = null;
|
|
208
|
+
// Character jump mode
|
|
209
|
+
this.jumpMode = null;
|
|
210
|
+
// Preferred visual column for vertical cursor movement (sticky column)
|
|
211
|
+
this.preferredVisualCol = null;
|
|
212
|
+
// When the cursor is snapped to the start of an atomic segment, e.g. a
|
|
213
|
+
// paste marker, cursorCol no longer reflects where the cursor would have
|
|
214
|
+
// landed. This field stores the pre-snap cursorCol so that the next
|
|
215
|
+
// vertical move can resolve it to a visual column on whatever VL it belongs
|
|
216
|
+
// to.
|
|
217
|
+
this.snappedFromCursorCol = null;
|
|
218
|
+
// Undo support
|
|
219
|
+
this.undoStack = new UndoStack();
|
|
220
|
+
this.disableSubmit = false;
|
|
217
221
|
this.tui = tui;
|
|
218
222
|
this.theme = theme;
|
|
219
223
|
this.borderColor = theme.borderColor;
|
|
@@ -253,6 +257,7 @@ export class Editor {
|
|
|
253
257
|
setAutocompleteProvider(provider) {
|
|
254
258
|
this.cancelAutocomplete();
|
|
255
259
|
this.autocompleteProvider = provider;
|
|
260
|
+
this.setAutocompleteTriggerCharacters(provider.triggerCharacters ?? []);
|
|
256
261
|
}
|
|
257
262
|
/**
|
|
258
263
|
* Add a prompt to history for up/down arrow navigation.
|
|
@@ -271,9 +276,6 @@ export class Editor {
|
|
|
271
276
|
this.history.pop();
|
|
272
277
|
}
|
|
273
278
|
}
|
|
274
|
-
isEditorEmpty() {
|
|
275
|
-
return this.state.lines.length === 1 && this.state.lines[0] === "";
|
|
276
|
-
}
|
|
277
279
|
isOnFirstVisualLine() {
|
|
278
280
|
const visualLines = this.buildVisualLineMap(this.lastWidth);
|
|
279
281
|
const currentVisualLine = this.findCurrentVisualLine(visualLines);
|
|
@@ -294,22 +296,38 @@ export class Editor {
|
|
|
294
296
|
// Capture state when first entering history browsing mode
|
|
295
297
|
if (this.historyIndex === -1 && newIndex >= 0) {
|
|
296
298
|
this.pushUndoSnapshot();
|
|
299
|
+
this.historyDraft = structuredClone(this.state);
|
|
297
300
|
}
|
|
298
301
|
this.historyIndex = newIndex;
|
|
299
302
|
if (this.historyIndex === -1) {
|
|
300
|
-
|
|
301
|
-
this.
|
|
303
|
+
const draft = this.historyDraft;
|
|
304
|
+
this.historyDraft = null;
|
|
305
|
+
if (draft) {
|
|
306
|
+
this.state = draft;
|
|
307
|
+
this.preferredVisualCol = null;
|
|
308
|
+
this.snappedFromCursorCol = null;
|
|
309
|
+
this.scrollOffset = 0;
|
|
310
|
+
if (this.onChange)
|
|
311
|
+
this.onChange(this.getText());
|
|
312
|
+
}
|
|
313
|
+
else {
|
|
314
|
+
this.setTextInternal("");
|
|
315
|
+
}
|
|
302
316
|
}
|
|
303
317
|
else {
|
|
304
|
-
this.setTextInternal(this.history[this.historyIndex] || "");
|
|
318
|
+
this.setTextInternal(this.history[this.historyIndex] || "", direction === -1 ? "start" : "end");
|
|
305
319
|
}
|
|
306
320
|
}
|
|
321
|
+
exitHistoryBrowsing() {
|
|
322
|
+
this.historyIndex = -1;
|
|
323
|
+
this.historyDraft = null;
|
|
324
|
+
}
|
|
307
325
|
/** Internal setText that doesn't reset history state - used by navigateHistory */
|
|
308
|
-
setTextInternal(text) {
|
|
326
|
+
setTextInternal(text, cursorPlacement = "end") {
|
|
309
327
|
const lines = text.split("\n");
|
|
310
328
|
this.state.lines = lines.length === 0 ? [""] : lines;
|
|
311
|
-
this.state.cursorLine = this.state.lines.length - 1;
|
|
312
|
-
this.setCursorCol(this.state.lines[this.state.cursorLine]?.length || 0);
|
|
329
|
+
this.state.cursorLine = cursorPlacement === "start" ? 0 : this.state.lines.length - 1;
|
|
330
|
+
this.setCursorCol(cursorPlacement === "start" ? 0 : this.state.lines[this.state.cursorLine]?.length || 0);
|
|
313
331
|
// Reset scroll - render() will adjust to show cursor
|
|
314
332
|
this.scrollOffset = 0;
|
|
315
333
|
if (this.onChange) {
|
|
@@ -368,8 +386,10 @@ export class Editor {
|
|
|
368
386
|
result.push(horizontal.repeat(width));
|
|
369
387
|
}
|
|
370
388
|
// Render each visible layout line
|
|
371
|
-
// Emit hardware cursor marker
|
|
372
|
-
|
|
389
|
+
// Emit hardware cursor marker when focused so TUI can position the
|
|
390
|
+
// hardware cursor for IME candidate-window placement even while
|
|
391
|
+
// autocomplete (e.g. slash-command menu) is visible.
|
|
392
|
+
const emitCursorMarker = this.focused;
|
|
373
393
|
for (const layoutLine of visibleLines) {
|
|
374
394
|
let displayText = layoutLine.text;
|
|
375
395
|
let lineVisibleWidth = visibleWidth(layoutLine.text);
|
|
@@ -616,10 +636,7 @@ export class Editor {
|
|
|
616
636
|
}
|
|
617
637
|
// Arrow key navigation (with history support)
|
|
618
638
|
if (kb.matches(data, "tui.editor.cursorUp")) {
|
|
619
|
-
if (this.
|
|
620
|
-
this.navigateHistory(-1);
|
|
621
|
-
}
|
|
622
|
-
else if (this.historyIndex > -1 && this.isOnFirstVisualLine()) {
|
|
639
|
+
if (this.isOnFirstVisualLine() && this.history.length > 0) {
|
|
623
640
|
this.navigateHistory(-1);
|
|
624
641
|
}
|
|
625
642
|
else if (this.isOnFirstVisualLine()) {
|
|
@@ -795,7 +812,7 @@ export class Editor {
|
|
|
795
812
|
setText(text) {
|
|
796
813
|
this.cancelAutocomplete();
|
|
797
814
|
this.lastAction = null;
|
|
798
|
-
this.
|
|
815
|
+
this.exitHistoryBrowsing();
|
|
799
816
|
const normalized = this.normalizeText(text);
|
|
800
817
|
// Push undo snapshot if content differs (makes programmatic changes undoable)
|
|
801
818
|
if (this.getText() !== normalized) {
|
|
@@ -814,7 +831,7 @@ export class Editor {
|
|
|
814
831
|
this.cancelAutocomplete();
|
|
815
832
|
this.pushUndoSnapshot();
|
|
816
833
|
this.lastAction = null;
|
|
817
|
-
this.
|
|
834
|
+
this.exitHistoryBrowsing();
|
|
818
835
|
this.insertTextAtCursorInternal(text);
|
|
819
836
|
}
|
|
820
837
|
/**
|
|
@@ -867,7 +884,7 @@ export class Editor {
|
|
|
867
884
|
}
|
|
868
885
|
// All the editor methods from before...
|
|
869
886
|
insertCharacter(char, skipUndoCoalescing) {
|
|
870
|
-
this.
|
|
887
|
+
this.exitHistoryBrowsing();
|
|
871
888
|
// Undo coalescing (fish-style):
|
|
872
889
|
// - Consecutive word chars coalesce into one undo unit
|
|
873
890
|
// - Space captures state before itself (so undo removes space+following word together)
|
|
@@ -893,8 +910,8 @@ export class Editor {
|
|
|
893
910
|
if (char === "/" && this.isAtStartOfMessage()) {
|
|
894
911
|
this.tryTriggerAutocomplete();
|
|
895
912
|
}
|
|
896
|
-
// Auto-trigger for symbol-based completion like
|
|
897
|
-
else if (char
|
|
913
|
+
// Auto-trigger for symbol-based completion like @, #, or provider triggers at token boundaries
|
|
914
|
+
else if (this.autocompleteTriggerCharacters.includes(char)) {
|
|
898
915
|
const currentLine = this.state.lines[this.state.cursorLine] || "";
|
|
899
916
|
const textBeforeCursor = currentLine.slice(0, this.state.cursorCol);
|
|
900
917
|
const charBeforeSymbol = textBeforeCursor[textBeforeCursor.length - 2];
|
|
@@ -910,8 +927,8 @@ export class Editor {
|
|
|
910
927
|
if (this.isInSlashCommandContext(textBeforeCursor)) {
|
|
911
928
|
this.tryTriggerAutocomplete();
|
|
912
929
|
}
|
|
913
|
-
// Check if we're in a symbol-based completion context like
|
|
914
|
-
else if (
|
|
930
|
+
// Check if we're in a symbol-based completion context like @, #, or provider triggers
|
|
931
|
+
else if (this.autocompleteTriggerPattern.test(textBeforeCursor)) {
|
|
915
932
|
this.tryTriggerAutocomplete();
|
|
916
933
|
}
|
|
917
934
|
}
|
|
@@ -922,7 +939,7 @@ export class Editor {
|
|
|
922
939
|
}
|
|
923
940
|
handlePaste(pastedText) {
|
|
924
941
|
this.cancelAutocomplete();
|
|
925
|
-
this.
|
|
942
|
+
this.exitHistoryBrowsing();
|
|
926
943
|
this.lastAction = null;
|
|
927
944
|
this.pushUndoSnapshot();
|
|
928
945
|
// Some terminals (e.g. tmux popups with extended-keys-format=csi-u) re-encode
|
|
@@ -980,7 +997,7 @@ export class Editor {
|
|
|
980
997
|
}
|
|
981
998
|
addNewLine() {
|
|
982
999
|
this.cancelAutocomplete();
|
|
983
|
-
this.
|
|
1000
|
+
this.exitHistoryBrowsing();
|
|
984
1001
|
this.lastAction = null;
|
|
985
1002
|
this.pushUndoSnapshot();
|
|
986
1003
|
const currentLine = this.state.lines[this.state.cursorLine] || "";
|
|
@@ -1014,7 +1031,7 @@ export class Editor {
|
|
|
1014
1031
|
this.state = { lines: [""], cursorLine: 0, cursorCol: 0 };
|
|
1015
1032
|
this.pastes.clear();
|
|
1016
1033
|
this.pasteCounter = 0;
|
|
1017
|
-
this.
|
|
1034
|
+
this.exitHistoryBrowsing();
|
|
1018
1035
|
this.scrollOffset = 0;
|
|
1019
1036
|
this.undoStack.clear();
|
|
1020
1037
|
this.lastAction = null;
|
|
@@ -1024,7 +1041,7 @@ export class Editor {
|
|
|
1024
1041
|
this.onSubmit(result);
|
|
1025
1042
|
}
|
|
1026
1043
|
handleBackspace() {
|
|
1027
|
-
this.
|
|
1044
|
+
this.exitHistoryBrowsing();
|
|
1028
1045
|
this.lastAction = null;
|
|
1029
1046
|
if (this.state.cursorCol > 0) {
|
|
1030
1047
|
this.pushUndoSnapshot();
|
|
@@ -1065,8 +1082,8 @@ export class Editor {
|
|
|
1065
1082
|
if (this.isInSlashCommandContext(textBeforeCursor)) {
|
|
1066
1083
|
this.tryTriggerAutocomplete();
|
|
1067
1084
|
}
|
|
1068
|
-
// Symbol-based completion context like
|
|
1069
|
-
else if (
|
|
1085
|
+
// Symbol-based completion context like @, #, or provider triggers
|
|
1086
|
+
else if (this.autocompleteTriggerPattern.test(textBeforeCursor)) {
|
|
1070
1087
|
this.tryTriggerAutocomplete();
|
|
1071
1088
|
}
|
|
1072
1089
|
}
|
|
@@ -1206,7 +1223,7 @@ export class Editor {
|
|
|
1206
1223
|
this.setCursorCol(currentLine.length);
|
|
1207
1224
|
}
|
|
1208
1225
|
deleteToStartOfLine() {
|
|
1209
|
-
this.
|
|
1226
|
+
this.exitHistoryBrowsing();
|
|
1210
1227
|
const currentLine = this.state.lines[this.state.cursorLine] || "";
|
|
1211
1228
|
if (this.state.cursorCol > 0) {
|
|
1212
1229
|
this.pushUndoSnapshot();
|
|
@@ -1234,7 +1251,7 @@ export class Editor {
|
|
|
1234
1251
|
}
|
|
1235
1252
|
}
|
|
1236
1253
|
deleteToEndOfLine() {
|
|
1237
|
-
this.
|
|
1254
|
+
this.exitHistoryBrowsing();
|
|
1238
1255
|
const currentLine = this.state.lines[this.state.cursorLine] || "";
|
|
1239
1256
|
if (this.state.cursorCol < currentLine.length) {
|
|
1240
1257
|
this.pushUndoSnapshot();
|
|
@@ -1259,7 +1276,7 @@ export class Editor {
|
|
|
1259
1276
|
}
|
|
1260
1277
|
}
|
|
1261
1278
|
deleteWordBackwards() {
|
|
1262
|
-
this.
|
|
1279
|
+
this.exitHistoryBrowsing();
|
|
1263
1280
|
const currentLine = this.state.lines[this.state.cursorLine] || "";
|
|
1264
1281
|
// If at start of line, behave like backspace at column 0 (merge with previous line)
|
|
1265
1282
|
if (this.state.cursorCol === 0) {
|
|
@@ -1295,7 +1312,7 @@ export class Editor {
|
|
|
1295
1312
|
}
|
|
1296
1313
|
}
|
|
1297
1314
|
deleteWordForward() {
|
|
1298
|
-
this.
|
|
1315
|
+
this.exitHistoryBrowsing();
|
|
1299
1316
|
const currentLine = this.state.lines[this.state.cursorLine] || "";
|
|
1300
1317
|
// If at end of line, merge with next line (delete the newline)
|
|
1301
1318
|
if (this.state.cursorCol >= currentLine.length) {
|
|
@@ -1328,7 +1345,7 @@ export class Editor {
|
|
|
1328
1345
|
}
|
|
1329
1346
|
}
|
|
1330
1347
|
handleForwardDelete() {
|
|
1331
|
-
this.
|
|
1348
|
+
this.exitHistoryBrowsing();
|
|
1332
1349
|
this.lastAction = null;
|
|
1333
1350
|
const currentLine = this.state.lines[this.state.cursorLine] || "";
|
|
1334
1351
|
if (this.state.cursorCol < currentLine.length) {
|
|
@@ -1364,8 +1381,8 @@ export class Editor {
|
|
|
1364
1381
|
if (this.isInSlashCommandContext(textBeforeCursor)) {
|
|
1365
1382
|
this.tryTriggerAutocomplete();
|
|
1366
1383
|
}
|
|
1367
|
-
// Symbol-based completion context like
|
|
1368
|
-
else if (
|
|
1384
|
+
// Symbol-based completion context like @, #, or provider triggers
|
|
1385
|
+
else if (this.autocompleteTriggerPattern.test(textBeforeCursor)) {
|
|
1369
1386
|
this.tryTriggerAutocomplete();
|
|
1370
1387
|
}
|
|
1371
1388
|
}
|
|
@@ -1476,6 +1493,17 @@ export class Editor {
|
|
|
1476
1493
|
}
|
|
1477
1494
|
}
|
|
1478
1495
|
}
|
|
1496
|
+
// Keep an open autocomplete picker in sync with the new cursor
|
|
1497
|
+
// position: cursor movement changes the text before the cursor, so a
|
|
1498
|
+
// picker computed for the old position is stale. Re-query so it
|
|
1499
|
+
// refreshes — or closes when the new position yields no suggestions —
|
|
1500
|
+
// mirroring insertCharacter()/handleBackspace(). Without this, arrowing
|
|
1501
|
+
// left from `/cmd ` back into the command name leaves the argument
|
|
1502
|
+
// picker showing against a `/cmd` prefix (and a Tab there would
|
|
1503
|
+
// concatenate the stale suggestion onto the partial command name).
|
|
1504
|
+
if (this.autocompleteState) {
|
|
1505
|
+
this.updateAutocomplete();
|
|
1506
|
+
}
|
|
1479
1507
|
}
|
|
1480
1508
|
/**
|
|
1481
1509
|
* Scroll by a page (direction: -1 for up, 1 for down).
|
|
@@ -1540,7 +1568,7 @@ export class Editor {
|
|
|
1540
1568
|
* Insert text at cursor position (used by yank operations).
|
|
1541
1569
|
*/
|
|
1542
1570
|
insertYankedText(text) {
|
|
1543
|
-
this.
|
|
1571
|
+
this.exitHistoryBrowsing();
|
|
1544
1572
|
const lines = text.split("\n");
|
|
1545
1573
|
if (lines.length === 1) {
|
|
1546
1574
|
// Single line - insert at cursor
|
|
@@ -1612,7 +1640,7 @@ export class Editor {
|
|
|
1612
1640
|
this.undoStack.push(this.state);
|
|
1613
1641
|
}
|
|
1614
1642
|
undo() {
|
|
1615
|
-
this.
|
|
1643
|
+
this.exitHistoryBrowsing();
|
|
1616
1644
|
const snapshot = this.undoStack.pop();
|
|
1617
1645
|
if (!snapshot)
|
|
1618
1646
|
return;
|
|
@@ -1773,14 +1801,25 @@ export class Editor {
|
|
|
1773
1801
|
})();
|
|
1774
1802
|
await this.autocompleteRequestTask;
|
|
1775
1803
|
}
|
|
1804
|
+
setAutocompleteTriggerCharacters(triggerCharacters) {
|
|
1805
|
+
const next = [...DEFAULT_AUTOCOMPLETE_TRIGGER_CHARACTERS];
|
|
1806
|
+
for (const character of triggerCharacters) {
|
|
1807
|
+
if (character.length !== 1 || character === "/" || isWhitespaceChar(character) || next.includes(character)) {
|
|
1808
|
+
continue;
|
|
1809
|
+
}
|
|
1810
|
+
next.push(character);
|
|
1811
|
+
}
|
|
1812
|
+
this.autocompleteTriggerCharacters = next;
|
|
1813
|
+
this.autocompleteTriggerPattern = buildTriggerPattern(next);
|
|
1814
|
+
this.autocompleteDebouncePattern = buildDebouncePattern(next);
|
|
1815
|
+
}
|
|
1776
1816
|
getAutocompleteDebounceMs(options) {
|
|
1777
1817
|
if (options.explicitTab || options.force) {
|
|
1778
1818
|
return 0;
|
|
1779
1819
|
}
|
|
1780
1820
|
const currentLine = this.state.lines[this.state.cursorLine] || "";
|
|
1781
1821
|
const textBeforeCursor = currentLine.slice(0, this.state.cursorCol);
|
|
1782
|
-
|
|
1783
|
-
return isSymbolAutocompleteContext ? ATTACHMENT_AUTOCOMPLETE_DEBOUNCE_MS : 0;
|
|
1822
|
+
return this.autocompleteDebouncePattern.test(textBeforeCursor) ? ATTACHMENT_AUTOCOMPLETE_DEBOUNCE_MS : 0;
|
|
1784
1823
|
}
|
|
1785
1824
|
async runAutocompleteRequest(requestId, controller, snapshotText, snapshotLine, snapshotCol, options) {
|
|
1786
1825
|
if (!this.autocompleteProvider)
|