minor README tweaks + rust progress
This commit is contained in:
parent
c99507ca1e
commit
45f7ac9071
9 changed files with 628 additions and 25 deletions
|
|
@ -57,6 +57,9 @@ pub fn parse_keystroke(s: &str) -> Option<ParsedKeystroke> {
|
|||
|
||||
for part in s.split('+') {
|
||||
let part = part.trim();
|
||||
if part.is_empty() {
|
||||
continue;
|
||||
}
|
||||
match part {
|
||||
"ctrl" | "control" => ctrl = true,
|
||||
"alt" | "opt" | "option" => alt = true,
|
||||
|
|
@ -164,6 +167,18 @@ pub struct UserKeybindings {
|
|||
pub bindings: Vec<UserBinding>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
struct JsonKeybindingConfig {
|
||||
#[serde(default)]
|
||||
bindings: Vec<JsonKeybindingBlock>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
struct JsonKeybindingBlock {
|
||||
context: String,
|
||||
bindings: HashMap<String, Option<String>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct UserBinding {
|
||||
pub chord: String, // e.g. "ctrl+k ctrl+d"
|
||||
|
|
@ -172,10 +187,16 @@ pub struct UserBinding {
|
|||
}
|
||||
|
||||
impl UserKeybindings {
|
||||
pub fn from_json_str(content: &str) -> Self {
|
||||
serde_json::from_str(content)
|
||||
.or_else(|_| Self::from_block_config(content))
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
pub fn load(config_dir: &Path) -> Self {
|
||||
let path = config_dir.join("keybindings.json");
|
||||
if let Ok(content) = std::fs::read_to_string(&path) {
|
||||
serde_json::from_str(&content).unwrap_or_default()
|
||||
Self::from_json_str(&content)
|
||||
} else {
|
||||
Self::default()
|
||||
}
|
||||
|
|
@ -187,6 +208,23 @@ impl UserKeybindings {
|
|||
std::fs::write(path, json)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn from_block_config(content: &str) -> Result<Self, serde_json::Error> {
|
||||
let config: JsonKeybindingConfig = serde_json::from_str(content)?;
|
||||
let bindings = config
|
||||
.bindings
|
||||
.into_iter()
|
||||
.flat_map(|block| {
|
||||
let context = block.context;
|
||||
block.bindings.into_iter().map(move |(chord, action)| UserBinding {
|
||||
chord,
|
||||
action,
|
||||
context: Some(context.clone()),
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
Ok(Self { bindings })
|
||||
}
|
||||
}
|
||||
|
||||
/// Resolved keybindings (defaults merged with user overrides)
|
||||
|
|
@ -420,4 +458,28 @@ mod tests {
|
|||
let user = UserKeybindings::default();
|
||||
assert!(user.bindings.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_user_keybindings_supports_ts_block_format() {
|
||||
let user = UserKeybindings::from_json_str(
|
||||
r#"{
|
||||
"bindings": [
|
||||
{
|
||||
"context": "Chat",
|
||||
"bindings": {
|
||||
"ctrl+g": "chat:externalEditor",
|
||||
"space": null
|
||||
}
|
||||
}
|
||||
]
|
||||
}"#,
|
||||
);
|
||||
|
||||
assert_eq!(user.bindings.len(), 2);
|
||||
assert_eq!(user.bindings[0].context.as_deref(), Some("Chat"));
|
||||
assert_eq!(user.bindings[0].chord, "ctrl+g");
|
||||
assert_eq!(user.bindings[0].action.as_deref(), Some("chat:externalEditor"));
|
||||
assert_eq!(user.bindings[1].chord, "space");
|
||||
assert_eq!(user.bindings[1].action, None);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -411,6 +411,8 @@ pub mod config {
|
|||
pub max_tokens: Option<u32>,
|
||||
pub permission_mode: PermissionMode,
|
||||
pub theme: Theme,
|
||||
#[serde(default)]
|
||||
pub output_style: Option<String>,
|
||||
pub auto_compact: bool,
|
||||
pub compact_threshold: f32,
|
||||
pub verbose: bool,
|
||||
|
|
@ -424,6 +426,8 @@ pub mod config {
|
|||
pub append_system_prompt: Option<String>,
|
||||
pub disable_claude_mds: bool,
|
||||
pub project_dir: Option<PathBuf>,
|
||||
#[serde(default)]
|
||||
pub workspace_paths: Vec<PathBuf>,
|
||||
/// Event hooks: map of event → list of hook commands.
|
||||
#[serde(default)]
|
||||
pub hooks: HashMap<HookEvent, Vec<HookEntry>>,
|
||||
|
|
@ -518,6 +522,14 @@ pub mod config {
|
|||
}
|
||||
}
|
||||
|
||||
/// Resolve the effective output style for system-prompt assembly.
|
||||
pub fn effective_output_style(&self) -> crate::system_prompt::OutputStyle {
|
||||
self.output_style
|
||||
.as_deref()
|
||||
.map(crate::system_prompt::OutputStyle::from_str)
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
/// Resolve the API key from the config, then from `ANTHROPIC_API_KEY`.
|
||||
pub fn resolve_api_key(&self) -> Option<String> {
|
||||
self.api_key
|
||||
|
|
@ -633,6 +645,28 @@ pub mod config {
|
|||
tokio::fs::write(&path, content).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Synchronous variant used by pre-session commands.
|
||||
pub fn load_sync() -> anyhow::Result<Self> {
|
||||
let path = Self::global_settings_path();
|
||||
if path.exists() {
|
||||
let content = std::fs::read_to_string(&path)?;
|
||||
Ok(serde_json::from_str(&content).unwrap_or_default())
|
||||
} else {
|
||||
Ok(Self::default())
|
||||
}
|
||||
}
|
||||
|
||||
/// Synchronous variant used by pre-session commands.
|
||||
pub fn save_sync(&self) -> anyhow::Result<()> {
|
||||
let path = Self::global_settings_path();
|
||||
if let Some(parent) = path.parent() {
|
||||
std::fs::create_dir_all(parent)?;
|
||||
}
|
||||
let content = serde_json::to_string_pretty(self)?;
|
||||
std::fs::write(&path, content)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -218,7 +218,7 @@ pub fn format_memory_manifest(memories: &[MemoryFileMeta]) -> String {
|
|||
|
||||
match &m.description {
|
||||
Some(desc) => format!("- {}{} ({}): {}", tag, m.filename, ts, desc),
|
||||
None => format!("- {}{} ({})", tag, m.filename, ts),
|
||||
None => format!("- {}{}", tag, m.filename),
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
|
|
@ -361,8 +361,7 @@ pub fn auto_memory_path(project_root: &Path) -> PathBuf {
|
|||
/// Sanitize an arbitrary string into a directory-name-safe component.
|
||||
/// Matches `sanitizePath` used inside `getAutoMemPath` in `paths.ts`.
|
||||
pub fn sanitize_path_component(s: &str) -> String {
|
||||
let sanitized: String = s
|
||||
.chars()
|
||||
s.chars()
|
||||
.map(|c| {
|
||||
if c.is_alphanumeric() || c == '-' || c == '_' || c == '.' {
|
||||
c
|
||||
|
|
@ -370,8 +369,7 @@ pub fn sanitize_path_component(s: &str) -> String {
|
|||
'_'
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
sanitized.trim_matches('_').to_string()
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Whether the auto-memory system is enabled for this session.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue