fix(tui): address PR review feedback

Fixes from OutThisLife review:
1. Restore Linux Alt+Enter newline: textInput.tsx now uses
   k.shift || (isMac ? isActionMod(k) : k.meta) so Alt+Enter
   inserts a newline on Linux (was broken by isMac guard).
2. Fix image.attach response type: useComposerState.ts now uses
   ImageAttachResponse (which already has remainder) instead of
   InputDetectDropResponse with intersection.
3. Expand looksLikeDroppedPath test coverage with edge cases for
   image extensions, file:// URIs, spaces, empty input, and
   non-file URLs.
4. Make terminalParity.test.ts hermetic: terminalParityHints() now
   accepts optional fileOps/homeDir and passes them through to
   shouldPromptForTerminalSetup(), so tests inject mock readFile
   instead of hitting the real filesystem.

Fixes from Copilot inline review:
5. Remove unused options.now parameter from configureTerminalKeybindings.
6. Replace naive stripJsonComments (full-line // only) with a proper
   JSONC stripper that handles inline // comments, block comments,
   trailing commas, and preserves comment-like sequences in strings.
7. Move backupFile() call from immediately after read to right before
   write - backups are only created when changes will actually be
   written, not on every /terminal-setup invocation.
This commit is contained in:
kshitijk4poor 2026-04-21 20:05:18 +05:30 committed by kshitij
parent 9556fef5a1
commit bc9927dc50
7 changed files with 160 additions and 13 deletions

View file

@ -4,7 +4,7 @@ import { join } from 'node:path'
export type SupportedTerminal = 'cursor' | 'vscode' | 'windsurf'
type FileOps = {
export type FileOps = {
copyFile: typeof copyFile
mkdir: typeof mkdir
readFile: typeof readFile
@ -83,8 +83,57 @@ export function detectVSCodeLikeTerminal(env: NodeJS.ProcessEnv = process.env):
return null
}
/**
* Strip JSONC features (// line comments, /* block comments *\/, trailing commas)
* so the result is valid JSON parseable by JSON.parse().
* Handles comments inside strings correctly (preserves them).
*/
export function stripJsonComments(content: string): string {
return content.replace(/^\s*\/\/.*$/gm, '')
let result = ''
let i = 0
const len = content.length
while (i < len) {
const ch = content[i]!
// String literal — copy as-is, including any comment-like chars inside
if (ch === '"') {
let j = i + 1
while (j < len) {
if (content[j] === '\\') {
j += 2 // skip escaped char
} else if (content[j] === '"') {
j++
break
} else {
j++
}
}
result += content.slice(i, j)
i = j
continue
}
// Line comment
if (ch === '/' && content[i + 1] === '/') {
const eol = content.indexOf('\n', i)
i = eol === -1 ? len : eol
continue
}
// Block comment
if (ch === '/' && content[i + 1] === '*') {
const end = content.indexOf('*/', i + 2)
i = end === -1 ? len : end + 2
continue
}
result += ch
i++
}
// Remove trailing commas before ] or }
return result.replace(/,(\s*[}\]])/g, '$1')
}
function isRemoteShellSession(env: NodeJS.ProcessEnv): boolean {
@ -127,7 +176,6 @@ export async function configureTerminalKeybindings(
env?: NodeJS.ProcessEnv
fileOps?: Partial<FileOps>
homeDir?: string
now?: () => Date
platform?: NodeJS.Platform
}
): Promise<TerminalSetupResult> {
@ -159,9 +207,10 @@ export async function configureTerminalKeybindings(
await ops.mkdir(configDir, { recursive: true })
let keybindings: unknown[] = []
let hasExistingFile = false
try {
const content = await ops.readFile(keybindingsFile, 'utf8')
await backupFile(keybindingsFile, ops)
hasExistingFile = true
const parsed: unknown = JSON.parse(stripJsonComments(content))
if (!Array.isArray(parsed)) {
return {
@ -208,6 +257,10 @@ export async function configureTerminalKeybindings(
}
}
if (hasExistingFile) {
await backupFile(keybindingsFile, ops)
}
await ops.writeFile(keybindingsFile, `${JSON.stringify(keybindings, null, 2)}\n`, 'utf8')
return {