mirror of
https://github.com/stablyai/orca.git
synced 2026-10-27 00:02:48 +00:00
* chore(lint): upgrade oxlint to 1.71 and enable 7 new rules Upgrade oxlint 1.67.0 -> 1.71.0 (1.72 was blocked by the repo's 3-day minimum-release-age supply-chain guard; nothing here needs it). The bump is a no-op on the existing config. Enable 3 error rules (backlog autofixed to zero in this commit) and 4 warn rules (surface signal without gating CI): error (autofixed, behavior-preserving): - unicorn/prefer-node-protocol (~1531 sites: bare builtin -> node:) - typescript/no-import-type-side-effects (~36: all-inline-type -> import type) - unicorn/no-array-reverse (19: copy-then-reverse -> toReversed) warn (real signal, current fires are test-only/correct): - unicorn/no-array-fill-with-reference-type (aliasing footgun guard) - typescript/no-unsafe-function-type (bans bare Function type) - unicorn/prefer-array-flat-map (map().flat() -> flatMap()) - unicorn/prefer-regexp-test (.match() in bool ctx -> .test()) mobile/.oxlintrc.json extends root, so it inherits all 7; the autofix ran from root and covered mobile/ too. Verification (all green): oxlint 0 errors (root+mobile+aux configs), oxfmt clean, typecheck (node+cli+web), vitest 22795 passed / 0 failed, builds (electron-vite + web + cli) succeed. node: rewrites confirmed to skip embedded SSH/CLI string payloads (AST-only); all toReversed sites verified to operate on fresh copies or write-once locals. * chore(lint): bump mobile oxlint to 1.71 so inherited rules parse mobile/ is a standalone pnpm project pinning its own oxlint@1.67, which lacks unicorn/no-array-fill-with-reference-type (needs >=1.70). Since mobile/.oxlintrc.json extends the root config, mobile CI's 'cd mobile && oxlint' failed to parse the new rule. Bump mobile to match root (1.71). Verified in mobile/: oxlint 0 errors, oxfmt --check clean, tsc --noEmit pass, vitest 978 passed / 0 failed. Co-authored-by: Orca <help@stably.ai> --------- Co-authored-by: Orca <help@stably.ai>
228 lines
6.3 KiB
TypeScript
228 lines
6.3 KiB
TypeScript
import { mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs'
|
|
import { tmpdir } from 'node:os'
|
|
import { join } from 'node:path'
|
|
import { afterEach, beforeEach, describe, expect, it } from 'vitest'
|
|
import {
|
|
getUserKeybindingsPath,
|
|
migrateLegacyKeybindings,
|
|
readKeybindingFile,
|
|
writeKeybindingOverride
|
|
} from './keybinding-file'
|
|
|
|
describe('keybinding-file', () => {
|
|
let dir: string
|
|
let filePath: string
|
|
|
|
beforeEach(() => {
|
|
dir = mkdtempSync(join(tmpdir(), 'orca-keybindings-'))
|
|
filePath = join(dir, 'keybindings.json')
|
|
})
|
|
|
|
afterEach(() => {
|
|
rmSync(dir, { recursive: true, force: true })
|
|
})
|
|
|
|
it('resolves the user-facing keybindings path under ~/.orca', () => {
|
|
expect(getUserKeybindingsPath('/home/test')).toBe(
|
|
join('/home/test', '.orca', 'keybindings.json')
|
|
)
|
|
})
|
|
|
|
it('returns an empty snapshot when the file does not exist', () => {
|
|
expect(readKeybindingFile(filePath, 'linux')).toMatchObject({
|
|
exists: false,
|
|
platform: 'linux',
|
|
overrides: {},
|
|
diagnostics: []
|
|
})
|
|
})
|
|
|
|
it('parses common and platform-specific overrides', () => {
|
|
writeFileSync(
|
|
filePath,
|
|
JSON.stringify({
|
|
version: 1,
|
|
keybindings: {
|
|
'worktree.quickOpen': 'Mod+Shift+P',
|
|
'view.tasks': null
|
|
},
|
|
platforms: {
|
|
linux: {
|
|
'terminal.paste': ['Ctrl+Shift+V', 'Shift+Insert'],
|
|
'terminal.search': 'Ctrl+Shift+F'
|
|
},
|
|
darwin: {
|
|
'terminal.search': 'Mod+F'
|
|
}
|
|
}
|
|
}),
|
|
'utf8'
|
|
)
|
|
|
|
expect(readKeybindingFile(filePath, 'linux')).toMatchObject({
|
|
exists: true,
|
|
overrides: {
|
|
'worktree.quickOpen': ['Mod+Shift+P'],
|
|
'view.tasks': [],
|
|
'terminal.paste': ['Ctrl+Shift+V', 'Shift+Insert'],
|
|
'terminal.search': ['Ctrl+Shift+F']
|
|
},
|
|
diagnostics: []
|
|
})
|
|
})
|
|
|
|
it('accepts bare keys for actions that explicitly opt in', () => {
|
|
writeFileSync(
|
|
filePath,
|
|
JSON.stringify({
|
|
keybindings: {
|
|
'fileExplorer.delete': 'Delete'
|
|
}
|
|
}),
|
|
'utf8'
|
|
)
|
|
|
|
expect(readKeybindingFile(filePath, 'linux')).toMatchObject({
|
|
overrides: {
|
|
'fileExplorer.delete': ['Delete']
|
|
},
|
|
diagnostics: []
|
|
})
|
|
})
|
|
|
|
it('ignores invalid, unknown, and conflicting manual edits', () => {
|
|
writeFileSync(
|
|
filePath,
|
|
JSON.stringify({
|
|
keybindings: {
|
|
unknownAction: 'Ctrl+Alt+U',
|
|
'terminal.search': 'not-a-keybinding',
|
|
'view.tasks': 'Mod+P'
|
|
}
|
|
}),
|
|
'utf8'
|
|
)
|
|
|
|
const snapshot = readKeybindingFile(filePath, 'linux')
|
|
|
|
expect(snapshot.overrides).toEqual({})
|
|
expect(snapshot.diagnostics.map((diagnostic) => diagnostic.severity)).toEqual([
|
|
'warning',
|
|
'error',
|
|
'error'
|
|
])
|
|
})
|
|
|
|
it('writes active-platform overrides while preserving other platforms', () => {
|
|
writeFileSync(
|
|
filePath,
|
|
JSON.stringify({
|
|
version: 1,
|
|
keybindings: {
|
|
'worktree.quickOpen': 'Mod+Shift+P'
|
|
},
|
|
platforms: {
|
|
darwin: {
|
|
'terminal.search': 'Mod+F'
|
|
}
|
|
}
|
|
}),
|
|
'utf8'
|
|
)
|
|
|
|
writeKeybindingOverride(filePath, 'linux', 'terminal.search', ['Ctrl+Shift+F'])
|
|
|
|
const written = JSON.parse(readFileSync(filePath, 'utf8')) as {
|
|
keybindings: Record<string, unknown>
|
|
platforms: Record<string, Record<string, unknown>>
|
|
}
|
|
expect(written.keybindings['worktree.quickOpen']).toBe('Mod+Shift+P')
|
|
expect(written.platforms.darwin['terminal.search']).toBe('Mod+F')
|
|
expect(written.platforms.linux['terminal.search']).toEqual(['Ctrl+Shift+F'])
|
|
})
|
|
|
|
it('migrates root-level legacy overrides before writing settings edits', () => {
|
|
writeFileSync(
|
|
filePath,
|
|
JSON.stringify({
|
|
version: 1,
|
|
'worktree.quickOpen': 'Mod+Shift+P',
|
|
platforms: {
|
|
darwin: {
|
|
'terminal.search': 'Mod+F'
|
|
}
|
|
}
|
|
}),
|
|
'utf8'
|
|
)
|
|
|
|
writeKeybindingOverride(filePath, 'linux', 'terminal.search', ['Ctrl+Shift+F'])
|
|
|
|
const written = JSON.parse(readFileSync(filePath, 'utf8')) as {
|
|
keybindings: Record<string, unknown>
|
|
platforms: Record<string, Record<string, unknown>>
|
|
'worktree.quickOpen'?: unknown
|
|
}
|
|
expect(written['worktree.quickOpen']).toBeUndefined()
|
|
expect(written.keybindings['worktree.quickOpen']).toEqual(['Mod+Shift+P'])
|
|
expect(written.platforms.darwin['terminal.search']).toBe('Mod+F')
|
|
expect(readKeybindingFile(filePath, 'linux').overrides).toEqual({
|
|
'worktree.quickOpen': ['Mod+Shift+P'],
|
|
'terminal.search': ['Ctrl+Shift+F']
|
|
})
|
|
})
|
|
|
|
it('rejects writes that would conflict with another effective shortcut', () => {
|
|
expect(() => writeKeybindingOverride(filePath, 'linux', 'view.tasks', ['Mod+P'])).toThrow(
|
|
'conflicts with another shortcut'
|
|
)
|
|
expect(readKeybindingFile(filePath, 'linux').overrides).toEqual({})
|
|
})
|
|
|
|
it('validates write inputs at the file boundary', () => {
|
|
expect(() => writeKeybindingOverride(filePath, 'linux', 'unknown.action', [])).toThrow(
|
|
'Unknown keybinding action'
|
|
)
|
|
expect(() => writeKeybindingOverride(filePath, 'linux', 'view.tasks', 'Ctrl+Alt+T')).toThrow(
|
|
'Use a string array or null.'
|
|
)
|
|
})
|
|
|
|
it('resets only the active platform override', () => {
|
|
writeFileSync(
|
|
filePath,
|
|
JSON.stringify({
|
|
keybindings: {
|
|
'terminal.search': 'Ctrl+Alt+F'
|
|
},
|
|
platforms: {
|
|
linux: {
|
|
'terminal.search': 'Ctrl+Shift+F'
|
|
}
|
|
}
|
|
}),
|
|
'utf8'
|
|
)
|
|
|
|
writeKeybindingOverride(filePath, 'linux', 'terminal.search', null)
|
|
|
|
const snapshot = readKeybindingFile(filePath, 'linux')
|
|
expect(snapshot.commonOverrides).toEqual({
|
|
'terminal.search': ['Ctrl+Alt+F']
|
|
})
|
|
expect(snapshot.platformOverrides.linux).toEqual({})
|
|
expect(snapshot.overrides).toEqual({
|
|
'terminal.search': ['Ctrl+Alt+F']
|
|
})
|
|
})
|
|
|
|
it('migrates legacy settings once when no file exists', () => {
|
|
migrateLegacyKeybindings(filePath, 'linux', { 'view.tasks': ['Ctrl+Alt+T'] })
|
|
migrateLegacyKeybindings(filePath, 'linux', { 'view.tasks': ['Ctrl+Alt+X'] })
|
|
|
|
expect(readKeybindingFile(filePath, 'linux').overrides).toEqual({
|
|
'view.tasks': ['Ctrl+Alt+T']
|
|
})
|
|
})
|
|
})
|