This commit is contained in:
+186
-13
@@ -2,13 +2,18 @@ use egui::TextEdit;
|
||||
use egui_commonmark::{CommonMarkCache, CommonMarkViewer};
|
||||
use serde::{self, Deserialize, Serialize};
|
||||
|
||||
use crate::{PROJECT_FOLDER, editors::tags::Tag, llm_integration::content_llm::ai_enabled, util};
|
||||
use crate::{
|
||||
PROJECT_FOLDER,
|
||||
editors::{context_editor::ProjectContext, tags::Tag},
|
||||
util,
|
||||
};
|
||||
|
||||
pub struct MainEditor {
|
||||
pub content: ContentSection,
|
||||
pub show_editor: bool,
|
||||
pub show_preview: bool,
|
||||
preview_cache: CommonMarkCache,
|
||||
dialog: Option<ContentAI>,
|
||||
}
|
||||
|
||||
impl Clone for MainEditor {
|
||||
@@ -19,6 +24,7 @@ impl Clone for MainEditor {
|
||||
show_editor: self.show_editor,
|
||||
show_preview: self.show_preview,
|
||||
preview_cache: CommonMarkCache::default(),
|
||||
dialog: self.dialog.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -48,6 +54,143 @@ pub struct ContentSection {
|
||||
pub saved: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum ContentAI {
|
||||
Summarise {
|
||||
open: bool,
|
||||
content: String,
|
||||
result: String,
|
||||
ready: bool,
|
||||
},
|
||||
Continue {
|
||||
open: bool,
|
||||
content: String,
|
||||
instruction: String,
|
||||
max_tokens: usize,
|
||||
context_override: String,
|
||||
result: String,
|
||||
ready: bool,
|
||||
},
|
||||
}
|
||||
|
||||
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("ContentAI")
|
||||
.open(&mut is_open)
|
||||
.show(ui.ctx(), |ui| match self {
|
||||
ContentAI::Summarise {
|
||||
content,
|
||||
result,
|
||||
ready,
|
||||
..
|
||||
} => {
|
||||
egui::ScrollArea::vertical()
|
||||
.auto_shrink([false, false])
|
||||
.max_height(200.0)
|
||||
.max_width(ui.available_width())
|
||||
.show(ui, |ui| {
|
||||
ui.add(
|
||||
egui::TextEdit::multiline(content)
|
||||
.frame(false)
|
||||
.interactive(false),
|
||||
);
|
||||
});
|
||||
ui.add(
|
||||
egui::TextEdit::multiline(result)
|
||||
.font(egui::TextStyle::Monospace)
|
||||
.interactive(false)
|
||||
.frame(false)
|
||||
.lock_focus(true)
|
||||
.hint_text("Summary will appear here..."),
|
||||
);
|
||||
if ui.button("Summarise").clicked() {
|
||||
*result = Self::summarise(content).unwrap();
|
||||
*ready = true;
|
||||
}
|
||||
}
|
||||
ContentAI::Continue {
|
||||
content,
|
||||
instruction,
|
||||
max_tokens,
|
||||
context_override,
|
||||
result,
|
||||
ready,
|
||||
..
|
||||
} => {
|
||||
ui.label("Continue");
|
||||
ui.add(
|
||||
egui::TextEdit::multiline(instruction)
|
||||
.frame(false)
|
||||
.hint_text("Instruction"),
|
||||
);
|
||||
ui.add(
|
||||
egui::TextEdit::multiline(context_override)
|
||||
.frame(false)
|
||||
.hint_text("Any additional context?"),
|
||||
);
|
||||
|
||||
ui.label("Max Tokens");
|
||||
ui.add(egui::Slider::new(max_tokens, 1000..=1000000));
|
||||
|
||||
if ui.button("Continue").clicked() {
|
||||
match Self::continue_content(
|
||||
instruction,
|
||||
*max_tokens,
|
||||
context_override,
|
||||
project,
|
||||
content,
|
||||
) {
|
||||
Ok(str) => {
|
||||
*result = str;
|
||||
*ready = true;
|
||||
}
|
||||
Err(err) => {
|
||||
*result = format!("Error: {err}");
|
||||
*ready = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
match self {
|
||||
ContentAI::Summarise { open, .. } => *open = is_open,
|
||||
ContentAI::Continue { open, .. } => *open = is_open,
|
||||
};
|
||||
}
|
||||
|
||||
fn summarise(_content: &str) -> Result<String, Box<dyn std::error::Error>> {
|
||||
// crate::llm_integration::content_llm::summarise_content(content, result)
|
||||
Ok(String::new())
|
||||
}
|
||||
|
||||
fn continue_content(
|
||||
instruction: &str,
|
||||
max_tokens: usize,
|
||||
context_override: &str,
|
||||
project: &mut ProjectContext,
|
||||
content: &mut String,
|
||||
) -> Result<String, Box<dyn std::error::Error>> {
|
||||
crate::llm_integration::content_llm::continue_content(
|
||||
if context_override.is_empty() {
|
||||
&project.ai_context_prompt
|
||||
} else {
|
||||
context_override
|
||||
},
|
||||
content,
|
||||
instruction,
|
||||
max_tokens,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl ContentSection {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
@@ -97,6 +240,7 @@ impl MainEditor {
|
||||
show_editor: false, // Start with editor hidden
|
||||
show_preview: false,
|
||||
preview_cache: CommonMarkCache::default(),
|
||||
dialog: None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,10 +250,11 @@ impl MainEditor {
|
||||
show_editor: true,
|
||||
show_preview: false,
|
||||
preview_cache: CommonMarkCache::default(),
|
||||
dialog: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ui(&mut self, ctx: &egui::Context) {
|
||||
pub fn ui(&mut self, ctx: &egui::Context, project: &mut ProjectContext) {
|
||||
// Show the editor window if enabled
|
||||
let mut show = self.show_editor;
|
||||
if show {
|
||||
@@ -119,6 +264,27 @@ impl MainEditor {
|
||||
.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 {
|
||||
self.content.content.push_str(result.as_str());
|
||||
self.content.saved = false;
|
||||
*ready = false;
|
||||
}
|
||||
}
|
||||
ContentAI::Continue { ready, result, .. } => {
|
||||
if *ready {
|
||||
self.content.content.push_str(result.as_str());
|
||||
self.content.saved = false;
|
||||
*ready = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ui.vertical(|ui| {
|
||||
// check for Ctrl+S to save
|
||||
if ui.input(|i| i.key_pressed(egui::Key::S) && i.modifiers.ctrl) {
|
||||
@@ -222,7 +388,7 @@ impl MainEditor {
|
||||
self.preview_ui(ui);
|
||||
}
|
||||
|
||||
self.editor_ui(ui);
|
||||
self.editor_ui(ui, project);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -263,7 +429,7 @@ impl MainEditor {
|
||||
});
|
||||
}
|
||||
|
||||
fn editor_ui(&mut self, ui: &mut egui::Ui) {
|
||||
fn editor_ui(&mut self, ui: &mut egui::Ui, project: &mut ProjectContext) {
|
||||
let _response = egui::ScrollArea::both()
|
||||
.auto_shrink([false, false])
|
||||
.id_salt("editor_scroll")
|
||||
@@ -301,19 +467,26 @@ impl MainEditor {
|
||||
ctx_menu = true;
|
||||
|
||||
ui.menu_button("AI Actions", |ui| {
|
||||
ui.add_enabled_ui(ai_enabled(), |ui| {
|
||||
ui.add_enabled_ui(project.ai_enabled(), |ui| {
|
||||
if ui.button("Summarise").clicked() {
|
||||
println!("Summarise");
|
||||
self.dialog = Some(ContentAI::Summarise {
|
||||
result: String::new(),
|
||||
content: self.content.content.clone(),
|
||||
open: true,
|
||||
ready: false,
|
||||
});
|
||||
}
|
||||
|
||||
if ui.button("Continue").clicked() {
|
||||
let content = self.content.content.clone();
|
||||
let response =
|
||||
crate::llm_integration::content_llm::continue_content(
|
||||
&content, "", 1024,
|
||||
)
|
||||
.unwrap();
|
||||
self.content.content.push_str(&response);
|
||||
self.dialog = Some(ContentAI::Continue {
|
||||
content: self.content.content.clone(),
|
||||
instruction: String::new(),
|
||||
max_tokens: 1024,
|
||||
context_override: "".to_string(),
|
||||
result: String::new(),
|
||||
open: true,
|
||||
ready: false,
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,17 +1,27 @@
|
||||
use std::io::Read;
|
||||
|
||||
use chrono::NaiveDate;
|
||||
use egui::TextEdit;
|
||||
use egui_extras::DatePickerButton;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::PROJECT_FOLDER;
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
pub struct ProjectContext {
|
||||
date: NaiveDate,
|
||||
project_name: String,
|
||||
project_author: String,
|
||||
project_description: String,
|
||||
|
||||
// settings
|
||||
enable_ai: bool,
|
||||
llm_api_uri: String,
|
||||
llm_api_key: String,
|
||||
pub ai_context_prompt: String,
|
||||
|
||||
#[serde(skip)]
|
||||
pub open: bool,
|
||||
}
|
||||
|
||||
impl ProjectContext {
|
||||
@@ -64,8 +74,59 @@ impl ProjectContext {
|
||||
ui.add(DatePickerButton::new(&mut self.date));
|
||||
|
||||
ui.end_row();
|
||||
|
||||
ui.label("Enable AI");
|
||||
ui.checkbox(&mut self.enable_ai, "Enable AI");
|
||||
|
||||
ui.end_row();
|
||||
|
||||
ui.label("LLM API URI");
|
||||
ui.text_edit_singleline(&mut self.llm_api_uri);
|
||||
|
||||
ui.end_row();
|
||||
|
||||
ui.label("LLM API Key");
|
||||
ui.text_edit_singleline(&mut self.llm_api_key);
|
||||
|
||||
ui.end_row();
|
||||
|
||||
ui.label("AI Context Prompt");
|
||||
ui.add(TextEdit::multiline(&mut self.ai_context_prompt)
|
||||
.font(egui::TextStyle::Monospace)
|
||||
.interactive(true)
|
||||
.frame(false)
|
||||
.lock_focus(true)
|
||||
.hint_text("What is this project about? what should the LLM know when generating content for this project?"));
|
||||
ui.end_row();
|
||||
});
|
||||
}
|
||||
|
||||
pub fn ai_enabled(&mut self) -> bool {
|
||||
let client = reqwest::blocking::Client::new();
|
||||
|
||||
if self.enable_ai {
|
||||
return true;
|
||||
}
|
||||
|
||||
if client
|
||||
.get(self.llm_api_uri.clone() + "/v1/models")
|
||||
.send()
|
||||
.is_ok()
|
||||
{
|
||||
self.enable_ai = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
pub fn open(&mut self) {
|
||||
self.open = true;
|
||||
}
|
||||
|
||||
pub fn close(&mut self) {
|
||||
self.open = false;
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for ProjectContext {
|
||||
@@ -75,6 +136,13 @@ impl Default for ProjectContext {
|
||||
project_name: "New Project".to_string(),
|
||||
project_author: "Your Name".to_string(),
|
||||
project_description: "Description of your project".to_string(),
|
||||
|
||||
enable_ai: true,
|
||||
llm_api_uri: "http://localhost:1234".to_string(),
|
||||
llm_api_key: "".to_string(),
|
||||
ai_context_prompt: "".to_string(),
|
||||
|
||||
open: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ use serde::{Deserialize, Serialize};
|
||||
|
||||
pub fn continue_content(
|
||||
context: &str,
|
||||
previous_content: &str,
|
||||
instruction: &str,
|
||||
_max_tokens: usize,
|
||||
) -> Result<String, Box<dyn std::error::Error>> {
|
||||
@@ -19,11 +20,15 @@ pub fn continue_content(
|
||||
},
|
||||
Message {
|
||||
role: "user".to_string(),
|
||||
content: context.to_string(),
|
||||
content: format!("Context / General instructions: {context}"),
|
||||
},
|
||||
Message {
|
||||
role: "user".to_string(),
|
||||
content: format!("Instructions: {instruction}"),
|
||||
content: format!("Previous content: {previous_content}"),
|
||||
},
|
||||
Message {
|
||||
role: "user".to_string(),
|
||||
content: format!("Specific instructions: {instruction}"),
|
||||
},
|
||||
];
|
||||
|
||||
@@ -49,12 +54,6 @@ pub fn continue_content(
|
||||
Err("No response from model".into())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ai_enabled() -> bool {
|
||||
let client = reqwest::blocking::Client::new();
|
||||
client.get("http://localhost:1234/v1/models").send().is_ok()
|
||||
}
|
||||
|
||||
// Simple request structure
|
||||
#[derive(Serialize)]
|
||||
struct ChatRequest {
|
||||
|
||||
+18
-3
@@ -90,7 +90,7 @@ impl Interface {
|
||||
}
|
||||
}
|
||||
|
||||
fn render_top_panel(&self, ctx: &egui::Context) {
|
||||
fn render_top_panel(&mut self, ctx: &egui::Context) {
|
||||
// Top bar with actions
|
||||
egui::TopBottomPanel::top("top").show(ctx, |ui| {
|
||||
ui.horizontal(|ui| {
|
||||
@@ -99,9 +99,24 @@ impl Interface {
|
||||
ui.separator();
|
||||
|
||||
// version
|
||||
ui.label(VERSION)
|
||||
ui.label(VERSION);
|
||||
|
||||
// Settings
|
||||
if ui.button("Settings").clicked() {
|
||||
self.project.open();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
if self.project.open {
|
||||
let mut open = self.project.open;
|
||||
egui::Window::new("Settings")
|
||||
.open(&mut open)
|
||||
.show(ctx, |ui| {
|
||||
self.project.ui(ui);
|
||||
});
|
||||
self.project.open = open;
|
||||
}
|
||||
}
|
||||
|
||||
fn render_left_panel(&mut self, ctx: &egui::Context) {
|
||||
@@ -188,7 +203,7 @@ impl Interface {
|
||||
|
||||
// render main content area
|
||||
fn render_main_content(&mut self, ctx: &egui::Context) {
|
||||
self.editor.ui(ctx);
|
||||
self.editor.ui(ctx, &mut self.project);
|
||||
self.scene.ui(ctx, &mut self.explorer.objects());
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user