use std::sync::Arc;
use bevy_ecs::prelude::*;
use bevy_log::warn;
use rig_harness::prelude::{Files, PluginData};
use super::jsonl::{self, Kept};
const HISTORY_FILE: &str = "tui/history.jsonl";
#[derive(Resource, Default)]
pub struct PromptHistory {
pub entries: Vec<String>,
pub file: Option<Arc<dyn Files>>,
unsaved: Vec<String>,
}
impl PromptHistory {
pub const KEPT: usize = 500;
pub fn load(files: Arc<dyn Files>) -> Self {
Self {
entries: jsonl::tail(&Kept(files.clone(), HISTORY_FILE), Self::KEPT),
file: Some(files),
unsaved: Vec::new(),
}
}
}
pub(crate) fn load(data: Option<Res<PluginData>>, mut history: ResMut<PromptHistory>) {
if let Some(data) = data {
*history = PromptHistory::load(data.user.clone());
}
}
#[derive(SystemSet, Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum HistorySystems {
Record,
Save,
}
#[derive(Message, Clone, Debug, PartialEq, Eq)]
pub struct Sent(pub String);
pub(crate) fn record(mut sent: MessageReader<Sent>, mut history: ResMut<PromptHistory>) {
for Sent(text) in sent.read() {
let text = text.trim();
if text.is_empty() || history.entries.last().is_some_and(|last| last == text) {
continue;
}
history.entries.push(text.to_owned());
history.unsaved.push(text.to_owned());
}
}
pub(crate) fn save(mut history: ResMut<PromptHistory>) {
if history.unsaved.is_empty() && history.entries.len() <= PromptHistory::KEPT {
return;
}
let unsaved = std::mem::take(&mut history.unsaved);
let kept = unsaved.iter().filter(|text| history.entries.contains(text));
if let Some(file) = &history.file
&& let Err(failure) = jsonl::append(&Kept(file.clone(), HISTORY_FILE), kept)
{
warn!("could not save the prompt history: {failure}");
}
let excess = history.entries.len().saturating_sub(PromptHistory::KEPT);
history.entries.drain(..excess);
}
#[cfg(test)]
mod tests;