@travisennis/acai 0.0.6 → 0.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +225 -39
- package/dist/agent/index.d.ts +30 -21
- package/dist/agent/index.d.ts.map +1 -1
- package/dist/agent/index.js +237 -198
- package/dist/cli.d.ts +4 -3
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +56 -18
- package/dist/commands/add-directory/index.d.ts +3 -0
- package/dist/commands/add-directory/index.d.ts.map +1 -0
- package/dist/commands/add-directory/index.js +50 -0
- package/dist/commands/add-directory/types.d.ts +6 -0
- package/dist/commands/add-directory/types.d.ts.map +1 -0
- package/dist/commands/add-directory/utils.d.ts +3 -0
- package/dist/commands/add-directory/utils.d.ts.map +1 -0
- package/dist/commands/add-directory/utils.js +15 -0
- package/dist/commands/clear/index.d.ts +3 -0
- package/dist/commands/clear/index.d.ts.map +1 -0
- package/dist/commands/{clear-command.js → clear/index.js} +1 -7
- package/dist/commands/copy/index.d.ts +3 -0
- package/dist/commands/copy/index.d.ts.map +1 -0
- package/dist/commands/copy/index.js +39 -0
- package/dist/commands/copy/types.d.ts +3 -0
- package/dist/commands/copy/types.d.ts.map +1 -0
- package/dist/commands/copy/types.js +1 -0
- package/dist/commands/copy/utils.d.ts +3 -0
- package/dist/commands/copy/utils.d.ts.map +1 -0
- package/dist/commands/copy/utils.js +22 -0
- package/dist/commands/exit/index.d.ts +10 -0
- package/dist/commands/exit/index.d.ts.map +1 -0
- package/dist/commands/exit/index.js +21 -0
- package/dist/commands/exit/types.d.ts +8 -0
- package/dist/commands/exit/types.d.ts.map +1 -0
- package/dist/commands/exit/types.js +1 -0
- package/dist/commands/exit/utils.d.ts +2 -0
- package/dist/commands/exit/utils.d.ts.map +1 -0
- package/dist/commands/exit/utils.js +13 -0
- package/dist/commands/generate-rules/index.d.ts +3 -0
- package/dist/commands/generate-rules/index.d.ts.map +1 -0
- package/dist/commands/{generate-rules-command.js → generate-rules/index.js} +65 -147
- package/dist/commands/generate-rules/utils.d.ts +5 -0
- package/dist/commands/generate-rules/utils.d.ts.map +1 -0
- package/dist/commands/generate-rules/utils.js +25 -0
- package/dist/commands/handoff/index.d.ts +3 -0
- package/dist/commands/handoff/index.d.ts.map +1 -0
- package/dist/commands/handoff/index.js +97 -0
- package/dist/commands/handoff/utils.d.ts +4 -0
- package/dist/commands/handoff/utils.d.ts.map +1 -0
- package/dist/commands/handoff/utils.js +123 -0
- package/dist/commands/health/index.d.ts +3 -0
- package/dist/commands/health/index.d.ts.map +1 -0
- package/dist/commands/health/index.js +56 -0
- package/dist/commands/health/utils.d.ts +15 -0
- package/dist/commands/health/utils.d.ts.map +1 -0
- package/dist/commands/health/utils.js +52 -0
- package/dist/commands/help/index.d.ts +3 -0
- package/dist/commands/help/index.d.ts.map +1 -0
- package/dist/commands/{help-command.js → help/index.js} +6 -14
- package/dist/commands/history/index.d.ts +3 -0
- package/dist/commands/history/index.d.ts.map +1 -0
- package/dist/commands/{history-command.js → history/index.js} +40 -200
- package/dist/commands/history/types.d.ts +11 -0
- package/dist/commands/history/types.d.ts.map +1 -0
- package/dist/commands/history/types.js +1 -0
- package/dist/commands/history/utils.d.ts +4 -0
- package/dist/commands/history/utils.d.ts.map +1 -0
- package/dist/commands/history/utils.js +86 -0
- package/dist/commands/init/index.d.ts +3 -0
- package/dist/commands/init/index.d.ts.map +1 -0
- package/dist/commands/{init-command.js → init/index.js} +17 -27
- package/dist/commands/init-project/index.d.ts +3 -0
- package/dist/commands/init-project/index.d.ts.map +1 -0
- package/dist/commands/init-project/index.js +51 -0
- package/dist/commands/init-project/utils.d.ts +9 -0
- package/dist/commands/init-project/utils.d.ts.map +1 -0
- package/dist/commands/init-project/utils.js +43 -0
- package/dist/commands/list-directories/index.d.ts +3 -0
- package/dist/commands/list-directories/index.d.ts.map +1 -0
- package/dist/commands/{list-directories-command.js → list-directories/index.js} +2 -15
- package/dist/commands/list-tools/index.d.ts +3 -0
- package/dist/commands/list-tools/index.d.ts.map +1 -0
- package/dist/commands/list-tools/index.js +89 -0
- package/dist/commands/manager.d.ts +2 -9
- package/dist/commands/manager.d.ts.map +1 -1
- package/dist/commands/manager.js +38 -83
- package/dist/commands/model/index.d.ts +3 -0
- package/dist/commands/model/index.d.ts.map +1 -0
- package/dist/commands/model/index.js +182 -0
- package/dist/commands/model/utils.d.ts +3 -0
- package/dist/commands/model/utils.d.ts.map +1 -0
- package/dist/commands/model/utils.js +5 -0
- package/dist/commands/paste/index.d.ts +3 -0
- package/dist/commands/paste/index.d.ts.map +1 -0
- package/dist/commands/paste/index.js +94 -0
- package/dist/commands/paste/utils.d.ts +5 -0
- package/dist/commands/paste/utils.d.ts.map +1 -0
- package/dist/commands/paste/utils.js +86 -0
- package/dist/commands/pickup/index.d.ts +3 -0
- package/dist/commands/pickup/index.d.ts.map +1 -0
- package/dist/commands/pickup/index.js +138 -0
- package/dist/commands/pickup/types.d.ts +6 -0
- package/dist/commands/pickup/types.d.ts.map +1 -0
- package/dist/commands/pickup/types.js +1 -0
- package/dist/commands/pickup/utils.d.ts +7 -0
- package/dist/commands/pickup/utils.d.ts.map +1 -0
- package/dist/commands/pickup/utils.js +56 -0
- package/dist/commands/prompt/index.d.ts +5 -0
- package/dist/commands/prompt/index.d.ts.map +1 -0
- package/dist/commands/prompt/index.js +126 -0
- package/dist/commands/prompt/types.d.ts +15 -0
- package/dist/commands/prompt/types.d.ts.map +1 -0
- package/dist/commands/prompt/types.js +1 -0
- package/dist/commands/prompt/utils.d.ts +12 -0
- package/dist/commands/prompt/utils.d.ts.map +1 -0
- package/dist/commands/prompt/utils.js +107 -0
- package/dist/commands/remove-directory/index.d.ts +3 -0
- package/dist/commands/remove-directory/index.d.ts.map +1 -0
- package/dist/commands/{remove-directory-command.js → remove-directory/index.js} +3 -35
- package/dist/commands/reset/index.d.ts +3 -0
- package/dist/commands/reset/index.d.ts.map +1 -0
- package/dist/commands/{reset-command.js → reset/index.js} +8 -11
- package/dist/commands/reset/types.d.ts +1 -0
- package/dist/commands/reset/types.d.ts.map +1 -0
- package/dist/commands/reset/types.js +3 -0
- package/dist/commands/resources/index.d.ts +3 -0
- package/dist/commands/resources/index.d.ts.map +1 -0
- package/dist/commands/resources/index.js +84 -0
- package/dist/commands/review/index.d.ts +3 -0
- package/dist/commands/review/index.d.ts.map +1 -0
- package/dist/commands/review/index.js +126 -0
- package/dist/commands/review/types.d.ts +12 -0
- package/dist/commands/review/types.d.ts.map +1 -0
- package/dist/commands/review/types.js +1 -0
- package/dist/commands/review/utils.d.ts +4 -0
- package/dist/commands/review/utils.d.ts.map +1 -0
- package/dist/commands/review/utils.js +87 -0
- package/dist/commands/save/index.d.ts +3 -0
- package/dist/commands/save/index.d.ts.map +1 -0
- package/dist/commands/{save-command.js → save/index.js} +3 -10
- package/dist/commands/session/index.d.ts +3 -0
- package/dist/commands/session/index.d.ts.map +1 -0
- package/dist/commands/session/index.js +197 -0
- package/dist/commands/session/types.d.ts +13 -0
- package/dist/commands/session/types.d.ts.map +1 -0
- package/dist/commands/session/types.js +7 -0
- package/dist/commands/shell/index.d.ts +3 -0
- package/dist/commands/shell/index.d.ts.map +1 -0
- package/dist/commands/{shell-command.js → shell/index.js} +4 -51
- package/dist/commands/types.d.ts +2 -5
- package/dist/commands/types.d.ts.map +1 -1
- package/dist/config.d.ts +36 -7
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +118 -60
- package/dist/dedent.d.ts.map +1 -1
- package/dist/dedent.js +7 -7
- package/dist/execution/index.d.ts +18 -2
- package/dist/execution/index.d.ts.map +1 -1
- package/dist/execution/index.js +119 -81
- package/dist/formatting.d.ts +46 -0
- package/dist/formatting.d.ts.map +1 -1
- package/dist/formatting.js +94 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +299 -177
- package/dist/logger.d.ts.map +1 -1
- package/dist/logger.js +4 -11
- package/dist/mentions.d.ts.map +1 -1
- package/dist/mentions.js +3 -53
- package/dist/middleware/audit-message.d.ts +2 -2
- package/dist/middleware/audit-message.d.ts.map +1 -1
- package/dist/middleware/audit-message.js +40 -2
- package/dist/middleware/cache.d.ts +2 -2
- package/dist/middleware/cache.d.ts.map +1 -1
- package/dist/middleware/cache.js +111 -27
- package/dist/middleware/rate-limit.d.ts +2 -2
- package/dist/middleware/rate-limit.d.ts.map +1 -1
- package/dist/middleware/rate-limit.js +1 -0
- package/dist/models/ai-config.d.ts.map +1 -1
- package/dist/models/ai-config.js +46 -29
- package/dist/models/anthropic-provider.d.ts +14 -13
- package/dist/models/anthropic-provider.d.ts.map +1 -1
- package/dist/models/anthropic-provider.js +0 -7
- package/dist/models/deepseek-provider.d.ts +9 -8
- package/dist/models/deepseek-provider.d.ts.map +1 -1
- package/dist/models/deepseek-provider.js +0 -2
- package/dist/models/google-provider.d.ts +10 -9
- package/dist/models/google-provider.d.ts.map +1 -1
- package/dist/models/google-provider.js +0 -3
- package/dist/models/groq-provider.d.ts +8 -7
- package/dist/models/groq-provider.d.ts.map +1 -1
- package/dist/models/groq-provider.js +0 -1
- package/dist/models/manager.d.ts +7 -4
- package/dist/models/manager.d.ts.map +1 -1
- package/dist/models/manager.js +5 -25
- package/dist/models/openai-provider.d.ts +11 -10
- package/dist/models/openai-provider.d.ts.map +1 -1
- package/dist/models/openai-provider.js +0 -4
- package/dist/models/opencode-zen-provider.d.ts +23 -0
- package/dist/models/opencode-zen-provider.d.ts.map +1 -0
- package/dist/models/opencode-zen-provider.js +76 -0
- package/dist/models/openrouter-provider.d.ts +34 -28
- package/dist/models/openrouter-provider.d.ts.map +1 -1
- package/dist/models/openrouter-provider.js +148 -139
- package/dist/models/providers.d.ts +6 -16
- package/dist/models/providers.d.ts.map +1 -1
- package/dist/models/providers.js +8 -58
- package/dist/models/xai-provider.d.ts +9 -8
- package/dist/models/xai-provider.d.ts.map +1 -1
- package/dist/models/xai-provider.js +0 -2
- package/dist/prompts/manager.d.ts +1 -1
- package/dist/prompts/manager.d.ts.map +1 -1
- package/dist/prompts/manager.js +1 -1
- package/dist/prompts.d.ts +8 -4
- package/dist/prompts.d.ts.map +1 -1
- package/dist/prompts.js +155 -177
- package/dist/repl/project-status.d.ts +19 -0
- package/dist/repl/project-status.d.ts.map +1 -0
- package/dist/repl/project-status.js +78 -0
- package/dist/repl-new.d.ts +15 -6
- package/dist/repl-new.d.ts.map +1 -1
- package/dist/repl-new.js +266 -83
- package/dist/{messages.d.ts → sessions/manager.d.ts} +13 -4
- package/dist/sessions/manager.d.ts.map +1 -0
- package/dist/{messages.js → sessions/manager.js} +126 -16
- package/dist/skills.d.ts +16 -0
- package/dist/skills.d.ts.map +1 -0
- package/dist/skills.js +233 -0
- package/dist/terminal/control.d.ts +56 -0
- package/dist/terminal/control.d.ts.map +1 -0
- package/dist/terminal/control.js +111 -0
- package/dist/terminal/default-theme.d.ts +1 -1
- package/dist/terminal/default-theme.d.ts.map +1 -1
- package/dist/terminal/default-theme.js +24 -28
- package/dist/terminal/formatting.d.ts +23 -26
- package/dist/terminal/formatting.d.ts.map +1 -1
- package/dist/terminal/formatting.js +35 -53
- package/dist/terminal/highlight/index.d.ts.map +1 -1
- package/dist/terminal/highlight/index.js +3 -6
- package/dist/terminal/highlight/theme.d.ts.map +1 -1
- package/dist/terminal/highlight/theme.js +2 -6
- package/dist/terminal/index.d.ts +2 -101
- package/dist/terminal/index.d.ts.map +1 -1
- package/dist/terminal/index.js +2 -464
- package/dist/terminal/keys.d.ts +211 -0
- package/dist/terminal/keys.d.ts.map +1 -0
- package/dist/terminal/keys.js +546 -0
- package/dist/terminal/segmenter.d.ts +6 -0
- package/dist/terminal/segmenter.d.ts.map +1 -0
- package/dist/terminal/segmenter.js +11 -0
- package/dist/terminal/select-prompt.d.ts.map +1 -1
- package/dist/terminal/select-prompt.js +9 -21
- package/dist/terminal/string-width.d.ts.map +1 -1
- package/dist/terminal/string-width.js +40 -21
- package/dist/terminal/strip-ansi.d.ts.map +1 -1
- package/dist/terminal/strip-ansi.js +9 -15
- package/dist/terminal/table/cell.d.ts +114 -0
- package/dist/terminal/table/cell.d.ts.map +1 -0
- package/dist/terminal/table/cell.js +407 -0
- package/dist/terminal/table/debug.d.ts +15 -0
- package/dist/terminal/table/debug.d.ts.map +1 -0
- package/dist/terminal/table/debug.js +32 -0
- package/dist/terminal/table/index.d.ts +3 -0
- package/dist/terminal/table/index.d.ts.map +1 -0
- package/dist/terminal/table/index.js +2 -0
- package/dist/terminal/table/layout-manager.d.ts +27 -0
- package/dist/terminal/table/layout-manager.d.ts.map +1 -0
- package/dist/terminal/table/layout-manager.js +257 -0
- package/dist/terminal/table/table.d.ts +9 -0
- package/dist/terminal/table/table.d.ts.map +1 -0
- package/dist/terminal/table/table.js +97 -0
- package/dist/terminal/table/utils.d.ts +63 -0
- package/dist/terminal/table/utils.d.ts.map +1 -0
- package/dist/terminal/table/utils.js +326 -0
- package/dist/tokens/tracker.d.ts.map +1 -1
- package/dist/tokens/tracker.js +58 -16
- package/dist/tools/bash.d.ts +11 -11
- package/dist/tools/bash.d.ts.map +1 -1
- package/dist/tools/bash.js +189 -81
- package/dist/tools/directory-tree.d.ts +9 -13
- package/dist/tools/directory-tree.d.ts.map +1 -1
- package/dist/tools/directory-tree.js +90 -60
- package/dist/tools/dynamic-tool-loader.d.ts +22 -8
- package/dist/tools/dynamic-tool-loader.d.ts.map +1 -1
- package/dist/tools/dynamic-tool-loader.js +41 -46
- package/dist/tools/edit-file.d.ts +6 -16
- package/dist/tools/edit-file.d.ts.map +1 -1
- package/dist/tools/edit-file.js +22 -66
- package/dist/tools/glob.d.ts +15 -16
- package/dist/tools/glob.d.ts.map +1 -1
- package/dist/tools/glob.js +78 -109
- package/dist/tools/grep.d.ts +19 -22
- package/dist/tools/grep.d.ts.map +1 -1
- package/dist/tools/grep.js +61 -93
- package/dist/tools/index.d.ts +202 -167
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +17 -273
- package/dist/tools/ls.d.ts +26 -0
- package/dist/tools/ls.d.ts.map +1 -0
- package/dist/tools/ls.js +80 -0
- package/dist/tools/read-file.d.ts +15 -15
- package/dist/tools/read-file.d.ts.map +1 -1
- package/dist/tools/read-file.js +52 -76
- package/dist/tools/save-file.d.ts +8 -8
- package/dist/tools/save-file.d.ts.map +1 -1
- package/dist/tools/save-file.js +42 -53
- package/dist/tools/think.d.ts +4 -4
- package/dist/tools/think.d.ts.map +1 -1
- package/dist/tools/think.js +9 -32
- package/dist/tools/types.d.ts +5 -21
- package/dist/tools/types.d.ts.map +1 -1
- package/dist/tools/types.js +0 -9
- package/dist/tui/autocomplete/attachment-provider.d.ts +18 -0
- package/dist/tui/autocomplete/attachment-provider.d.ts.map +1 -0
- package/dist/tui/autocomplete/attachment-provider.js +159 -0
- package/dist/tui/autocomplete/base-provider.d.ts +17 -0
- package/dist/tui/autocomplete/base-provider.d.ts.map +1 -0
- package/dist/tui/autocomplete/base-provider.js +1 -0
- package/dist/tui/autocomplete/combined-provider.d.ts +20 -0
- package/dist/tui/autocomplete/combined-provider.d.ts.map +1 -0
- package/dist/tui/autocomplete/combined-provider.js +61 -0
- package/dist/tui/autocomplete/command-provider.d.ts +20 -0
- package/dist/tui/autocomplete/command-provider.d.ts.map +1 -0
- package/dist/tui/autocomplete/command-provider.js +90 -0
- package/dist/tui/autocomplete/file-search-provider.d.ts +16 -0
- package/dist/tui/autocomplete/file-search-provider.d.ts.map +1 -0
- package/dist/tui/autocomplete/file-search-provider.js +123 -0
- package/dist/tui/autocomplete/path-provider.d.ts +21 -0
- package/dist/tui/autocomplete/path-provider.d.ts.map +1 -0
- package/dist/tui/autocomplete/path-provider.js +164 -0
- package/dist/tui/autocomplete/utils.d.ts +16 -0
- package/dist/tui/autocomplete/utils.d.ts.map +1 -0
- package/dist/tui/autocomplete/utils.js +137 -0
- package/dist/tui/autocomplete.d.ts +12 -43
- package/dist/tui/autocomplete.d.ts.map +1 -1
- package/dist/tui/autocomplete.js +20 -465
- package/dist/tui/components/assistant-message.js +1 -1
- package/dist/tui/components/box.d.ts +20 -0
- package/dist/tui/components/box.d.ts.map +1 -0
- package/dist/tui/components/box.js +87 -0
- package/dist/tui/components/editor.d.ts +61 -7
- package/dist/tui/components/editor.d.ts.map +1 -1
- package/dist/tui/components/editor.js +630 -127
- package/dist/tui/components/footer.d.ts +19 -18
- package/dist/tui/components/footer.d.ts.map +1 -1
- package/dist/tui/components/footer.js +90 -185
- package/dist/tui/components/header.d.ts +21 -0
- package/dist/tui/components/header.d.ts.map +1 -0
- package/dist/tui/components/header.js +63 -0
- package/dist/tui/components/input.d.ts.map +1 -1
- package/dist/tui/components/input.js +8 -7
- package/dist/tui/components/loader.d.ts +6 -1
- package/dist/tui/components/loader.d.ts.map +1 -1
- package/dist/tui/components/loader.js +8 -3
- package/dist/tui/components/markdown.d.ts +31 -27
- package/dist/tui/components/markdown.d.ts.map +1 -1
- package/dist/tui/components/markdown.js +131 -67
- package/dist/tui/components/modal.d.ts +0 -11
- package/dist/tui/components/modal.d.ts.map +1 -1
- package/dist/tui/components/modal.js +9 -37
- package/dist/tui/components/notification.d.ts +28 -0
- package/dist/tui/components/notification.d.ts.map +1 -0
- package/dist/tui/components/notification.js +63 -0
- package/dist/tui/components/progress-bar.d.ts +19 -0
- package/dist/tui/components/progress-bar.d.ts.map +1 -0
- package/dist/tui/components/progress-bar.js +66 -0
- package/dist/tui/components/select-list.d.ts +12 -1
- package/dist/tui/components/select-list.d.ts.map +1 -1
- package/dist/tui/components/select-list.js +73 -32
- package/dist/tui/components/spacer.d.ts +1 -1
- package/dist/tui/components/spacer.d.ts.map +1 -1
- package/dist/tui/components/spacer.js +2 -2
- package/dist/tui/components/table.d.ts +27 -0
- package/dist/tui/components/table.d.ts.map +1 -0
- package/dist/tui/components/table.js +125 -0
- package/dist/tui/components/thinking-block.d.ts.map +1 -1
- package/dist/tui/components/thinking-block.js +4 -1
- package/dist/tui/components/tool-execution.d.ts +7 -6
- package/dist/tui/components/tool-execution.d.ts.map +1 -1
- package/dist/tui/components/tool-execution.js +81 -85
- package/dist/tui/components/user-message.d.ts.map +1 -1
- package/dist/tui/components/user-message.js +6 -4
- package/dist/tui/components/welcome.d.ts +8 -1
- package/dist/tui/components/welcome.d.ts.map +1 -1
- package/dist/tui/components/welcome.js +45 -6
- package/dist/tui/index.d.ts +12 -6
- package/dist/tui/index.d.ts.map +1 -1
- package/dist/tui/index.js +7 -3
- package/dist/tui/terminal.d.ts +4 -3
- package/dist/tui/terminal.d.ts.map +1 -1
- package/dist/tui/terminal.js +43 -49
- package/dist/tui/tui.d.ts +3 -0
- package/dist/tui/tui.d.ts.map +1 -1
- package/dist/tui/tui.js +58 -43
- package/dist/tui/utils.d.ts +5 -0
- package/dist/tui/utils.d.ts.map +1 -1
- package/dist/tui/utils.js +80 -1
- package/dist/{tools/bash-utils.d.ts → utils/bash.d.ts} +3 -3
- package/dist/utils/bash.d.ts.map +1 -0
- package/dist/{tools/bash-utils.js → utils/bash.js} +22 -11
- package/dist/utils/{filesystem.d.ts → filesystem/operations.d.ts} +1 -1
- package/dist/utils/filesystem/operations.d.ts.map +1 -0
- package/dist/{tools/filesystem-utils.d.ts → utils/filesystem/security.d.ts} +3 -2
- package/dist/utils/filesystem/security.d.ts.map +1 -0
- package/dist/{tools/filesystem-utils.js → utils/filesystem/security.js} +62 -4
- package/dist/utils/funcs.d.ts +6 -0
- package/dist/utils/funcs.d.ts.map +1 -0
- package/dist/utils/funcs.js +6 -0
- package/dist/{tools/git-utils.d.ts → utils/git.d.ts} +1 -1
- package/dist/utils/git.d.ts.map +1 -0
- package/dist/{tools/git-utils.js → utils/git.js} +0 -6
- package/dist/utils/glob.js +1 -1
- package/dist/utils/yaml.d.ts +11 -0
- package/dist/utils/yaml.d.ts.map +1 -0
- package/dist/utils/yaml.js +207 -0
- package/dist/utils/{zod-utils.d.ts → zod.d.ts} +2 -1
- package/dist/utils/zod.d.ts.map +1 -0
- package/dist/utils/zod.js +24 -0
- package/package.json +31 -29
- package/dist/agent/manual-loop.d.ts +0 -41
- package/dist/agent/manual-loop.d.ts.map +0 -1
- package/dist/agent/manual-loop.js +0 -278
- package/dist/api/exa/index.d.ts +0 -177
- package/dist/api/exa/index.d.ts.map +0 -1
- package/dist/api/exa/index.js +0 -439
- package/dist/commands/add-directory-command.d.ts +0 -3
- package/dist/commands/add-directory-command.d.ts.map +0 -1
- package/dist/commands/add-directory-command.js +0 -85
- package/dist/commands/application-log-command.d.ts +0 -3
- package/dist/commands/application-log-command.d.ts.map +0 -1
- package/dist/commands/application-log-command.js +0 -79
- package/dist/commands/clear-command.d.ts +0 -3
- package/dist/commands/clear-command.d.ts.map +0 -1
- package/dist/commands/compact-command.d.ts +0 -3
- package/dist/commands/compact-command.d.ts.map +0 -1
- package/dist/commands/compact-command.js +0 -64
- package/dist/commands/context-command.d.ts +0 -3
- package/dist/commands/context-command.d.ts.map +0 -1
- package/dist/commands/context-command.js +0 -183
- package/dist/commands/copy-command.d.ts +0 -3
- package/dist/commands/copy-command.d.ts.map +0 -1
- package/dist/commands/copy-command.js +0 -80
- package/dist/commands/edit-command.d.ts +0 -3
- package/dist/commands/edit-command.d.ts.map +0 -1
- package/dist/commands/edit-command.js +0 -88
- package/dist/commands/edit-prompt-command.d.ts +0 -3
- package/dist/commands/edit-prompt-command.d.ts.map +0 -1
- package/dist/commands/edit-prompt-command.js +0 -61
- package/dist/commands/exit-command.d.ts +0 -13
- package/dist/commands/exit-command.d.ts.map +0 -1
- package/dist/commands/exit-command.js +0 -46
- package/dist/commands/files-command.d.ts +0 -3
- package/dist/commands/files-command.d.ts.map +0 -1
- package/dist/commands/files-command.js +0 -121
- package/dist/commands/generate-rules-command.d.ts +0 -3
- package/dist/commands/generate-rules-command.d.ts.map +0 -1
- package/dist/commands/handoff-command.d.ts +0 -3
- package/dist/commands/handoff-command.d.ts.map +0 -1
- package/dist/commands/handoff-command.js +0 -202
- package/dist/commands/health-command.d.ts +0 -4
- package/dist/commands/health-command.d.ts.map +0 -1
- package/dist/commands/health-command.js +0 -213
- package/dist/commands/help-command.d.ts +0 -3
- package/dist/commands/help-command.d.ts.map +0 -1
- package/dist/commands/history-command.d.ts +0 -3
- package/dist/commands/history-command.d.ts.map +0 -1
- package/dist/commands/init-command.d.ts +0 -3
- package/dist/commands/init-command.d.ts.map +0 -1
- package/dist/commands/last-log-command.d.ts +0 -3
- package/dist/commands/last-log-command.d.ts.map +0 -1
- package/dist/commands/last-log-command.js +0 -98
- package/dist/commands/list-directories-command.d.ts +0 -3
- package/dist/commands/list-directories-command.d.ts.map +0 -1
- package/dist/commands/list-tools-command.d.ts +0 -3
- package/dist/commands/list-tools-command.d.ts.map +0 -1
- package/dist/commands/list-tools-command.js +0 -124
- package/dist/commands/model-command.d.ts +0 -25
- package/dist/commands/model-command.d.ts.map +0 -1
- package/dist/commands/model-command.js +0 -340
- package/dist/commands/paste-command.d.ts +0 -3
- package/dist/commands/paste-command.d.ts.map +0 -1
- package/dist/commands/paste-command.js +0 -277
- package/dist/commands/pickup-command.d.ts +0 -3
- package/dist/commands/pickup-command.d.ts.map +0 -1
- package/dist/commands/pickup-command.js +0 -161
- package/dist/commands/prompt-command.d.ts +0 -3
- package/dist/commands/prompt-command.d.ts.map +0 -1
- package/dist/commands/prompt-command.js +0 -280
- package/dist/commands/remove-directory-command.d.ts +0 -3
- package/dist/commands/remove-directory-command.d.ts.map +0 -1
- package/dist/commands/reset-command.d.ts +0 -3
- package/dist/commands/reset-command.d.ts.map +0 -1
- package/dist/commands/rules-command.d.ts +0 -3
- package/dist/commands/rules-command.d.ts.map +0 -1
- package/dist/commands/rules-command.js +0 -135
- package/dist/commands/save-command.d.ts +0 -3
- package/dist/commands/save-command.d.ts.map +0 -1
- package/dist/commands/shell-command.d.ts +0 -3
- package/dist/commands/shell-command.d.ts.map +0 -1
- package/dist/commands/usage-command.d.ts +0 -3
- package/dist/commands/usage-command.d.ts.map +0 -1
- package/dist/commands/usage-command.js +0 -42
- package/dist/messages.d.ts.map +0 -1
- package/dist/repl/display-tool-messages.d.ts +0 -4
- package/dist/repl/display-tool-messages.d.ts.map +0 -1
- package/dist/repl/display-tool-messages.js +0 -58
- package/dist/repl/display-tool-use.d.ts +0 -14
- package/dist/repl/display-tool-use.d.ts.map +0 -1
- package/dist/repl/display-tool-use.js +0 -63
- package/dist/repl/get-prompt-header.d.ts +0 -8
- package/dist/repl/get-prompt-header.d.ts.map +0 -1
- package/dist/repl/get-prompt-header.js +0 -9
- package/dist/repl/project-status-line.d.ts +0 -2
- package/dist/repl/project-status-line.d.ts.map +0 -1
- package/dist/repl/project-status-line.js +0 -31
- package/dist/repl/prompt.d.ts +0 -21
- package/dist/repl/prompt.d.ts.map +0 -1
- package/dist/repl/prompt.js +0 -244
- package/dist/repl/tool-call-repair.d.ts +0 -4
- package/dist/repl/tool-call-repair.d.ts.map +0 -1
- package/dist/repl/tool-call-repair.js +0 -54
- package/dist/repl.d.ts +0 -29
- package/dist/repl.d.ts.map +0 -1
- package/dist/repl.js +0 -218
- package/dist/terminal/checkbox-prompt.d.ts +0 -36
- package/dist/terminal/checkbox-prompt.d.ts.map +0 -1
- package/dist/terminal/checkbox-prompt.js +0 -368
- package/dist/terminal/editor-prompt.d.ts +0 -10
- package/dist/terminal/editor-prompt.d.ts.map +0 -1
- package/dist/terminal/editor-prompt.js +0 -61
- package/dist/terminal/errors.d.ts +0 -19
- package/dist/terminal/errors.d.ts.map +0 -1
- package/dist/terminal/errors.js +0 -37
- package/dist/terminal/input-prompt.d.ts +0 -17
- package/dist/terminal/input-prompt.d.ts.map +0 -1
- package/dist/terminal/input-prompt.js +0 -181
- package/dist/terminal/markdown.d.ts +0 -2
- package/dist/terminal/markdown.d.ts.map +0 -1
- package/dist/terminal/markdown.js +0 -118
- package/dist/terminal/search-prompt.d.ts +0 -20
- package/dist/terminal/search-prompt.d.ts.map +0 -1
- package/dist/terminal/search-prompt.js +0 -280
- package/dist/terminal/types.d.ts +0 -35
- package/dist/terminal/types.d.ts.map +0 -1
- package/dist/tokens/threshold.d.ts +0 -35
- package/dist/tokens/threshold.d.ts.map +0 -1
- package/dist/tokens/threshold.js +0 -85
- package/dist/tools/advanced-edit-file.d.ts +0 -69
- package/dist/tools/advanced-edit-file.d.ts.map +0 -1
- package/dist/tools/advanced-edit-file.js +0 -281
- package/dist/tools/agent.d.ts +0 -29
- package/dist/tools/agent.d.ts.map +0 -1
- package/dist/tools/agent.js +0 -103
- package/dist/tools/bash-utils.d.ts.map +0 -1
- package/dist/tools/code-interpreter.d.ts +0 -25
- package/dist/tools/code-interpreter.d.ts.map +0 -1
- package/dist/tools/code-interpreter.js +0 -167
- package/dist/tools/delete-file.d.ts +0 -24
- package/dist/tools/delete-file.d.ts.map +0 -1
- package/dist/tools/delete-file.js +0 -70
- package/dist/tools/dynamic-tool-parser.d.ts +0 -21
- package/dist/tools/dynamic-tool-parser.d.ts.map +0 -1
- package/dist/tools/dynamic-tool-parser.js +0 -22
- package/dist/tools/filesystem-utils.d.ts.map +0 -1
- package/dist/tools/git-utils.d.ts.map +0 -1
- package/dist/tools/llm-edit-fixer.d.ts +0 -25
- package/dist/tools/llm-edit-fixer.d.ts.map +0 -1
- package/dist/tools/llm-edit-fixer.js +0 -150
- package/dist/tools/move-file.d.ts +0 -26
- package/dist/tools/move-file.d.ts.map +0 -1
- package/dist/tools/move-file.js +0 -58
- package/dist/tools/read-multiple-files.d.ts +0 -26
- package/dist/tools/read-multiple-files.d.ts.map +0 -1
- package/dist/tools/read-multiple-files.js +0 -139
- package/dist/tools/web-fetch.d.ts +0 -56
- package/dist/tools/web-fetch.d.ts.map +0 -1
- package/dist/tools/web-fetch.js +0 -247
- package/dist/tools/web-search.d.ts +0 -23
- package/dist/tools/web-search.d.ts.map +0 -1
- package/dist/tools/web-search.js +0 -133
- package/dist/tui/components/prompt-status.d.ts +0 -16
- package/dist/tui/components/prompt-status.d.ts.map +0 -1
- package/dist/tui/components/prompt-status.js +0 -21
- package/dist/utils/filesystem.d.ts.map +0 -1
- package/dist/utils/zod-utils.d.ts.map +0 -1
- package/dist/utils/zod-utils.js +0 -7
- /package/dist/{terminal → commands/add-directory}/types.js +0 -0
- /package/dist/utils/{filesystem.js → filesystem/operations.js} +0 -0
|
@@ -1,12 +1,158 @@
|
|
|
1
|
+
import { isCtrlC, isEnter, isEscape, isNavigationKey, isTab, } from "../../terminal/keys.js";
|
|
2
|
+
import { getSegmenter } from "../../terminal/segmenter.js";
|
|
1
3
|
import style from "../../terminal/style.js";
|
|
4
|
+
import { visibleWidth } from "../utils.js";
|
|
2
5
|
import { SelectList } from "./select-list.js";
|
|
6
|
+
// Cache for line metrics to avoid repeated segmentation (LRU-style)
|
|
7
|
+
const lineMetricsCache = {
|
|
8
|
+
maxSize: 1000,
|
|
9
|
+
cache: new Map(),
|
|
10
|
+
get(line) {
|
|
11
|
+
const cached = this.cache.get(line);
|
|
12
|
+
if (cached) {
|
|
13
|
+
// Move to end for LRU behavior (most recently used)
|
|
14
|
+
this.cache.delete(line);
|
|
15
|
+
this.cache.set(line, cached);
|
|
16
|
+
return cached;
|
|
17
|
+
}
|
|
18
|
+
// Compute metrics
|
|
19
|
+
let result;
|
|
20
|
+
// Fast path for ASCII-only lines (common case)
|
|
21
|
+
if (/^[\x20-\x7E\t]*$/.test(line)) {
|
|
22
|
+
const graphemes = line.split("");
|
|
23
|
+
const widths = graphemes.map((char) => (char === "\t" ? 3 : 1));
|
|
24
|
+
const totalWidth = widths.reduce((sum, w) => sum + w, 0);
|
|
25
|
+
result = { graphemes, widths, totalWidth };
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
// Complex Unicode line, use shared segmenter
|
|
29
|
+
const graphemes = [...getSegmenter().segment(line)].map((seg) => seg.segment);
|
|
30
|
+
const widths = graphemes.map((g) => visibleWidth(g));
|
|
31
|
+
const totalWidth = widths.reduce((sum, w) => sum + w, 0);
|
|
32
|
+
result = { graphemes, widths, totalWidth };
|
|
33
|
+
}
|
|
34
|
+
// Enforce size limit before adding (evict oldest = first entry)
|
|
35
|
+
if (this.cache.size >= this.maxSize) {
|
|
36
|
+
const firstKey = this.cache.keys().next().value;
|
|
37
|
+
if (firstKey !== undefined) {
|
|
38
|
+
this.cache.delete(firstKey);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
this.cache.set(line, result);
|
|
42
|
+
return result;
|
|
43
|
+
},
|
|
44
|
+
clear() {
|
|
45
|
+
this.cache.clear();
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
49
|
+
* Compute word-wrapped chunks for a line that exceeds the given width.
|
|
50
|
+
* Shared between layoutText() and buildVisualLineMap() to avoid duplication.
|
|
51
|
+
*/
|
|
52
|
+
function computeLineChunks(line, maxWidth) {
|
|
53
|
+
const chunks = [];
|
|
54
|
+
let currentChunk = "";
|
|
55
|
+
let currentWidth = 0;
|
|
56
|
+
let chunkStartIndex = 0;
|
|
57
|
+
let currentIndex = 0;
|
|
58
|
+
// Split into words (separated by whitespace) while preserving separators
|
|
59
|
+
const wordPattern = /(\s+)|(\S+\s*)|(\S)/g;
|
|
60
|
+
const parts = [];
|
|
61
|
+
let match = wordPattern.exec(line);
|
|
62
|
+
let pos = 0;
|
|
63
|
+
while (match !== null) {
|
|
64
|
+
const matchedText = match[0];
|
|
65
|
+
const isWhitespace = /^\s*$/.test(matchedText);
|
|
66
|
+
const width = matchedText
|
|
67
|
+
.split("")
|
|
68
|
+
.reduce((sum, c) => sum + (c === "\t" ? 3 : 1), 0);
|
|
69
|
+
parts.push({ text: matchedText, isWord: !isWhitespace, width });
|
|
70
|
+
pos = match.index + matchedText.length;
|
|
71
|
+
match = wordPattern.exec(line);
|
|
72
|
+
}
|
|
73
|
+
// Handle remaining text after last match
|
|
74
|
+
if (pos < line.length) {
|
|
75
|
+
const remaining = line.slice(pos);
|
|
76
|
+
const width = remaining
|
|
77
|
+
.split("")
|
|
78
|
+
.reduce((sum, c) => sum + (c === "\t" ? 3 : 1), 0);
|
|
79
|
+
parts.push({ text: remaining, isWord: true, width });
|
|
80
|
+
}
|
|
81
|
+
for (const part of parts) {
|
|
82
|
+
const wouldExceed = currentWidth + part.width > maxWidth;
|
|
83
|
+
const isWordPart = part.isWord && currentChunk.length > 0 && !currentChunk.endsWith(" ");
|
|
84
|
+
if (wouldExceed && isWordPart && currentChunk.trimEnd().length > 0) {
|
|
85
|
+
// Current word would overflow - break before this word
|
|
86
|
+
chunks.push({
|
|
87
|
+
text: currentChunk,
|
|
88
|
+
startIndex: chunkStartIndex,
|
|
89
|
+
endIndex: currentIndex,
|
|
90
|
+
width: currentWidth,
|
|
91
|
+
});
|
|
92
|
+
currentChunk = part.text;
|
|
93
|
+
currentWidth = part.width;
|
|
94
|
+
chunkStartIndex = currentIndex;
|
|
95
|
+
}
|
|
96
|
+
else if (wouldExceed && currentChunk !== "") {
|
|
97
|
+
// Non-word or start of line overflow - break here
|
|
98
|
+
chunks.push({
|
|
99
|
+
text: currentChunk,
|
|
100
|
+
startIndex: chunkStartIndex,
|
|
101
|
+
endIndex: currentIndex,
|
|
102
|
+
width: currentWidth,
|
|
103
|
+
});
|
|
104
|
+
currentChunk = part.text;
|
|
105
|
+
currentWidth = part.width;
|
|
106
|
+
chunkStartIndex = currentIndex;
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
// Add to current chunk
|
|
110
|
+
currentChunk += part.text;
|
|
111
|
+
currentWidth += part.width;
|
|
112
|
+
}
|
|
113
|
+
currentIndex += part.text.length;
|
|
114
|
+
}
|
|
115
|
+
// Push the last chunk
|
|
116
|
+
if (currentChunk !== "") {
|
|
117
|
+
chunks.push({
|
|
118
|
+
text: currentChunk,
|
|
119
|
+
startIndex: chunkStartIndex,
|
|
120
|
+
endIndex: currentIndex,
|
|
121
|
+
width: currentWidth,
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
return chunks;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Text editor component with support for multi-line input and autocomplete.
|
|
128
|
+
*
|
|
129
|
+
* Key bindings:
|
|
130
|
+
* - Enter: Create new line
|
|
131
|
+
* - Shift+Enter / Ctrl+Enter / Option+Enter: Submit prompt
|
|
132
|
+
* - Tab: Trigger autocomplete
|
|
133
|
+
* - Escape: Cancel autocomplete or custom handler
|
|
134
|
+
* - Ctrl+C: Custom handler
|
|
135
|
+
* - Arrow keys: Navigate text
|
|
136
|
+
* - Backspace/Delete: Delete characters
|
|
137
|
+
* - Ctrl+A: Move to start of line
|
|
138
|
+
* - Ctrl+E: Move to end of line
|
|
139
|
+
* - Ctrl+K: Delete to end of line
|
|
140
|
+
* - Ctrl+U: Delete to start of line
|
|
141
|
+
* - Ctrl+W / Option+Backspace: Delete word backwards
|
|
142
|
+
* - Ctrl+Left/Right / Option+Left/Right: Word navigation
|
|
143
|
+
* - Up/Down: History navigation when editor is empty
|
|
144
|
+
*/
|
|
3
145
|
export class Editor {
|
|
4
146
|
state = {
|
|
5
147
|
lines: [""],
|
|
6
148
|
cursorLine: 0,
|
|
7
149
|
cursorCol: 0,
|
|
8
150
|
};
|
|
9
|
-
|
|
151
|
+
theme;
|
|
152
|
+
// Store last render width for cursor navigation
|
|
153
|
+
lastWidth = 80;
|
|
154
|
+
// Border color (can be changed dynamically)
|
|
155
|
+
borderColor;
|
|
10
156
|
// Autocomplete support
|
|
11
157
|
autocompleteProvider;
|
|
12
158
|
autocompleteList;
|
|
@@ -19,26 +165,87 @@ export class Editor {
|
|
|
19
165
|
// Bracketed paste mode buffering
|
|
20
166
|
pasteBuffer = "";
|
|
21
167
|
isInPaste = false;
|
|
168
|
+
// Prompt history for up/down navigation
|
|
169
|
+
history = [];
|
|
170
|
+
historyIndex = -1; // -1 = not browsing, 0 = most recent, 1 = older, etc.
|
|
22
171
|
onSubmit;
|
|
23
172
|
onChange;
|
|
24
173
|
disableSubmit = false;
|
|
25
174
|
// Custom key handlers for coding-agent
|
|
26
175
|
onEscape;
|
|
27
|
-
onCtrlC;
|
|
28
176
|
onRenderRequested;
|
|
29
|
-
constructor(
|
|
30
|
-
if (
|
|
31
|
-
|
|
32
|
-
|
|
177
|
+
constructor(theme) {
|
|
178
|
+
// Default theme if none provided (backward compatibility)
|
|
179
|
+
this.theme = theme || {
|
|
180
|
+
borderColor: style.gray,
|
|
181
|
+
};
|
|
182
|
+
this.borderColor = this.theme.borderColor;
|
|
33
183
|
}
|
|
34
|
-
|
|
35
|
-
|
|
184
|
+
/**
|
|
185
|
+
* Add a prompt to history for up/down arrow navigation.
|
|
186
|
+
* Called after successful submission.
|
|
187
|
+
*/
|
|
188
|
+
addToHistory(text) {
|
|
189
|
+
const trimmed = text.trim();
|
|
190
|
+
if (!trimmed)
|
|
191
|
+
return;
|
|
192
|
+
// Don't add consecutive duplicates
|
|
193
|
+
if (this.history.length > 0 && this.history[0] === trimmed)
|
|
194
|
+
return;
|
|
195
|
+
this.history.unshift(trimmed);
|
|
196
|
+
// Limit history size
|
|
197
|
+
if (this.history.length > 100) {
|
|
198
|
+
this.history.pop();
|
|
199
|
+
}
|
|
36
200
|
}
|
|
37
201
|
setAutocompleteProvider(provider) {
|
|
38
202
|
this.autocompleteProvider = provider;
|
|
39
203
|
}
|
|
204
|
+
isEditorEmpty() {
|
|
205
|
+
return this.state.lines.length === 1 && this.state.lines[0] === "";
|
|
206
|
+
}
|
|
207
|
+
isOnFirstVisualLine() {
|
|
208
|
+
const visualLines = this.buildVisualLineMap(this.lastWidth);
|
|
209
|
+
const currentVisualLine = this.findCurrentVisualLine(visualLines);
|
|
210
|
+
return currentVisualLine === 0;
|
|
211
|
+
}
|
|
212
|
+
isOnLastVisualLine() {
|
|
213
|
+
const visualLines = this.buildVisualLineMap(this.lastWidth);
|
|
214
|
+
const currentVisualLine = this.findCurrentVisualLine(visualLines);
|
|
215
|
+
return currentVisualLine === visualLines.length - 1;
|
|
216
|
+
}
|
|
217
|
+
navigateHistory(direction) {
|
|
218
|
+
if (this.history.length === 0)
|
|
219
|
+
return;
|
|
220
|
+
const newIndex = this.historyIndex - direction; // Up(-1) increases index, Down(1) decreases
|
|
221
|
+
if (newIndex < -1 || newIndex >= this.history.length)
|
|
222
|
+
return;
|
|
223
|
+
this.historyIndex = newIndex;
|
|
224
|
+
if (this.historyIndex === -1) {
|
|
225
|
+
// Returned to "current" state - clear editor
|
|
226
|
+
this.setTextInternal("");
|
|
227
|
+
}
|
|
228
|
+
else {
|
|
229
|
+
this.setTextInternal(this.history[this.historyIndex] || "");
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
/** Internal setText that doesn't reset history state - used by navigateHistory */
|
|
233
|
+
setTextInternal(text) {
|
|
234
|
+
const lines = text.replace(/\r\n/g, "\n").replace(/\r/g, "\n").split("\n");
|
|
235
|
+
this.state.lines = lines.length === 0 ? [""] : lines;
|
|
236
|
+
this.state.cursorLine = this.state.lines.length - 1;
|
|
237
|
+
this.state.cursorCol = this.state.lines[this.state.cursorLine]?.length || 0;
|
|
238
|
+
if (this.onChange) {
|
|
239
|
+
this.onChange(this.getText());
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
invalidate() {
|
|
243
|
+
// No cached state to invalidate currently
|
|
244
|
+
}
|
|
40
245
|
render(width) {
|
|
41
|
-
|
|
246
|
+
// Store width for cursor navigation
|
|
247
|
+
this.lastWidth = width;
|
|
248
|
+
const horizontal = this.borderColor("─");
|
|
42
249
|
// Layout the text - use full width
|
|
43
250
|
const layoutLines = this.layoutText(width);
|
|
44
251
|
const result = [];
|
|
@@ -47,41 +254,50 @@ export class Editor {
|
|
|
47
254
|
// Render each layout line
|
|
48
255
|
for (const layoutLine of layoutLines) {
|
|
49
256
|
let displayText = layoutLine.text;
|
|
50
|
-
let
|
|
257
|
+
let lineVisibleWidth = layoutLine.width;
|
|
51
258
|
// Add cursor if this line has it
|
|
52
259
|
if (layoutLine.hasCursor && layoutLine.cursorPos !== undefined) {
|
|
53
260
|
const before = displayText.slice(0, layoutLine.cursorPos);
|
|
54
261
|
const after = displayText.slice(layoutLine.cursorPos);
|
|
55
262
|
if (after.length > 0) {
|
|
56
|
-
// Cursor is on a character - replace it with highlighted version
|
|
57
|
-
|
|
58
|
-
const
|
|
263
|
+
// Cursor is on a character (grapheme) - replace it with highlighted version
|
|
264
|
+
// Get the first grapheme from 'after'
|
|
265
|
+
const afterGraphemes = [...getSegmenter().segment(after)];
|
|
266
|
+
const firstGrapheme = afterGraphemes[0]?.segment || "";
|
|
267
|
+
const restAfter = after.slice(firstGrapheme.length);
|
|
268
|
+
const cursor = `\x1b[7m${firstGrapheme}\x1b[0m`;
|
|
59
269
|
displayText = before + cursor + restAfter;
|
|
60
|
-
//
|
|
270
|
+
// lineVisibleWidth stays the same - we're replacing, not adding
|
|
61
271
|
}
|
|
62
272
|
else {
|
|
63
273
|
// Cursor is at the end - check if we have room for the space
|
|
64
|
-
if (
|
|
274
|
+
if (lineVisibleWidth < width) {
|
|
65
275
|
// We have room - add highlighted space
|
|
66
276
|
const cursor = "\x1b[7m \x1b[0m";
|
|
67
277
|
displayText = before + cursor;
|
|
68
|
-
//
|
|
69
|
-
|
|
278
|
+
// lineVisibleWidth increases by 1 - we're adding a space
|
|
279
|
+
lineVisibleWidth = lineVisibleWidth + 1;
|
|
70
280
|
}
|
|
71
281
|
else {
|
|
72
|
-
// Line is at full width - use reverse video on last
|
|
282
|
+
// Line is at full width - use reverse video on last grapheme if possible
|
|
73
283
|
// or just show cursor at the end without adding space
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
const
|
|
77
|
-
|
|
284
|
+
const beforeGraphemes = [...getSegmenter().segment(before)];
|
|
285
|
+
if (beforeGraphemes.length > 0) {
|
|
286
|
+
const lastGrapheme = beforeGraphemes[beforeGraphemes.length - 1]?.segment || "";
|
|
287
|
+
const cursor = `\x1b[7m${lastGrapheme}\x1b[0m`;
|
|
288
|
+
// Rebuild 'before' without the last grapheme
|
|
289
|
+
const beforeWithoutLast = beforeGraphemes
|
|
290
|
+
.slice(0, -1)
|
|
291
|
+
.map((g) => g.segment)
|
|
292
|
+
.join("");
|
|
293
|
+
displayText = beforeWithoutLast + cursor;
|
|
78
294
|
}
|
|
79
|
-
//
|
|
295
|
+
// lineVisibleWidth stays the same
|
|
80
296
|
}
|
|
81
297
|
}
|
|
82
298
|
}
|
|
83
|
-
// Calculate padding based on actual visible
|
|
84
|
-
const padding = " ".repeat(Math.max(0, width -
|
|
299
|
+
// Calculate padding based on actual visible width
|
|
300
|
+
const padding = " ".repeat(Math.max(0, width - lineVisibleWidth));
|
|
85
301
|
// Render the line (no side borders, just horizontal lines above and below)
|
|
86
302
|
result.push(displayText + padding);
|
|
87
303
|
}
|
|
@@ -134,13 +350,12 @@ export class Editor {
|
|
|
134
350
|
}
|
|
135
351
|
// Intercept Escape key - but only if autocomplete is NOT active
|
|
136
352
|
// (let parent handle escape for autocomplete cancellation)
|
|
137
|
-
if (data
|
|
353
|
+
if (isEscape(data) && this.onEscape && !this.isShowingAutocomplete()) {
|
|
138
354
|
this.onEscape();
|
|
139
355
|
return;
|
|
140
356
|
}
|
|
141
357
|
// Intercept Ctrl+C
|
|
142
|
-
if (data
|
|
143
|
-
this.onCtrlC();
|
|
358
|
+
if (isCtrlC(data)) {
|
|
144
359
|
return;
|
|
145
360
|
}
|
|
146
361
|
// Process regular input data
|
|
@@ -155,49 +370,54 @@ export class Editor {
|
|
|
155
370
|
// Handle autocomplete special keys first (but don't block other input)
|
|
156
371
|
if (this.isAutocompleting && this.autocompleteList) {
|
|
157
372
|
// Escape - cancel autocomplete
|
|
158
|
-
if (data
|
|
373
|
+
if (isEscape(data)) {
|
|
159
374
|
this.cancelAutocomplete();
|
|
160
375
|
return;
|
|
161
376
|
}
|
|
162
|
-
//
|
|
163
|
-
if (data
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
this.
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
const selected = this.autocompleteList.getSelectedItem();
|
|
174
|
-
if (selected && this.autocompleteProvider) {
|
|
175
|
-
const result = this.autocompleteProvider.applyCompletion(this.state.lines, this.state.cursorLine, this.state.cursorCol, selected, this.autocompletePrefix);
|
|
176
|
-
this.state.lines = result.lines;
|
|
177
|
-
this.state.cursorLine = result.cursorLine;
|
|
178
|
-
this.state.cursorCol = result.cursorCol;
|
|
179
|
-
this.cancelAutocomplete();
|
|
180
|
-
if (this.onChange) {
|
|
181
|
-
this.onChange(this.getText());
|
|
182
|
-
}
|
|
377
|
+
// Enter - apply selection
|
|
378
|
+
if (isEnter(data)) {
|
|
379
|
+
const selected = this.autocompleteList.getSelectedItem();
|
|
380
|
+
if (selected && this.autocompleteProvider) {
|
|
381
|
+
const result = this.autocompleteProvider.applyCompletion(this.state.lines, this.state.cursorLine, this.state.cursorCol, selected, this.autocompletePrefix);
|
|
382
|
+
this.state.lines = result.lines;
|
|
383
|
+
this.state.cursorLine = result.cursorLine;
|
|
384
|
+
this.state.cursorCol = result.cursorCol;
|
|
385
|
+
this.cancelAutocomplete();
|
|
386
|
+
if (this.onChange) {
|
|
387
|
+
this.onChange(this.getText());
|
|
183
388
|
}
|
|
184
|
-
return;
|
|
185
389
|
}
|
|
186
|
-
|
|
390
|
+
return;
|
|
391
|
+
}
|
|
392
|
+
// Navigation keys (arrows, Tab, Shift+Tab) - pass to autocomplete list
|
|
393
|
+
if (isNavigationKey(data)) {
|
|
394
|
+
this.autocompleteList.handleInput(data);
|
|
187
395
|
return;
|
|
188
396
|
}
|
|
189
397
|
// For other keys (like regular typing), DON'T return here
|
|
190
398
|
// Let them fall through to normal character handling
|
|
191
399
|
}
|
|
192
400
|
// Tab key - context-aware completion (but not when already autocompleting)
|
|
193
|
-
if (data
|
|
401
|
+
if (isTab(data) && !this.isAutocompleting) {
|
|
194
402
|
void this.handleTabCompletion();
|
|
195
403
|
return;
|
|
196
404
|
}
|
|
197
405
|
// Continue with rest of input handling
|
|
198
|
-
// Ctrl+K - Delete
|
|
406
|
+
// Ctrl+K - Delete to end of line
|
|
199
407
|
if (data.charCodeAt(0) === 11) {
|
|
200
|
-
this.
|
|
408
|
+
this.deleteToEndOfLine();
|
|
409
|
+
}
|
|
410
|
+
// Ctrl+U - Delete to start of line
|
|
411
|
+
else if (data.charCodeAt(0) === 21) {
|
|
412
|
+
this.deleteToStartOfLine();
|
|
413
|
+
}
|
|
414
|
+
// Ctrl+W - Delete word backwards
|
|
415
|
+
else if (data.charCodeAt(0) === 23) {
|
|
416
|
+
this.deleteWordBackwards();
|
|
417
|
+
}
|
|
418
|
+
// Option/Alt+Backspace (e.g. Ghostty sends ESC + DEL)
|
|
419
|
+
else if (data === "\x1b\x7f") {
|
|
420
|
+
this.deleteWordBackwards();
|
|
201
421
|
}
|
|
202
422
|
// Ctrl+A - Move to start of line
|
|
203
423
|
else if (data.charCodeAt(0) === 1) {
|
|
@@ -207,13 +427,12 @@ export class Editor {
|
|
|
207
427
|
else if (data.charCodeAt(0) === 5) {
|
|
208
428
|
this.moveToLineEnd();
|
|
209
429
|
}
|
|
210
|
-
//
|
|
211
|
-
else if (
|
|
212
|
-
// Modifier + Enter = new line
|
|
430
|
+
// Plain Enter (char code 13 for CR) - create new line
|
|
431
|
+
else if (data.charCodeAt(0) === 13 && data.length === 1) {
|
|
213
432
|
this.addNewLine();
|
|
214
433
|
}
|
|
215
|
-
//
|
|
216
|
-
else if (
|
|
434
|
+
// Modified Enter keys (Shift+Enter, Ctrl+Enter, etc.) - submit
|
|
435
|
+
else if (this.isModifiedEnter(data)) {
|
|
217
436
|
// If submit is disabled, do nothing
|
|
218
437
|
if (this.disableSubmit) {
|
|
219
438
|
return;
|
|
@@ -234,6 +453,7 @@ export class Editor {
|
|
|
234
453
|
};
|
|
235
454
|
this.pastes.clear();
|
|
236
455
|
this.pasteCounter = 0;
|
|
456
|
+
this.historyIndex = -1; // Exit history browsing mode
|
|
237
457
|
// Notify that editor is now empty
|
|
238
458
|
if (this.onChange) {
|
|
239
459
|
this.onChange("");
|
|
@@ -260,14 +480,42 @@ export class Editor {
|
|
|
260
480
|
// Delete key
|
|
261
481
|
this.handleForwardDelete();
|
|
262
482
|
}
|
|
483
|
+
// Word navigation (Option/Alt + Arrow or Ctrl + Arrow)
|
|
484
|
+
// Option+Left: \x1b[1;3D or \x1bb
|
|
485
|
+
// Option+Right: \x1b[1;3C or \x1bf
|
|
486
|
+
// Ctrl+Left: \x1b[1;5D
|
|
487
|
+
// Ctrl+Right: \x1b[1;5C
|
|
488
|
+
else if (data === "\x1b[1;3D" || data === "\x1bb" || data === "\x1b[1;5D") {
|
|
489
|
+
// Word left
|
|
490
|
+
this.moveWordBackwards();
|
|
491
|
+
}
|
|
492
|
+
else if (data === "\x1b[1;3C" ||
|
|
493
|
+
data === "\x1bf" ||
|
|
494
|
+
data === "\x1b[1;5C") {
|
|
495
|
+
// Word right
|
|
496
|
+
this.moveWordForwards();
|
|
497
|
+
}
|
|
263
498
|
// Arrow keys
|
|
264
499
|
else if (data === "\x1b[A") {
|
|
265
|
-
// Up
|
|
266
|
-
this.
|
|
500
|
+
// Up - history navigation or cursor movement
|
|
501
|
+
if (this.isEditorEmpty()) {
|
|
502
|
+
this.navigateHistory(-1); // Start browsing history
|
|
503
|
+
}
|
|
504
|
+
else if (this.historyIndex > -1 && this.isOnFirstVisualLine()) {
|
|
505
|
+
this.navigateHistory(-1); // Navigate to older history entry
|
|
506
|
+
}
|
|
507
|
+
else {
|
|
508
|
+
this.moveCursor(-1, 0); // Cursor movement (within text or history entry)
|
|
509
|
+
}
|
|
267
510
|
}
|
|
268
511
|
else if (data === "\x1b[B") {
|
|
269
|
-
// Down
|
|
270
|
-
this.
|
|
512
|
+
// Down - history navigation or cursor movement
|
|
513
|
+
if (this.historyIndex > -1 && this.isOnLastVisualLine()) {
|
|
514
|
+
this.navigateHistory(1); // Navigate to newer history entry or clear
|
|
515
|
+
}
|
|
516
|
+
else {
|
|
517
|
+
this.moveCursor(1, 0); // Cursor movement (within text or history entry)
|
|
518
|
+
}
|
|
271
519
|
}
|
|
272
520
|
else if (data === "\x1b[C") {
|
|
273
521
|
// Right
|
|
@@ -277,8 +525,8 @@ export class Editor {
|
|
|
277
525
|
// Left
|
|
278
526
|
this.moveCursor(0, -1);
|
|
279
527
|
}
|
|
280
|
-
// Regular characters (printable
|
|
281
|
-
else if (data.charCodeAt(0) >= 32
|
|
528
|
+
// Regular characters (printable characters and unicode, but not control characters)
|
|
529
|
+
else if (data.charCodeAt(0) >= 32) {
|
|
282
530
|
this.insertCharacter(data);
|
|
283
531
|
}
|
|
284
532
|
}
|
|
@@ -291,6 +539,7 @@ export class Editor {
|
|
|
291
539
|
text: "",
|
|
292
540
|
hasCursor: true,
|
|
293
541
|
cursorPos: 0,
|
|
542
|
+
width: 0,
|
|
294
543
|
});
|
|
295
544
|
return layoutLines;
|
|
296
545
|
}
|
|
@@ -298,48 +547,54 @@ export class Editor {
|
|
|
298
547
|
for (let i = 0; i < this.state.lines.length; i++) {
|
|
299
548
|
const line = this.state.lines[i] || "";
|
|
300
549
|
const isCurrentLine = i === this.state.cursorLine;
|
|
301
|
-
const
|
|
302
|
-
|
|
550
|
+
const metrics = lineMetricsCache.get(line);
|
|
551
|
+
const lineVisibleWidth = metrics.totalWidth;
|
|
552
|
+
if (lineVisibleWidth <= contentWidth) {
|
|
303
553
|
// Line fits in one layout line
|
|
304
554
|
if (isCurrentLine) {
|
|
305
555
|
layoutLines.push({
|
|
306
556
|
text: line,
|
|
307
557
|
hasCursor: true,
|
|
308
558
|
cursorPos: this.state.cursorCol,
|
|
559
|
+
width: lineVisibleWidth,
|
|
309
560
|
});
|
|
310
561
|
}
|
|
311
562
|
else {
|
|
312
563
|
layoutLines.push({
|
|
313
564
|
text: line,
|
|
314
565
|
hasCursor: false,
|
|
566
|
+
width: lineVisibleWidth,
|
|
315
567
|
});
|
|
316
568
|
}
|
|
317
569
|
}
|
|
318
570
|
else {
|
|
319
|
-
// Line needs wrapping
|
|
320
|
-
const chunks =
|
|
321
|
-
for (let pos = 0; pos < line.length; pos += maxLineLength) {
|
|
322
|
-
chunks.push(line.slice(pos, pos + maxLineLength));
|
|
323
|
-
}
|
|
571
|
+
// Line needs wrapping - use shared helper
|
|
572
|
+
const chunks = computeLineChunks(line, contentWidth);
|
|
324
573
|
for (let chunkIndex = 0; chunkIndex < chunks.length; chunkIndex++) {
|
|
325
574
|
const chunk = chunks[chunkIndex];
|
|
326
575
|
if (!chunk)
|
|
327
576
|
continue;
|
|
328
|
-
const chunkStart = chunkIndex * maxLineLength;
|
|
329
|
-
const chunkEnd = chunkStart + chunk.length;
|
|
330
577
|
const cursorPos = this.state.cursorCol;
|
|
331
|
-
const
|
|
578
|
+
const isLastChunk = chunkIndex === chunks.length - 1;
|
|
579
|
+
// For non-last chunks, cursor at endIndex belongs to the next chunk
|
|
580
|
+
const hasCursorInChunk = isCurrentLine &&
|
|
581
|
+
cursorPos >= chunk.startIndex &&
|
|
582
|
+
(isLastChunk
|
|
583
|
+
? cursorPos <= chunk.endIndex
|
|
584
|
+
: cursorPos < chunk.endIndex);
|
|
332
585
|
if (hasCursorInChunk) {
|
|
333
586
|
layoutLines.push({
|
|
334
|
-
text: chunk,
|
|
587
|
+
text: chunk.text,
|
|
335
588
|
hasCursor: true,
|
|
336
|
-
cursorPos: cursorPos -
|
|
589
|
+
cursorPos: cursorPos - chunk.startIndex,
|
|
590
|
+
width: chunk.width,
|
|
337
591
|
});
|
|
338
592
|
}
|
|
339
593
|
else {
|
|
340
594
|
layoutLines.push({
|
|
341
|
-
text: chunk,
|
|
595
|
+
text: chunk.text,
|
|
342
596
|
hasCursor: false,
|
|
597
|
+
width: chunk.width,
|
|
343
598
|
});
|
|
344
599
|
}
|
|
345
600
|
}
|
|
@@ -351,20 +606,12 @@ export class Editor {
|
|
|
351
606
|
return this.state.lines.join("\n");
|
|
352
607
|
}
|
|
353
608
|
setText(text) {
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
// Ensure at least one empty line
|
|
357
|
-
this.state.lines = lines.length === 0 ? [""] : lines;
|
|
358
|
-
// Reset cursor to end of text
|
|
359
|
-
this.state.cursorLine = this.state.lines.length - 1;
|
|
360
|
-
this.state.cursorCol = this.state.lines[this.state.cursorLine]?.length || 0;
|
|
361
|
-
// Notify of change
|
|
362
|
-
if (this.onChange) {
|
|
363
|
-
this.onChange(this.getText());
|
|
364
|
-
}
|
|
609
|
+
this.historyIndex = -1; // Exit history browsing mode
|
|
610
|
+
this.setTextInternal(text);
|
|
365
611
|
}
|
|
366
612
|
// All the editor methods from before...
|
|
367
613
|
insertCharacter(char) {
|
|
614
|
+
this.historyIndex = -1; // Exit history browsing mode
|
|
368
615
|
const line = this.state.lines[this.state.cursorLine] || "";
|
|
369
616
|
const before = line.slice(0, this.state.cursorCol);
|
|
370
617
|
const after = line.slice(this.state.cursorCol);
|
|
@@ -379,13 +626,44 @@ export class Editor {
|
|
|
379
626
|
if (char === "/" && this.isAtStartOfMessage()) {
|
|
380
627
|
void this.tryTriggerAutocomplete();
|
|
381
628
|
}
|
|
629
|
+
// Auto-trigger for "@" file reference (fuzzy search)
|
|
630
|
+
else if (char === "@") {
|
|
631
|
+
const currentLine = this.state.lines[this.state.cursorLine] || "";
|
|
632
|
+
const textBeforeCursor = currentLine.slice(0, this.state.cursorCol);
|
|
633
|
+
// Only trigger if @ is after whitespace or at start of line
|
|
634
|
+
const charBeforeAt = textBeforeCursor[textBeforeCursor.length - 2];
|
|
635
|
+
if (textBeforeCursor.length === 1 ||
|
|
636
|
+
charBeforeAt === " " ||
|
|
637
|
+
charBeforeAt === "\t") {
|
|
638
|
+
void this.tryTriggerAutocomplete();
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
// Auto-trigger for "#" file search
|
|
642
|
+
else if (char === "#") {
|
|
643
|
+
const currentLine = this.state.lines[this.state.cursorLine] || "";
|
|
644
|
+
const textBeforeCursor = currentLine.slice(0, this.state.cursorCol);
|
|
645
|
+
// Only trigger if # is after whitespace or at start of line
|
|
646
|
+
const charBeforeHash = textBeforeCursor[textBeforeCursor.length - 2];
|
|
647
|
+
if (textBeforeCursor.length === 1 ||
|
|
648
|
+
charBeforeHash === " " ||
|
|
649
|
+
charBeforeHash === "\t") {
|
|
650
|
+
void this.tryTriggerAutocomplete();
|
|
651
|
+
}
|
|
652
|
+
}
|
|
382
653
|
// Also auto-trigger when typing letters in a slash command context
|
|
383
654
|
else if (/[a-zA-Z0-9]/.test(char)) {
|
|
384
655
|
const currentLine = this.state.lines[this.state.cursorLine] || "";
|
|
385
656
|
const textBeforeCursor = currentLine.slice(0, this.state.cursorCol);
|
|
386
|
-
// Check if we're in a slash command with
|
|
387
|
-
if (textBeforeCursor.startsWith("/")
|
|
388
|
-
|
|
657
|
+
// Check if we're in a slash command (with or without space for arguments)
|
|
658
|
+
if (textBeforeCursor.trimStart().startsWith("/")) {
|
|
659
|
+
void this.tryTriggerAutocomplete();
|
|
660
|
+
}
|
|
661
|
+
// Check if we're in an @ file reference context
|
|
662
|
+
else if (textBeforeCursor.match(/(?:^|[\s])@[^\s]*$/)) {
|
|
663
|
+
void this.tryTriggerAutocomplete();
|
|
664
|
+
}
|
|
665
|
+
// Check if we're in a # file search context
|
|
666
|
+
else if (textBeforeCursor.match(/(?:^|[\s])#[^\s]*$/)) {
|
|
389
667
|
void this.tryTriggerAutocomplete();
|
|
390
668
|
}
|
|
391
669
|
}
|
|
@@ -501,6 +779,15 @@ export class Editor {
|
|
|
501
779
|
if (this.isAutocompleting) {
|
|
502
780
|
void this.updateAutocomplete();
|
|
503
781
|
}
|
|
782
|
+
else {
|
|
783
|
+
// Check if we should trigger autocomplete after backspace in slash command context
|
|
784
|
+
const currentLine = this.state.lines[this.state.cursorLine] || "";
|
|
785
|
+
const textBeforeCursor = currentLine.slice(0, this.state.cursorCol);
|
|
786
|
+
// Trigger autocomplete if we're in a slash command context (typing command name)
|
|
787
|
+
if (textBeforeCursor.startsWith("/") && !textBeforeCursor.includes(" ")) {
|
|
788
|
+
void this.tryTriggerAutocomplete();
|
|
789
|
+
}
|
|
790
|
+
}
|
|
504
791
|
}
|
|
505
792
|
moveToLineStart() {
|
|
506
793
|
this.state.cursorCol = 0;
|
|
@@ -527,45 +814,254 @@ export class Editor {
|
|
|
527
814
|
this.onChange(this.getText());
|
|
528
815
|
}
|
|
529
816
|
}
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
817
|
+
deleteToStartOfLine() {
|
|
818
|
+
this.historyIndex = -1; // Exit history browsing mode
|
|
819
|
+
const currentLine = this.state.lines[this.state.cursorLine] || "";
|
|
820
|
+
if (this.state.cursorCol > 0) {
|
|
821
|
+
// Delete from start of line up to cursor
|
|
822
|
+
this.state.lines[this.state.cursorLine] = currentLine.slice(this.state.cursorCol);
|
|
534
823
|
this.state.cursorCol = 0;
|
|
535
824
|
}
|
|
536
|
-
else {
|
|
537
|
-
//
|
|
825
|
+
else if (this.state.cursorLine > 0) {
|
|
826
|
+
// At start of line - merge with previous line
|
|
827
|
+
const previousLine = this.state.lines[this.state.cursorLine - 1] || "";
|
|
828
|
+
this.state.lines[this.state.cursorLine - 1] = previousLine + currentLine;
|
|
538
829
|
this.state.lines.splice(this.state.cursorLine, 1);
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
830
|
+
this.state.cursorLine--;
|
|
831
|
+
this.state.cursorCol = previousLine.length;
|
|
832
|
+
}
|
|
833
|
+
if (this.onChange) {
|
|
834
|
+
this.onChange(this.getText());
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
deleteToEndOfLine() {
|
|
838
|
+
this.historyIndex = -1; // Exit history browsing mode
|
|
839
|
+
const currentLine = this.state.lines[this.state.cursorLine] || "";
|
|
840
|
+
if (this.state.cursorCol < currentLine.length) {
|
|
841
|
+
// Delete from cursor to end of line
|
|
842
|
+
this.state.lines[this.state.cursorLine] = currentLine.slice(0, this.state.cursorCol);
|
|
843
|
+
}
|
|
844
|
+
else if (this.state.cursorLine < this.state.lines.length - 1) {
|
|
845
|
+
// At end of line - merge with next line
|
|
846
|
+
const nextLine = this.state.lines[this.state.cursorLine + 1] || "";
|
|
847
|
+
this.state.lines[this.state.cursorLine] = currentLine + nextLine;
|
|
848
|
+
this.state.lines.splice(this.state.cursorLine + 1, 1);
|
|
849
|
+
}
|
|
850
|
+
if (this.onChange) {
|
|
851
|
+
this.onChange(this.getText());
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
deleteWordBackwards() {
|
|
855
|
+
this.historyIndex = -1; // Exit history browsing mode
|
|
856
|
+
const currentLine = this.state.lines[this.state.cursorLine] || "";
|
|
857
|
+
// If at start of line, behave like backspace at column 0 (merge with previous line)
|
|
858
|
+
if (this.state.cursorCol === 0) {
|
|
859
|
+
if (this.state.cursorLine > 0) {
|
|
860
|
+
const previousLine = this.state.lines[this.state.cursorLine - 1] || "";
|
|
861
|
+
this.state.lines[this.state.cursorLine - 1] =
|
|
862
|
+
previousLine + currentLine;
|
|
863
|
+
this.state.lines.splice(this.state.cursorLine, 1);
|
|
864
|
+
this.state.cursorLine--;
|
|
865
|
+
this.state.cursorCol = previousLine.length;
|
|
543
866
|
}
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
867
|
+
}
|
|
868
|
+
else {
|
|
869
|
+
const textBeforeCursor = currentLine.slice(0, this.state.cursorCol);
|
|
870
|
+
const isWhitespace = (char) => /\s/.test(char);
|
|
871
|
+
const isPunctuation = (char) => {
|
|
872
|
+
// Treat obvious code punctuation as boundaries
|
|
873
|
+
return /[(){}[\]<>.,;:'"!?+\-=*/\\|&%^$#@~`]/.test(char);
|
|
874
|
+
};
|
|
875
|
+
let deleteFrom = this.state.cursorCol;
|
|
876
|
+
const lastChar = textBeforeCursor[deleteFrom - 1] ?? "";
|
|
877
|
+
// If immediately on whitespace or punctuation, delete that single boundary char
|
|
878
|
+
if (isWhitespace(lastChar) || isPunctuation(lastChar)) {
|
|
879
|
+
deleteFrom -= 1;
|
|
880
|
+
}
|
|
881
|
+
else {
|
|
882
|
+
// Otherwise, delete a run of non-boundary characters (the "word")
|
|
883
|
+
while (deleteFrom > 0) {
|
|
884
|
+
const ch = textBeforeCursor[deleteFrom - 1] ?? "";
|
|
885
|
+
if (isWhitespace(ch) || isPunctuation(ch)) {
|
|
886
|
+
break;
|
|
887
|
+
}
|
|
888
|
+
deleteFrom -= 1;
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
this.state.lines[this.state.cursorLine] =
|
|
892
|
+
currentLine.slice(0, deleteFrom) +
|
|
893
|
+
currentLine.slice(this.state.cursorCol);
|
|
894
|
+
this.state.cursorCol = deleteFrom;
|
|
547
895
|
}
|
|
548
896
|
if (this.onChange) {
|
|
549
897
|
this.onChange(this.getText());
|
|
550
898
|
}
|
|
551
899
|
}
|
|
900
|
+
/**
|
|
901
|
+
* Build a mapping from visual lines to logical positions.
|
|
902
|
+
* Returns an array where each element represents a visual line with:
|
|
903
|
+
* - logicalLine: index into this.state.lines
|
|
904
|
+
* - startCol: starting column in the logical line
|
|
905
|
+
* - length: length of this visual line segment
|
|
906
|
+
*/
|
|
907
|
+
buildVisualLineMap(width) {
|
|
908
|
+
const visualLines = [];
|
|
909
|
+
for (let i = 0; i < this.state.lines.length; i++) {
|
|
910
|
+
const line = this.state.lines[i] || "";
|
|
911
|
+
const metrics = lineMetricsCache.get(line);
|
|
912
|
+
const lineVisWidth = metrics.totalWidth;
|
|
913
|
+
if (line.length === 0) {
|
|
914
|
+
// Empty line still takes one visual line
|
|
915
|
+
visualLines.push({ logicalLine: i, startCol: 0, length: 0 });
|
|
916
|
+
}
|
|
917
|
+
else if (lineVisWidth <= width) {
|
|
918
|
+
visualLines.push({ logicalLine: i, startCol: 0, length: line.length });
|
|
919
|
+
}
|
|
920
|
+
else {
|
|
921
|
+
// Line needs wrapping - use shared helper
|
|
922
|
+
const chunks = computeLineChunks(line, width);
|
|
923
|
+
for (const chunk of chunks) {
|
|
924
|
+
visualLines.push({
|
|
925
|
+
logicalLine: i,
|
|
926
|
+
startCol: chunk.startIndex,
|
|
927
|
+
length: chunk.endIndex - chunk.startIndex,
|
|
928
|
+
});
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
|
+
}
|
|
932
|
+
return visualLines;
|
|
933
|
+
}
|
|
934
|
+
/**
|
|
935
|
+
* Find the visual line index for the current cursor position.
|
|
936
|
+
*/
|
|
937
|
+
findCurrentVisualLine(visualLines) {
|
|
938
|
+
for (let i = 0; i < visualLines.length; i++) {
|
|
939
|
+
const vl = visualLines[i];
|
|
940
|
+
if (!vl)
|
|
941
|
+
continue;
|
|
942
|
+
if (vl.logicalLine === this.state.cursorLine) {
|
|
943
|
+
const colInSegment = this.state.cursorCol - vl.startCol;
|
|
944
|
+
// Cursor is in this segment if it's within range
|
|
945
|
+
// For the last segment of a logical line, cursor can be at length (end position)
|
|
946
|
+
const isLastSegmentOfLine = i === visualLines.length - 1 ||
|
|
947
|
+
visualLines[i + 1]?.logicalLine !== vl.logicalLine;
|
|
948
|
+
if (colInSegment >= 0 &&
|
|
949
|
+
(colInSegment < vl.length ||
|
|
950
|
+
(isLastSegmentOfLine && colInSegment <= vl.length))) {
|
|
951
|
+
return i;
|
|
952
|
+
}
|
|
953
|
+
}
|
|
954
|
+
}
|
|
955
|
+
// Fallback: return last visual line
|
|
956
|
+
return visualLines.length - 1;
|
|
957
|
+
}
|
|
552
958
|
moveCursor(deltaLine, deltaCol) {
|
|
959
|
+
const width = this.lastWidth;
|
|
553
960
|
if (deltaLine !== 0) {
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
961
|
+
// Build visual line map for navigation
|
|
962
|
+
const visualLines = this.buildVisualLineMap(width);
|
|
963
|
+
const currentVisualLine = this.findCurrentVisualLine(visualLines);
|
|
964
|
+
// Calculate column position within current visual line
|
|
965
|
+
const currentVl = visualLines[currentVisualLine];
|
|
966
|
+
const visualCol = currentVl
|
|
967
|
+
? this.state.cursorCol - currentVl.startCol
|
|
968
|
+
: 0;
|
|
969
|
+
// Move to target visual line
|
|
970
|
+
const targetVisualLine = currentVisualLine + deltaLine;
|
|
971
|
+
if (targetVisualLine >= 0 && targetVisualLine < visualLines.length) {
|
|
972
|
+
const targetVl = visualLines[targetVisualLine];
|
|
973
|
+
if (targetVl) {
|
|
974
|
+
this.state.cursorLine = targetVl.logicalLine;
|
|
975
|
+
// Try to maintain visual column position, clamped to line length
|
|
976
|
+
const targetCol = targetVl.startCol + Math.min(visualCol, targetVl.length);
|
|
977
|
+
const logicalLine = this.state.lines[targetVl.logicalLine] || "";
|
|
978
|
+
this.state.cursorCol = Math.min(targetCol, logicalLine.length);
|
|
979
|
+
}
|
|
560
980
|
}
|
|
561
981
|
}
|
|
562
982
|
if (deltaCol !== 0) {
|
|
563
|
-
// Move column
|
|
564
|
-
const newCol = this.state.cursorCol + deltaCol;
|
|
565
983
|
const currentLine = this.state.lines[this.state.cursorLine] || "";
|
|
566
|
-
|
|
567
|
-
|
|
984
|
+
if (deltaCol > 0) {
|
|
985
|
+
// Moving right
|
|
986
|
+
if (this.state.cursorCol < currentLine.length) {
|
|
987
|
+
this.state.cursorCol++;
|
|
988
|
+
}
|
|
989
|
+
else if (this.state.cursorLine < this.state.lines.length - 1) {
|
|
990
|
+
// Wrap to start of next logical line
|
|
991
|
+
this.state.cursorLine++;
|
|
992
|
+
this.state.cursorCol = 0;
|
|
993
|
+
}
|
|
994
|
+
}
|
|
995
|
+
else {
|
|
996
|
+
// Moving left
|
|
997
|
+
if (this.state.cursorCol > 0) {
|
|
998
|
+
this.state.cursorCol--;
|
|
999
|
+
}
|
|
1000
|
+
else if (this.state.cursorLine > 0) {
|
|
1001
|
+
// Wrap to end of previous logical line
|
|
1002
|
+
this.state.cursorLine--;
|
|
1003
|
+
const prevLine = this.state.lines[this.state.cursorLine] || "";
|
|
1004
|
+
this.state.cursorCol = prevLine.length;
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
1008
|
+
}
|
|
1009
|
+
isWordBoundary(char) {
|
|
1010
|
+
return /\s/.test(char) || /[(){}[\]<>.,;:'"!?+\-=*/\\|&%^$#@~`]/.test(char);
|
|
1011
|
+
}
|
|
1012
|
+
moveWordBackwards() {
|
|
1013
|
+
const currentLine = this.state.lines[this.state.cursorLine] || "";
|
|
1014
|
+
// If at start of line, move to end of previous line
|
|
1015
|
+
if (this.state.cursorCol === 0) {
|
|
1016
|
+
if (this.state.cursorLine > 0) {
|
|
1017
|
+
this.state.cursorLine--;
|
|
1018
|
+
const prevLine = this.state.lines[this.state.cursorLine] || "";
|
|
1019
|
+
this.state.cursorCol = prevLine.length;
|
|
1020
|
+
}
|
|
1021
|
+
return;
|
|
1022
|
+
}
|
|
1023
|
+
const textBeforeCursor = currentLine.slice(0, this.state.cursorCol);
|
|
1024
|
+
let newCol = this.state.cursorCol;
|
|
1025
|
+
const lastChar = textBeforeCursor[newCol - 1] ?? "";
|
|
1026
|
+
// If immediately on whitespace or punctuation, skip that single boundary char
|
|
1027
|
+
if (this.isWordBoundary(lastChar)) {
|
|
1028
|
+
newCol -= 1;
|
|
1029
|
+
}
|
|
1030
|
+
// Now skip the "word" (non-boundary characters)
|
|
1031
|
+
while (newCol > 0) {
|
|
1032
|
+
const ch = textBeforeCursor[newCol - 1] ?? "";
|
|
1033
|
+
if (this.isWordBoundary(ch)) {
|
|
1034
|
+
break;
|
|
1035
|
+
}
|
|
1036
|
+
newCol -= 1;
|
|
1037
|
+
}
|
|
1038
|
+
this.state.cursorCol = newCol;
|
|
1039
|
+
}
|
|
1040
|
+
moveWordForwards() {
|
|
1041
|
+
const currentLine = this.state.lines[this.state.cursorLine] || "";
|
|
1042
|
+
// If at end of line, move to start of next line
|
|
1043
|
+
if (this.state.cursorCol >= currentLine.length) {
|
|
1044
|
+
if (this.state.cursorLine < this.state.lines.length - 1) {
|
|
1045
|
+
this.state.cursorLine++;
|
|
1046
|
+
this.state.cursorCol = 0;
|
|
1047
|
+
}
|
|
1048
|
+
return;
|
|
1049
|
+
}
|
|
1050
|
+
let newCol = this.state.cursorCol;
|
|
1051
|
+
const charAtCursor = currentLine[newCol] ?? "";
|
|
1052
|
+
// If on whitespace or punctuation, skip it
|
|
1053
|
+
if (this.isWordBoundary(charAtCursor)) {
|
|
1054
|
+
newCol += 1;
|
|
1055
|
+
}
|
|
1056
|
+
// Skip the "word" (non-boundary characters)
|
|
1057
|
+
while (newCol < currentLine.length) {
|
|
1058
|
+
const ch = currentLine[newCol] ?? "";
|
|
1059
|
+
if (this.isWordBoundary(ch)) {
|
|
1060
|
+
break;
|
|
1061
|
+
}
|
|
1062
|
+
newCol += 1;
|
|
568
1063
|
}
|
|
1064
|
+
this.state.cursorCol = newCol;
|
|
569
1065
|
}
|
|
570
1066
|
// Helper method to check if cursor is at start of message (for slash command detection)
|
|
571
1067
|
isAtStartOfMessage() {
|
|
@@ -597,7 +1093,7 @@ export class Editor {
|
|
|
597
1093
|
this.autocompleteList.updateItems(suggestions.items);
|
|
598
1094
|
}
|
|
599
1095
|
else {
|
|
600
|
-
this.autocompleteList = new SelectList(suggestions.items, 5);
|
|
1096
|
+
this.autocompleteList = new SelectList(suggestions.items, 5, this.theme.selectList);
|
|
601
1097
|
}
|
|
602
1098
|
// Request re-render to show autocomplete list
|
|
603
1099
|
this.onRenderRequested?.();
|
|
@@ -640,7 +1136,7 @@ export class Editor {
|
|
|
640
1136
|
this.autocompleteList.updateItems(suggestions.items);
|
|
641
1137
|
}
|
|
642
1138
|
else {
|
|
643
|
-
this.autocompleteList = new SelectList(suggestions.items, 5);
|
|
1139
|
+
this.autocompleteList = new SelectList(suggestions.items, 5, this.theme.selectList);
|
|
644
1140
|
}
|
|
645
1141
|
this.isAutocompleting = true;
|
|
646
1142
|
// Request re-render to show autocomplete list
|
|
@@ -670,14 +1166,16 @@ export class Editor {
|
|
|
670
1166
|
const currentLine = this.state.lines[this.state.cursorLine] || "";
|
|
671
1167
|
const textBeforeCursor = currentLine.slice(0, this.state.cursorCol);
|
|
672
1168
|
// If we're no longer in the context that triggered autocomplete, cancel it
|
|
673
|
-
// For slash commands,
|
|
674
|
-
// For file paths, check if we're still in the same path context
|
|
675
|
-
if (textBeforeCursor.startsWith("/")
|
|
676
|
-
|
|
677
|
-
|
|
1169
|
+
// For slash commands, @ file attachments, and # file search, allow progressive typing
|
|
1170
|
+
// For other file paths, check if we're still in the same path context
|
|
1171
|
+
if (textBeforeCursor.startsWith("/") ||
|
|
1172
|
+
textBeforeCursor.match(/(?:^|[\s])@[^\s]*$/) ||
|
|
1173
|
+
textBeforeCursor.match(/(?:^|[\s])#[^\s]*$/)) {
|
|
1174
|
+
// For slash commands, @ file attachments, and # file search,
|
|
1175
|
+
// continue autocomplete as long as we're in the right context
|
|
678
1176
|
}
|
|
679
1177
|
else {
|
|
680
|
-
// For file paths, check if we're still in the same path context
|
|
1178
|
+
// For other file paths, check if we're still in the same path context
|
|
681
1179
|
if (!textBeforeCursor.endsWith(this.autocompletePrefix)) {
|
|
682
1180
|
this.cancelAutocomplete();
|
|
683
1181
|
return;
|
|
@@ -697,7 +1195,7 @@ export class Editor {
|
|
|
697
1195
|
this.autocompleteList.updateItems(suggestions.items);
|
|
698
1196
|
}
|
|
699
1197
|
else {
|
|
700
|
-
this.autocompleteList = new SelectList(suggestions.items, 5);
|
|
1198
|
+
this.autocompleteList = new SelectList(suggestions.items, 5, this.theme.selectList);
|
|
701
1199
|
}
|
|
702
1200
|
this.isAutocompleting = true;
|
|
703
1201
|
// Request re-render to show updated autocomplete list
|
|
@@ -719,8 +1217,12 @@ export class Editor {
|
|
|
719
1217
|
"\x1bOM", // Some terminals
|
|
720
1218
|
"\\\r", // VS Code terminal
|
|
721
1219
|
"\x1b\r", // Option+Enter (macOS)
|
|
1220
|
+
"\x1b[27;2;13~", // xterm shift+enter
|
|
1221
|
+
"\x1b[13;2u", // libtermkey shift+enter
|
|
722
1222
|
// Ctrl+Enter sequences
|
|
723
1223
|
"\x1b[13;5~", // Some terminals
|
|
1224
|
+
"\x1b[27;5;13~", // xterm ctrl+enter
|
|
1225
|
+
"\x1b[13;5u", // libtermkey ctrl+enter
|
|
724
1226
|
];
|
|
725
1227
|
// Check for known sequences
|
|
726
1228
|
if (sequences.includes(data)) {
|
|
@@ -732,8 +1234,9 @@ export class Editor {
|
|
|
732
1234
|
(data.includes("\r") || data.includes("\n"))) {
|
|
733
1235
|
return true;
|
|
734
1236
|
}
|
|
735
|
-
// Check for Ctrl+Enter (Ctrl + CR)
|
|
736
|
-
if (data.charCodeAt(0) === 13
|
|
1237
|
+
// Check for Ctrl+Enter (Ctrl + CR) or Ctrl+Enter with LF
|
|
1238
|
+
if ((data.charCodeAt(0) === 13 || data.charCodeAt(0) === 10) &&
|
|
1239
|
+
data.length > 1) {
|
|
737
1240
|
return true;
|
|
738
1241
|
}
|
|
739
1242
|
return false;
|