yoda: lecture seule par onglet, a la Ghostty (toggle_readonly)
Surveiller une session Claude Code ou Modjo sans risquer de lui taper dedans, ni de lui coller quoi que ce soit. - Terminal::read_only : input_no_scroll, input_scroll (donc paste) et la molette en mode souris n'ecrivent plus rien, et une frappe ne ramene plus la vue en bas. Couvre touches legacy et kitty, collage, clic milieu, Ctrl+C, gestionnaire de mots de passe, IME, glisser-deposer. - Terminal::respond : ecriture non bloquee pour ce que le terminal repond lui-meme (PtyWrite, couleurs, taille, rapports de focus), sinon une application attendrait une reponse qui ne vient pas (CSI ? u de Claude Code, DA, DSR...). - terminal_box : en lecture seule la souris revient au terminal (selection, copie et defilement locaux, meme dans une application qui la capture). - Case « Lecture seule » dans Affichage et dans le menu contextuel ; action ToggleReadOnly dans les raccourcis (groupe Autre, sans raccourci par defaut, comme Ghostty et Konsole) ; cadenas changes-prevent-symbolic sur l'onglet, apres les alertes. Test de bout en bout dans un vrai PTY (cat) : saisie et collage bloques, reponse transmise, saisie retablie ; il echoue si le verrou est retire. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
db9ee02d8d
commit
a035b47e98
7 changed files with 142 additions and 8 deletions
42
src/main.rs
42
src/main.rs
|
|
@ -115,6 +115,7 @@ fn monitor_icon_name(terminal: &Terminal) -> Option<&'static str> {
|
|||
Some(MonitorKind::Activity) => Some("dialog-information-symbolic"),
|
||||
Some(MonitorKind::Silence) => Some("alarm-symbolic"),
|
||||
Some(MonitorKind::ProcessFinished) => Some("emblem-ok-symbolic"),
|
||||
None if terminal.read_only => Some("changes-prevent-symbolic"),
|
||||
None if terminal.monitoring_enabled() => Some("view-reveal-symbolic"),
|
||||
None => None,
|
||||
}
|
||||
|
|
@ -390,6 +391,7 @@ pub enum Action {
|
|||
LayoutLoad,
|
||||
LayoutSave,
|
||||
Monitor(MonitorKind),
|
||||
ToggleReadOnly,
|
||||
PaneFocusDown,
|
||||
PaneFocusLeft,
|
||||
PaneFocusRight,
|
||||
|
|
@ -449,6 +451,7 @@ impl Action {
|
|||
Self::LayoutLoad => Message::LayoutLoad,
|
||||
Self::LayoutSave => Message::LayoutSave,
|
||||
Self::Monitor(monitor_kind) => Message::MonitorToggle(*monitor_kind, entity_opt),
|
||||
Self::ToggleReadOnly => Message::ReadOnlyToggle(entity_opt),
|
||||
Self::PaneFocusDown => Message::PaneFocusAdjacent(pane_grid::Direction::Down),
|
||||
Self::PaneFocusLeft => Message::PaneFocusAdjacent(pane_grid::Direction::Left),
|
||||
Self::PaneFocusRight => Message::PaneFocusAdjacent(pane_grid::Direction::Right),
|
||||
|
|
@ -556,6 +559,7 @@ pub enum Message {
|
|||
ShortcutSearch(String),
|
||||
MonitorTick,
|
||||
MonitorToggle(MonitorKind, Option<segmented_button::Entity>),
|
||||
ReadOnlyToggle(Option<segmented_button::Entity>),
|
||||
MouseEnter(pane_grid::Pane),
|
||||
Opacity(u8),
|
||||
PaneClicked(pane_grid::Pane),
|
||||
|
|
@ -1809,6 +1813,22 @@ impl App {
|
|||
}
|
||||
}
|
||||
|
||||
fn terminal_read_only(&self, pane: pane_grid::Pane, entity: segmented_button::Entity) -> bool {
|
||||
self.pane_model
|
||||
.panes
|
||||
.get(pane)
|
||||
.and_then(|tab_model| tab_model.data::<Mutex<Terminal>>(entity))
|
||||
.is_some_and(|terminal| terminal.lock().unwrap().read_only)
|
||||
}
|
||||
|
||||
fn active_terminal_read_only(&self) -> bool {
|
||||
let pane = self.pane_model.focused();
|
||||
self.pane_model
|
||||
.panes
|
||||
.get(pane)
|
||||
.is_some_and(|tab_model| self.terminal_read_only(pane, tab_model.active()))
|
||||
}
|
||||
|
||||
fn active_terminal_monitors(&self) -> (bool, bool, bool) {
|
||||
let pane = self.pane_model.focused();
|
||||
if let Some(tab_model) = self.pane_model.panes.get(pane) {
|
||||
|
|
@ -3034,6 +3054,20 @@ impl Application for App {
|
|||
}
|
||||
}
|
||||
}
|
||||
Message::ReadOnlyToggle(entity_opt) => {
|
||||
if let Some(tab_model) = self.pane_model.active_mut() {
|
||||
let entity = entity_opt.unwrap_or_else(|| tab_model.active());
|
||||
let mut icon_update = None;
|
||||
if let Some(terminal) = tab_model.data::<Mutex<Terminal>>(entity) {
|
||||
let mut terminal = terminal.lock().unwrap();
|
||||
terminal.read_only = !terminal.read_only;
|
||||
icon_update = Some(monitor_icon_name(&terminal));
|
||||
}
|
||||
if let Some(icon_name) = icon_update {
|
||||
monitor_update_tab_icon(tab_model, entity, icon_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
Message::MonitorToggle(monitor_kind, entity_opt) => {
|
||||
if let Some(tab_model) = self.pane_model.active_mut() {
|
||||
let entity = entity_opt.unwrap_or_else(|| tab_model.active());
|
||||
|
|
@ -3718,7 +3752,7 @@ impl Application for App {
|
|||
let terminal = terminal.lock().unwrap();
|
||||
let rgb = terminal.effective_color(index);
|
||||
let text = f(rgb);
|
||||
terminal.input_no_scroll(text.into_bytes());
|
||||
terminal.respond(text.into_bytes());
|
||||
}
|
||||
}
|
||||
TermEvent::CursorBlinkingChange => {
|
||||
|
|
@ -3732,7 +3766,7 @@ impl Application for App {
|
|||
&& let Some(terminal) = tab_model.data::<Mutex<Terminal>>(entity)
|
||||
{
|
||||
let terminal = terminal.lock().unwrap();
|
||||
terminal.input_no_scroll(text.into_bytes());
|
||||
terminal.respond(text.into_bytes());
|
||||
}
|
||||
}
|
||||
TermEvent::ResetTitle => {
|
||||
|
|
@ -3757,7 +3791,7 @@ impl Application for App {
|
|||
{
|
||||
let terminal = terminal.lock().unwrap();
|
||||
let text = f(terminal.size().into());
|
||||
terminal.input_no_scroll(text.into_bytes());
|
||||
terminal.respond(text.into_bytes());
|
||||
}
|
||||
}
|
||||
TermEvent::Title(title) => {
|
||||
|
|
@ -4160,6 +4194,7 @@ impl Application for App {
|
|||
&self.config,
|
||||
&self.key_binds,
|
||||
self.active_terminal_monitors(),
|
||||
self.active_terminal_read_only(),
|
||||
&self.ssh_hosts,
|
||||
)]
|
||||
}
|
||||
|
|
@ -4292,6 +4327,7 @@ impl Application for App {
|
|||
entity,
|
||||
link,
|
||||
self.terminal_monitors(pane, entity),
|
||||
self.terminal_read_only(pane, entity),
|
||||
)
|
||||
}),
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue