feat: 将 keybinding 纳入 ink 管辖

This commit is contained in:
claude-code-best
2026-04-07 17:51:01 +08:00
parent e5782e732c
commit f268b16b31
17 changed files with 1101 additions and 1090 deletions

View File

@@ -4,7 +4,7 @@ import {
useExitOnCtrlCDWithKeybindings,
} from '../hooks/useExitOnCtrlCD.js'
import { Box, Text } from '../index.js'
import { useKeybinding } from './keybindings.js'
import { useKeybinding } from '../keybindings/useKeybinding.js'
import type { Theme } from './theme-types.js'
import { ConfigurableShortcutHint } from './ConfigurableShortcutHint.js'
import { Byline } from './Byline.js'

View File

@@ -14,7 +14,7 @@ import ScrollBox from '../components/ScrollBox.js'
import type { KeyboardEvent } from '../core/events/keyboard-event.js'
import { stringWidth } from '../core/stringWidth.js'
import { Box, Text } from '../index.js'
import { useKeybindings } from './keybindings.js'
import { useKeybindings } from '../keybindings/useKeybinding.js'
import type { Theme } from './theme-types.js'
type TabsProps = {

View File

@@ -1,87 +0,0 @@
/**
* Minimal stub of the keybinding system for the standalone @anthropic/ink package.
*
* The full keybinding system (src/keybindings/) depends on KeybindingContext,
* KeybindingRegistry, and chord handling. This stub provides the same hook
* interfaces (useKeybinding / useKeybindings) but routes directly through
* useInput, matching common key sequences to action names.
*
* Only the keybindings used by theme components are mapped:
* - confirm:no → Escape
* - tabs:next → Tab / Right arrow
* - tabs:previous → Shift+Tab / Left arrow
*/
import { useCallback } from 'react'
import useInput from '../hooks/use-input.js'
import type { Key } from '../core/events/input-event.js'
type Options = {
context?: string
isActive?: boolean
}
/** Maps action names to key matching logic. */
const ACTION_MATCHERS: Record<
string,
(input: string, key: Key) => boolean
> = {
'confirm:no': (_input, key) => key.escape === true,
'tabs:next': (input, key) =>
(key.tab && !key.shift) || (key.rightArrow && !key.shift),
'tabs:previous': (_input, key) =>
(key.tab && key.shift) || (key.leftArrow && !key.shift),
}
/**
* Register a single keybinding action handler.
*/
export function useKeybinding(
action: string,
handler: () => void | false | Promise<void>,
options: Options = {},
): void {
const { isActive = true } = options
const handleInput = useCallback(
(input: string, key: Key) => {
if (!isActive) return
const matcher = ACTION_MATCHERS[action]
if (matcher && matcher(input, key)) {
if (handler() !== false) {
// consumed
}
}
},
[action, handler, isActive],
)
useInput(handleInput, { isActive })
}
/**
* Register multiple keybinding action handlers in one hook.
*/
export function useKeybindings(
handlers: Record<string, () => void | false | Promise<void>>,
options: Options = {},
): void {
const { isActive = true } = options
const handleInput = useCallback(
(input: string, key: Key) => {
if (!isActive) return
for (const [action, handler] of Object.entries(handlers)) {
const matcher = ACTION_MATCHERS[action]
if (matcher && matcher(input, key)) {
if (handler() !== false) {
break // consumed, stop checking other handlers
}
}
}
},
[handlers, isActive],
)
useInput(handleInput, { isActive })
}