@travisennis/acai 0.0.5 → 0.0.7
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 +190 -19
- package/bin/acai-wrapper.js +26 -0
- package/dist/agent/index.d.ts +132 -0
- package/dist/agent/index.d.ts.map +1 -0
- package/dist/agent/index.js +434 -0
- package/dist/api/exa/index.js +1 -1
- package/dist/cli.d.ts +4 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +67 -40
- package/dist/commands/add-directory-command.d.ts +3 -0
- package/dist/commands/add-directory-command.d.ts.map +1 -0
- package/dist/commands/add-directory-command.js +54 -0
- package/dist/commands/application-log-command.d.ts +1 -1
- package/dist/commands/application-log-command.d.ts.map +1 -1
- package/dist/commands/application-log-command.js +18 -20
- package/dist/commands/clear-command.d.ts +1 -1
- package/dist/commands/clear-command.d.ts.map +1 -1
- package/dist/commands/clear-command.js +7 -3
- package/dist/commands/compact-command.d.ts.map +1 -1
- package/dist/commands/compact-command.js +9 -5
- package/dist/commands/context-command.d.ts +3 -0
- package/dist/commands/context-command.d.ts.map +1 -0
- package/dist/commands/context-command.js +124 -0
- package/dist/commands/copy-command.d.ts.map +1 -1
- package/dist/commands/copy-command.js +14 -5
- package/dist/commands/edit-command.d.ts +1 -1
- package/dist/commands/edit-command.d.ts.map +1 -1
- package/dist/commands/edit-command.js +21 -34
- package/dist/commands/edit-prompt-command.d.ts +1 -1
- package/dist/commands/edit-prompt-command.d.ts.map +1 -1
- package/dist/commands/edit-prompt-command.js +18 -15
- package/dist/commands/exit-command.d.ts +1 -4
- package/dist/commands/exit-command.d.ts.map +1 -1
- package/dist/commands/exit-command.js +9 -5
- package/dist/commands/files-command.d.ts +1 -1
- package/dist/commands/files-command.d.ts.map +1 -1
- package/dist/commands/files-command.js +20 -16
- package/dist/commands/generate-rules-command.d.ts +1 -1
- package/dist/commands/generate-rules-command.d.ts.map +1 -1
- package/dist/commands/generate-rules-command.js +307 -39
- package/dist/commands/handoff-command.d.ts +3 -0
- package/dist/commands/handoff-command.d.ts.map +1 -0
- package/dist/commands/handoff-command.js +191 -0
- package/dist/commands/health-command.d.ts +1 -1
- package/dist/commands/health-command.d.ts.map +1 -1
- package/dist/commands/health-command.js +49 -27
- package/dist/commands/help-command.d.ts +1 -1
- package/dist/commands/help-command.d.ts.map +1 -1
- package/dist/commands/help-command.js +25 -5
- package/dist/commands/history-command.d.ts +3 -0
- package/dist/commands/history-command.d.ts.map +1 -0
- package/dist/commands/history-command.js +458 -0
- package/dist/commands/init-command.d.ts +1 -1
- package/dist/commands/init-command.d.ts.map +1 -1
- package/dist/commands/init-command.js +40 -22
- package/dist/commands/last-log-command.d.ts +1 -1
- package/dist/commands/last-log-command.d.ts.map +1 -1
- package/dist/commands/last-log-command.js +15 -15
- package/dist/commands/list-directories-command.d.ts +3 -0
- package/dist/commands/list-directories-command.d.ts.map +1 -0
- package/dist/commands/list-directories-command.js +35 -0
- package/dist/commands/list-tools-command.d.ts.map +1 -1
- package/dist/commands/list-tools-command.js +61 -21
- package/dist/commands/manager.d.ts +9 -4
- package/dist/commands/manager.d.ts.map +1 -1
- package/dist/commands/manager.js +64 -39
- package/dist/commands/model-command.d.ts.map +1 -1
- package/dist/commands/model-command.js +201 -66
- package/dist/commands/paste-command.d.ts +1 -1
- package/dist/commands/paste-command.d.ts.map +1 -1
- package/dist/commands/paste-command.js +23 -9
- package/dist/commands/pickup-command.d.ts +3 -0
- package/dist/commands/pickup-command.d.ts.map +1 -0
- package/dist/commands/pickup-command.js +109 -0
- package/dist/commands/prompt-command.d.ts +19 -1
- package/dist/commands/prompt-command.d.ts.map +1 -1
- package/dist/commands/prompt-command.js +191 -98
- package/dist/commands/remove-directory-command.d.ts +3 -0
- package/dist/commands/remove-directory-command.d.ts.map +1 -0
- package/dist/commands/remove-directory-command.js +55 -0
- package/dist/commands/reset-command.d.ts +1 -1
- package/dist/commands/reset-command.d.ts.map +1 -1
- package/dist/commands/reset-command.js +8 -5
- package/dist/commands/rules-command.d.ts +1 -1
- package/dist/commands/rules-command.d.ts.map +1 -1
- package/dist/commands/rules-command.js +25 -22
- package/dist/commands/save-command.d.ts +1 -1
- package/dist/commands/save-command.d.ts.map +1 -1
- package/dist/commands/save-command.js +8 -3
- package/dist/commands/shell-command.d.ts.map +1 -1
- package/dist/commands/shell-command.js +45 -24
- package/dist/commands/types.d.ts +9 -7
- package/dist/commands/types.d.ts.map +1 -1
- package/dist/commands/usage-command.d.ts +1 -1
- package/dist/commands/usage-command.d.ts.map +1 -1
- package/dist/commands/usage-command.js +18 -7
- package/dist/config.d.ts +21 -11
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +90 -63
- package/dist/execution/index.d.ts +17 -2
- package/dist/execution/index.d.ts.map +1 -1
- package/dist/execution/index.js +62 -20
- package/dist/formatting.d.ts +127 -0
- package/dist/formatting.d.ts.map +1 -1
- package/dist/formatting.js +201 -0
- package/dist/index.d.ts +7 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +263 -102
- package/dist/logger.d.ts.map +1 -1
- package/dist/logger.js +47 -18
- package/dist/mentions.d.ts +2 -1
- package/dist/mentions.d.ts.map +1 -1
- package/dist/mentions.js +16 -1
- package/dist/messages.d.ts +11 -0
- package/dist/messages.d.ts.map +1 -1
- package/dist/messages.js +122 -21
- package/dist/middleware/cache.d.ts +3 -0
- package/dist/middleware/cache.d.ts.map +1 -0
- package/dist/middleware/cache.js +53 -0
- package/dist/middleware/index.d.ts +1 -0
- package/dist/middleware/index.d.ts.map +1 -1
- package/dist/middleware/index.js +1 -0
- package/dist/models/ai-config.d.ts +4 -2
- package/dist/models/ai-config.d.ts.map +1 -1
- package/dist/models/ai-config.js +12 -2
- package/dist/models/anthropic-provider.d.ts.map +1 -1
- package/dist/models/anthropic-provider.js +3 -67
- 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.map +1 -1
- package/dist/models/google-provider.js +0 -3
- package/dist/models/groq-provider.d.ts.map +1 -1
- package/dist/models/groq-provider.js +0 -1
- package/dist/models/manager.d.ts +2 -1
- package/dist/models/manager.d.ts.map +1 -1
- package/dist/models/manager.js +26 -2
- package/dist/models/openai-provider.d.ts.map +1 -1
- package/dist/models/openai-provider.js +0 -4
- package/dist/models/openrouter-provider.d.ts +16 -22
- package/dist/models/openrouter-provider.d.ts.map +1 -1
- package/dist/models/openrouter-provider.js +175 -236
- package/dist/models/providers.d.ts +4 -14
- package/dist/models/providers.d.ts.map +1 -1
- package/dist/models/providers.js +1 -57
- package/dist/models/xai-provider.d.ts.map +1 -1
- package/dist/models/xai-provider.js +0 -2
- package/dist/prompts.d.ts +10 -4
- package/dist/prompts.d.ts.map +1 -1
- package/dist/prompts.js +447 -70
- package/dist/repl/project-status-line.d.ts +3 -0
- package/dist/repl/project-status-line.d.ts.map +1 -0
- package/dist/repl/project-status-line.js +61 -0
- package/dist/repl/tool-call-repair.d.ts.map +1 -1
- package/dist/repl/tool-call-repair.js +8 -4
- package/dist/repl-new.d.ts +51 -0
- package/dist/repl-new.d.ts.map +1 -0
- package/dist/repl-new.js +354 -0
- package/dist/skills.d.ts +20 -0
- package/dist/skills.d.ts.map +1 -0
- package/dist/skills.js +192 -0
- package/dist/terminal/control.d.ts +55 -0
- package/dist/terminal/control.d.ts.map +1 -0
- package/dist/terminal/control.js +109 -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 -25
- package/dist/terminal/formatting.d.ts.map +1 -1
- package/dist/terminal/formatting.js +35 -52
- 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 -94
- package/dist/terminal/index.d.ts.map +1 -1
- package/dist/terminal/index.js +2 -370
- package/dist/terminal/markdown.js +10 -5
- package/dist/terminal/select-prompt.d.ts +2 -2
- package/dist/terminal/select-prompt.d.ts.map +1 -1
- package/dist/terminal/select-prompt.js +47 -39
- package/dist/terminal/strip-ansi.js +4 -4
- 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/threshold.d.ts +20 -0
- package/dist/tokens/threshold.d.ts.map +1 -0
- package/dist/tokens/threshold.js +67 -0
- package/dist/tools/advanced-edit-file.d.ts +69 -0
- package/dist/tools/advanced-edit-file.d.ts.map +1 -0
- package/dist/tools/advanced-edit-file.js +285 -0
- package/dist/tools/agent.d.ts +16 -5
- package/dist/tools/agent.d.ts.map +1 -1
- package/dist/tools/agent.js +86 -59
- package/dist/tools/bash.d.ts +23 -12
- package/dist/tools/bash.d.ts.map +1 -1
- package/dist/tools/bash.js +243 -128
- package/dist/tools/batch.d.ts +34 -0
- package/dist/tools/batch.d.ts.map +1 -0
- package/dist/tools/batch.js +174 -0
- package/dist/tools/code-interpreter.d.ts +21 -9
- package/dist/tools/code-interpreter.d.ts.map +1 -1
- package/dist/tools/code-interpreter.js +151 -134
- package/dist/tools/delete-file.d.ts +17 -10
- package/dist/tools/delete-file.d.ts.map +1 -1
- package/dist/tools/delete-file.js +60 -97
- package/dist/tools/directory-tree.d.ts +17 -12
- package/dist/tools/directory-tree.d.ts.map +1 -1
- package/dist/tools/directory-tree.js +57 -48
- package/dist/tools/dynamic-tool-loader.d.ts +16 -10
- package/dist/tools/dynamic-tool-loader.d.ts.map +1 -1
- package/dist/tools/dynamic-tool-loader.js +122 -130
- package/dist/tools/dynamic-tool-parser.d.ts +1 -0
- package/dist/tools/dynamic-tool-parser.d.ts.map +1 -1
- package/dist/tools/dynamic-tool-parser.js +1 -0
- package/dist/tools/edit-file.d.ts +35 -15
- package/dist/tools/edit-file.d.ts.map +1 -1
- package/dist/tools/edit-file.js +127 -114
- package/dist/tools/glob.d.ts +36 -0
- package/dist/tools/glob.d.ts.map +1 -0
- package/dist/tools/glob.js +154 -0
- package/dist/tools/grep.d.ts +73 -12
- package/dist/tools/grep.d.ts.map +1 -1
- package/dist/tools/grep.js +425 -165
- package/dist/tools/index.d.ts +220 -126
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +284 -135
- package/dist/tools/llm-edit-fixer.d.ts +24 -0
- package/dist/tools/llm-edit-fixer.d.ts.map +1 -0
- package/dist/tools/llm-edit-fixer.js +136 -0
- package/dist/tools/move-file.d.ts +19 -7
- package/dist/tools/move-file.d.ts.map +1 -1
- package/dist/tools/move-file.js +48 -34
- package/dist/tools/read-file.d.ts +47 -9
- package/dist/tools/read-file.d.ts.map +1 -1
- package/dist/tools/read-file.js +84 -70
- package/dist/tools/read-multiple-files.d.ts +17 -6
- package/dist/tools/read-multiple-files.d.ts.map +1 -1
- package/dist/tools/read-multiple-files.js +132 -72
- package/dist/tools/save-file.d.ts +45 -12
- package/dist/tools/save-file.d.ts.map +1 -1
- package/dist/tools/save-file.js +76 -101
- package/dist/tools/think.d.ts +15 -7
- package/dist/tools/think.d.ts.map +1 -1
- package/dist/tools/think.js +34 -20
- package/dist/tools/types.d.ts +8 -10
- package/dist/tools/types.d.ts.map +1 -1
- package/dist/tools/types.js +9 -0
- package/dist/tools/utils.d.ts +14 -0
- package/dist/tools/utils.d.ts.map +1 -0
- package/dist/tools/utils.js +16 -0
- package/dist/tools/web-fetch.d.ts +11 -4
- package/dist/tools/web-fetch.d.ts.map +1 -1
- package/dist/tools/web-fetch.js +39 -38
- package/dist/tools/web-search.d.ts +15 -6
- package/dist/tools/web-search.d.ts.map +1 -1
- package/dist/tools/web-search.js +64 -31
- package/dist/tui/autocomplete.d.ts +44 -0
- package/dist/tui/autocomplete.d.ts.map +1 -0
- package/dist/tui/autocomplete.js +466 -0
- package/dist/tui/components/assistant-message.d.ts +18 -0
- package/dist/tui/components/assistant-message.d.ts.map +1 -0
- package/dist/tui/components/assistant-message.js +29 -0
- 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 +81 -0
- package/dist/tui/components/editor.d.ts +106 -0
- package/dist/tui/components/editor.d.ts.map +1 -0
- package/dist/tui/components/editor.js +1220 -0
- package/dist/tui/components/footer.d.ts +12 -0
- package/dist/tui/components/footer.d.ts.map +1 -0
- package/dist/tui/components/footer.js +209 -0
- 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 +14 -0
- package/dist/tui/components/input.d.ts.map +1 -0
- package/dist/tui/components/input.js +122 -0
- package/dist/tui/components/loader.d.ts +23 -0
- package/dist/tui/components/loader.d.ts.map +1 -0
- package/dist/tui/components/loader.js +45 -0
- package/dist/tui/components/markdown.d.ts +106 -0
- package/dist/tui/components/markdown.d.ts.map +1 -0
- package/dist/tui/components/markdown.js +586 -0
- package/dist/tui/components/modal.d.ts +29 -0
- package/dist/tui/components/modal.d.ts.map +1 -0
- package/dist/tui/components/modal.js +263 -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 +78 -0
- package/dist/tui/components/prompt-status.d.ts +17 -0
- package/dist/tui/components/prompt-status.d.ts.map +1 -0
- package/dist/tui/components/prompt-status.js +26 -0
- package/dist/tui/components/select-list.d.ts +48 -0
- package/dist/tui/components/select-list.d.ts.map +1 -0
- package/dist/tui/components/select-list.js +207 -0
- package/dist/tui/components/spacer.d.ts +16 -0
- package/dist/tui/components/spacer.d.ts.map +1 -0
- package/dist/tui/components/spacer.js +27 -0
- 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/text.d.ts +26 -0
- package/dist/tui/components/text.d.ts.map +1 -0
- package/dist/tui/components/text.js +143 -0
- package/dist/tui/components/thinking-block.d.ts +14 -0
- package/dist/tui/components/thinking-block.d.ts.map +1 -0
- package/dist/tui/components/thinking-block.js +33 -0
- package/dist/tui/components/tool-execution.d.ts +21 -0
- package/dist/tui/components/tool-execution.d.ts.map +1 -0
- package/dist/tui/components/tool-execution.js +161 -0
- package/dist/tui/components/user-message.d.ts +9 -0
- package/dist/tui/components/user-message.d.ts.map +1 -0
- package/dist/tui/components/user-message.js +23 -0
- package/dist/tui/components/welcome.d.ts +6 -0
- package/dist/tui/components/welcome.d.ts.map +1 -0
- package/dist/tui/components/welcome.js +30 -0
- package/dist/tui/index.d.ts +18 -0
- package/dist/tui/index.d.ts.map +1 -0
- package/dist/tui/index.js +22 -0
- package/dist/tui/terminal.d.ts +38 -0
- package/dist/tui/terminal.d.ts.map +1 -0
- package/dist/tui/terminal.js +94 -0
- package/dist/tui/tui.d.ts +69 -0
- package/dist/tui/tui.d.ts.map +1 -0
- package/dist/tui/tui.js +204 -0
- package/dist/tui/utils.d.ts +24 -0
- package/dist/tui/utils.d.ts.map +1 -0
- package/dist/tui/utils.js +111 -0
- package/dist/utils/bash.d.ts +7 -0
- package/dist/utils/bash.d.ts.map +1 -0
- package/dist/{tools/bash-utils.js → utils/bash.js} +31 -12
- package/dist/utils/{filesystem.d.ts → filesystem/operations.d.ts} +1 -1
- package/dist/utils/filesystem/operations.d.ts.map +1 -0
- package/dist/utils/filesystem/security.d.ts +9 -0
- package/dist/utils/filesystem/security.d.ts.map +1 -0
- package/dist/{tools/filesystem-utils.js → utils/filesystem/security.js} +93 -21
- 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/utils/generators.d.ts +3 -0
- package/dist/utils/generators.d.ts.map +1 -0
- package/dist/utils/generators.js +25 -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/iterables.d.ts +2 -0
- package/dist/utils/iterables.d.ts.map +1 -0
- package/dist/utils/iterables.js +6 -0
- package/dist/utils/{zod-utils.d.ts → zod.d.ts} +1 -1
- package/dist/utils/zod.d.ts.map +1 -0
- package/package.json +21 -21
- package/dist/conversation-analyzer.d.ts +0 -11
- package/dist/conversation-analyzer.d.ts.map +0 -1
- package/dist/conversation-analyzer.js +0 -88
- 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 -55
- 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 -38
- package/dist/repl-prompt.d.ts +0 -15
- package/dist/repl-prompt.d.ts.map +0 -1
- package/dist/repl-prompt.js +0 -147
- package/dist/repl.d.ts +0 -31
- package/dist/repl.d.ts.map +0 -1
- package/dist/repl.js +0 -310
- 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 -362
- 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 -16
- package/dist/terminal/input-prompt.d.ts.map +0 -1
- package/dist/terminal/input-prompt.js +0 -181
- 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 -279
- package/dist/terminal/types.d.ts +0 -35
- package/dist/terminal/types.d.ts.map +0 -1
- package/dist/terminal/types.js +0 -1
- package/dist/tokens/manage-output.d.ts +0 -34
- package/dist/tokens/manage-output.d.ts.map +0 -1
- package/dist/tokens/manage-output.js +0 -44
- package/dist/tool-executor.d.ts +0 -28
- package/dist/tool-executor.d.ts.map +0 -1
- package/dist/tool-executor.js +0 -74
- package/dist/tools/bash-utils.d.ts +0 -7
- package/dist/tools/bash-utils.d.ts.map +0 -1
- package/dist/tools/file-editing-utils.d.ts +0 -2
- package/dist/tools/file-editing-utils.d.ts.map +0 -1
- package/dist/tools/file-editing-utils.js +0 -135
- package/dist/tools/filesystem-utils.d.ts +0 -7
- package/dist/tools/filesystem-utils.d.ts.map +0 -1
- package/dist/tools/git-utils.d.ts.map +0 -1
- package/dist/utils/filesystem.d.ts.map +0 -1
- package/dist/utils/zod-utils.d.ts.map +0 -1
- /package/dist/utils/{filesystem.js → filesystem/operations.js} +0 -0
- /package/dist/utils/{zod-utils.js → zod.js} +0 -0
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
import { getTerminalSize } from "../../terminal/formatting.js";
|
|
2
|
+
import style from "../../terminal/style.js";
|
|
3
|
+
import { Container } from "../tui.js";
|
|
4
|
+
import { visibleWidth } from "../utils.js";
|
|
5
|
+
/**
|
|
6
|
+
* Modal component - displays content in an overlay on top of the main UI
|
|
7
|
+
*/
|
|
8
|
+
export class Modal extends Container {
|
|
9
|
+
title;
|
|
10
|
+
backdrop;
|
|
11
|
+
onClose;
|
|
12
|
+
maxWidth;
|
|
13
|
+
maxHeight;
|
|
14
|
+
scrollPosition = 0;
|
|
15
|
+
constructor(title, content, backdrop = true, onClose, maxWidth, maxHeight) {
|
|
16
|
+
super();
|
|
17
|
+
this.title = title;
|
|
18
|
+
this.backdrop = backdrop;
|
|
19
|
+
this.onClose = onClose;
|
|
20
|
+
const { columns, rows } = getTerminalSize();
|
|
21
|
+
this.maxWidth = maxWidth ?? columns;
|
|
22
|
+
this.maxHeight = maxHeight ?? rows;
|
|
23
|
+
this.addChild(content);
|
|
24
|
+
}
|
|
25
|
+
handleInput(data) {
|
|
26
|
+
// Handle Escape key to close modal
|
|
27
|
+
if (data === "\x1b") {
|
|
28
|
+
this.close();
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
// Handle scrolling
|
|
32
|
+
if (data === "\x1b[A") {
|
|
33
|
+
// Up arrow
|
|
34
|
+
this.scrollPosition = Math.max(0, this.scrollPosition - 1);
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
if (data === "\x1b[B") {
|
|
38
|
+
// Down arrow
|
|
39
|
+
this.scrollPosition += 1;
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
if (data === "\x1b[5~") {
|
|
43
|
+
// Page up
|
|
44
|
+
this.scrollPosition = Math.max(0, this.scrollPosition - 10);
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
if (data === "\x1b[6~") {
|
|
48
|
+
// Page down
|
|
49
|
+
this.scrollPosition += 10;
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
if (data === "\x1b[H" || data === "g") {
|
|
53
|
+
// Home key or 'g' for top
|
|
54
|
+
this.scrollPosition = 0;
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
if (data === "\x1b[F" || data === "G") {
|
|
58
|
+
// End key or 'G' for bottom
|
|
59
|
+
this.scrollPosition = Number.POSITIVE_INFINITY; // Will be clamped in render
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
// Pass input to focused child component
|
|
63
|
+
if (this.children.length > 0) {
|
|
64
|
+
const focusedChild = this.children[0];
|
|
65
|
+
if (focusedChild.handleInput) {
|
|
66
|
+
focusedChild.handleInput(data);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
close() {
|
|
71
|
+
if (this.onClose) {
|
|
72
|
+
this.onClose();
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
render(width) {
|
|
76
|
+
const lines = [];
|
|
77
|
+
// Calculate modal dimensions - use dynamic sizing
|
|
78
|
+
// Use the provided width directly, ensuring reasonable minimum and maximum
|
|
79
|
+
const modalWidth = Math.max(40, width - 2);
|
|
80
|
+
const contentWidth = modalWidth - 4; // Account for borders and padding
|
|
81
|
+
// Calculate content height
|
|
82
|
+
const contentLines = super.render(contentWidth);
|
|
83
|
+
const contentHeight = contentLines.length;
|
|
84
|
+
// Calculate modal height based on content
|
|
85
|
+
const modalHeight = Math.min(this.maxHeight, Math.max(8, contentHeight + 4));
|
|
86
|
+
// Calculate vertical positioning (centered)
|
|
87
|
+
const { rows } = getTerminalSize();
|
|
88
|
+
const terminalHeight = rows;
|
|
89
|
+
const topOffset = Math.max(0, Math.floor((terminalHeight - modalHeight) / 2));
|
|
90
|
+
// Add top offset for centering
|
|
91
|
+
for (let i = 0; i < topOffset; i++) {
|
|
92
|
+
lines.push(" ".repeat(width));
|
|
93
|
+
}
|
|
94
|
+
// Render modal frame
|
|
95
|
+
const horizontalBorder = style.white("─".repeat(modalWidth - 2));
|
|
96
|
+
const emptyLine = style.white("│") + " ".repeat(modalWidth - 2) + style.white("│");
|
|
97
|
+
// Calculate horizontal offset for centering
|
|
98
|
+
// Use different offsets for left and right to ensure full width coverage
|
|
99
|
+
const leftOffset = Math.floor((width - modalWidth) / 2);
|
|
100
|
+
const rightOffset = width - modalWidth - leftOffset;
|
|
101
|
+
// Top border
|
|
102
|
+
lines.push(" ".repeat(leftOffset) +
|
|
103
|
+
style.white("┌") +
|
|
104
|
+
horizontalBorder +
|
|
105
|
+
style.white("┐") +
|
|
106
|
+
" ".repeat(rightOffset));
|
|
107
|
+
// Render content lines with scrolling support
|
|
108
|
+
const visibleContentHeight = Math.min(contentHeight, modalHeight - 4);
|
|
109
|
+
const maxScroll = Math.max(0, contentHeight - visibleContentHeight);
|
|
110
|
+
// Clamp scroll position
|
|
111
|
+
this.scrollPosition = Math.min(maxScroll, Math.max(0, this.scrollPosition));
|
|
112
|
+
// Add scroll indicator to title if content is scrollable
|
|
113
|
+
let displayTitle = this.title;
|
|
114
|
+
if (contentHeight > visibleContentHeight) {
|
|
115
|
+
const scrollInfo = ` (${this.scrollPosition + 1}-${Math.min(this.scrollPosition + visibleContentHeight, contentHeight)}/${contentHeight})`;
|
|
116
|
+
displayTitle = this.title + scrollInfo;
|
|
117
|
+
}
|
|
118
|
+
displayTitle += " [esc to exit]";
|
|
119
|
+
// Update title line with scroll info
|
|
120
|
+
const titleText = ` ${displayTitle} `;
|
|
121
|
+
const titlePadding = Math.max(0, modalWidth - 2 - visibleWidth(titleText));
|
|
122
|
+
const titleLine = style.white("│") +
|
|
123
|
+
style.bold(titleText) +
|
|
124
|
+
" ".repeat(titlePadding) +
|
|
125
|
+
style.white("│");
|
|
126
|
+
lines.push(" ".repeat(leftOffset) + titleLine + " ".repeat(rightOffset));
|
|
127
|
+
// Separator line
|
|
128
|
+
const separator = style.white("├") + "─".repeat(modalWidth - 2) + style.white("┤");
|
|
129
|
+
lines.push(" ".repeat(leftOffset) + separator + " ".repeat(rightOffset));
|
|
130
|
+
for (let i = 0; i < visibleContentHeight; i++) {
|
|
131
|
+
const contentLineIndex = this.scrollPosition + i;
|
|
132
|
+
const contentLine = contentLines[contentLineIndex] || "";
|
|
133
|
+
const visibleLength = visibleWidth(contentLine);
|
|
134
|
+
const padding = " ".repeat(Math.max(0, contentWidth - visibleLength));
|
|
135
|
+
const line = `${style.white("│")} ${contentLine}${padding} ${style.white("│")}`;
|
|
136
|
+
lines.push(" ".repeat(leftOffset) + line + " ".repeat(rightOffset));
|
|
137
|
+
}
|
|
138
|
+
// Fill remaining content area with empty lines if needed
|
|
139
|
+
const remainingLines = modalHeight - 4 - visibleContentHeight;
|
|
140
|
+
for (let i = 0; i < remainingLines; i++) {
|
|
141
|
+
lines.push(" ".repeat(leftOffset) + emptyLine + " ".repeat(rightOffset));
|
|
142
|
+
}
|
|
143
|
+
// Bottom border
|
|
144
|
+
lines.push(" ".repeat(leftOffset) +
|
|
145
|
+
style.white("└") +
|
|
146
|
+
horizontalBorder +
|
|
147
|
+
style.white("┘") +
|
|
148
|
+
" ".repeat(rightOffset));
|
|
149
|
+
// Fill remaining terminal height with empty lines
|
|
150
|
+
const totalLinesSoFar = lines.length;
|
|
151
|
+
const remainingTerminalLines = Math.max(0, terminalHeight - totalLinesSoFar);
|
|
152
|
+
for (let i = 0; i < remainingTerminalLines; i++) {
|
|
153
|
+
lines.push(" ".repeat(width));
|
|
154
|
+
}
|
|
155
|
+
return lines;
|
|
156
|
+
}
|
|
157
|
+
getCursorPosition() {
|
|
158
|
+
// Modal doesn't have its own cursor, but children might
|
|
159
|
+
if (this.children.length > 0) {
|
|
160
|
+
const childCursor = this.children[0].getCursorPosition?.();
|
|
161
|
+
if (childCursor) {
|
|
162
|
+
// Adjust cursor position for modal frame and centering
|
|
163
|
+
const [childRow, childCol] = childCursor;
|
|
164
|
+
const modalWidth = Math.min(this.maxWidth, 80 - 4);
|
|
165
|
+
const horizontalOffset = Math.floor((80 - modalWidth) / 2);
|
|
166
|
+
// Top padding + title + separator + content offset
|
|
167
|
+
const { columns } = getTerminalSize();
|
|
168
|
+
const terminalHeight = columns;
|
|
169
|
+
const modalHeight = Math.min(this.maxHeight, terminalHeight - 4);
|
|
170
|
+
const topPadding = Math.max(0, Math.floor((terminalHeight - modalHeight) / 2));
|
|
171
|
+
return [
|
|
172
|
+
topPadding + 3 + childRow, // 3 = top border + title + separator
|
|
173
|
+
horizontalOffset + 2 + childCol, // 2 = left border + padding
|
|
174
|
+
];
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
return null;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* ModalText component - displays text content in a modal with word wrapping
|
|
182
|
+
*/
|
|
183
|
+
export class ModalText extends Container {
|
|
184
|
+
text;
|
|
185
|
+
paddingX;
|
|
186
|
+
paddingY;
|
|
187
|
+
constructor(text, paddingX = 1, paddingY = 0) {
|
|
188
|
+
super();
|
|
189
|
+
this.text = text;
|
|
190
|
+
this.paddingX = paddingX;
|
|
191
|
+
this.paddingY = paddingY;
|
|
192
|
+
}
|
|
193
|
+
render(width) {
|
|
194
|
+
const lines = [];
|
|
195
|
+
const contentWidth = Math.max(1, width - this.paddingX * 2);
|
|
196
|
+
if (!this.text || this.text.trim() === "") {
|
|
197
|
+
return [];
|
|
198
|
+
}
|
|
199
|
+
// Replace tabs with spaces
|
|
200
|
+
const normalizedText = this.text.replace(/\t/g, " ");
|
|
201
|
+
const textLines = normalizedText.split("\n");
|
|
202
|
+
for (const line of textLines) {
|
|
203
|
+
const visibleLineLength = visibleWidth(line);
|
|
204
|
+
if (visibleLineLength <= contentWidth) {
|
|
205
|
+
lines.push(line);
|
|
206
|
+
}
|
|
207
|
+
else {
|
|
208
|
+
// Word wrap
|
|
209
|
+
const words = line.split(" ");
|
|
210
|
+
let currentLine = "";
|
|
211
|
+
for (const word of words) {
|
|
212
|
+
const currentVisible = visibleWidth(currentLine);
|
|
213
|
+
const wordVisible = visibleWidth(word);
|
|
214
|
+
let finalWord = word;
|
|
215
|
+
if (wordVisible > contentWidth) {
|
|
216
|
+
// Truncate word to fit
|
|
217
|
+
let truncated = "";
|
|
218
|
+
for (const char of word) {
|
|
219
|
+
if (visibleWidth(truncated + char) > contentWidth) {
|
|
220
|
+
break;
|
|
221
|
+
}
|
|
222
|
+
truncated += char;
|
|
223
|
+
}
|
|
224
|
+
finalWord = truncated;
|
|
225
|
+
}
|
|
226
|
+
if (currentVisible === 0) {
|
|
227
|
+
currentLine = finalWord;
|
|
228
|
+
}
|
|
229
|
+
else if (currentVisible + 1 + visibleWidth(finalWord) <=
|
|
230
|
+
contentWidth) {
|
|
231
|
+
currentLine += ` ${finalWord}`;
|
|
232
|
+
}
|
|
233
|
+
else {
|
|
234
|
+
lines.push(currentLine);
|
|
235
|
+
currentLine = finalWord;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
if (currentLine.length > 0) {
|
|
239
|
+
lines.push(currentLine);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
// Add padding
|
|
244
|
+
const leftPad = " ".repeat(this.paddingX);
|
|
245
|
+
const paddedLines = [];
|
|
246
|
+
// Top padding
|
|
247
|
+
for (let i = 0; i < this.paddingY; i++) {
|
|
248
|
+
paddedLines.push("");
|
|
249
|
+
}
|
|
250
|
+
// Content with horizontal padding
|
|
251
|
+
for (const line of lines) {
|
|
252
|
+
const visibleLength = visibleWidth(line);
|
|
253
|
+
const rightPadLength = Math.max(0, width - this.paddingX - visibleLength);
|
|
254
|
+
const rightPad = " ".repeat(rightPadLength);
|
|
255
|
+
paddedLines.push(leftPad + line + rightPad);
|
|
256
|
+
}
|
|
257
|
+
// Bottom padding
|
|
258
|
+
for (let i = 0; i < this.paddingY; i++) {
|
|
259
|
+
paddedLines.push("");
|
|
260
|
+
}
|
|
261
|
+
return paddedLines;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { Component } from "../tui.ts";
|
|
2
|
+
/**
|
|
3
|
+
* Progress bar component - displays a visual progress indicator
|
|
4
|
+
*/
|
|
5
|
+
export declare class ProgressBarComponent implements Component {
|
|
6
|
+
private current;
|
|
7
|
+
private total;
|
|
8
|
+
private width;
|
|
9
|
+
private cachedOutput?;
|
|
10
|
+
private cachedCurrent?;
|
|
11
|
+
private cachedTotal?;
|
|
12
|
+
private cachedWidth?;
|
|
13
|
+
constructor(current: number, total: number, width: number);
|
|
14
|
+
setCurrent(current: number): void;
|
|
15
|
+
setTotal(total: number): void;
|
|
16
|
+
private invalidateCache;
|
|
17
|
+
render(width: number): string[];
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=progress-bar.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"progress-bar.d.ts","sourceRoot":"","sources":["../../../source/tui/components/progress-bar.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAE3C;;GAEG;AACH,qBAAa,oBAAqB,YAAW,SAAS;IACpD,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,KAAK,CAAS;IAGtB,OAAO,CAAC,YAAY,CAAC,CAAW;IAChC,OAAO,CAAC,aAAa,CAAC,CAAS;IAC/B,OAAO,CAAC,WAAW,CAAC,CAAS;IAC7B,OAAO,CAAC,WAAW,CAAC,CAAS;gBAEjB,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM;IAMzD,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAKjC,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAK7B,OAAO,CAAC,eAAe;IAOvB,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE;CA+DhC"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import style from "../../terminal/style.js";
|
|
2
|
+
/**
|
|
3
|
+
* Progress bar component - displays a visual progress indicator
|
|
4
|
+
*/
|
|
5
|
+
export class ProgressBarComponent {
|
|
6
|
+
current;
|
|
7
|
+
total;
|
|
8
|
+
width;
|
|
9
|
+
// Cache for rendered output
|
|
10
|
+
cachedOutput;
|
|
11
|
+
cachedCurrent;
|
|
12
|
+
cachedTotal;
|
|
13
|
+
cachedWidth;
|
|
14
|
+
constructor(current, total, width) {
|
|
15
|
+
this.current = current;
|
|
16
|
+
this.total = total;
|
|
17
|
+
this.width = width;
|
|
18
|
+
}
|
|
19
|
+
setCurrent(current) {
|
|
20
|
+
this.current = current;
|
|
21
|
+
this.invalidateCache();
|
|
22
|
+
}
|
|
23
|
+
setTotal(total) {
|
|
24
|
+
this.total = total;
|
|
25
|
+
this.invalidateCache();
|
|
26
|
+
}
|
|
27
|
+
invalidateCache() {
|
|
28
|
+
this.cachedOutput = undefined;
|
|
29
|
+
this.cachedCurrent = undefined;
|
|
30
|
+
this.cachedTotal = undefined;
|
|
31
|
+
this.cachedWidth = undefined;
|
|
32
|
+
}
|
|
33
|
+
render(width) {
|
|
34
|
+
// Use provided width if specified, otherwise use component width
|
|
35
|
+
const renderWidth = width || this.width;
|
|
36
|
+
// Check cache
|
|
37
|
+
if (this.cachedOutput &&
|
|
38
|
+
this.cachedCurrent === this.current &&
|
|
39
|
+
this.cachedTotal === this.total &&
|
|
40
|
+
this.cachedWidth === renderWidth) {
|
|
41
|
+
return this.cachedOutput;
|
|
42
|
+
}
|
|
43
|
+
// Function to format numbers concisely (e.g., 1.2K, 5M)
|
|
44
|
+
const formatNumber = (num) => {
|
|
45
|
+
if (num < 1000) {
|
|
46
|
+
return num.toString();
|
|
47
|
+
}
|
|
48
|
+
if (num < 1_000_000) {
|
|
49
|
+
return `${(num / 1000).toFixed(1)}K`;
|
|
50
|
+
}
|
|
51
|
+
if (num < 1_000_000_000) {
|
|
52
|
+
return `${(num / 1_000_000).toFixed(1)}M`;
|
|
53
|
+
}
|
|
54
|
+
return `${(num / 1_000_000_000).toFixed(1)}G`;
|
|
55
|
+
};
|
|
56
|
+
const currentFormatted = formatNumber(this.current);
|
|
57
|
+
const totalFormatted = formatNumber(this.total);
|
|
58
|
+
const progressText = `${currentFormatted}/${totalFormatted}`;
|
|
59
|
+
const progressTextLength = progressText.length + 1; // Add 1 for space
|
|
60
|
+
const progressBarMaxWidth = Math.max(1, renderWidth - progressTextLength);
|
|
61
|
+
const percentage = this.total === 0 ? 1 : this.current / this.total;
|
|
62
|
+
const filledWidth = Math.max(0, Math.min(progressBarMaxWidth, Math.floor(percentage * progressBarMaxWidth)));
|
|
63
|
+
const emptyWidth = Math.max(0, progressBarMaxWidth - filledWidth);
|
|
64
|
+
const a = filledWidth / progressBarMaxWidth > 0.5
|
|
65
|
+
? style.red("─")
|
|
66
|
+
: style.yellow("─");
|
|
67
|
+
const b = style.gray("─");
|
|
68
|
+
const filledBar = a.repeat(filledWidth);
|
|
69
|
+
const emptyBar = b.repeat(emptyWidth);
|
|
70
|
+
const result = [`${filledBar}${emptyBar} ${progressText}`];
|
|
71
|
+
// Update cache
|
|
72
|
+
this.cachedOutput = result;
|
|
73
|
+
this.cachedCurrent = this.current;
|
|
74
|
+
this.cachedTotal = this.total;
|
|
75
|
+
this.cachedWidth = renderWidth;
|
|
76
|
+
return result;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { ModelManager } from "../../models/manager.ts";
|
|
2
|
+
import type { Component } from "../tui.ts";
|
|
3
|
+
type State = {
|
|
4
|
+
projectStatus: string;
|
|
5
|
+
currentContextWindow: number;
|
|
6
|
+
contextWindow: number;
|
|
7
|
+
};
|
|
8
|
+
export declare class PromptStatusComponent implements Component {
|
|
9
|
+
private modelManager;
|
|
10
|
+
private state;
|
|
11
|
+
private progressBar;
|
|
12
|
+
constructor(modelManager: ModelManager, state: State);
|
|
13
|
+
setState(state: State): void;
|
|
14
|
+
render(width: number): string[];
|
|
15
|
+
}
|
|
16
|
+
export {};
|
|
17
|
+
//# sourceMappingURL=prompt-status.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompt-status.d.ts","sourceRoot":"","sources":["../../../source/tui/components/prompt-status.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAE5D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAG3C,KAAK,KAAK,GAAG;IACX,aAAa,EAAE,MAAM,CAAC;IACtB,oBAAoB,EAAE,MAAM,CAAC;IAC7B,aAAa,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF,qBAAa,qBAAsB,YAAW,SAAS;IACrD,OAAO,CAAC,YAAY,CAAe;IACnC,OAAO,CAAC,KAAK,CAAQ;IACrB,OAAO,CAAC,WAAW,CAAuB;gBAC9B,YAAY,EAAE,YAAY,EAAE,KAAK,EAAE,KAAK;IAUpD,QAAQ,CAAC,KAAK,EAAE,KAAK;IAMrB,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE;CAahC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import style from "../../terminal/style.js";
|
|
2
|
+
import { ProgressBarComponent } from "./progress-bar.js";
|
|
3
|
+
export class PromptStatusComponent {
|
|
4
|
+
modelManager;
|
|
5
|
+
state;
|
|
6
|
+
progressBar;
|
|
7
|
+
constructor(modelManager, state) {
|
|
8
|
+
this.modelManager = modelManager;
|
|
9
|
+
this.state = state;
|
|
10
|
+
this.progressBar = new ProgressBarComponent(state.currentContextWindow, state.contextWindow, 0);
|
|
11
|
+
}
|
|
12
|
+
setState(state) {
|
|
13
|
+
this.state = state;
|
|
14
|
+
this.progressBar.setCurrent(state.currentContextWindow);
|
|
15
|
+
this.progressBar.setTotal(state.contextWindow);
|
|
16
|
+
}
|
|
17
|
+
render(width) {
|
|
18
|
+
const results = [];
|
|
19
|
+
// results.push(style.dim(hr(width)));
|
|
20
|
+
results.push(this.state.projectStatus);
|
|
21
|
+
results.push(style.dim(`${this.modelManager.getModelMetadata("repl").id} [${this.modelManager.getModel("repl").modelId}]`));
|
|
22
|
+
// Add progress bar output
|
|
23
|
+
results.push(...this.progressBar.render(width));
|
|
24
|
+
return results;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { Component } from "../tui.ts";
|
|
2
|
+
/**
|
|
3
|
+
* Check if key data represents Shift+Tab.
|
|
4
|
+
* Supports multiple terminal escape sequences:
|
|
5
|
+
* - \x1b[Z (standard)
|
|
6
|
+
* - \x1b[1;2Z (with modifier)
|
|
7
|
+
*/
|
|
8
|
+
export declare function isShiftTab(keyData: string): boolean;
|
|
9
|
+
/**
|
|
10
|
+
* Check if key data represents Tab (without Shift).
|
|
11
|
+
*/
|
|
12
|
+
export declare function isTab(keyData: string): boolean;
|
|
13
|
+
/**
|
|
14
|
+
* Check if key data represents navigation key (arrows, Tab, Shift+Tab).
|
|
15
|
+
*/
|
|
16
|
+
export declare function isNavigationKey(keyData: string): boolean;
|
|
17
|
+
export interface SelectItem {
|
|
18
|
+
value: string;
|
|
19
|
+
label: string;
|
|
20
|
+
description?: string;
|
|
21
|
+
}
|
|
22
|
+
export interface SelectListTheme {
|
|
23
|
+
selectedPrefix: (text: string) => string;
|
|
24
|
+
selectedText: (text: string) => string;
|
|
25
|
+
description: (text: string) => string;
|
|
26
|
+
scrollInfo: (text: string) => string;
|
|
27
|
+
noMatch: (text: string) => string;
|
|
28
|
+
}
|
|
29
|
+
export declare class SelectList implements Component {
|
|
30
|
+
private items;
|
|
31
|
+
private filteredItems;
|
|
32
|
+
private selectedIndex;
|
|
33
|
+
private maxVisible;
|
|
34
|
+
private theme;
|
|
35
|
+
onSelect?: (item: SelectItem) => void;
|
|
36
|
+
onCancel?: () => void;
|
|
37
|
+
onSelectionChange?: (item: SelectItem) => void;
|
|
38
|
+
constructor(items: SelectItem[], maxVisible?: number, theme?: SelectListTheme);
|
|
39
|
+
private createDefaultTheme;
|
|
40
|
+
updateItems(items: SelectItem[]): void;
|
|
41
|
+
setFilter(filter: string): void;
|
|
42
|
+
setSelectedIndex(index: number): void;
|
|
43
|
+
render(width: number): string[];
|
|
44
|
+
handleInput(keyData: string): void;
|
|
45
|
+
private notifySelectionChange;
|
|
46
|
+
getSelectedItem(): SelectItem | null;
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=select-list.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"select-list.d.ts","sourceRoot":"","sources":["../../../source/tui/components/select-list.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAG3C;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAEnD;AAED;;GAEG;AACH,wBAAgB,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAE9C;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAOxD;AAED,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,eAAe;IAC9B,cAAc,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;IACzC,YAAY,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;IACvC,WAAW,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;IACtC,UAAU,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;IACrC,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;CACnC;AAED,qBAAa,UAAW,YAAW,SAAS;IAC1C,OAAO,CAAC,KAAK,CAAoB;IACjC,OAAO,CAAC,aAAa,CAAoB;IACzC,OAAO,CAAC,aAAa,CAAK;IAC1B,OAAO,CAAC,UAAU,CAAK;IACvB,OAAO,CAAC,KAAK,CAAkB;IAExB,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,UAAU,KAAK,IAAI,CAAC;IACtC,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,iBAAiB,CAAC,EAAE,CAAC,IAAI,EAAE,UAAU,KAAK,IAAI,CAAC;gBAE1C,KAAK,EAAE,UAAU,EAAE,EAAE,UAAU,SAAI,EAAE,KAAK,CAAC,EAAE,eAAe;IAOxE,OAAO,CAAC,kBAAkB;IAU1B,WAAW,CAAC,KAAK,EAAE,UAAU,EAAE;IAQ/B,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAQ/B,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAOrC,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE;IAgI/B,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAgDlC,OAAO,CAAC,qBAAqB;IAO7B,eAAe,IAAI,UAAU,GAAG,IAAI;CAIrC"}
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
import style from "../../terminal/style.js";
|
|
2
|
+
import { truncateToWidth } from "../utils.js";
|
|
3
|
+
/**
|
|
4
|
+
* Check if key data represents Shift+Tab.
|
|
5
|
+
* Supports multiple terminal escape sequences:
|
|
6
|
+
* - \x1b[Z (standard)
|
|
7
|
+
* - \x1b[1;2Z (with modifier)
|
|
8
|
+
*/
|
|
9
|
+
export function isShiftTab(keyData) {
|
|
10
|
+
return keyData === "\x1b[Z" || keyData === "\x1b[1;2Z";
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Check if key data represents Tab (without Shift).
|
|
14
|
+
*/
|
|
15
|
+
export function isTab(keyData) {
|
|
16
|
+
return keyData === "\t";
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Check if key data represents navigation key (arrows, Tab, Shift+Tab).
|
|
20
|
+
*/
|
|
21
|
+
export function isNavigationKey(keyData) {
|
|
22
|
+
return (keyData === "\x1b[A" ||
|
|
23
|
+
keyData === "\x1b[B" ||
|
|
24
|
+
isTab(keyData) ||
|
|
25
|
+
isShiftTab(keyData));
|
|
26
|
+
}
|
|
27
|
+
export class SelectList {
|
|
28
|
+
items = [];
|
|
29
|
+
filteredItems = [];
|
|
30
|
+
selectedIndex = 0;
|
|
31
|
+
maxVisible = 5;
|
|
32
|
+
theme;
|
|
33
|
+
onSelect;
|
|
34
|
+
onCancel;
|
|
35
|
+
onSelectionChange;
|
|
36
|
+
constructor(items, maxVisible = 5, theme) {
|
|
37
|
+
this.items = items;
|
|
38
|
+
this.filteredItems = items;
|
|
39
|
+
this.maxVisible = maxVisible;
|
|
40
|
+
this.theme = theme || this.createDefaultTheme();
|
|
41
|
+
}
|
|
42
|
+
createDefaultTheme() {
|
|
43
|
+
return {
|
|
44
|
+
selectedPrefix: (text) => style.blue(text),
|
|
45
|
+
selectedText: (text) => style.blue(text),
|
|
46
|
+
description: (text) => style.gray(text),
|
|
47
|
+
scrollInfo: (text) => style.gray(text),
|
|
48
|
+
noMatch: (text) => style.gray(text),
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
updateItems(items) {
|
|
52
|
+
this.items = items;
|
|
53
|
+
this.filteredItems = items;
|
|
54
|
+
// Reset selection to first item when items change
|
|
55
|
+
this.setSelectedIndex(0);
|
|
56
|
+
this.notifySelectionChange();
|
|
57
|
+
}
|
|
58
|
+
setFilter(filter) {
|
|
59
|
+
this.filteredItems = this.items.filter((item) => item.value.toLowerCase().startsWith(filter.toLowerCase()));
|
|
60
|
+
// Reset selection when filter changes
|
|
61
|
+
this.selectedIndex = 0;
|
|
62
|
+
}
|
|
63
|
+
setSelectedIndex(index) {
|
|
64
|
+
this.selectedIndex = Math.max(0, Math.min(index, this.filteredItems.length - 1));
|
|
65
|
+
}
|
|
66
|
+
render(width) {
|
|
67
|
+
const lines = [];
|
|
68
|
+
// If no items match filter, show message
|
|
69
|
+
if (this.filteredItems.length === 0) {
|
|
70
|
+
lines.push(this.theme.noMatch(" No matching commands"));
|
|
71
|
+
return lines;
|
|
72
|
+
}
|
|
73
|
+
// Calculate visible range with scrolling
|
|
74
|
+
const startIndex = Math.max(0, Math.min(this.selectedIndex - Math.floor(this.maxVisible / 2), this.filteredItems.length - this.maxVisible));
|
|
75
|
+
const endIndex = Math.min(startIndex + this.maxVisible, this.filteredItems.length);
|
|
76
|
+
// Render visible items
|
|
77
|
+
for (let i = startIndex; i < endIndex; i++) {
|
|
78
|
+
const item = this.filteredItems[i];
|
|
79
|
+
if (!item)
|
|
80
|
+
continue;
|
|
81
|
+
const isSelected = i === this.selectedIndex;
|
|
82
|
+
let line = "";
|
|
83
|
+
if (isSelected) {
|
|
84
|
+
// Use arrow indicator for selection - entire line uses selectedText color
|
|
85
|
+
const prefixWidth = 2; // "→ " is 2 characters visually
|
|
86
|
+
const displayValue = item.label || item.value;
|
|
87
|
+
if (item.description && width > 40) {
|
|
88
|
+
// Calculate how much space we have for value + description
|
|
89
|
+
const maxValueWidth = Math.min(30, width - prefixWidth - 4);
|
|
90
|
+
const truncatedValue = truncateToWidth(displayValue, maxValueWidth, "");
|
|
91
|
+
const spacing = " ".repeat(Math.max(1, 32 - truncatedValue.length));
|
|
92
|
+
// Calculate remaining space for description using visible widths
|
|
93
|
+
const descriptionStart = prefixWidth + truncatedValue.length + spacing.length;
|
|
94
|
+
const remainingWidth = width - descriptionStart - 2; // -2 for safety
|
|
95
|
+
if (remainingWidth > 10) {
|
|
96
|
+
const truncatedDesc = truncateToWidth(item.description, remainingWidth, "");
|
|
97
|
+
// Apply selectedText to entire line content
|
|
98
|
+
line = this.theme.selectedText(`→ ${truncatedValue}${spacing}${truncatedDesc}`);
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
// Not enough space for description
|
|
102
|
+
const maxWidth = width - prefixWidth - 2;
|
|
103
|
+
line = this.theme.selectedText(`→ ${truncateToWidth(displayValue, maxWidth, "")}`);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
// No description or not enough width
|
|
108
|
+
const maxWidth = width - prefixWidth - 2;
|
|
109
|
+
line = this.theme.selectedText(`→ ${truncateToWidth(displayValue, maxWidth, "")}`);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
const displayValue = item.label || item.value;
|
|
114
|
+
const prefix = " ";
|
|
115
|
+
if (item.description && width > 40) {
|
|
116
|
+
// Calculate how much space we have for value + description
|
|
117
|
+
const maxValueWidth = Math.min(30, width - prefix.length - 4);
|
|
118
|
+
const truncatedValue = truncateToWidth(displayValue, maxValueWidth, "");
|
|
119
|
+
const spacing = " ".repeat(Math.max(1, 32 - truncatedValue.length));
|
|
120
|
+
// Calculate remaining space for description
|
|
121
|
+
const descriptionStart = prefix.length + truncatedValue.length + spacing.length;
|
|
122
|
+
const remainingWidth = width - descriptionStart - 2; // -2 for safety
|
|
123
|
+
if (remainingWidth > 10) {
|
|
124
|
+
const truncatedDesc = truncateToWidth(item.description, remainingWidth, "");
|
|
125
|
+
const descText = this.theme.description(spacing + truncatedDesc);
|
|
126
|
+
line = prefix + truncatedValue + descText;
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
// Not enough space for description
|
|
130
|
+
const maxWidth = width - prefix.length - 2;
|
|
131
|
+
line = prefix + truncateToWidth(displayValue, maxWidth, "");
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
// No description or not enough width
|
|
136
|
+
const maxWidth = width - prefix.length - 2;
|
|
137
|
+
line = prefix + truncateToWidth(displayValue, maxWidth, "");
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
lines.push(line);
|
|
141
|
+
}
|
|
142
|
+
// Add scroll indicators if needed
|
|
143
|
+
if (startIndex > 0 || endIndex < this.filteredItems.length) {
|
|
144
|
+
const scrollText = ` (${this.selectedIndex + 1}/${this.filteredItems.length})`;
|
|
145
|
+
// Truncate if too long for terminal
|
|
146
|
+
lines.push(this.theme.scrollInfo(truncateToWidth(scrollText, width - 2, "")));
|
|
147
|
+
}
|
|
148
|
+
return lines;
|
|
149
|
+
}
|
|
150
|
+
handleInput(keyData) {
|
|
151
|
+
// Up arrow - wrap to bottom when at top
|
|
152
|
+
if (keyData === "\x1b[A") {
|
|
153
|
+
this.selectedIndex =
|
|
154
|
+
this.selectedIndex === 0
|
|
155
|
+
? this.filteredItems.length - 1
|
|
156
|
+
: this.selectedIndex - 1;
|
|
157
|
+
this.notifySelectionChange();
|
|
158
|
+
}
|
|
159
|
+
// Down arrow - wrap to top when at bottom
|
|
160
|
+
else if (keyData === "\x1b[B") {
|
|
161
|
+
this.selectedIndex =
|
|
162
|
+
this.selectedIndex === this.filteredItems.length - 1
|
|
163
|
+
? 0
|
|
164
|
+
: this.selectedIndex + 1;
|
|
165
|
+
this.notifySelectionChange();
|
|
166
|
+
}
|
|
167
|
+
// Tab - move down with wrapping (like down arrow)
|
|
168
|
+
else if (isTab(keyData)) {
|
|
169
|
+
this.selectedIndex =
|
|
170
|
+
this.selectedIndex === this.filteredItems.length - 1
|
|
171
|
+
? 0
|
|
172
|
+
: this.selectedIndex + 1;
|
|
173
|
+
this.notifySelectionChange();
|
|
174
|
+
}
|
|
175
|
+
// Shift+Tab - move up with wrapping (like up arrow)
|
|
176
|
+
else if (isShiftTab(keyData)) {
|
|
177
|
+
this.selectedIndex =
|
|
178
|
+
this.selectedIndex === 0
|
|
179
|
+
? this.filteredItems.length - 1
|
|
180
|
+
: this.selectedIndex - 1;
|
|
181
|
+
this.notifySelectionChange();
|
|
182
|
+
}
|
|
183
|
+
// Enter
|
|
184
|
+
else if (keyData === "\r") {
|
|
185
|
+
const selectedItem = this.filteredItems[this.selectedIndex];
|
|
186
|
+
if (selectedItem && this.onSelect) {
|
|
187
|
+
this.onSelect(selectedItem);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
// Escape or Ctrl+C
|
|
191
|
+
else if (keyData === "\x1b" || keyData === "\x03") {
|
|
192
|
+
if (this.onCancel) {
|
|
193
|
+
this.onCancel();
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
notifySelectionChange() {
|
|
198
|
+
const selectedItem = this.filteredItems[this.selectedIndex];
|
|
199
|
+
if (selectedItem && this.onSelectionChange) {
|
|
200
|
+
this.onSelectionChange(selectedItem);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
getSelectedItem() {
|
|
204
|
+
const item = this.filteredItems[this.selectedIndex];
|
|
205
|
+
return item || null;
|
|
206
|
+
}
|
|
207
|
+
}
|