superqode 0.1.5__py3-none-any.whl
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.
- superqode/__init__.py +33 -0
- superqode/acp/__init__.py +23 -0
- superqode/acp/client.py +913 -0
- superqode/acp/permission_screen.py +457 -0
- superqode/acp/types.py +480 -0
- superqode/acp_discovery.py +856 -0
- superqode/agent/__init__.py +22 -0
- superqode/agent/edit_strategies.py +334 -0
- superqode/agent/loop.py +892 -0
- superqode/agent/qe_report_templates.py +39 -0
- superqode/agent/system_prompts.py +353 -0
- superqode/agent_output.py +721 -0
- superqode/agent_stream.py +953 -0
- superqode/agents/__init__.py +59 -0
- superqode/agents/acp_registry.py +305 -0
- superqode/agents/client.py +249 -0
- superqode/agents/data/augmentcode.com.toml +51 -0
- superqode/agents/data/cagent.dev.toml +51 -0
- superqode/agents/data/claude.com.toml +60 -0
- superqode/agents/data/codeassistant.dev.toml +51 -0
- superqode/agents/data/codex.openai.com.toml +57 -0
- superqode/agents/data/fastagent.ai.toml +66 -0
- superqode/agents/data/geminicli.com.toml +77 -0
- superqode/agents/data/goose.block.xyz.toml +54 -0
- superqode/agents/data/junie.jetbrains.com.toml +56 -0
- superqode/agents/data/kimi.moonshot.cn.toml +57 -0
- superqode/agents/data/llmlingagent.dev.toml +51 -0
- superqode/agents/data/molt.bot.toml +49 -0
- superqode/agents/data/opencode.ai.toml +60 -0
- superqode/agents/data/stakpak.dev.toml +51 -0
- superqode/agents/data/vtcode.dev.toml +51 -0
- superqode/agents/discovery.py +266 -0
- superqode/agents/messaging.py +160 -0
- superqode/agents/persona.py +166 -0
- superqode/agents/registry.py +421 -0
- superqode/agents/schema.py +72 -0
- superqode/agents/unified.py +367 -0
- superqode/app/__init__.py +111 -0
- superqode/app/constants.py +314 -0
- superqode/app/css.py +366 -0
- superqode/app/models.py +118 -0
- superqode/app/suggester.py +125 -0
- superqode/app/widgets.py +1591 -0
- superqode/app_enhanced.py +399 -0
- superqode/app_main.py +17187 -0
- superqode/approval.py +312 -0
- superqode/atomic.py +296 -0
- superqode/commands/__init__.py +1 -0
- superqode/commands/acp.py +965 -0
- superqode/commands/agents.py +180 -0
- superqode/commands/auth.py +278 -0
- superqode/commands/config.py +374 -0
- superqode/commands/init.py +826 -0
- superqode/commands/providers.py +819 -0
- superqode/commands/qe.py +1145 -0
- superqode/commands/roles.py +380 -0
- superqode/commands/serve.py +172 -0
- superqode/commands/suggestions.py +127 -0
- superqode/commands/superqe.py +460 -0
- superqode/config/__init__.py +51 -0
- superqode/config/loader.py +812 -0
- superqode/config/schema.py +498 -0
- superqode/core/__init__.py +111 -0
- superqode/core/roles.py +281 -0
- superqode/danger.py +386 -0
- superqode/data/superqode-template.yaml +1522 -0
- superqode/design_system.py +1080 -0
- superqode/dialogs/__init__.py +6 -0
- superqode/dialogs/base.py +39 -0
- superqode/dialogs/model.py +130 -0
- superqode/dialogs/provider.py +870 -0
- superqode/diff_view.py +919 -0
- superqode/enterprise.py +21 -0
- superqode/evaluation/__init__.py +25 -0
- superqode/evaluation/adapters.py +93 -0
- superqode/evaluation/behaviors.py +89 -0
- superqode/evaluation/engine.py +209 -0
- superqode/evaluation/scenarios.py +96 -0
- superqode/execution/__init__.py +36 -0
- superqode/execution/linter.py +538 -0
- superqode/execution/modes.py +347 -0
- superqode/execution/resolver.py +283 -0
- superqode/execution/runner.py +642 -0
- superqode/file_explorer.py +811 -0
- superqode/file_viewer.py +471 -0
- superqode/flash.py +183 -0
- superqode/guidance/__init__.py +58 -0
- superqode/guidance/config.py +203 -0
- superqode/guidance/prompts.py +71 -0
- superqode/harness/__init__.py +54 -0
- superqode/harness/accelerator.py +291 -0
- superqode/harness/config.py +319 -0
- superqode/harness/validator.py +147 -0
- superqode/history.py +279 -0
- superqode/integrations/superopt_runner.py +124 -0
- superqode/logging/__init__.py +49 -0
- superqode/logging/adapters.py +219 -0
- superqode/logging/formatter.py +923 -0
- superqode/logging/integration.py +341 -0
- superqode/logging/sinks.py +170 -0
- superqode/logging/unified_log.py +417 -0
- superqode/lsp/__init__.py +26 -0
- superqode/lsp/client.py +544 -0
- superqode/main.py +1069 -0
- superqode/mcp/__init__.py +89 -0
- superqode/mcp/auth_storage.py +380 -0
- superqode/mcp/client.py +1236 -0
- superqode/mcp/config.py +319 -0
- superqode/mcp/integration.py +337 -0
- superqode/mcp/oauth.py +436 -0
- superqode/mcp/oauth_callback.py +385 -0
- superqode/mcp/types.py +290 -0
- superqode/memory/__init__.py +31 -0
- superqode/memory/feedback.py +342 -0
- superqode/memory/store.py +522 -0
- superqode/notifications.py +369 -0
- superqode/optimization/__init__.py +5 -0
- superqode/optimization/config.py +33 -0
- superqode/permissions/__init__.py +25 -0
- superqode/permissions/rules.py +488 -0
- superqode/plan.py +323 -0
- superqode/providers/__init__.py +33 -0
- superqode/providers/gateway/__init__.py +165 -0
- superqode/providers/gateway/base.py +228 -0
- superqode/providers/gateway/litellm_gateway.py +1170 -0
- superqode/providers/gateway/openresponses_gateway.py +436 -0
- superqode/providers/health.py +297 -0
- superqode/providers/huggingface/__init__.py +74 -0
- superqode/providers/huggingface/downloader.py +472 -0
- superqode/providers/huggingface/endpoints.py +442 -0
- superqode/providers/huggingface/hub.py +531 -0
- superqode/providers/huggingface/inference.py +394 -0
- superqode/providers/huggingface/transformers_runner.py +516 -0
- superqode/providers/local/__init__.py +100 -0
- superqode/providers/local/base.py +438 -0
- superqode/providers/local/discovery.py +418 -0
- superqode/providers/local/lmstudio.py +256 -0
- superqode/providers/local/mlx.py +457 -0
- superqode/providers/local/ollama.py +486 -0
- superqode/providers/local/sglang.py +268 -0
- superqode/providers/local/tgi.py +260 -0
- superqode/providers/local/tool_support.py +477 -0
- superqode/providers/local/vllm.py +258 -0
- superqode/providers/manager.py +1338 -0
- superqode/providers/models.py +1016 -0
- superqode/providers/models_dev.py +578 -0
- superqode/providers/openresponses/__init__.py +87 -0
- superqode/providers/openresponses/converters/__init__.py +17 -0
- superqode/providers/openresponses/converters/messages.py +343 -0
- superqode/providers/openresponses/converters/tools.py +268 -0
- superqode/providers/openresponses/schema/__init__.py +56 -0
- superqode/providers/openresponses/schema/models.py +585 -0
- superqode/providers/openresponses/streaming/__init__.py +5 -0
- superqode/providers/openresponses/streaming/parser.py +338 -0
- superqode/providers/openresponses/tools/__init__.py +21 -0
- superqode/providers/openresponses/tools/apply_patch.py +352 -0
- superqode/providers/openresponses/tools/code_interpreter.py +290 -0
- superqode/providers/openresponses/tools/file_search.py +333 -0
- superqode/providers/openresponses/tools/mcp_adapter.py +252 -0
- superqode/providers/registry.py +716 -0
- superqode/providers/usage.py +332 -0
- superqode/pure_mode.py +384 -0
- superqode/qr/__init__.py +23 -0
- superqode/qr/dashboard.py +781 -0
- superqode/qr/generator.py +1018 -0
- superqode/qr/templates.py +135 -0
- superqode/safety/__init__.py +41 -0
- superqode/safety/sandbox.py +413 -0
- superqode/safety/warnings.py +256 -0
- superqode/server/__init__.py +33 -0
- superqode/server/lsp_server.py +775 -0
- superqode/server/web.py +250 -0
- superqode/session/__init__.py +25 -0
- superqode/session/persistence.py +580 -0
- superqode/session/sharing.py +477 -0
- superqode/session.py +475 -0
- superqode/sidebar.py +2991 -0
- superqode/stream_view.py +648 -0
- superqode/styles/__init__.py +3 -0
- superqode/superqe/__init__.py +184 -0
- superqode/superqe/acp_runner.py +1064 -0
- superqode/superqe/constitution/__init__.py +62 -0
- superqode/superqe/constitution/evaluator.py +308 -0
- superqode/superqe/constitution/loader.py +432 -0
- superqode/superqe/constitution/schema.py +250 -0
- superqode/superqe/events.py +591 -0
- superqode/superqe/frameworks/__init__.py +65 -0
- superqode/superqe/frameworks/base.py +234 -0
- superqode/superqe/frameworks/e2e.py +263 -0
- superqode/superqe/frameworks/executor.py +237 -0
- superqode/superqe/frameworks/javascript.py +409 -0
- superqode/superqe/frameworks/python.py +373 -0
- superqode/superqe/frameworks/registry.py +92 -0
- superqode/superqe/mcp_tools/__init__.py +47 -0
- superqode/superqe/mcp_tools/core_tools.py +418 -0
- superqode/superqe/mcp_tools/registry.py +230 -0
- superqode/superqe/mcp_tools/testing_tools.py +167 -0
- superqode/superqe/noise.py +89 -0
- superqode/superqe/orchestrator.py +778 -0
- superqode/superqe/roles.py +609 -0
- superqode/superqe/session.py +713 -0
- superqode/superqe/skills/__init__.py +57 -0
- superqode/superqe/skills/base.py +106 -0
- superqode/superqe/skills/core_skills.py +899 -0
- superqode/superqe/skills/registry.py +90 -0
- superqode/superqe/verifier.py +101 -0
- superqode/superqe_cli.py +76 -0
- superqode/tool_call.py +358 -0
- superqode/tools/__init__.py +93 -0
- superqode/tools/agent_tools.py +496 -0
- superqode/tools/base.py +324 -0
- superqode/tools/batch_tool.py +133 -0
- superqode/tools/diagnostics.py +311 -0
- superqode/tools/edit_tools.py +653 -0
- superqode/tools/enhanced_base.py +515 -0
- superqode/tools/file_tools.py +269 -0
- superqode/tools/file_tracking.py +45 -0
- superqode/tools/lsp_tools.py +610 -0
- superqode/tools/network_tools.py +350 -0
- superqode/tools/permissions.py +400 -0
- superqode/tools/question_tool.py +324 -0
- superqode/tools/search_tools.py +598 -0
- superqode/tools/shell_tools.py +259 -0
- superqode/tools/todo_tools.py +121 -0
- superqode/tools/validation.py +80 -0
- superqode/tools/web_tools.py +639 -0
- superqode/tui.py +1152 -0
- superqode/tui_integration.py +875 -0
- superqode/tui_widgets/__init__.py +27 -0
- superqode/tui_widgets/widgets/__init__.py +18 -0
- superqode/tui_widgets/widgets/progress.py +185 -0
- superqode/tui_widgets/widgets/tool_display.py +188 -0
- superqode/undo_manager.py +574 -0
- superqode/utils/__init__.py +5 -0
- superqode/utils/error_handling.py +323 -0
- superqode/utils/fuzzy.py +257 -0
- superqode/widgets/__init__.py +477 -0
- superqode/widgets/agent_collab.py +390 -0
- superqode/widgets/agent_store.py +936 -0
- superqode/widgets/agent_switcher.py +395 -0
- superqode/widgets/animation_manager.py +284 -0
- superqode/widgets/code_context.py +356 -0
- superqode/widgets/command_palette.py +412 -0
- superqode/widgets/connection_status.py +537 -0
- superqode/widgets/conversation_history.py +470 -0
- superqode/widgets/diff_indicator.py +155 -0
- superqode/widgets/enhanced_status_bar.py +385 -0
- superqode/widgets/enhanced_toast.py +476 -0
- superqode/widgets/file_browser.py +809 -0
- superqode/widgets/file_reference.py +585 -0
- superqode/widgets/issue_timeline.py +340 -0
- superqode/widgets/leader_key.py +264 -0
- superqode/widgets/mode_switcher.py +445 -0
- superqode/widgets/model_picker.py +234 -0
- superqode/widgets/permission_preview.py +1205 -0
- superqode/widgets/prompt.py +358 -0
- superqode/widgets/provider_connect.py +725 -0
- superqode/widgets/pty_shell.py +587 -0
- superqode/widgets/qe_dashboard.py +321 -0
- superqode/widgets/resizable_sidebar.py +377 -0
- superqode/widgets/response_changes.py +218 -0
- superqode/widgets/response_display.py +528 -0
- superqode/widgets/rich_tool_display.py +613 -0
- superqode/widgets/sidebar_panels.py +1180 -0
- superqode/widgets/slash_complete.py +356 -0
- superqode/widgets/split_view.py +612 -0
- superqode/widgets/status_bar.py +273 -0
- superqode/widgets/superqode_display.py +786 -0
- superqode/widgets/thinking_display.py +815 -0
- superqode/widgets/throbber.py +87 -0
- superqode/widgets/toast.py +206 -0
- superqode/widgets/unified_output.py +1073 -0
- superqode/workspace/__init__.py +75 -0
- superqode/workspace/artifacts.py +472 -0
- superqode/workspace/coordinator.py +353 -0
- superqode/workspace/diff_tracker.py +429 -0
- superqode/workspace/git_guard.py +373 -0
- superqode/workspace/git_snapshot.py +526 -0
- superqode/workspace/manager.py +750 -0
- superqode/workspace/snapshot.py +357 -0
- superqode/workspace/watcher.py +535 -0
- superqode/workspace/worktree.py +440 -0
- superqode-0.1.5.dist-info/METADATA +204 -0
- superqode-0.1.5.dist-info/RECORD +288 -0
- superqode-0.1.5.dist-info/WHEEL +5 -0
- superqode-0.1.5.dist-info/entry_points.txt +3 -0
- superqode-0.1.5.dist-info/licenses/LICENSE +648 -0
- superqode-0.1.5.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,358 @@
|
|
|
1
|
+
"""Smart prompt widget with completion support."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import Callable
|
|
7
|
+
|
|
8
|
+
from textual import events, on
|
|
9
|
+
from textual.app import ComposeResult
|
|
10
|
+
from textual.binding import Binding
|
|
11
|
+
from textual.containers import Horizontal, Vertical
|
|
12
|
+
from textual.message import Message
|
|
13
|
+
from textual.reactive import reactive
|
|
14
|
+
from textual.widget import Widget
|
|
15
|
+
from textual.widgets import Input, Static
|
|
16
|
+
|
|
17
|
+
from superqode.widgets.slash_complete import SlashComplete, SlashCommand
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class SmartPrompt(Widget):
|
|
21
|
+
"""
|
|
22
|
+
Smart input prompt with:
|
|
23
|
+
- Slash command completion (/)
|
|
24
|
+
- Path completion (Tab)
|
|
25
|
+
- History navigation (Up/Down)
|
|
26
|
+
- Multi-line support (Shift+Enter)
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
DEFAULT_CSS = """
|
|
30
|
+
SmartPrompt {
|
|
31
|
+
dock: bottom;
|
|
32
|
+
height: auto;
|
|
33
|
+
max-height: 8;
|
|
34
|
+
padding: 0 1 1 1;
|
|
35
|
+
background: #0a0a0a;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
SmartPrompt #prompt-row {
|
|
39
|
+
height: auto;
|
|
40
|
+
min-height: 3;
|
|
41
|
+
background: #0a0a0a;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
SmartPrompt #prompt-prefix {
|
|
45
|
+
height: 3;
|
|
46
|
+
width: auto;
|
|
47
|
+
color: #00ffff;
|
|
48
|
+
text-style: bold;
|
|
49
|
+
padding: 0 1 0 0;
|
|
50
|
+
background: #001a33;
|
|
51
|
+
content-align: center middle;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
SmartPrompt #prompt-input {
|
|
55
|
+
height: auto;
|
|
56
|
+
min-height: 3;
|
|
57
|
+
background: #1a1a1a;
|
|
58
|
+
border: double #00ffff;
|
|
59
|
+
padding: 0 1;
|
|
60
|
+
color: #ffffff;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
SmartPrompt #prompt-input:focus {
|
|
64
|
+
border: double #00ff00;
|
|
65
|
+
background: #0a1a0a;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
SmartPrompt #suggestions-row {
|
|
69
|
+
height: auto;
|
|
70
|
+
max-height: 3;
|
|
71
|
+
margin-top: 1;
|
|
72
|
+
display: none;
|
|
73
|
+
background: #1a1a1a;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
SmartPrompt #suggestions-row.visible {
|
|
77
|
+
display: block;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
SmartPrompt .suggestion {
|
|
81
|
+
color: #00ff00;
|
|
82
|
+
padding: 0 1;
|
|
83
|
+
background: #1a1a1a;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
SmartPrompt .suggestion.selected {
|
|
87
|
+
color: #00ffff;
|
|
88
|
+
text-style: bold;
|
|
89
|
+
}
|
|
90
|
+
"""
|
|
91
|
+
|
|
92
|
+
BINDINGS = [
|
|
93
|
+
Binding("enter", "submit", "Submit", show=False),
|
|
94
|
+
Binding("escape", "cancel", "Cancel", show=False),
|
|
95
|
+
Binding("up", "history_prev", "Previous", show=False),
|
|
96
|
+
Binding("down", "history_next", "Next", show=False),
|
|
97
|
+
Binding("tab", "complete", "Complete", show=False),
|
|
98
|
+
]
|
|
99
|
+
|
|
100
|
+
class Submitted(Message):
|
|
101
|
+
"""Message sent when input is submitted."""
|
|
102
|
+
|
|
103
|
+
def __init__(self, value: str) -> None:
|
|
104
|
+
self.value = value
|
|
105
|
+
super().__init__()
|
|
106
|
+
|
|
107
|
+
class SlashTriggered(Message):
|
|
108
|
+
"""Message sent when slash command mode is triggered."""
|
|
109
|
+
|
|
110
|
+
def __init__(self, query: str) -> None:
|
|
111
|
+
self.query = query
|
|
112
|
+
super().__init__()
|
|
113
|
+
|
|
114
|
+
# State
|
|
115
|
+
value: reactive[str] = reactive("")
|
|
116
|
+
prefix: reactive[str] = reactive("superqode")
|
|
117
|
+
mode_suffix: reactive[str] = reactive("HOME")
|
|
118
|
+
placeholder: reactive[str] = reactive("Type a message or / for commands...")
|
|
119
|
+
|
|
120
|
+
def __init__(
|
|
121
|
+
self,
|
|
122
|
+
prefix: str = "superqode",
|
|
123
|
+
mode_suffix: str = "HOME",
|
|
124
|
+
placeholder: str = "Type a message or / for commands...",
|
|
125
|
+
on_submit: Callable[[str], None] | None = None,
|
|
126
|
+
**kwargs,
|
|
127
|
+
) -> None:
|
|
128
|
+
super().__init__(**kwargs)
|
|
129
|
+
self.prefix = prefix
|
|
130
|
+
self.mode_suffix = mode_suffix
|
|
131
|
+
self.placeholder = placeholder
|
|
132
|
+
self._on_submit = on_submit
|
|
133
|
+
self._history: list[str] = []
|
|
134
|
+
self._history_index: int = -1
|
|
135
|
+
self._suggestions: list[str] = []
|
|
136
|
+
self._suggestion_index: int = 0
|
|
137
|
+
|
|
138
|
+
def compose(self) -> ComposeResult:
|
|
139
|
+
with Vertical():
|
|
140
|
+
with Horizontal(id="prompt-row"):
|
|
141
|
+
yield Static(self._get_prefix_text(), id="prompt-prefix")
|
|
142
|
+
yield Input(
|
|
143
|
+
placeholder=self.placeholder,
|
|
144
|
+
id="prompt-input",
|
|
145
|
+
)
|
|
146
|
+
with Horizontal(id="suggestions-row"):
|
|
147
|
+
yield Static("", id="suggestions-display", classes="suggestion")
|
|
148
|
+
|
|
149
|
+
def _get_prefix_text(self) -> str:
|
|
150
|
+
"""Get the formatted prefix text."""
|
|
151
|
+
return f"{self.prefix} {self.mode_suffix} >"
|
|
152
|
+
|
|
153
|
+
def on_mount(self) -> None:
|
|
154
|
+
"""Focus the input on mount."""
|
|
155
|
+
self.query_one("#prompt-input", Input).focus()
|
|
156
|
+
|
|
157
|
+
def watch_prefix(self, prefix: str) -> None:
|
|
158
|
+
"""React to prefix changes."""
|
|
159
|
+
self._update_prefix()
|
|
160
|
+
|
|
161
|
+
def watch_mode_suffix(self, mode_suffix: str) -> None:
|
|
162
|
+
"""React to mode suffix changes."""
|
|
163
|
+
self._update_prefix()
|
|
164
|
+
|
|
165
|
+
def _update_prefix(self) -> None:
|
|
166
|
+
"""Update the prefix display."""
|
|
167
|
+
try:
|
|
168
|
+
prefix_widget = self.query_one("#prompt-prefix", Static)
|
|
169
|
+
prefix_widget.update(self._get_prefix_text())
|
|
170
|
+
except Exception:
|
|
171
|
+
# Widget not yet composed
|
|
172
|
+
pass
|
|
173
|
+
|
|
174
|
+
@on(Input.Changed, "#prompt-input")
|
|
175
|
+
def on_input_changed(self, event: Input.Changed) -> None:
|
|
176
|
+
"""Handle input changes."""
|
|
177
|
+
self.value = event.value
|
|
178
|
+
|
|
179
|
+
# Check for slash command trigger
|
|
180
|
+
if event.value.startswith("/"):
|
|
181
|
+
self.post_message(self.SlashTriggered(event.value))
|
|
182
|
+
|
|
183
|
+
@on(Input.Submitted, "#prompt-input")
|
|
184
|
+
def on_input_submitted(self, event: Input.Submitted) -> None:
|
|
185
|
+
"""Handle input submission."""
|
|
186
|
+
value = event.value.strip()
|
|
187
|
+
if value:
|
|
188
|
+
# Add to history
|
|
189
|
+
if not self._history or self._history[-1] != value:
|
|
190
|
+
self._history.append(value)
|
|
191
|
+
self._history_index = -1
|
|
192
|
+
|
|
193
|
+
# Clear input
|
|
194
|
+
input_widget = self.query_one("#prompt-input", Input)
|
|
195
|
+
input_widget.value = ""
|
|
196
|
+
|
|
197
|
+
# Post message
|
|
198
|
+
self.post_message(self.Submitted(value))
|
|
199
|
+
|
|
200
|
+
# Call callback if provided
|
|
201
|
+
if self._on_submit:
|
|
202
|
+
self._on_submit(value)
|
|
203
|
+
|
|
204
|
+
def action_submit(self) -> None:
|
|
205
|
+
"""Submit the current input."""
|
|
206
|
+
input_widget = self.query_one("#prompt-input", Input)
|
|
207
|
+
if input_widget.value.strip():
|
|
208
|
+
self.on_input_submitted(Input.Submitted(input_widget, input_widget.value))
|
|
209
|
+
|
|
210
|
+
def action_cancel(self) -> None:
|
|
211
|
+
"""Cancel current input / close completions."""
|
|
212
|
+
input_widget = self.query_one("#prompt-input", Input)
|
|
213
|
+
input_widget.value = ""
|
|
214
|
+
self._hide_suggestions()
|
|
215
|
+
|
|
216
|
+
def action_history_prev(self) -> None:
|
|
217
|
+
"""Navigate to previous history item."""
|
|
218
|
+
if not self._history:
|
|
219
|
+
return
|
|
220
|
+
|
|
221
|
+
if self._history_index < 0:
|
|
222
|
+
self._history_index = len(self._history) - 1
|
|
223
|
+
elif self._history_index > 0:
|
|
224
|
+
self._history_index -= 1
|
|
225
|
+
|
|
226
|
+
input_widget = self.query_one("#prompt-input", Input)
|
|
227
|
+
input_widget.value = self._history[self._history_index]
|
|
228
|
+
input_widget.cursor_position = len(input_widget.value)
|
|
229
|
+
|
|
230
|
+
def action_history_next(self) -> None:
|
|
231
|
+
"""Navigate to next history item."""
|
|
232
|
+
if not self._history or self._history_index < 0:
|
|
233
|
+
return
|
|
234
|
+
|
|
235
|
+
if self._history_index < len(self._history) - 1:
|
|
236
|
+
self._history_index += 1
|
|
237
|
+
input_widget = self.query_one("#prompt-input", Input)
|
|
238
|
+
input_widget.value = self._history[self._history_index]
|
|
239
|
+
input_widget.cursor_position = len(input_widget.value)
|
|
240
|
+
else:
|
|
241
|
+
self._history_index = -1
|
|
242
|
+
input_widget = self.query_one("#prompt-input", Input)
|
|
243
|
+
input_widget.value = ""
|
|
244
|
+
|
|
245
|
+
def action_complete(self) -> None:
|
|
246
|
+
"""Trigger tab completion."""
|
|
247
|
+
input_widget = self.query_one("#prompt-input", Input)
|
|
248
|
+
value = input_widget.value
|
|
249
|
+
|
|
250
|
+
# If we have suggestions, cycle through them
|
|
251
|
+
if self._suggestions:
|
|
252
|
+
self._suggestion_index = (self._suggestion_index + 1) % len(self._suggestions)
|
|
253
|
+
# Apply suggestion
|
|
254
|
+
suggestion = self._suggestions[self._suggestion_index]
|
|
255
|
+
input_widget.value = suggestion
|
|
256
|
+
input_widget.cursor_position = len(suggestion)
|
|
257
|
+
self._show_suggestions()
|
|
258
|
+
return
|
|
259
|
+
|
|
260
|
+
# Try path completion
|
|
261
|
+
if " " in value:
|
|
262
|
+
# Complete the last token as a path
|
|
263
|
+
parts = value.rsplit(" ", 1)
|
|
264
|
+
prefix = parts[0] + " "
|
|
265
|
+
path_query = parts[1] if len(parts) > 1 else ""
|
|
266
|
+
completions = self._get_path_completions(path_query)
|
|
267
|
+
if completions:
|
|
268
|
+
self._suggestions = [prefix + c for c in completions]
|
|
269
|
+
self._suggestion_index = 0
|
|
270
|
+
input_widget.value = self._suggestions[0]
|
|
271
|
+
input_widget.cursor_position = len(input_widget.value)
|
|
272
|
+
self._show_suggestions()
|
|
273
|
+
|
|
274
|
+
def _get_path_completions(self, query: str) -> list[str]:
|
|
275
|
+
"""Get file path completions for the query."""
|
|
276
|
+
try:
|
|
277
|
+
if not query:
|
|
278
|
+
# List current directory
|
|
279
|
+
cwd = Path.cwd()
|
|
280
|
+
return [p.name + ("/" if p.is_dir() else "") for p in cwd.iterdir()][:10]
|
|
281
|
+
|
|
282
|
+
# Expand path
|
|
283
|
+
path = Path(query).expanduser()
|
|
284
|
+
|
|
285
|
+
if path.is_dir():
|
|
286
|
+
# List directory contents
|
|
287
|
+
return [
|
|
288
|
+
query.rstrip("/") + "/" + p.name + ("/" if p.is_dir() else "")
|
|
289
|
+
for p in path.iterdir()
|
|
290
|
+
][:10]
|
|
291
|
+
|
|
292
|
+
# Complete partial filename
|
|
293
|
+
parent = path.parent
|
|
294
|
+
prefix = path.name
|
|
295
|
+
|
|
296
|
+
if parent.is_dir():
|
|
297
|
+
matches = [
|
|
298
|
+
str(parent / p.name) + ("/" if p.is_dir() else "")
|
|
299
|
+
for p in parent.iterdir()
|
|
300
|
+
if p.name.lower().startswith(prefix.lower())
|
|
301
|
+
]
|
|
302
|
+
return matches[:10]
|
|
303
|
+
|
|
304
|
+
except (OSError, PermissionError):
|
|
305
|
+
pass
|
|
306
|
+
|
|
307
|
+
return []
|
|
308
|
+
|
|
309
|
+
def _show_suggestions(self) -> None:
|
|
310
|
+
"""Show the suggestions display."""
|
|
311
|
+
if not self._suggestions:
|
|
312
|
+
self._hide_suggestions()
|
|
313
|
+
return
|
|
314
|
+
|
|
315
|
+
suggestions_row = self.query_one("#suggestions-row", Horizontal)
|
|
316
|
+
suggestions_row.add_class("visible")
|
|
317
|
+
|
|
318
|
+
display = self.query_one("#suggestions-display", Static)
|
|
319
|
+
# Show current suggestion index and total
|
|
320
|
+
text = f"Tab: {self._suggestion_index + 1}/{len(self._suggestions)}"
|
|
321
|
+
display.update(text)
|
|
322
|
+
|
|
323
|
+
def _hide_suggestions(self) -> None:
|
|
324
|
+
"""Hide the suggestions display."""
|
|
325
|
+
self._suggestions = []
|
|
326
|
+
self._suggestion_index = 0
|
|
327
|
+
suggestions_row = self.query_one("#suggestions-row", Horizontal)
|
|
328
|
+
suggestions_row.remove_class("visible")
|
|
329
|
+
|
|
330
|
+
def set_value(self, value: str) -> None:
|
|
331
|
+
"""Set the input value programmatically."""
|
|
332
|
+
input_widget = self.query_one("#prompt-input", Input)
|
|
333
|
+
input_widget.value = value
|
|
334
|
+
input_widget.cursor_position = len(value)
|
|
335
|
+
self.value = value
|
|
336
|
+
|
|
337
|
+
def clear(self) -> None:
|
|
338
|
+
"""Clear the input."""
|
|
339
|
+
input_widget = self.query_one("#prompt-input", Input)
|
|
340
|
+
input_widget.value = ""
|
|
341
|
+
self.value = ""
|
|
342
|
+
self._hide_suggestions()
|
|
343
|
+
|
|
344
|
+
def focus_input(self) -> None:
|
|
345
|
+
"""Focus the input widget."""
|
|
346
|
+
self.query_one("#prompt-input", Input).focus()
|
|
347
|
+
|
|
348
|
+
def set_mode(self, mode: str) -> None:
|
|
349
|
+
"""Set the mode suffix."""
|
|
350
|
+
self.mode_suffix = mode
|
|
351
|
+
|
|
352
|
+
def insert_command(self, command: str) -> None:
|
|
353
|
+
"""Insert a slash command into the input."""
|
|
354
|
+
input_widget = self.query_one("#prompt-input", Input)
|
|
355
|
+
input_widget.value = command
|
|
356
|
+
input_widget.cursor_position = len(command)
|
|
357
|
+
self.value = command
|
|
358
|
+
input_widget.focus()
|