@@ -42,3 +42,8 @@ Feature: TUI input modes
|
||||
Given a TUI prompt value "inspect @reso"
|
||||
When I clear pending TUI reference token via escape
|
||||
Then the TUI prompt value should be "inspect"
|
||||
|
||||
Scenario: Escape removes pending reference token embedded in text
|
||||
Given a TUI prompt value "draft notes about @README.md formatting"
|
||||
When I clear pending TUI reference token via escape
|
||||
Then the TUI prompt value should be "draft notes about formatting"
|
||||
|
||||
+24
-39
@@ -61,11 +61,13 @@ class SessionView:
|
||||
|
||||
|
||||
def _strip_pending_reference_token(value: str) -> str:
|
||||
"""Remove the trailing @ reference trigger (if any).
|
||||
"""Remove the most recent ``@`` reference trigger (if any).
|
||||
|
||||
This trims whitespace and drops the last token that begins with ``@`` when the
|
||||
token does not contain any embedded whitespace. Escaped triggers (``\\@``)
|
||||
remain untouched.
|
||||
This trims whitespace and removes the final token that begins with ``@``. The
|
||||
token is removed even when additional text follows it so prompts such as
|
||||
``"draft @README.md summary"`` normalise to ``"draft summary"``. Escaped
|
||||
triggers (``\\@``) are preserved to avoid stripping literal ``@``
|
||||
characters.
|
||||
"""
|
||||
|
||||
if not isinstance(value, str):
|
||||
@@ -82,11 +84,24 @@ def _strip_pending_reference_token(value: str) -> str:
|
||||
if last_at > 0 and candidate[last_at - 1] == "\\":
|
||||
return candidate
|
||||
|
||||
suffix = candidate[last_at:]
|
||||
if any(ch.isspace() for ch in suffix):
|
||||
return candidate
|
||||
prefix = candidate[:last_at]
|
||||
suffix = candidate[last_at + 1 :]
|
||||
|
||||
return candidate[:last_at].rstrip()
|
||||
token_end = 0
|
||||
while token_end < len(suffix) and not suffix[token_end].isspace():
|
||||
token_end += 1
|
||||
|
||||
suffix_after = suffix[token_end:]
|
||||
prefix_clean = prefix.rstrip()
|
||||
suffix_clean = suffix_after.lstrip()
|
||||
|
||||
if prefix_clean and suffix_clean:
|
||||
return f"{prefix_clean} {suffix_clean}"
|
||||
if prefix_clean:
|
||||
return prefix_clean
|
||||
if suffix_clean:
|
||||
return suffix_clean
|
||||
return ""
|
||||
|
||||
|
||||
class _CommandRouter(Protocol):
|
||||
@@ -202,12 +217,10 @@ if _TEXTUAL_AVAILABLE:
|
||||
|
||||
ref_picker = self.query_one("#reference-picker", ReferencePickerOverlay)
|
||||
if getattr(ref_picker, "visible", False):
|
||||
self._remove_reference_trigger(prompt)
|
||||
ref_picker.hide()
|
||||
prompt = self.query_one("#prompt", PromptInput)
|
||||
prompt_value = getattr(prompt, "value", "")
|
||||
if isinstance(prompt_value, str):
|
||||
prompt.value = _strip_pending_reference_token(prompt_value)
|
||||
ref_picker.hide()
|
||||
focus_method = getattr(prompt, "focus", None)
|
||||
if callable(focus_method):
|
||||
focus_method()
|
||||
@@ -217,34 +230,6 @@ if _TEXTUAL_AVAILABLE:
|
||||
if callable(focus_method):
|
||||
focus_method()
|
||||
|
||||
@staticmethod
|
||||
def _remove_reference_trigger(prompt: PromptInput) -> None:
|
||||
value = getattr(prompt, "value", "")
|
||||
if not value:
|
||||
return
|
||||
|
||||
trigger_index = value.rfind("@")
|
||||
if trigger_index == -1:
|
||||
return
|
||||
|
||||
prefix = value[:trigger_index]
|
||||
suffix = value[trigger_index + 1 :]
|
||||
|
||||
token_end = 0
|
||||
while token_end < len(suffix) and not suffix[token_end].isspace():
|
||||
token_end += 1
|
||||
|
||||
suffix_after = suffix[token_end:]
|
||||
prefix_clean = prefix.rstrip()
|
||||
suffix_clean = suffix_after.lstrip()
|
||||
|
||||
if prefix_clean and suffix_clean:
|
||||
prompt.value = f"{prefix_clean} {suffix_clean}"
|
||||
elif prefix_clean:
|
||||
prompt.value = prefix_clean
|
||||
else:
|
||||
prompt.value = suffix_clean
|
||||
|
||||
def _refresh_persona_bar(self) -> None:
|
||||
persona = self._persona_state.active_persona(self._session.session_id)
|
||||
preset = self._persona_state.current_preset(self._session.session_id)
|
||||
|
||||
Reference in New Issue
Block a user