add gui perhaps
This commit is contained in:
196
src/gui.rs
196
src/gui.rs
@@ -1,24 +1,200 @@
|
||||
use iced::{Element, widget::text_editor};
|
||||
use crate::fossil::FossilManager;
|
||||
use iced::{
|
||||
Element,
|
||||
widget::{button, column, row, text, text_editor},
|
||||
};
|
||||
use iced_aw::{TabLabel, Tabs};
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct State {
|
||||
content: text_editor::Content,
|
||||
fossil_manager: FossilManager,
|
||||
tab_editors: HashMap<String, text_editor::Content>,
|
||||
active_tab: Option<String>,
|
||||
tab_order: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Message {
|
||||
Edit(text_editor::Action),
|
||||
Edit(String, text_editor::Action),
|
||||
TabSelected(String),
|
||||
CreateFossil,
|
||||
SyncFossil(String),
|
||||
DeleteFossil(String),
|
||||
RefreshFromManager,
|
||||
}
|
||||
|
||||
impl State {
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
pub fn with_shared_manager(fossil_manager: FossilManager) -> Self {
|
||||
Self {
|
||||
fossil_manager,
|
||||
tab_editors: HashMap::new(),
|
||||
active_tab: None,
|
||||
tab_order: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_fossil(&mut self, id: String, content: String) {
|
||||
// Add to fossil manager
|
||||
{
|
||||
let mut fossils = self.fossil_manager.fossils.lock().unwrap();
|
||||
fossils.insert(id.clone(), crate::fossil::Fossil::new(content.clone()));
|
||||
}
|
||||
|
||||
// Add editor tab
|
||||
self.tab_editors
|
||||
.insert(id.clone(), text_editor::Content::with_text(&content));
|
||||
self.tab_order.push(id.clone());
|
||||
self.active_tab = Some(id);
|
||||
}
|
||||
|
||||
pub fn sync_fossil(&mut self, id: &str) {
|
||||
if let Some(editor_content) = self.tab_editors.get(id) {
|
||||
let content = editor_content.text();
|
||||
let mut fossils = self.fossil_manager.fossils.lock().unwrap();
|
||||
if let Some(fossil) = fossils.get_mut(id) {
|
||||
fossil.commit(content);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn load_fossil_to_editor(&mut self, id: &str) {
|
||||
let fossils = self.fossil_manager.fossils.lock().unwrap();
|
||||
if let Some(fossil) = fossils.get(id) {
|
||||
if let Some(latest_content) = fossil.latest() {
|
||||
self.tab_editors.insert(
|
||||
id.to_string(),
|
||||
text_editor::Content::with_text(latest_content),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn refresh_from_manager(&mut self) {
|
||||
let fossils = self.fossil_manager.fossils.lock().unwrap();
|
||||
let fossil_ids: Vec<String> = fossils.keys().cloned().collect();
|
||||
drop(fossils);
|
||||
|
||||
// Add new fossils that don't exist in GUI
|
||||
for id in &fossil_ids {
|
||||
if !self.tab_editors.contains_key(id) {
|
||||
self.load_fossil_to_editor(id);
|
||||
self.tab_order.push(id.clone());
|
||||
}
|
||||
}
|
||||
|
||||
// Remove fossils that no longer exist in manager
|
||||
let gui_fossil_ids: Vec<String> = self.tab_editors.keys().cloned().collect();
|
||||
for id in gui_fossil_ids {
|
||||
if !fossil_ids.contains(&id) {
|
||||
self.tab_editors.remove(&id);
|
||||
self.tab_order.retain(|x| x != &id);
|
||||
if self.active_tab.as_ref() == Some(&id) {
|
||||
self.active_tab = self.tab_order.first().cloned();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update active tab if none is selected but tabs exist
|
||||
if self.active_tab.is_none() && !self.tab_order.is_empty() {
|
||||
self.active_tab = Some(self.tab_order[0].clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn view(state: &State) -> Element<'_, Message> {
|
||||
text_editor(&state.content)
|
||||
.placeholder("Type something here...")
|
||||
.on_action(Message::Edit)
|
||||
.into()
|
||||
let mut tabs = Tabs::new(Message::TabSelected);
|
||||
|
||||
for id in &state.tab_order {
|
||||
tabs = tabs.push(
|
||||
id.clone(),
|
||||
TabLabel::Text(id.clone()),
|
||||
tab_content(state, id),
|
||||
);
|
||||
}
|
||||
|
||||
let tabs = if let Some(active_id) = &state.active_tab {
|
||||
tabs.set_active_tab(active_id)
|
||||
} else {
|
||||
tabs
|
||||
};
|
||||
|
||||
let controls = row![
|
||||
button("New Fossil").on_press(Message::CreateFossil),
|
||||
button("Sync Active").on_press_maybe(
|
||||
state
|
||||
.active_tab
|
||||
.as_ref()
|
||||
.map(|id| Message::SyncFossil(id.clone()))
|
||||
),
|
||||
button("Delete Active").on_press_maybe(
|
||||
state
|
||||
.active_tab
|
||||
.as_ref()
|
||||
.map(|id| Message::DeleteFossil(id.clone()))
|
||||
),
|
||||
button("Refresh").on_press(Message::RefreshFromManager),
|
||||
]
|
||||
.spacing(10)
|
||||
.padding(10);
|
||||
|
||||
column![controls, tabs].spacing(10).padding(10).into()
|
||||
}
|
||||
|
||||
pub fn update(state: &mut State, message: Message) {
|
||||
match message {
|
||||
Message::Edit(action) => {}
|
||||
fn tab_content<'a>(state: &'a State, fossil_id: &str) -> Element<'a, Message> {
|
||||
if let Some(content) = state.tab_editors.get(fossil_id) {
|
||||
text_editor(content)
|
||||
.placeholder("Edit your fossil content here...")
|
||||
.on_action({
|
||||
let fossil_id = fossil_id.to_string();
|
||||
move |action| Message::Edit(fossil_id.clone(), action)
|
||||
})
|
||||
.into()
|
||||
} else {
|
||||
text("No content available").into()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update(state: &mut State, message: Message) -> iced::Task<Message> {
|
||||
match message {
|
||||
Message::Edit(fossil_id, action) => {
|
||||
if let Some(content) = state.tab_editors.get_mut(&fossil_id) {
|
||||
content.perform(action);
|
||||
}
|
||||
}
|
||||
Message::TabSelected(tab_id) => {
|
||||
state.active_tab = Some(tab_id);
|
||||
}
|
||||
Message::CreateFossil => {
|
||||
let new_id = format!("fossil_{}", state.tab_order.len() + 1);
|
||||
state.add_fossil(new_id, String::new());
|
||||
}
|
||||
Message::SyncFossil(fossil_id) => {
|
||||
state.sync_fossil(&fossil_id);
|
||||
}
|
||||
Message::DeleteFossil(fossil_id) => {
|
||||
// Remove from fossil manager
|
||||
{
|
||||
let mut fossils = state.fossil_manager.fossils.lock().unwrap();
|
||||
fossils.remove(&fossil_id);
|
||||
}
|
||||
|
||||
// Remove from GUI state
|
||||
state.tab_editors.remove(&fossil_id);
|
||||
state.tab_order.retain(|id| id != &fossil_id);
|
||||
|
||||
// Update active tab
|
||||
if state.active_tab.as_ref() == Some(&fossil_id) {
|
||||
state.active_tab = state.tab_order.first().cloned();
|
||||
}
|
||||
}
|
||||
Message::RefreshFromManager => {
|
||||
state.refresh_from_manager();
|
||||
}
|
||||
}
|
||||
iced::Task::none()
|
||||
}
|
||||
|
||||
19
src/main.rs
19
src/main.rs
@@ -1,8 +1,9 @@
|
||||
use crate::{
|
||||
gui::{update, view},
|
||||
gui::{update, view, State},
|
||||
mcp::FossilEditor,
|
||||
fossil::FossilManager,
|
||||
};
|
||||
use iced::Executor;
|
||||
use iced::Task;
|
||||
use rmcp::{ServiceExt, transport::stdio};
|
||||
use tokio::runtime::Runtime;
|
||||
use tracing_subscriber::{self, EnvFilter};
|
||||
@@ -21,7 +22,10 @@ fn main() -> iced::Result {
|
||||
|
||||
let rt = Runtime::new().unwrap();
|
||||
|
||||
let editor = FossilEditor::new();
|
||||
// Create shared fossil manager
|
||||
let shared_fossil_manager = FossilManager::new();
|
||||
|
||||
let editor = FossilEditor::with_shared_state(shared_fossil_manager.clone());
|
||||
rt.spawn(async {
|
||||
let service = editor
|
||||
.serve(stdio())
|
||||
@@ -34,5 +38,12 @@ fn main() -> iced::Result {
|
||||
service.waiting().await.unwrap();
|
||||
});
|
||||
|
||||
iced::run("A cool counter", update, view)
|
||||
iced::application("Fossil Editor", update, view)
|
||||
.default_font(iced::Font::MONOSPACE)
|
||||
.run_with(move || {
|
||||
let mut state = State::with_shared_manager(shared_fossil_manager);
|
||||
// Add a sample fossil to start with
|
||||
state.add_fossil("sample.py".to_string(), "def hello():\n print('Hello, World!')".to_string());
|
||||
(state, Task::none())
|
||||
})
|
||||
}
|
||||
|
||||
@@ -80,6 +80,13 @@ impl FossilEditor {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_shared_state(state: FossilManager) -> Self {
|
||||
Self {
|
||||
tool_router: Self::tool_router(),
|
||||
state,
|
||||
}
|
||||
}
|
||||
|
||||
#[tool(description = "Creates a new code fossil in memory with initial content.")]
|
||||
fn create_fossil(
|
||||
&self,
|
||||
|
||||
Reference in New Issue
Block a user