[1.0 Review] keymap / Action / chord grammar と pipe/capture/run 分離の凍結 #8

Closed
opened 2026-09-03 16:04:00 +09:00 by natsukium · 1 comment
Owner

背景

felis の入力は felis-client-coreAction / IpcAction という closed enum と、[keymap] の chord→BindingValue 解決で構成される。explanation/input.md に「Action mapping」「Keybinding design」「A menu instead of a prefix」が仕様化。chord は単一ストローク(ctrl+shift+f 等)で unbind sentinel で default を除去。

現状の問題 / 凍結前に決めるべき点

1. 単一ストロークのみ、prefix / leader 無し

現行: multi-stroke(ctrl+space, ctrl+w のような tmux prefix)は非対応。input.md で「runfzf メニューに束ねることで prefix の代替とする」設計。

問い: 1.0 で単一ストロークを凍結するか? 将来 prefix を追加すると keymap の value が chord から chords[] に破壊的変更が必要。今なら keymap = [{chords: ["ctrl+space", "w"], kind: "detach"}] のような配列形にできる。

現行の「単一ストローク + run メニュー」でも switch_sessionPrevious/Next 巡回(roster 取得→creation order で step)は leader 無しで tab-like に動くが、大量 session を持つユーザには直接 jump の UX が弱い。

2. unbind sentinel

default を除去する sentinel kind = "unbind" は、TOML 上で {kind="unbind"} という typed value として表現される。BindingValue::Unbindinto_action() -> None で dispatcher の fall-through にする。

代替: keymap."ctrl+shift+r" = false / null のような簡潔な除去記法。現行の typed sentinel は、将来 keymap の value が Action 以外(例: mode)に拡張された時に拡張しやすい利点がある。1.0 で sentinel 形を凍結するか。

3. chord grammar の strictness

  • 修飾子は ctrl/shift/alt/super の4つのみ(control / cmd / win / esc は unknown token で拒否)。
  • named key は enter/tab/escape/space/backspace/insert/delete/home/end/page_up/page_down/up/down/left/right/f1..f35 のみ。
  • 文字キーは単一 Unicode codepoint で case-sensitive(shift+a vs shift+A)。
  • 修飾子の重複(ctrl+ctrl+a)は config error。

この「一つの綴りのみ」原則は typo 検出に強いが、他 terminal からの移行時に cmd / control を書いてしまう罠になる。1.0 で alias(cmdsuper)を許容するか、現行の厳密さを凍結するか。

4. closed Action enum

ActionSendString, Paste, Copy, Reload, Detach, FontSize, Scroll*, ScrollToPrompt, ToggleFullscreen, Ipc(IpcAction), Pipe, Run の closed set。IpcActionSwitchSession, KillSession, OpenScrollbackSearch, NewSession の closed set。variant は typed param を持ち、expression / callback を持たない(principle 1)。

PipeRunRegionSource / PipeTarget / command: Vec<String> の typed union。SendStringtext + escapes: c_style|noneescapes.decode() の検証を伴う。

問い: 1.0 で Action set を凍結し、将来の追加は minor 扱い(additive)にするか? 新 Action が wire の RegionMsg / SessionMsg::Create に依存する場合は、control surface と wire の両方に跨る変更になるため、1.0 で「Action 追加は必ず wire 追加を伴う」規則を control-surfaces.md に明記すべき。

5. pipe vs capture vs run の分離

  • pipe(keymap-only): region → sink(command/clipboard/file/temp_file/paste)の push。
  • capture(CLI-only): region → stdout の pull。
  • run(keymap-only): region 無しで command を transient session で実行。

control-surfaces.md の「Deliberate asymmetries」で「pipe は keymap-only, capture は CLI-only」は意図的とされている。1.0 でこの push/pull 分離を凍結するか、将来 sessions pipe(headless で region を sink に送る)や keymap capture(keymap から stdout 的な sink)を許すか。

6. mouse / wheel / IME

mouse.scroll_multiplier0.1..=100.0 に clamp、touchpad smooth scroll は velocity-scaled で無視。Ctrl+Wheel は font zoom で scroll と latch。IME は winit 経由(macOS native / Wayland zwp_text_input_v3 / X11 XIM / Windows TSF)。

1.0 で mouse gesture(select/dismiss/PRIMARY auto-copy/middle-click paste)のデフォルトを凍結するか、将来的に mouse 節で gesture を config 可能にする余地を残すか。

提案

  • 1.0 で「単一ストロークのみ」を凍結するなら、explanation/input.md の「Keybinding design」に「なぜ prefix を持たないか」の Revisit trigger(例: 100 session を持つユーザが direct jump を要望したら)を明記。
  • chord grammar の alias(cmd / control)を 1.0 で追加しないなら、reference/keybindings.md の quick facts に「なぜ alias が無いか」を追記し、将来の alias 追加は additive だが敢えてしない決定として記録。
  • Action set の凍結を reference/spec.md の REQ-806 / 806a に「1.0 で凍結、追加は minor」としてラベル付け。

判定基準

  • 新しい keymap 機能を議論する際に、「それは新しい Action variant か、新しい chord grammar か、新しい surface(CLI vs keymap)か」が一意に判定できること。
  • felis config check が malformed chord / unknown kind / malformed binding args を全て diagnostics で報告し、一つの bad entry が他の entry を巻き込まないこと。

cc @natsukium

## 背景 felis の入力は `felis-client-core` の `Action` / `IpcAction` という closed enum と、`[keymap]` の chord→`BindingValue` 解決で構成される。`explanation/input.md` に「Action mapping」「Keybinding design」「A menu instead of a prefix」が仕様化。chord は単一ストローク(`ctrl+shift+f` 等)で `unbind` sentinel で default を除去。 ## 現状の問題 / 凍結前に決めるべき点 ### 1. 単一ストロークのみ、prefix / leader 無し 現行: multi-stroke(`ctrl+space, ctrl+w` のような tmux prefix)は非対応。`input.md` で「`run` を `fzf` メニューに束ねることで prefix の代替とする」設計。 **問い:** 1.0 で単一ストロークを凍結するか? 将来 `prefix` を追加すると `keymap` の value が `chord` から `chords[]` に破壊的変更が必要。今なら `keymap = [{chords: ["ctrl+space", "w"], kind: "detach"}]` のような配列形にできる。 現行の「単一ストローク + `run` メニュー」でも `switch_session` の `Previous/Next` 巡回(roster 取得→creation order で step)は leader 無しで tab-like に動くが、大量 session を持つユーザには直接 jump の UX が弱い。 ### 2. `unbind` sentinel default を除去する sentinel `kind = "unbind"` は、TOML 上で `{kind="unbind"}` という typed value として表現される。`BindingValue::Unbind` は `into_action() -> None` で dispatcher の fall-through にする。 代替: `keymap."ctrl+shift+r" = false` / `null` のような簡潔な除去記法。現行の typed sentinel は、将来 `keymap` の value が `Action` 以外(例: `mode`)に拡張された時に拡張しやすい利点がある。1.0 で sentinel 形を凍結するか。 ### 3. chord grammar の strictness - 修飾子は `ctrl/shift/alt/super` の4つのみ(`control` / `cmd` / `win` / `esc` は unknown token で拒否)。 - named key は `enter/tab/escape/space/backspace/insert/delete/home/end/page_up/page_down/up/down/left/right/f1..f35` のみ。 - 文字キーは単一 Unicode codepoint で case-sensitive(`shift+a` vs `shift+A`)。 - 修飾子の重複(`ctrl+ctrl+a`)は config error。 この「一つの綴りのみ」原則は typo 検出に強いが、他 terminal からの移行時に `cmd` / `control` を書いてしまう罠になる。1.0 で alias(`cmd` → `super`)を許容するか、現行の厳密さを凍結するか。 ### 4. closed Action enum `Action` は `SendString, Paste, Copy, Reload, Detach, FontSize, Scroll*, ScrollToPrompt, ToggleFullscreen, Ipc(IpcAction), Pipe, Run` の closed set。`IpcAction` も `SwitchSession, KillSession, OpenScrollbackSearch, NewSession` の closed set。variant は typed param を持ち、expression / callback を持たない(principle 1)。 `Pipe` と `Run` は `RegionSource` / `PipeTarget` / `command: Vec<String>` の typed union。`SendString` は `text` + `escapes: c_style|none` で `escapes.decode()` の検証を伴う。 **問い:** 1.0 で Action set を凍結し、将来の追加は minor 扱い(additive)にするか? 新 Action が wire の `RegionMsg` / `SessionMsg::Create` に依存する場合は、control surface と wire の両方に跨る変更になるため、1.0 で「Action 追加は必ず wire 追加を伴う」規則を `control-surfaces.md` に明記すべき。 ### 5. `pipe` vs `capture` vs `run` の分離 - `pipe`(keymap-only): region → sink(command/clipboard/file/temp_file/paste)の push。 - `capture`(CLI-only): region → stdout の pull。 - `run`(keymap-only): region 無しで command を transient session で実行。 `control-surfaces.md` の「Deliberate asymmetries」で「`pipe` は keymap-only, `capture` は CLI-only」は意図的とされている。1.0 でこの push/pull 分離を凍結するか、将来 `sessions pipe`(headless で region を sink に送る)や `keymap capture`(keymap から stdout 的な sink)を許すか。 ### 6. mouse / wheel / IME `mouse.scroll_multiplier` は `0.1..=100.0` に clamp、touchpad smooth scroll は velocity-scaled で無視。`Ctrl+Wheel` は font zoom で scroll と latch。IME は winit 経由(macOS native / Wayland zwp_text_input_v3 / X11 XIM / Windows TSF)。 1.0 で mouse gesture(select/dismiss/PRIMARY auto-copy/middle-click paste)のデフォルトを凍結するか、将来的に `mouse` 節で gesture を config 可能にする余地を残すか。 ## 提案 - 1.0 で「単一ストロークのみ」を凍結するなら、`explanation/input.md` の「Keybinding design」に「なぜ prefix を持たないか」の Revisit trigger(例: 100 session を持つユーザが direct jump を要望したら)を明記。 - chord grammar の alias(`cmd` / `control`)を 1.0 で追加しないなら、`reference/keybindings.md` の quick facts に「なぜ alias が無いか」を追記し、将来の alias 追加は additive だが敢えてしない決定として記録。 - Action set の凍結を `reference/spec.md` の REQ-806 / 806a に「1.0 で凍結、追加は minor」としてラベル付け。 ## 判定基準 - 新しい keymap 機能を議論する際に、「それは新しい Action variant か、新しい chord grammar か、新しい surface(CLI vs keymap)か」が一意に判定できること。 - `felis config check` が malformed chord / unknown kind / malformed binding args を全て `diagnostics` で報告し、一つの bad entry が他の entry を巻き込まないこと。 cc @natsukium
Author
Owner

Resolved by the later review in #12: keep typed Action/BindingValue surfaces and the existing no-evaluator, no-prefix product boundary. No release-breaking keymap redesign was selected; future proposals should be filed against an observed need.

Resolved by the later review in #12: keep typed Action/BindingValue surfaces and the existing no-evaluator, no-prefix product boundary. No release-breaking keymap redesign was selected; future proposals should be filed against an observed need.
Sign in to join this conversation.
No description provided.