feat(app,tui): slash picker on any line and multi-command prompts#34710
feat(app,tui): slash picker on any line and multi-command prompts#34710mayankkg wants to merge 2 commits into
Conversation
…ion 0
Previously both the web UI and TUI only opened the slash command picker
when / was the very first character of the entire input (position 0).
This made it impossible to compose multi-line prompts that combine
context text on earlier lines with a slash command on a new line.
Web UI (prompt-input.tsx):
- Was: rawText.match(/^\/(\S*)$/) — anchored to whole-string start+end
- Now: rawText.substring(0, cursorPosition).match(/(?:^\|\n)\/(\S*)$/)
Slices to cursor first (same as @ trigger) and matches / at the start
of any line, not just the absolute start of the input.
TUI (autocomplete.tsx):
- Was: value.startsWith('/') — only triggered at position 0
- Now: finds the current line start via lastIndexOf('\n'), then checks
if that line starts with /. store.index is set to lineStart so the
filter correctly reads text from / to cursor.
- Hide condition updated to check value.slice(store.index) instead of
the whole value, so it works regardless of which line the / is on.
Path collision handling: / mid-sentence (e.g. 'see /Users/foo/bar') does
NOT trigger the picker because there is non-whitespace content before /
on the same line. Only / at the first non-whitespace position of a line
opens the picker, matching the intent of every other slash-command UI.
AI-Assist: no
Allows composing multiple slash commands in a single prompt by putting each command on its own line. The picker now opens whenever / appears at the start of any line (not just position 0), and selecting a command inserts it at that line without clearing the rest of the editor. transient-state.ts: - Add slashTriggerOffset: number — records the character offset of the / that opened the picker so handleSlashSelect knows which token to replace. prompt-input.tsx (handleInput): - When slashMatch is found, compute slashOffset from match.index and store it as slashTriggerOffset before opening the picker. prompt-input.tsx (handleSlashSelect): - Fast path unchanged when the editor is empty (triggerOffset 0, cursor at end of only content). - Multi-command path: reconstruct fullText from prompt parts, splice in the chosen command between the before-slash content and anything after the cursor, then restore cursor to end of the inserted token. submit.ts: - Before the existing single-command check, parse all non-empty lines. - If every line resolves to a known slash command, fire them sequentially via fireSequentially(index) recursion. - Single-command and plain-prompt paths are untouched (backward compat). tui/autocomplete.tsx (from previous commit): - Already updated to trigger on any line start and track lineStart as store.index so the filter and hide logic work correctly. AI-Assist: no
|
Hey! Your PR title Please update it to start with one of:
Where See CONTRIBUTING.md for details. |
|
This PR doesn't fully meet our contributing guidelines and PR template. What needs to be fixed:
Please edit this PR description to address the above within 2 hours, or it will be automatically closed. If you believe this was flagged incorrectly, please let a maintainer know. |
|
The following comment was made by an LLM, it may be inaccurate: Based on my search, here are potentially related PRs to monitor: Related PRs (not exact duplicates):
Note: PR #34710 (the current PR) is the primary result in all searches, indicating it's likely a new, unique contribution. The related PRs above address complementary issues with slash command handling and text preservation. |
|
This pull request has been automatically closed because it was not updated to meet our contributing guidelines within the 2-hour window. Feel free to open a new pull request that follows our guidelines. |
Issue for this PR
Closes #
Type of change
What does this PR do?
Problem
The slash command picker (
/) only opened when/was the very first character of the entire input (position 0). The regex wasrawText.match(/^\/(\S*)$/)— anchored to the whole string. This made two things impossible:Changes
Fix: picker triggers at the start of any line (web + TUI)
Web UI (
prompt-input.tsx): changed the regex from a whole-string anchor to a cursor-aware line-start match that mirrors the existing@trigger:TUI (
autocomplete.tsx): replacedvalue.startsWith('/')with alastIndexOf('\n')line-start computation.store.indexis now set tolineStartso the filter and hide logic work correctly on any line.Feature: multi-command prompts
Each slash command goes on its own line. The picker opens per-line. On submit, if every non-empty line is a known custom command they fire sequentially.
transient-state.ts: addedslashTriggerOffset: numberto record where the/was in the editor text.handleSlashSelectinprompt-input.tsx: previously calledsetEditorText(text)which replaced the entire editor. Now splices only the/filtertoken atslashTriggerOffset, keeping the rest of the editor intact.submit.ts: added a multi-command path before the existing single-command check. Parses all lines, verifies each resolves to a known command, fires them sequentially. Single-command and plain-prompt paths are fully unchanged.Path collision handling
/Users/foo/barmid-sentence does not trigger the picker. The rule is/must be the first non-whitespace character on its line — matching how Notion, Linear, and Slack handle slash commands.How did you verify your code works?
packages/appvite dev against the running opencode backend on port 4096)/on an empty input opens the picker (existing behaviour unchanged)/opens the picker on the second line/update-docson line 1, Shift+Enter,/code-reviewon line 2, then Enter fires both commands sequentially/Users/foo/barmid-sentence does not open the pickerScreenshots / recordings
UI change — picker now opens on the second line after context text on the first line.
Checklist