mirror of
https://github.com/MoonshotAI/kimi-code.git
synced 2026-07-09 17:29:12 +00:00
fix(kimi-code): warn about tmux key configuration (#18)
This commit is contained in:
parent
67f8a2d606
commit
a964bd2430
4 changed files with 153 additions and 0 deletions
5
.changeset/warn-tmux-key-config.md
Normal file
5
.changeset/warn-tmux-key-config.md
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
"@moonshot-ai/kimi-code": patch
|
||||
---
|
||||
|
||||
Warn tmux users when extended key settings may prevent modified Enter shortcuts from working.
|
||||
|
|
@ -232,6 +232,7 @@ import { installTerminalFocusTracking } from './utils/terminal-focus';
|
|||
import { notifyTerminalOnce } from './utils/terminal-notification';
|
||||
import { createTerminalState, type TerminalState } from './utils/terminal-state';
|
||||
import { installTerminalThemeTracking } from './utils/terminal-theme';
|
||||
import { detectTmuxKeyboardWarning } from './utils/tmux-keyboard';
|
||||
import { nextTranscriptId } from './utils/transcript-id';
|
||||
|
||||
export interface KimiTUIStartupInput {
|
||||
|
|
@ -763,6 +764,7 @@ export class KimiTUI {
|
|||
this.showStatus(this.state.startupNotice);
|
||||
this.state.startupNotice = undefined;
|
||||
}
|
||||
void this.showTmuxKeyboardWarningIfNeeded();
|
||||
if (this.state.startupState === 'picker') {
|
||||
void this.bootstrapFromPicker();
|
||||
// resumeSession (fired on picker select) owns post-pick init; nothing
|
||||
|
|
@ -786,6 +788,13 @@ export class KimiTUI {
|
|||
void this.refreshSkillCommands(this.session);
|
||||
}
|
||||
|
||||
// Warns tmux users when modified Enter shortcuts are likely to be swallowed.
|
||||
private async showTmuxKeyboardWarningIfNeeded(): Promise<void> {
|
||||
const warning = await detectTmuxKeyboardWarning();
|
||||
if (warning === undefined || this.aborted) return;
|
||||
this.showStatus(warning, this.state.theme.colors.warning);
|
||||
}
|
||||
|
||||
// Creates or resumes the startup session and reports whether history should replay.
|
||||
private async init(): Promise<boolean> {
|
||||
await this.refreshAvailableModels();
|
||||
|
|
|
|||
68
apps/kimi-code/src/tui/utils/tmux-keyboard.ts
Normal file
68
apps/kimi-code/src/tui/utils/tmux-keyboard.ts
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
import { spawn } from 'node:child_process';
|
||||
|
||||
const TMUX_QUERY_TIMEOUT_MS = 2000;
|
||||
|
||||
export const TMUX_EXTENDED_KEYS_OFF_WARNING =
|
||||
'tmux extended-keys is off. Modified Enter keys may not work. Add `set -g extended-keys on` to ~/.tmux.conf and restart tmux.';
|
||||
|
||||
export const TMUX_EXTENDED_KEYS_FORMAT_XTERM_WARNING =
|
||||
'tmux extended-keys-format is xterm. Kimi Code works best with csi-u. Add `set -g extended-keys-format csi-u` to ~/.tmux.conf and restart tmux.';
|
||||
|
||||
export type TmuxOptionReader = (option: string) => Promise<string | undefined>;
|
||||
|
||||
export async function detectTmuxKeyboardWarning(
|
||||
env: NodeJS.ProcessEnv = process.env,
|
||||
readTmuxOption: TmuxOptionReader = readTmuxOptionFromProcess,
|
||||
): Promise<string | undefined> {
|
||||
if ((env['TMUX'] ?? '').length === 0) return undefined;
|
||||
|
||||
const [extendedKeys, extendedKeysFormat] = await Promise.all([
|
||||
readTmuxOption('extended-keys'),
|
||||
readTmuxOption('extended-keys-format'),
|
||||
]);
|
||||
|
||||
if (extendedKeys === undefined) return undefined;
|
||||
|
||||
if (extendedKeys !== 'on' && extendedKeys !== 'always') {
|
||||
return TMUX_EXTENDED_KEYS_OFF_WARNING;
|
||||
}
|
||||
|
||||
if (extendedKeysFormat === 'xterm') {
|
||||
return TMUX_EXTENDED_KEYS_FORMAT_XTERM_WARNING;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function readTmuxOptionFromProcess(option: string): Promise<string | undefined> {
|
||||
return new Promise((resolve) => {
|
||||
const proc = spawn('tmux', ['show', '-gv', option], {
|
||||
stdio: ['ignore', 'pipe', 'ignore'],
|
||||
});
|
||||
let stdout = '';
|
||||
let settled = false;
|
||||
let timer: NodeJS.Timeout;
|
||||
|
||||
const finish = (value: string | undefined) => {
|
||||
if (settled) return;
|
||||
settled = true;
|
||||
clearTimeout(timer);
|
||||
resolve(value);
|
||||
};
|
||||
|
||||
timer = setTimeout(() => {
|
||||
proc.kill();
|
||||
finish(undefined);
|
||||
}, TMUX_QUERY_TIMEOUT_MS);
|
||||
|
||||
proc.stdout?.on('data', (data: Buffer) => {
|
||||
stdout += data.toString('utf8');
|
||||
});
|
||||
proc.on('error', () => {
|
||||
finish(undefined);
|
||||
});
|
||||
proc.on('close', (code) => {
|
||||
finish(code === 0 ? stdout.trim() : undefined);
|
||||
});
|
||||
});
|
||||
}
|
||||
71
apps/kimi-code/test/tui/tmux-keyboard.test.ts
Normal file
71
apps/kimi-code/test/tui/tmux-keyboard.test.ts
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
import { describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import {
|
||||
detectTmuxKeyboardWarning,
|
||||
TMUX_EXTENDED_KEYS_FORMAT_XTERM_WARNING,
|
||||
TMUX_EXTENDED_KEYS_OFF_WARNING,
|
||||
type TmuxOptionReader,
|
||||
} from '#/tui/utils/tmux-keyboard';
|
||||
|
||||
function optionReader(values: Record<string, string | undefined>): TmuxOptionReader {
|
||||
return vi.fn(async (option: string) => values[option]);
|
||||
}
|
||||
|
||||
describe('tmux keyboard setup detection', () => {
|
||||
it('skips checks outside tmux', async () => {
|
||||
const readOption = optionReader({});
|
||||
|
||||
await expect(detectTmuxKeyboardWarning({}, readOption)).resolves.toBeUndefined();
|
||||
|
||||
expect(readOption).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('does not warn when tmux options cannot be queried', async () => {
|
||||
const readOption = optionReader({
|
||||
'extended-keys': undefined,
|
||||
'extended-keys-format': undefined,
|
||||
});
|
||||
|
||||
await expect(
|
||||
detectTmuxKeyboardWarning({ TMUX: '/tmp/tmux/default,123,0' }, readOption),
|
||||
).resolves.toBeUndefined();
|
||||
});
|
||||
|
||||
it('warns when extended-keys is off', async () => {
|
||||
const readOption = optionReader({
|
||||
'extended-keys': 'off',
|
||||
'extended-keys-format': 'csi-u',
|
||||
});
|
||||
|
||||
await expect(
|
||||
detectTmuxKeyboardWarning({ TMUX: '/tmp/tmux/default,123,0' }, readOption),
|
||||
).resolves.toBe(TMUX_EXTENDED_KEYS_OFF_WARNING);
|
||||
});
|
||||
|
||||
it('warns when extended-keys-format is xterm', async () => {
|
||||
const readOption = optionReader({
|
||||
'extended-keys': 'on',
|
||||
'extended-keys-format': 'xterm',
|
||||
});
|
||||
|
||||
await expect(
|
||||
detectTmuxKeyboardWarning({ TMUX: '/tmp/tmux/default,123,0' }, readOption),
|
||||
).resolves.toBe(TMUX_EXTENDED_KEYS_FORMAT_XTERM_WARNING);
|
||||
});
|
||||
|
||||
it('accepts on and always with csi-u or absent format', async () => {
|
||||
await expect(
|
||||
detectTmuxKeyboardWarning(
|
||||
{ TMUX: '/tmp/tmux/default,123,0' },
|
||||
optionReader({ 'extended-keys': 'on', 'extended-keys-format': 'csi-u' }),
|
||||
),
|
||||
).resolves.toBeUndefined();
|
||||
|
||||
await expect(
|
||||
detectTmuxKeyboardWarning(
|
||||
{ TMUX: '/tmp/tmux/default,123,0' },
|
||||
optionReader({ 'extended-keys': 'always', 'extended-keys-format': undefined }),
|
||||
),
|
||||
).resolves.toBeUndefined();
|
||||
});
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue