summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--README.md23
-rw-r--r--src/config.rs9
-rw-r--r--src/repl.rs69
3 files changed, 77 insertions, 24 deletions
diff --git a/README.md b/README.md
index 013be65..e7d418b 100644
--- a/README.md
+++ b/README.md
@@ -52,18 +52,17 @@ no_highlight: false # optional, Whether to disable highlight
proxy: "socks5://127.0.0.1:1080" # optional, set proxy server. e.g. http://127.0.0.1:8080 or socks5://127.0.0.1:1080
```
-The default config dir is as follows, You can override config dir with `$AICHAT_CONFIG_DIR`.
-
-- Linux: `/home/alice/.config/aichat`
-- Windows: `C:\Users\Alice\AppData\Roaming\aichat`
-- MacOS: `/Users/Alice/Library/Application Support`
-
-aichat may generate the following files in the config dir:
-
-- `config.yaml`: configuration file.
-- `roles.yaml`: role definition file.
-- `history.txt`: repl history file.
-- `messages.md`: chat messages file.
+Use `.info` command to view the configuration file path.
+```
+〉.info
+config file /home/alice/.config/aichat/config.yaml
+roles file /home/alice/.config/aichat/roles.yaml
+messages file /home/alice/.config/aichat/messages.md
+current role
+proxy
+save messages true
+highlight true
+```
### Roles
diff --git a/src/config.rs b/src/config.rs
index 1c6abc5..b5220dd 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -55,7 +55,7 @@ impl Config {
self.roles.iter().find(|v| v.name == name).cloned()
}
- pub fn local_file(name: &str) -> Result<PathBuf> {
+ pub fn config_dir() -> Result<PathBuf> {
let env_name = format!(
"{}_CONFIG_DIR",
env!("CARGO_CRATE_NAME").to_ascii_uppercase()
@@ -70,6 +70,11 @@ impl Config {
anyhow!("Failed to create config dir at {}, {err}", path.display())
})?;
}
+ Ok(path)
+ }
+
+ pub fn local_file(name: &str) -> Result<PathBuf> {
+ let mut path = Self::config_dir()?;
path.push(name);
Ok(path)
}
@@ -117,7 +122,7 @@ impl Config {
Self::local_file(HISTORY_FILE_NAME)
}
- fn messages_file() -> Result<PathBuf> {
+ pub fn messages_file() -> Result<PathBuf> {
Self::local_file(MESSAGE_FILE_NAME)
}
diff --git a/src/repl.rs b/src/repl.rs
index 81a5474..2a3a48f 100644
--- a/src/repl.rs
+++ b/src/repl.rs
@@ -13,19 +13,21 @@ use reedline::{
};
use std::cell::RefCell;
use std::fs::File;
+use std::path::Path;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::thread::spawn;
-const REPL_COMMANDS: [(&str, &str); 9] = [
+const REPL_COMMANDS: [(&str, &str); 10] = [
(".clear", "Clear the screen"),
(".clear-history", "Clear the history"),
- (".clear-role", "Clear the role status"),
+ (".clear-role", "Clear the currently selected role"),
(".copy", "Copy last reply message"),
(".editor", "Enter multiline editor"),
(".exit", "Exit the REPL"),
(".help", "Print this help message"),
(".history", "Print the history"),
+ (".info", "Print the information"),
(".role", "Specify the role that the AI will play"),
];
@@ -47,7 +49,9 @@ impl Repl {
.with_completer(Box::new(completer))
.with_history(history)
.with_menu(menu)
- .with_edit_mode(edit_mode);
+ .with_edit_mode(edit_mode)
+ .with_quick_completions(true)
+ .with_partial_completions(true);
let prompt = Self::create_prompt();
Ok(Self { editor, prompt })
}
@@ -150,6 +154,10 @@ impl Repl {
dump("Pasted", 1);
}
}
+ ".info" => {
+ handler.handle(ReplCmd::Info)?;
+ dump("", 1);
+ }
_ => dump_unknown_command(),
}
} else {
@@ -209,8 +217,8 @@ pub struct ReplCmdHandler {
}
struct ReplCmdHandlerState {
- prompt: String,
reply: String,
+ role: Option<Role>,
save_file: Option<File>,
}
@@ -221,11 +229,10 @@ impl ReplCmdHandler {
} else {
Some(Arc::new(MarkdownRender::init()?))
};
- let prompt = role.map(|v| v.prompt).unwrap_or_default();
let save_file = config.open_message_file()?;
let ctrlc = Arc::new(AtomicBool::new(false));
let state = RefCell::new(ReplCmdHandlerState {
- prompt,
+ role,
save_file,
reply: String::new(),
});
@@ -244,7 +251,13 @@ impl ReplCmdHandler {
self.state.borrow_mut().reply.clear();
return Ok(());
}
- let prompt = self.state.borrow().prompt.to_string();
+ let prompt = self
+ .state
+ .borrow()
+ .role
+ .as_ref()
+ .map(|v| v.prompt.to_string())
+ .unwrap_or_default();
let prompt = if prompt.is_empty() {
None
} else {
@@ -274,8 +287,8 @@ impl ReplCmdHandler {
self.state.borrow_mut().reply = receiver.output;
}
ReplCmd::SetRole(name) => match self.config.find_role(&name) {
- Some(v) => {
- self.state.borrow_mut().prompt = v.prompt;
+ Some(role) => {
+ self.state.borrow_mut().role = Some(role);
dump("", 1);
}
None => {
@@ -283,7 +296,42 @@ impl ReplCmdHandler {
}
},
ReplCmd::UnsetRole => {
- self.state.borrow_mut().prompt = String::new();
+ self.state.borrow_mut().role = None;
+ }
+ ReplCmd::Info => {
+ let state = self.state.borrow();
+ let file_info = |path: &Path| {
+ let state = if path.exists() { "" } else { " [not found]" };
+ format!("{}{state}", path.display())
+ };
+ let items = vec![
+ ("config file", file_info(&Config::config_file()?)),
+ ("roles file", file_info(&Config::roles_file()?)),
+ ("messages file", file_info(&Config::messages_file()?)),
+ (
+ "current role",
+ state
+ .role
+ .as_ref()
+ .map(|v| v.name.to_string())
+ .unwrap_or_default(),
+ ),
+ (
+ "proxy",
+ self.config
+ .proxy
+ .as_ref()
+ .map(|v| v.to_string())
+ .unwrap_or_default(),
+ ),
+ ("save messages", self.config.save.to_string()),
+ ("highlight", (!self.config.no_highlight).to_string()),
+ ];
+ let mut info = String::new();
+ for (name, value) in items {
+ info.push_str(&format!("{name:<20}{value}\n"));
+ }
+ dump(info, 0);
}
}
Ok(())
@@ -340,6 +388,7 @@ enum ReplCmd {
UnsetRole,
Input(String),
SetRole(String),
+ Info,
}
fn dump_repl_help() {