reworked settings, general interface improvements, more AI configuration, bugfixes and QOL.
Continuous integration / build (push) Has been cancelled

This commit is contained in:
2025-07-30 02:48:49 +01:00
parent 6c40f34122
commit e5a485d3a7
10 changed files with 850 additions and 632 deletions
+159 -286
View File
@@ -6,14 +6,15 @@ use serde::{self, Deserialize, Serialize};
use crate::{
PROJECT_FOLDER,
editors::{context_editor::ProjectContext, tags::Tag},
llm_integration::content_llm::ReadyState,
editors::{settings_editor::ProjectSettings, tags::Tag},
llm_integration::content_llm::{ContentAI, ReadyState},
util,
};
pub struct MainEditor {
pub content: ContentSection,
pub show_editor: bool,
pub editor_separate_window: bool,
pub show_preview: bool,
preview_cache: CommonMarkCache,
dialog: Option<ContentAI>,
@@ -25,6 +26,7 @@ impl Clone for MainEditor {
content: self.content.clone(),
show_editor: self.show_editor,
editor_separate_window: self.editor_separate_window,
show_preview: self.show_preview,
preview_cache: CommonMarkCache::default(),
dialog: self.dialog.clone(),
@@ -57,158 +59,6 @@ pub struct ContentSection {
pub saved: bool,
}
#[derive(Clone)]
pub enum ContentAI {
Summarise {
open: bool,
content: String,
result: Arc<Mutex<String>>,
ready: Arc<Mutex<ReadyState>>,
},
Continue {
open: bool,
content: String,
instruction: String,
max_tokens: usize,
context_override: String,
result: Arc<Mutex<String>>,
ready: Arc<Mutex<ReadyState>>,
},
}
impl ContentAI {
pub fn ui(&mut self, ui: &mut egui::Ui, project: &mut ProjectContext) {
let mut is_open = *match self {
ContentAI::Summarise { open, .. } => open,
ContentAI::Continue { open, .. } => open,
};
if is_open {
egui::Window::new("AI Assistant")
.open(&mut is_open)
.show(ui.ctx(), |ui| match self {
ContentAI::Summarise {
content,
result,
ready,
..
} => {
egui::ScrollArea::vertical()
.auto_shrink([false, false])
.max_width(ui.available_width())
.show(ui, |ui| {
ui.add(
egui::TextEdit::multiline(content)
.frame(false)
.interactive(false),
);
});
ui.add(
egui::TextEdit::multiline(&mut *result.lock().unwrap())
.font(egui::TextStyle::Monospace)
.interactive(false)
.frame(false)
.lock_focus(true)
.hint_text("Summary will appear here..."),
);
if ui.button("Summarise").clicked() {
// Self::summarise(content, result.clone());
*ready.lock().unwrap() = ReadyState::Generating;
}
}
ContentAI::Continue {
content,
instruction,
max_tokens,
context_override,
result,
ready,
..
} => {
ui.weak("(The model will see current file content)");
ui.separator();
ui.add(
egui::TextEdit::multiline(instruction)
.frame(false)
.hint_text("Writing Instructions"),
);
ui.separator();
ui.add(
egui::TextEdit::multiline(context_override)
.frame(false)
.hint_text("Any additional context?"),
);
ui.separator();
ui.label("Max Tokens");
ui.add(egui::Slider::new(max_tokens, 1000..=1000000));
ui.separator();
if ui.button("Continue").clicked() {
let context_override = context_override.clone();
let content = content.clone();
let instruction = instruction.clone();
let max_tokens = *max_tokens;
let project = project.clone();
let ai_context = project.ai_context_prompt.clone();
let result = result.clone();
let ready = ready.clone();
std::thread::spawn(move || {
*ready.lock().unwrap() = ReadyState::Generating;
let result = crate::llm_integration::content_llm::continue_content(
if context_override.is_empty() {
ai_context
} else {
context_override
},
content,
instruction,
max_tokens,
project,
result,
);
if let Err(e) = result {
eprintln!("Error in content generation: {e}");
}
*ready.lock().unwrap() = ReadyState::Ready;
});
}
if *ready.lock().unwrap() == ReadyState::Generating {
ui.horizontal(|ui| {
ui.spinner();
ui.label("Generating...");
});
egui::ScrollArea::both()
.auto_shrink([false, false])
.max_width(ui.available_width())
.show(ui, |ui| {
ui.add(
egui::TextEdit::multiline(&mut *result.lock().unwrap())
.font(egui::TextStyle::Monospace)
.interactive(false)
.frame(false)
.desired_width(ui.available_width())
.lock_focus(true)
.hint_text("Content will appear here..."),
);
});
} else if *ready.lock().unwrap() == ReadyState::Idle {
ui.label("Idle");
}
}
});
}
match self {
ContentAI::Summarise { open, .. } => *open = is_open,
ContentAI::Continue { open, .. } => *open = is_open,
};
}
}
impl ContentSection {
pub fn new() -> Self {
Self {
@@ -257,6 +107,7 @@ impl MainEditor {
content: ContentSection::new(),
show_editor: false, // Start with editor hidden
show_preview: false,
editor_separate_window: false,
preview_cache: CommonMarkCache::default(),
dialog: None,
}
@@ -267,147 +118,167 @@ impl MainEditor {
content,
show_editor: true,
show_preview: false,
editor_separate_window: false,
preview_cache: CommonMarkCache::default(),
dialog: None,
}
}
pub fn ui(&mut self, ctx: &egui::Context, project: &mut ProjectContext) {
pub fn render_ui(&mut self, project: &mut ProjectSettings, ui: &mut egui::Ui) {
if let Some(dialog) = &mut self.dialog {
dialog.ui(ui, project);
match dialog {
ContentAI::Summarise { ready, result, .. } => {
if *ready.lock().unwrap() == ReadyState::Ready {
self.content.content.push_str(&result.lock().unwrap());
self.content.saved = false;
*ready.lock().unwrap() = ReadyState::Idle;
} else if *ready.lock().unwrap() == ReadyState::Halted {
*ready.lock().unwrap() = ReadyState::Idle;
}
}
ContentAI::Continue {
ready,
result,
content,
..
} => {
*content = self.content.content.clone();
if *ready.lock().unwrap() == ReadyState::Ready {
self.content.content.push_str(&result.lock().unwrap());
self.content.saved = false;
*ready.lock().unwrap() = ReadyState::Idle;
} else if *ready.lock().unwrap() == ReadyState::Halted {
*ready.lock().unwrap() = ReadyState::Idle;
}
}
}
}
ui.vertical(|ui| {
// check for Ctrl+S to save
if ui.input(|i| i.key_pressed(egui::Key::S) && i.modifiers.ctrl) {
if let Err(e) = self.content.save() {
eprintln!("Failed to save: {e}");
}
}
// display save state
util::saved_status(
ui,
self.content.saved,
&self.content.id,
&self.content.title,
);
// Save/Cancel buttons
ui.horizontal(|ui| {
// save button
if ui.button("Save").clicked() {
if let Err(e) = self.content.save() {
eprintln!("Failed to save: {e}");
}
}
// create copy button
if ui.button("Create Copy").clicked() {
let mut copy = self.clone();
copy.content.id = uuid::Uuid::new_v4().to_string();
copy.content.title = format!("{} (Copy)", self.content.title);
copy.content.save().unwrap();
}
// delete button
if ui.button("Delete").clicked() {
std::fs::remove_file(
PROJECT_FOLDER
.join("documents")
.join(format!("{}.json", self.content.id)),
)
.unwrap();
*self = Self::new();
}
// revert changes button
if ui.button("Revert changes").clicked() {
self.content = ContentSection::load(&self.content.id).unwrap();
}
// preview toggle
ui.checkbox(&mut self.show_preview, "Preview");
// editor toggle
ui.checkbox(&mut self.editor_separate_window, "Pop out editor");
});
});
ui.separator();
// Name and description grid
egui::Grid::new("top_grid")
.striped(true)
.num_columns(2)
.show(ui, |ui| {
ui.strong("Name");
if ui
.add(
TextEdit::singleline(&mut self.content.title)
.desired_width(f32::INFINITY)
.frame(false),
)
.changed()
{
self.content.saved = false;
}
ui.end_row();
ui.strong("Description");
if ui
.add(
TextEdit::singleline(&mut self.content.description)
.desired_width(f32::INFINITY)
.frame(false),
)
.changed()
{
self.content.saved = false;
}
ui.end_row();
ui.strong("Tags");
Tag::selector_ui(&mut self.content.tags, ui, Some(&mut self.content.saved));
ui.end_row();
});
ui.separator();
if self.show_preview {
self.preview_ui(ui);
}
self.editor_ui(ui, project);
}
pub fn ui(&mut self, ctx: &egui::Context, project: &mut ProjectSettings) {
// Show the editor window if enabled
let mut show = self.show_editor;
if show {
egui::Window::new("Markdown Editor")
.resizable(true)
.default_width(1000.0)
.default_height(800.0)
.open(&mut show)
.show(ctx, |ui| {
if let Some(dialog) = &mut self.dialog {
dialog.ui(ui, project);
match dialog {
ContentAI::Summarise { ready, result, .. } => {
if *ready.lock().unwrap() == ReadyState::Ready {
self.content.content.push_str(&result.lock().unwrap());
self.content.saved = false;
*ready.lock().unwrap() = ReadyState::Idle;
}
}
ContentAI::Continue { ready, result, .. } => {
if *ready.lock().unwrap() == ReadyState::Ready {
self.content.content.push_str(&result.lock().unwrap());
self.content.saved = false;
*ready.lock().unwrap() = ReadyState::Idle;
}
}
}
}
ui.vertical(|ui| {
// check for Ctrl+S to save
if ui.input(|i| i.key_pressed(egui::Key::S) && i.modifiers.ctrl) {
if let Err(e) = self.content.save() {
eprintln!("Failed to save: {e}");
}
}
// display save state
util::saved_status(
ui,
self.content.saved,
&self.content.id,
&self.content.title,
);
// Save/Cancel buttons
ui.horizontal(|ui| {
// save button
if ui.button("Save").clicked() {
if let Err(e) = self.content.save() {
eprintln!("Failed to save: {e}");
}
}
// create copy button
if ui.button("Create Copy").clicked() {
let mut copy = self.clone();
copy.content.id = uuid::Uuid::new_v4().to_string();
copy.content.title = format!("{} (Copy)", self.content.title);
copy.content.save().unwrap();
}
// delete button
if ui.button("Delete").clicked() {
std::fs::remove_file(
PROJECT_FOLDER
.join("documents")
.join(format!("{}.json", self.content.id)),
)
.unwrap();
*self = Self::new();
}
// revert changes button
if ui.button("Revert changes").clicked() {
self.content = ContentSection::load(&self.content.id).unwrap();
}
// preview toggle
ui.checkbox(&mut self.show_preview, "Preview");
});
if self.editor_separate_window {
egui::Window::new("Editor")
.resizable(true)
.default_width(1000.0)
.default_height(800.0)
.open(&mut show)
.show(ctx, |ui| {
self.render_ui(project, ui);
});
ui.separator();
// Name and description grid
egui::Grid::new("top_grid")
.striped(true)
.num_columns(2)
.show(ui, |ui| {
ui.strong("Name");
if ui
.add(
TextEdit::singleline(&mut self.content.title)
.desired_width(f32::INFINITY)
.frame(false),
)
.changed()
{
self.content.saved = false;
}
ui.end_row();
ui.strong("Description");
if ui
.add(
TextEdit::singleline(&mut self.content.description)
.desired_width(f32::INFINITY)
.frame(false),
)
.changed()
{
self.content.saved = false;
}
ui.end_row();
ui.strong("Tags");
Tag::selector_ui(
&mut self.content.tags,
ui,
Some(&mut self.content.saved),
);
ui.end_row();
});
ui.separator();
if self.show_preview {
self.preview_ui(ui);
}
self.editor_ui(ui, project);
} else {
egui::CentralPanel::default().show(ctx, |ui| {
self.render_ui(project, ui);
});
}
}
self.show_editor = show;
@@ -447,7 +318,7 @@ impl MainEditor {
});
}
fn editor_ui(&mut self, ui: &mut egui::Ui, project: &mut ProjectContext) {
fn editor_ui(&mut self, ui: &mut egui::Ui, project: &mut ProjectSettings) {
let _response = egui::ScrollArea::both()
.auto_shrink([false, false])
.id_salt("editor_scroll")
@@ -504,6 +375,8 @@ impl MainEditor {
result: Arc::new(Mutex::new(String::new())),
open: true,
ready: Arc::new(Mutex::new(ReadyState::Idle)),
temperature: 0.7,
model_override: "".to_string(),
});
}
});