Reviewed linked issue and code files.
UnderstandChecked related documentation for updates.
UnderstandSummarized code changes and tests.
VerifyYou are given one or more GitHub PR URLs: https://github.com/badlogic/pi-mono/pull/907
For each PR URL, do the following in order:
packages/*/CHANGELOG.md files. Report whether an entry exists. If missing, state that a changelog entry is required before merge and that you will add it if the user decides to merge. Follow the changelog format rules in AGENTS.md. Verify:
### Breaking Changes, ### Added, ### Fixed, etc.)Fixed foo ([#123](https://github.com/badlogic/pi-mono/pull/123) by [@user](https://github.com/user))### Breaking Changes, not just ### FixedOutput format per PR: PR: Changelog:
If no issues are found, say so under Bad and Ugly.
Let me read the linked issue #904 and the full editor.ts file:
Let me read the rest of the editor.ts file to see the isAtStartOfMessage and isInSlashCommandContext methods:
Now let me check if any coding-agent documentation needs updating:
Let me check for any existing tests related to slash command behavior:
PR: https://github.com/badlogic/pi-mono/pull/907
Changelog:
packages/tui/CHANGELOG.md has no entry for this fix under ## [Unreleased]. A changelog entry is required before merge. I will add it if you decide to merge.Good:
/ appeared at the start of any line in multi-line input, even when other content existedisSlashMenuAllowed() helper that checks all lines for non-whitespace contentisInSlashCommandContext() encapsulates the gating logic, replacing 4 instances of inline checksBad:
/ on a new line after entering text) could be covered by a unit test to prevent regressionUgly:
Questions or Assumptions:
isSlashMenuAllowed() check iterates over all lines on every keystroke in slash command context. For typical editor usage (< 100 lines), this is negligible. If large pastes become common, this could be optimized, but it's not a concern for current usage patterns.Change summary:
isSlashMenuAllowed() method (lines 1805-1819): Returns false if there's non-whitespace after cursor on current line, or non-whitespace on any other lineisAtStartOfMessage() (lines 1822-1832): Now calls isSlashMenuAllowed() first before checking cursor positionisInSlashCommandContext() method (lines 1834-1840): Combines isSlashMenuAllowed() check with the existing / prefix detectiontextBeforeCursor.trimStart().startsWith("/") to use the new gated isInSlashCommandContext():
insertCharacter() for typing alphanumericshandleBackspace() for re-triggering after backspacehandleForwardDelete() for re-triggering after deletehandleTabCompletion() for Tab-triggered completionTests:
npm run check, ./test.sh, and manual testingwhat inputs would that fix? line \n\n/?
Yes, exactly. With the fix:
line\n\n/ (or line \n\n/) → slash menu is blocked
The isSlashMenuAllowed() check iterates all lines and returns false if any line (other than the cursor line) contains non-whitespace:
for (let i = 0; i < this.state.lines.length; i++) { if (i === this.state.cursorLine) continue; if ((this.state.lines[i] ?? "").trim() !== "") { return false; // "line" on line 0 triggers this }}Scenarios now blocked:
hello\n/ — text on line 0, slash on line 1\nhello\n/ — text on line 1, slash on line 2/model gpt-4\n/ — after completing one command, starting anotherScenarios still allowed:
/ — empty editor, just typed slash / — only whitespace before slash\n\n/ — all other lines are empty/whitespace-onlyThe fix matches the issue description: "the menu should only open when / is the first thing inputted" (meaning editor is otherwise empty).
can't this be done way simpler?
like, we only want to trigger if the first char in the first line is /, or what am i missing?
You're right, it could be simpler. Looking at the issue again:
"the menu should only open when
/is the first thing inputted"
The simplest interpretation:
private isSlashMenuAllowed(): boolean { // Only allow slash menu on line 0 with no content on other lines if (this.state.cursorLine !== 0) return false; for (let i = 1; i < this.state.lines.length; i++) { if (this.state.lines[i].trim() !== "") return false; } return true;}Or even simpler if we're strict about "first thing":
private isSlashMenuAllowed(): boolean { return this.state.lines.length === 1 || this.state.lines.slice(1).every(l => l.trim() === "");}The current PR has an extra check for "non-whitespace after cursor on current line" which handles edge cases like cursor in the middle of hello /world, but that seems unnecessary for slash commands since they must start at the beginning anyway.
The question is whether cursorLine === 0 check is even needed. If you type / on line 3, the existing isAtStartOfMessage() check (beforeCursor.trim() === "" || beforeCursor.trim() === "/") would pass, but then the "other lines have content" check would fail.
So yes, the PR could be simplified. Want me to suggest a cleaner version to the contributor?
implement the most concise, correct fix locally then i test
Now I'll implement the simplified fix:
Done. Here's the simplified implementation:
// Slash menu only allowed when all other lines are empty (no mixed content)private isSlashMenuAllowed(): boolean { for (let i = 0; i < this.state.lines.length; i++) { if (i === this.state.cursorLine) continue; if (this.state.lines[i].trim() !== "") return false; } return true;}
// Helper method to check if cursor is at start of message (for slash command detection)private isAtStartOfMessage(): boolean { if (!this.isSlashMenuAllowed()) return false; const currentLine = this.state.lines[this.state.cursorLine] || ""; const beforeCursor = currentLine.slice(0, this.state.cursorCol); return beforeCursor.trim() === "" || beforeCursor.trim() === "/";}
private isInSlashCommandContext(textBeforeCursor: string): boolean { return this.isSlashMenuAllowed() && textBeforeCursor.trimStart().startsWith("/");}Differences from PR:
isAtStartOfMessage already handles that case)npm run check passes. Ready for you to test.
oh ok, so this doesn't prevent \n\n/ from triggering, but it does if i do
asf /, right?
wtf?
gah
With the current logic:
\n\n/ does trigger the menu because all other lines are empty.asf\n/ does not trigger because line 0 has non whitespace.So yes, blank lines still allow the slash menu. If you want to block \n\n/ and only allow / on the first line, we can add this.state.cursorLine === 0 to isSlashMenuAllowed().
no it's fine. commit and push, close pr with a thank you and that we implementd this manually in a more concise way
Summary:
packages/tui/src/components/editor.ts and reused it in autocomplete paths.packages/tui/CHANGELOG.md.Tests:
npm run checkfor (let i = 0; i < this.state.lines.length; i++) { if (i === this.state.cursorLine) continue; if ((this.state.lines[i] ?? "").trim() !== "") { return false; // "line" on line 0 triggers this }}private isSlashMenuAllowed(): boolean { // Only allow slash menu on line 0 with no content on other lines if (this.state.cursorLine !== 0) return false; for (let i = 1; i < this.state.lines.length; i++) { if (this.state.lines[i].trim() !== "") return false; } return true;}private isSlashMenuAllowed(): boolean { return this.state.lines.length === 1 || this.state.lines.slice(1).every(l => l.trim() === "");}// Slash menu only allowed when all other lines are empty (no mixed content)private isSlashMenuAllowed(): boolean { for (let i = 0; i < this.state.lines.length; i++) { if (i === this.state.cursorLine) continue; if (this.state.lines[i].trim() !== "") return false; } return true;}
// Helper method to check if cursor is at start of message (for slash command detection)private isAtStartOfMessage(): boolean { if (!this.isSlashMenuAllowed()) return false; const currentLine = this.state.lines[this.state.cursorLine] || ""; const beforeCursor = currentLine.slice(0, this.state.cursorCol); return beforeCursor.trim() === "" || beforeCursor.trim() === "/";}
private isInSlashCommandContext(textBeforeCursor: string): boolean { return this.isSlashMenuAllowed() && textBeforeCursor.trimStart().startsWith("/");}