feat(viewport): RMB=Enter option + timed right-click

Adds a configurable right-mouse-button behavior without disturbing orbit
or the context menu.

- New persisted setting `rmb_enter` (command `RMBENTER [ON|OFF]`, default
  off): when on, a right-click while a command is active acts as Enter
  (commit/close); when idle it still opens the context menu. Right-drag
  always orbits, so nothing is lost.
- Right-click vs orbit is now time-based: orbit engages only after the
  button is held >=150ms and the pointer moves, so a quick right-click is
  no longer swallowed by slight pointer jitter (was an 8px distance gate).
- The right-click Repeat menu now lists commands from every source
  (command line, ribbon, context menu, shortcuts) via a unified
  recents list recorded at the dispatch choke point, instead of only
  typed commands.

Closes #185

Co-Authored-By: Claude Opus 4.8 <[email protected]>
This commit is contained in:
Hakan SevenandClaude Opus 4.8 committed 2026-06-27 16:16:08 +03:00
1 parent 63de137106
commit 098585bb2d
10 files changed
+160 -56

No files matched your search

+22 -1
View File
@@ -25,8 +25,13 @@ const MAX_HISTORY: usize = 64;
pub struct CommandLine {
pub input: String,
pub history: Vec<HistoryEntry>,
/// Commands the user has typed (for ↑/↓ recall).
/// Commands the user has typed (for ↑/↓ recall). Holds the raw typed
/// strings so the line can be re-edited; distinct from `recent_commands`.
pub cmd_recall: Vec<String>,
/// Commands actually dispatched, from any source (command line, ribbon,
/// context menu, shortcuts), newest last. Drives the right-click "Repeat"
/// menu so it reflects every command source, not only typed ones.
pub recent_commands: Vec<String>,
/// Current position in `cmd_recall` while navigating (None = not navigating).
recall_cursor: Option<usize>,
/// Saved draft input before the user started navigating history.
@@ -100,6 +105,22 @@ impl CommandLine {
Some(cmd)
}
/// Record a dispatched command for the right-click "Repeat" menu, skipping
/// a consecutive duplicate and capping the list. Called from the dispatch
/// choke point so commands from every source are captured.
pub fn record_recent(&mut self, cmd: &str) {
let cmd = cmd.trim();
if cmd.is_empty() {
return;
}
if self.recent_commands.last().map(|s| s.as_str()) != Some(cmd) {
self.recent_commands.push(cmd.to_string());
if self.recent_commands.len() > 50 {
self.recent_commands.remove(0);
}
}
}
/// Navigate to the previous command in recall history (↑).
pub fn history_prev(&mut self) {
if self.cmd_recall.is_empty() {