Synced from monorepo
Changes: - Gate session-lifecycle heap steady state with a dhat soak - Unbreak merge lifecycle e2e after default model → grok-4.5 - Scan home-scope rules dirs at <root>/rules - Complete text-input paste and terminal parity - Gate project roles and personas - Use canonical editing in dialogs - Use canonical editing in search bars - Reject ambiguous MCP tool IDs - Harden Git operands for plugins - Simplify queue drain API - Pass RFC 9207 iss through MCP OAuth token exchange - Show leader roster when local agents map is empty - Use canonical editing in Persona views - Remove marketplace default-skills auto-install and purge old installs - Use canonical editing in extension forms - Add canonical dashboard text editing - Use canonical editing in settings - Add /summarize as a /recap alias - Restore previous agent when exiting dashboard - Use tool_choice auto for compaction - Settings toggle for snap-prompt-to-top on send - Update default models to grok-4.5 - Source login shell once for local bash (env + alias/function snapshot) - Template hardcoded param names in server-native tool descriptions - Fix System-Reminder XML tag injection in CLAUDE.md via agents_md - Fix remote workspace-server hardcoding LSP trust (repo code execution risk) - Clear orphaned tool-call updates at turn end - Suppress task wake after cancel - Send x-grok-client-identifier on direct API tool calls - Harden dashboard peek lease transitions - Host /btw side panel in live region (minimal mode) - Bound scroll presentation latency - Highlight multi-line constructs correctly in diffs and the file viewer - Block web_fetch non-public IPs; local opt-in is explicit-host only - Seed coding_data_retention_opt_out=false for OAuth e2es in pty-harness - Follow up clipboard delivery feedback - Use canonical editing in pickers - Route TextArea through canonical editor - Persistent "watching" status row; quieter turn markers - Gate sensitive edit targets - Expose agent registry counts and gate session churn on them - Default coding data sharing to opt-out until server preference applies - Wire chat attachment ids through gateway prompts - On auth refresh failure, issue retry - Forward preview provenance and computer lifecycle state - Document independent privacy controls and scope /privacy output - Strip SamplingError Display prefix on rate-limit UI copy - Stop dumping Cloudflare HTML into Retry failed - Disable in-place prompt edit (scroll jank on enter) - Strip forced ANSI color from gh pr view JSON - Plumb bash tool description onto ToolUsageCard wire
This commit is contained in:
parent
98c3b2438a
commit
7cfcb20d2b
292 changed files with 23315 additions and 9209 deletions
|
|
@ -1,4 +1,4 @@
|
|||
use std::ops::Range;
|
||||
use std::ops::{Deref, Range};
|
||||
use std::sync::Arc;
|
||||
|
||||
use unicode_segmentation::{GraphemeCursor, UnicodeSegmentation as _};
|
||||
|
|
@ -32,6 +32,35 @@ pub enum EditCommand {
|
|||
DeleteToLineEnd,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub(crate) enum EditCommandCategory {
|
||||
Insert,
|
||||
Navigation,
|
||||
Delete,
|
||||
Kill,
|
||||
}
|
||||
|
||||
impl EditCommand {
|
||||
pub(crate) fn category(self) -> EditCommandCategory {
|
||||
match self {
|
||||
Self::Insert(_) => EditCommandCategory::Insert,
|
||||
Self::MoveGraphemeLeft
|
||||
| Self::MoveGraphemeRight
|
||||
| Self::MoveWordLeft(_)
|
||||
| Self::MoveWordRight(_)
|
||||
| Self::MoveLogicalLineStart
|
||||
| Self::MoveLogicalLineEnd => EditCommandCategory::Navigation,
|
||||
Self::DeleteGraphemeBackward | Self::DeleteGraphemeForward => {
|
||||
EditCommandCategory::Delete
|
||||
}
|
||||
Self::DeleteWordBackward(_)
|
||||
| Self::DeleteWordForward(_)
|
||||
| Self::DeleteToLineStart
|
||||
| Self::DeleteToLineEnd => EditCommandCategory::Kill,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct EditDelta {
|
||||
pub replaced_byte_range: Range<usize>,
|
||||
|
|
@ -155,6 +184,14 @@ impl PartialEq for EditBuffer {
|
|||
|
||||
impl Eq for EditBuffer {}
|
||||
|
||||
impl Deref for EditBuffer {
|
||||
type Target = str;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.text()
|
||||
}
|
||||
}
|
||||
|
||||
impl EditBuffer {
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
|
|
@ -210,14 +247,14 @@ impl EditBuffer {
|
|||
#[must_use]
|
||||
pub fn insert_str(&mut self, text: &str) -> EditOutcome {
|
||||
let plan = self.plan_replace_byte_range(self.cursor_byte..self.cursor_byte, text, &[]);
|
||||
self.apply_valid_plan(&plan)
|
||||
self.apply_validated_plan(&plan)
|
||||
}
|
||||
|
||||
/// Edit-result cursors keep right affinity when adjacent text merges into one grapheme.
|
||||
#[must_use]
|
||||
pub fn replace_byte_range(&mut self, range: Range<usize>, replacement: &str) -> EditOutcome {
|
||||
let plan = self.plan_replace_byte_range(range, replacement, &[]);
|
||||
self.apply_valid_plan(&plan)
|
||||
self.apply_validated_plan(&plan)
|
||||
}
|
||||
|
||||
pub fn plan_replace_byte_range(
|
||||
|
|
@ -380,13 +417,13 @@ impl EditBuffer {
|
|||
|
||||
pub fn apply_plan(&mut self, plan: &EditPlan) -> Result<EditOutcome, ApplyEditPlanError> {
|
||||
self.validate_plan(plan)?;
|
||||
Ok(self.apply_valid_plan(plan))
|
||||
Ok(self.apply_validated_plan(plan))
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn apply(&mut self, command: EditCommand) -> EditOutcome {
|
||||
let plan = self.plan_command(command, &[]);
|
||||
self.apply_valid_plan(&plan)
|
||||
self.apply_validated_plan(&plan)
|
||||
}
|
||||
|
||||
fn make_plan(
|
||||
|
|
@ -408,7 +445,7 @@ impl EditBuffer {
|
|||
}
|
||||
}
|
||||
|
||||
fn validate_plan(&self, plan: &EditPlan) -> Result<(), ApplyEditPlanError> {
|
||||
pub(crate) fn validate_plan(&self, plan: &EditPlan) -> Result<(), ApplyEditPlanError> {
|
||||
if !Arc::ptr_eq(&plan.source_identity, &self.identity)
|
||||
|| plan.source_generation != self.generation
|
||||
{
|
||||
|
|
@ -447,7 +484,7 @@ impl EditBuffer {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn apply_valid_plan(&mut self, plan: &EditPlan) -> EditOutcome {
|
||||
pub(crate) fn apply_validated_plan(&mut self, plan: &EditPlan) -> EditOutcome {
|
||||
let old_cursor = self.cursor_byte;
|
||||
let text_changed = plan.removed_text != plan.replacement;
|
||||
let inserted_len = plan.replacement.len();
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ use super::{EditCommand, WordStyle};
|
|||
|
||||
pub fn classify_key_event(event: &KeyEvent) -> Option<EditCommand> {
|
||||
match event {
|
||||
// Some terminals encode Ctrl-B/Ctrl-F as bare C0 characters.
|
||||
KeyEvent {
|
||||
code: KeyCode::Char('\u{0002}'),
|
||||
modifiers: KeyModifiers::NONE,
|
||||
|
|
@ -21,20 +22,21 @@ pub fn classify_key_event(event: &KeyEvent) -> Option<EditCommand> {
|
|||
} if *modifiers == (KeyModifiers::CONTROL | KeyModifiers::ALT) => {
|
||||
Some(EditCommand::DeleteWordBackward(WordStyle::Small))
|
||||
}
|
||||
// Kitty protocol loss can surface Backspace as raw BS or DEL; modifiers are unreliable.
|
||||
KeyEvent {
|
||||
code: KeyCode::Backspace | KeyCode::Char('\u{0008}' | '\u{007f}'),
|
||||
code: KeyCode::Char('\u{0008}' | '\u{007f}'),
|
||||
..
|
||||
} => Some(EditCommand::DeleteGraphemeBackward),
|
||||
KeyEvent {
|
||||
code: KeyCode::Backspace,
|
||||
modifiers,
|
||||
..
|
||||
} => Some(backspace_command(*modifiers)),
|
||||
KeyEvent {
|
||||
code: KeyCode::Delete,
|
||||
modifiers: KeyModifiers::ALT | KeyModifiers::CONTROL,
|
||||
modifiers,
|
||||
..
|
||||
} => Some(EditCommand::DeleteWordForward(WordStyle::Small)),
|
||||
KeyEvent {
|
||||
code: KeyCode::Delete,
|
||||
..
|
||||
} => Some(EditCommand::DeleteGraphemeForward),
|
||||
} => Some(delete_command(*modifiers)),
|
||||
KeyEvent {
|
||||
code: KeyCode::Char('w'),
|
||||
modifiers: KeyModifiers::CONTROL,
|
||||
|
|
@ -44,14 +46,18 @@ pub fn classify_key_event(event: &KeyEvent) -> Option<EditCommand> {
|
|||
)),
|
||||
KeyEvent {
|
||||
code: KeyCode::Left,
|
||||
modifiers: KeyModifiers::ALT | KeyModifiers::CONTROL,
|
||||
modifiers,
|
||||
..
|
||||
} => Some(EditCommand::MoveWordLeft(WordStyle::Small)),
|
||||
} if modifiers.intersects(KeyModifiers::ALT | KeyModifiers::CONTROL) => {
|
||||
Some(EditCommand::MoveWordLeft(WordStyle::Small))
|
||||
}
|
||||
KeyEvent {
|
||||
code: KeyCode::Right,
|
||||
modifiers: KeyModifiers::ALT | KeyModifiers::CONTROL,
|
||||
modifiers,
|
||||
..
|
||||
} => Some(EditCommand::MoveWordRight(WordStyle::Small)),
|
||||
} if modifiers.intersects(KeyModifiers::ALT | KeyModifiers::CONTROL) => {
|
||||
Some(EditCommand::MoveWordRight(WordStyle::Small))
|
||||
}
|
||||
KeyEvent {
|
||||
code: KeyCode::Char('a'),
|
||||
modifiers: KeyModifiers::CONTROL,
|
||||
|
|
@ -114,9 +120,11 @@ pub fn classify_key_event(event: &KeyEvent) -> Option<EditCommand> {
|
|||
} => Some(EditCommand::DeleteGraphemeForward),
|
||||
KeyEvent {
|
||||
code: KeyCode::Char('d'),
|
||||
modifiers: KeyModifiers::ALT | KeyModifiers::SUPER,
|
||||
modifiers,
|
||||
..
|
||||
} => Some(EditCommand::DeleteWordForward(WordStyle::Small)),
|
||||
} if modifiers.intersects(KeyModifiers::ALT | KeyModifiers::SUPER) => {
|
||||
Some(EditCommand::DeleteWordForward(WordStyle::Small))
|
||||
}
|
||||
KeyEvent {
|
||||
code: KeyCode::Char(character),
|
||||
modifiers: KeyModifiers::NONE | KeyModifiers::SHIFT,
|
||||
|
|
@ -149,6 +157,7 @@ fn shifted_char(character: char) -> char {
|
|||
}
|
||||
|
||||
fn backspace_command(modifiers: KeyModifiers) -> EditCommand {
|
||||
// Backspace preserves exact historical chords; extra modifiers fall back to grapheme delete.
|
||||
match modifiers {
|
||||
KeyModifiers::ALT | KeyModifiers::CONTROL => {
|
||||
EditCommand::DeleteWordBackward(WordStyle::Small)
|
||||
|
|
@ -157,3 +166,12 @@ fn backspace_command(modifiers: KeyModifiers) -> EditCommand {
|
|||
_ => EditCommand::DeleteGraphemeBackward,
|
||||
}
|
||||
}
|
||||
|
||||
fn delete_command(modifiers: KeyModifiers) -> EditCommand {
|
||||
// Delete accepts Shift in addition to a word modifier because enhanced protocols retain it.
|
||||
if modifiers.intersects(KeyModifiers::ALT | KeyModifiers::CONTROL | KeyModifiers::SUPER) {
|
||||
EditCommand::DeleteWordForward(WordStyle::Small)
|
||||
} else {
|
||||
EditCommand::DeleteGraphemeForward
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -168,6 +168,7 @@ fn lifecycle_and_host_owned_keys_remain_unclassified() {
|
|||
key(KeyCode::Esc, KeyModifiers::NONE),
|
||||
key(KeyCode::Enter, KeyModifiers::NONE),
|
||||
key(KeyCode::Tab, KeyModifiers::NONE),
|
||||
key(KeyCode::Char('\t'), KeyModifiers::NONE),
|
||||
key(KeyCode::BackTab, KeyModifiers::SHIFT),
|
||||
key(KeyCode::Up, KeyModifiers::NONE),
|
||||
key(KeyCode::Down, KeyModifiers::NONE),
|
||||
|
|
@ -209,37 +210,37 @@ fn backspace_delete_and_raw_encodings_have_modifier_parity() {
|
|||
(
|
||||
KeyModifiers::SUPER,
|
||||
EditCommand::DeleteToLineStart,
|
||||
EditCommand::DeleteGraphemeForward,
|
||||
EditCommand::DeleteWordForward(WordStyle::Small),
|
||||
),
|
||||
(
|
||||
KeyModifiers::CONTROL | KeyModifiers::SHIFT,
|
||||
EditCommand::DeleteGraphemeBackward,
|
||||
EditCommand::DeleteGraphemeForward,
|
||||
EditCommand::DeleteWordForward(WordStyle::Small),
|
||||
),
|
||||
(
|
||||
KeyModifiers::ALT | KeyModifiers::SHIFT,
|
||||
EditCommand::DeleteGraphemeBackward,
|
||||
EditCommand::DeleteGraphemeForward,
|
||||
EditCommand::DeleteWordForward(WordStyle::Small),
|
||||
),
|
||||
(
|
||||
KeyModifiers::SUPER | KeyModifiers::SHIFT,
|
||||
EditCommand::DeleteGraphemeBackward,
|
||||
EditCommand::DeleteGraphemeForward,
|
||||
EditCommand::DeleteWordForward(WordStyle::Small),
|
||||
),
|
||||
(
|
||||
KeyModifiers::CONTROL | KeyModifiers::ALT,
|
||||
EditCommand::DeleteGraphemeBackward,
|
||||
EditCommand::DeleteGraphemeForward,
|
||||
EditCommand::DeleteWordForward(WordStyle::Small),
|
||||
),
|
||||
(
|
||||
KeyModifiers::CONTROL | KeyModifiers::SUPER,
|
||||
EditCommand::DeleteGraphemeBackward,
|
||||
EditCommand::DeleteGraphemeForward,
|
||||
EditCommand::DeleteWordForward(WordStyle::Small),
|
||||
),
|
||||
(
|
||||
KeyModifiers::ALT | KeyModifiers::SUPER,
|
||||
EditCommand::DeleteGraphemeBackward,
|
||||
EditCommand::DeleteGraphemeForward,
|
||||
EditCommand::DeleteWordForward(WordStyle::Small),
|
||||
),
|
||||
(
|
||||
KeyModifiers::META,
|
||||
|
|
@ -254,7 +255,7 @@ fn backspace_delete_and_raw_encodings_have_modifier_parity() {
|
|||
(
|
||||
KeyModifiers::CONTROL | KeyModifiers::ALT | KeyModifiers::SHIFT,
|
||||
EditCommand::DeleteGraphemeBackward,
|
||||
EditCommand::DeleteGraphemeForward,
|
||||
EditCommand::DeleteWordForward(WordStyle::Small),
|
||||
),
|
||||
];
|
||||
|
||||
|
|
@ -270,11 +271,15 @@ fn backspace_delete_and_raw_encodings_have_modifier_parity() {
|
|||
Some(expected_delete),
|
||||
"{delete:?}"
|
||||
);
|
||||
assert_eq!(classify_key_event(&raw_bs), backspace_command, "{raw_bs:?}");
|
||||
assert_eq!(
|
||||
classify_key_event(&raw_bs),
|
||||
Some(EditCommand::DeleteGraphemeBackward),
|
||||
"{raw_bs:?}",
|
||||
);
|
||||
assert_eq!(
|
||||
classify_key_event(&raw_del),
|
||||
backspace_command,
|
||||
"{raw_del:?}"
|
||||
Some(EditCommand::DeleteGraphemeBackward),
|
||||
"{raw_del:?}",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue