aboutsummaryrefslogtreecommitdiff
path: root/src/config
diff options
context:
space:
mode:
authorCan Celik <ogulcancelik@gmail.com>2026-09-01 15:48:22 +0300
committerGitHub <noreply@github.com>2026-09-01 15:48:22 +0300
commit207be3c771d281baae6e5fa0fb74be9a056e97a2 (patch)
treed7b037ecdf629224a048c841d11659efdc23507d /src/config
parent8a6d697308162874a236c84aac0b0f8c7ac01f83 (diff)
refactor: render the shell in the client (#3487)
* feat: add opt-in client-rendered shell * feat: add client-owned shell mouse controls * feat: complete client-owned shell chrome * feat: add client-owned shell settings and notifications * feat: run client shell custom commands * feat: render popup terminals in client shell * feat: add client-owned scrollback and copy mode * fix: preserve client worktree trust defaults * feat: render startup diagnostics in client shell * feat: render onboarding in client shell * feat: render product announcements in client shell * feat: render release notes in client shell * feat: render mobile switcher in client shell * feat: render kitty graphics in client shell * fix: preserve client shell lifecycle coherence * refactor: route client shell commands through endpoint connections * refactor: make the client-rendered shell the default * refactor: remove monolithic launch mode * refactor: complete client-rendered shell cutover * perf: retain client pane surface updates * perf: scale retained pane surface updates * perf: remove remote pane input latency * test: make client shell checks platform-independent * fix: prevent client timer starvation * test: fix client shell platform gates * test: accept retained surfaces in cross-area checks * test: make client mode assertions platform-neutral * fix: badge only outdated integrations
Diffstat (limited to 'src/config')
-rw-r--r--src/config/keybinds.rs12
-rw-r--r--src/config/model.rs17
-rw-r--r--src/config/write.rs75
3 files changed, 94 insertions, 10 deletions
diff --git a/src/config/keybinds.rs b/src/config/keybinds.rs
index 6cddd3ac..bec1af66 100644
--- a/src/config/keybinds.rs
+++ b/src/config/keybinds.rs
@@ -169,6 +169,18 @@ pub struct ActionKeybinds {
}
impl ActionKeybinds {
+ pub(crate) fn from_labels(labels: &[String]) -> Result<Self, String> {
+ let mut bindings = Vec::new();
+ for label in labels {
+ match parse_binding_string(label) {
+ Some(ParsedBinding::Single(binding)) => bindings.push(binding),
+ Some(ParsedBinding::Range(range)) => bindings.extend(range),
+ None => return Err(format!("invalid endpoint command binding: {label}")),
+ }
+ }
+ Ok(Self { bindings })
+ }
+
#[cfg(test)]
pub fn prefix(label: &str) -> Self {
let raw = if label.starts_with("prefix+") {
diff --git a/src/config/model.rs b/src/config/model.rs
index c10cd733..552c19de 100644
--- a/src/config/model.rs
+++ b/src/config/model.rs
@@ -101,15 +101,6 @@ pub enum AgentPanelSortConfig {
Priority,
}
-impl AgentPanelSortConfig {
- pub fn as_str(self) -> &'static str {
- match self {
- Self::Spaces => "spaces",
- Self::Priority => "priority",
- }
- }
-}
-
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize)]
#[serde(rename_all = "lowercase")]
enum LegacyAgentPanelScopeConfig {
@@ -372,7 +363,7 @@ pub struct KeysConfig {
pub navigate_pane_up: BindingConfig,
/// Focus the pane to the right in navigate mode. Default: "l". Right arrow is always an alias.
pub navigate_pane_right: BindingConfig,
- /// Detach from server/client mode, or exit --no-session mode. Default: "prefix+q".
+ /// Detach the current client from its Herdr server. Default: "prefix+q".
pub detach: BindingConfig,
/// Reload config.toml in the running app/server. Default: "prefix+shift+r".
pub reload_config: BindingConfig,
@@ -593,6 +584,12 @@ pub(crate) struct KeysConfigOverlay {
command: Option<Vec<CommandKeybindConfig>>,
}
+impl KeysConfigOverlay {
+ pub(crate) fn set_prefix(&mut self, prefix: String) {
+ self.prefix = Some(prefix);
+ }
+}
+
impl<'de> Deserialize<'de> for KeysConfig {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
diff --git a/src/config/write.rs b/src/config/write.rs
new file mode 100644
index 00000000..75244583
--- /dev/null
+++ b/src/config/write.rs
@@ -0,0 +1,75 @@
+#[derive(Clone, Copy)]
+pub(crate) enum ConfigEdit<'a> {
+ Theme(&'a str),
+ StatusIndicators(super::StatusIndicatorStyle),
+ Sound(bool),
+ ToastDelivery(super::ToastDelivery),
+}
+
+impl ConfigEdit<'_> {
+ pub(crate) fn description(self) -> &'static str {
+ match self {
+ Self::Theme(_) => "theme",
+ Self::StatusIndicators(_) => "status indicators",
+ Self::Sound(_) => "sound setting",
+ Self::ToastDelivery(_) => "toast setting",
+ }
+ }
+
+ pub(crate) fn apply(self, content: &str) -> String {
+ match self {
+ Self::Theme(name) => {
+ let content =
+ super::upsert_section_value(content, "theme", "name", &format!("\"{name}\""));
+ super::upsert_section_bool(&content, "theme", "auto_switch", false)
+ }
+ Self::StatusIndicators(style) => super::upsert_section_value(
+ content,
+ "ui",
+ "status_indicators",
+ &format!("\"{}\"", style.as_str()),
+ ),
+ Self::Sound(enabled) => {
+ super::upsert_section_bool(content, "ui.sound", "enabled", enabled)
+ }
+ Self::ToastDelivery(delivery) => {
+ let value = match delivery {
+ super::ToastDelivery::Off => "\"off\"",
+ super::ToastDelivery::Herdr => "\"herdr\"",
+ super::ToastDelivery::Terminal => "\"terminal\"",
+ super::ToastDelivery::System => "\"system\"",
+ };
+ let content = super::upsert_section_value(content, "ui.toast", "delivery", value);
+ super::remove_section_key(&content, "ui.toast", "enabled")
+ }
+ }
+ }
+}
+
+pub(crate) fn update_file_at(
+ path: &std::path::Path,
+ description: &str,
+ update: impl FnOnce(&str) -> String,
+) -> Result<(), String> {
+ if let Some(parent) = path.parent() {
+ std::fs::create_dir_all(parent)
+ .map_err(|error| format!("failed to create config directory: {error}"))?;
+ }
+ let content = match std::fs::read_to_string(path) {
+ Ok(content) => content,
+ Err(error) if error.kind() == std::io::ErrorKind::NotFound => String::new(),
+ Err(error) => {
+ return Err(format!(
+ "failed to read config before saving {description}: {error}"
+ ));
+ }
+ };
+ std::fs::write(path, update(&content))
+ .map_err(|error| format!("failed to save {description}: {error}"))
+}
+
+pub(crate) fn write_edit(edit: ConfigEdit<'_>) -> Result<(), String> {
+ update_file_at(&super::config_path(), edit.description(), |content| {
+ edit.apply(content)
+ })
+}