@pheem49/mint 1.7.0 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Cargo.lock +3 -3
- package/Cargo.toml +1 -1
- package/README.md +9 -0
- package/crates/mint-cli/src/agent.rs +1 -1
- package/crates/mint-cli/src/main.rs +47 -24
- package/crates/mint-cli/src/setup.rs +1 -1
- package/crates/mint-core/src/api_server.rs +64 -10
- package/crates/mint-core/src/chat.rs +79 -0
- package/crates/mint-core/src/lib.rs +1 -1
- package/crates/mint-core/src/plugins.rs +4 -1
- package/crates/mint-core/src/workflows.rs +8 -0
- package/package.json +13 -2
- package/src/renderer/src/App.tsx +1 -1
- package/src/renderer/src/components/ChatPanel.tsx +27 -14
- package/src/renderer/src/components/DashboardSidebar.tsx +71 -12
- package/src/renderer/src/components/ImageStudioPanel.tsx +7 -3
- package/src/renderer/src/components/MintDashboard.tsx +199 -5
- package/src/renderer/src/components/PicturesLibrary.tsx +1 -3
- package/src/renderer/src/components/Settings/AudioTab.tsx +97 -0
- package/src/renderer/src/components/Settings/AutomationTab.tsx +195 -0
- package/src/renderer/src/components/Settings/GeneralTab.tsx +950 -0
- package/src/renderer/src/components/Settings/MemoryTab.tsx +60 -0
- package/src/renderer/src/components/Settings/PluginsTab.tsx +452 -0
- package/src/renderer/src/components/Settings/ShortcutsTab.tsx +30 -0
- package/src/renderer/src/components/Settings/ThemeTab.tsx +129 -0
- package/src/renderer/src/components/SettingsWindow.tsx +175 -962
- package/src/renderer/src/components/WorkflowBuilderPanel.tsx +536 -0
- package/src/renderer/src/components/WorkflowsWindow.tsx +46 -0
- package/src/renderer/src/css/image-studio.css +9 -4
- package/src/renderer/src/css/settings.css +75 -6
- package/src/renderer/src/css/styles.css +355 -38
- package/src/renderer/src/css/workflows.css +542 -0
- package/src/renderer/src/env.d.ts +2 -0
- package/src/renderer/src/index.css +1 -0
- package/src/renderer/src/tauri.ts +97 -0
- package/src/renderer/src-web/components/ChatPanel.tsx +27 -14
- package/src/renderer/src-web/components/DashboardSidebar.tsx +17 -1
- package/src/renderer/src-web/components/ImageStudioPanel.tsx +7 -3
- package/src/renderer/src-web/components/MintDashboard.tsx +189 -0
- package/src/renderer/src-web/components/PicturesLibrary.tsx +1 -3
- package/src/renderer/src-web/components/Settings/AudioTab.tsx +97 -0
- package/src/renderer/src-web/components/Settings/AutomationTab.tsx +196 -0
- package/src/renderer/src-web/components/Settings/GeneralTab.tsx +948 -0
- package/src/renderer/src-web/components/Settings/MemoryTab.tsx +60 -0
- package/src/renderer/src-web/components/Settings/PluginsTab.tsx +452 -0
- package/src/renderer/src-web/components/Settings/ShortcutsTab.tsx +30 -0
- package/src/renderer/src-web/components/Settings/ThemeTab.tsx +129 -0
- package/src/renderer/src-web/components/SettingsWindow.tsx +187 -968
- package/src/renderer/src-web/css/image-studio.css +9 -4
- package/src/renderer/src-web/css/settings.css +75 -6
- package/src/renderer/src-web/css/styles.css +294 -38
- package/src/renderer/src-web/env.d.ts +2 -0
- package/src/renderer/src-web/tauri.ts +36 -0
- package/src-tauri/src/lib.rs +84 -92
- package/src-tauri/tauri.conf.json +1 -1
package/src-tauri/src/lib.rs
CHANGED
|
@@ -11,7 +11,6 @@ mod updater;
|
|
|
11
11
|
mod webhooks;
|
|
12
12
|
mod workflows;
|
|
13
13
|
|
|
14
|
-
use base64::{Engine as _, engine::general_purpose::STANDARD as BASE64_STANDARD};
|
|
15
14
|
use browser::{
|
|
16
15
|
BrowserTab, click as browser_click, list_tabs as browser_list_tabs,
|
|
17
16
|
navigate as browser_navigate, read_page_text,
|
|
@@ -34,10 +33,10 @@ use mint_core::{
|
|
|
34
33
|
AgentApproval, AgentProgress, AppliedCodeEdit, ApprovalOutcome, ChatRequest, ChatResponse,
|
|
35
34
|
ChatSession, CodeEdit, CodeEditProposal, ImageGenRequest, InteractionMemory, MemoryStore,
|
|
36
35
|
MintConfig, PictureEntry, TtsUrl, WeatherReport, apply_code_edits, classify_shell_command,
|
|
37
|
-
config_path,
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
36
|
+
config_path, google_tts_urls, list_saved_pictures, load_config, load_workflows,
|
|
37
|
+
orchestrate_agent_loop, orchestrate_chat_stream_with_fallback, orchestrate_chat_with_fallback,
|
|
38
|
+
propose_code_edits, save_chat_images, save_config, save_workflows, start_channels, weather,
|
|
39
|
+
workflows_path,
|
|
41
40
|
};
|
|
42
41
|
use plugins::execute_plugin;
|
|
43
42
|
|
|
@@ -94,9 +93,6 @@ enum DesktopStreamEvent {
|
|
|
94
93
|
Chunk { chunk: String },
|
|
95
94
|
Progress { progress: AgentProgress },
|
|
96
95
|
}
|
|
97
|
-
|
|
98
|
-
const MAX_DOCUMENT_BYTES: usize = 10 * 1024 * 1024;
|
|
99
|
-
const MAX_DOCUMENT_CONTEXT_CHARS: usize = 30_000;
|
|
100
96
|
const WORKSPACE_TREE_MAX_DEPTH: usize = 9;
|
|
101
97
|
const WORKSPACE_TREE_MAX_CHILDREN: usize = 400;
|
|
102
98
|
const WORKSPACE_TREE_COLLAPSED_DIRS: &[&str] = &[
|
|
@@ -114,85 +110,6 @@ const WORKSPACE_TREE_COLLAPSED_DIRS: &[&str] = &[
|
|
|
114
110
|
"target",
|
|
115
111
|
];
|
|
116
112
|
|
|
117
|
-
fn request_with_document_context(
|
|
118
|
-
config: &MintConfig,
|
|
119
|
-
request: &ChatRequest,
|
|
120
|
-
) -> Result<ChatRequest, String> {
|
|
121
|
-
let Some(document) = &request.document_attachment else {
|
|
122
|
-
return Ok(request.clone());
|
|
123
|
-
};
|
|
124
|
-
|
|
125
|
-
let (mime_type, encoded) = document
|
|
126
|
-
.data_uri
|
|
127
|
-
.strip_prefix("data:")
|
|
128
|
-
.and_then(|payload| payload.split_once(";base64,"))
|
|
129
|
-
.ok_or_else(|| "invalid PDF attachment data URI".to_string())?;
|
|
130
|
-
|
|
131
|
-
if !mime_type.eq_ignore_ascii_case("application/pdf") {
|
|
132
|
-
return Err("only PDF document attachments are supported".into());
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
let bytes = BASE64_STANDARD
|
|
136
|
-
.decode(encoded)
|
|
137
|
-
.map_err(|error| format!("invalid PDF attachment encoding: {error}"))?;
|
|
138
|
-
if bytes.len() > MAX_DOCUMENT_BYTES {
|
|
139
|
-
return Err("PDF attachment is too large (> 10 MiB)".into());
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
let directory = config_path()
|
|
143
|
-
.map_err(|error| error.to_string())?
|
|
144
|
-
.with_file_name("Attachments");
|
|
145
|
-
fs::create_dir_all(&directory).map_err(|error| error.to_string())?;
|
|
146
|
-
let path = directory.join(format!(
|
|
147
|
-
"mint-pdf-{}-{}.pdf",
|
|
148
|
-
COUNTER.fetch_add(1, Ordering::SeqCst),
|
|
149
|
-
sanitize_attachment_name(&document.filename)
|
|
150
|
-
));
|
|
151
|
-
fs::write(&path, bytes).map_err(|error| error.to_string())?;
|
|
152
|
-
|
|
153
|
-
let extracted = extract_document_text(&path, config).map_err(|error| error.to_string());
|
|
154
|
-
let _ = fs::remove_file(&path);
|
|
155
|
-
let extracted = extracted?;
|
|
156
|
-
let context = truncate_document_context(&extracted);
|
|
157
|
-
let filename = document.filename.trim();
|
|
158
|
-
let filename = if filename.is_empty() {
|
|
159
|
-
"attached.pdf"
|
|
160
|
-
} else {
|
|
161
|
-
filename
|
|
162
|
-
};
|
|
163
|
-
|
|
164
|
-
let mut enriched = request.clone();
|
|
165
|
-
enriched.document_attachment = None;
|
|
166
|
-
enriched.message = format!(
|
|
167
|
-
"{}\n\nAttached PDF: {filename}\nUse the extracted PDF text below when answering. If the user asks for a summary, summarize this document.\n\n--- Extracted PDF text ---\n{context}\n--- End extracted PDF text ---",
|
|
168
|
-
request.message
|
|
169
|
-
);
|
|
170
|
-
Ok(enriched)
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
fn truncate_document_context(text: &str) -> String {
|
|
174
|
-
let mut output: String = text.chars().take(MAX_DOCUMENT_CONTEXT_CHARS).collect();
|
|
175
|
-
if text.chars().count() > MAX_DOCUMENT_CONTEXT_CHARS {
|
|
176
|
-
output.push_str("\n\n[PDF text truncated because it is long.]");
|
|
177
|
-
}
|
|
178
|
-
output
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
fn sanitize_attachment_name(filename: &str) -> String {
|
|
182
|
-
let sanitized: String = filename
|
|
183
|
-
.chars()
|
|
184
|
-
.filter(|character| {
|
|
185
|
-
character.is_ascii_alphanumeric() || matches!(character, '.' | '-' | '_')
|
|
186
|
-
})
|
|
187
|
-
.take(80)
|
|
188
|
-
.collect();
|
|
189
|
-
if sanitized.is_empty() {
|
|
190
|
-
"document".into()
|
|
191
|
-
} else {
|
|
192
|
-
sanitized
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
|
|
196
113
|
#[tauri::command]
|
|
197
114
|
fn get_runtime_status() -> Result<RuntimeStatus, String> {
|
|
198
115
|
let config = load_config().map_err(|error| error.to_string())?;
|
|
@@ -376,7 +293,7 @@ fn inspect_shell_command(command: String) -> mint_core::ShellClassification {
|
|
|
376
293
|
#[tauri::command]
|
|
377
294
|
async fn send_chat_message(app: AppHandle, request: ChatRequest) -> Result<ChatResponse, String> {
|
|
378
295
|
let config = load_config().map_err(|error| error.to_string())?;
|
|
379
|
-
let request =
|
|
296
|
+
let request = request.with_document_context(&config)?;
|
|
380
297
|
|
|
381
298
|
if request.message.starts_with("/chat ") {
|
|
382
299
|
let mut clean_request = request.clone();
|
|
@@ -456,7 +373,7 @@ async fn stream_chat_message(
|
|
|
456
373
|
on_event: Channel<DesktopStreamEvent>,
|
|
457
374
|
) -> Result<ChatResponse, String> {
|
|
458
375
|
let config = load_config().map_err(|error| error.to_string())?;
|
|
459
|
-
let request =
|
|
376
|
+
let request = request.with_document_context(&config)?;
|
|
460
377
|
|
|
461
378
|
if request.message.starts_with("/chat ") {
|
|
462
379
|
let mut clean_request = request.clone();
|
|
@@ -552,8 +469,7 @@ fn save_interaction_agent_activity(
|
|
|
552
469
|
interaction_id: i64,
|
|
553
470
|
activity: Vec<AgentProgress>,
|
|
554
471
|
) -> Result<(), String> {
|
|
555
|
-
let activity_json =
|
|
556
|
-
serde_json::to_string(&activity).map_err(|error| error.to_string())?;
|
|
472
|
+
let activity_json = serde_json::to_string(&activity).map_err(|error| error.to_string())?;
|
|
557
473
|
MemoryStore::open_default()
|
|
558
474
|
.and_then(|memory| {
|
|
559
475
|
memory
|
|
@@ -563,6 +479,20 @@ fn save_interaction_agent_activity(
|
|
|
563
479
|
.map_err(|error| error.to_string())
|
|
564
480
|
}
|
|
565
481
|
|
|
482
|
+
#[tauri::command]
|
|
483
|
+
fn save_system_interaction(
|
|
484
|
+
chat_id: String,
|
|
485
|
+
user_text: String,
|
|
486
|
+
provider: String,
|
|
487
|
+
model: String,
|
|
488
|
+
) -> Result<i64, String> {
|
|
489
|
+
MemoryStore::open_default()
|
|
490
|
+
.and_then(|memory| {
|
|
491
|
+
memory.add_interaction_for_chat(&chat_id, &user_text, "", &provider, &model)
|
|
492
|
+
})
|
|
493
|
+
.map_err(|error| error.to_string())
|
|
494
|
+
}
|
|
495
|
+
|
|
566
496
|
#[tauri::command]
|
|
567
497
|
fn get_recent_interactions(
|
|
568
498
|
limit: Option<usize>,
|
|
@@ -615,6 +545,54 @@ fn set_profile_value(key: String, value: String) -> Result<(), String> {
|
|
|
615
545
|
.map_err(|error| error.to_string())
|
|
616
546
|
}
|
|
617
547
|
|
|
548
|
+
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)]
|
|
549
|
+
#[serde(rename_all = "camelCase")]
|
|
550
|
+
struct LearnedSkillDto {
|
|
551
|
+
id: i64,
|
|
552
|
+
name: String,
|
|
553
|
+
source_path: String,
|
|
554
|
+
content: String,
|
|
555
|
+
updated_at: String,
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
#[tauri::command]
|
|
559
|
+
fn list_learned_skills() -> Result<Vec<LearnedSkillDto>, String> {
|
|
560
|
+
let store = MemoryStore::open_default().map_err(|e| e.to_string())?;
|
|
561
|
+
let skills = store.learned_skills(100).map_err(|e| e.to_string())?;
|
|
562
|
+
let dtos = skills
|
|
563
|
+
.into_iter()
|
|
564
|
+
.map(|s| LearnedSkillDto {
|
|
565
|
+
id: s.id,
|
|
566
|
+
name: s.name,
|
|
567
|
+
source_path: s.source_path,
|
|
568
|
+
content: s.content,
|
|
569
|
+
updated_at: s.created_at,
|
|
570
|
+
})
|
|
571
|
+
.collect();
|
|
572
|
+
Ok(dtos)
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
#[tauri::command]
|
|
576
|
+
fn add_learned_skill(name: String, content: String) -> Result<LearnedSkillDto, String> {
|
|
577
|
+
let store = MemoryStore::open_default().map_err(|e| e.to_string())?;
|
|
578
|
+
let skill = store
|
|
579
|
+
.add_learned_skill(&name, "ui_manual", &content)
|
|
580
|
+
.map_err(|e| e.to_string())?;
|
|
581
|
+
Ok(LearnedSkillDto {
|
|
582
|
+
id: skill.id,
|
|
583
|
+
name: skill.name,
|
|
584
|
+
source_path: skill.source_path,
|
|
585
|
+
content: skill.content,
|
|
586
|
+
updated_at: skill.created_at,
|
|
587
|
+
})
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
#[tauri::command]
|
|
591
|
+
fn delete_learned_skill(name: String) -> Result<usize, String> {
|
|
592
|
+
let store = MemoryStore::open_default().map_err(|e| e.to_string())?;
|
|
593
|
+
store.delete_learned_skill(&name).map_err(|e| e.to_string())
|
|
594
|
+
}
|
|
595
|
+
|
|
618
596
|
#[tauri::command]
|
|
619
597
|
fn clear_chat_history(chat_id: Option<String>) -> Result<usize, String> {
|
|
620
598
|
MemoryStore::open_default()
|
|
@@ -930,6 +908,15 @@ fn reload_custom_workflows() -> Result<Value, String> {
|
|
|
930
908
|
}))
|
|
931
909
|
}
|
|
932
910
|
|
|
911
|
+
#[tauri::command]
|
|
912
|
+
fn save_custom_workflows(workflows: Vec<serde_json::Value>) -> Result<ActionResult, String> {
|
|
913
|
+
save_workflows(&workflows).map_err(|error| error.to_string())?;
|
|
914
|
+
Ok(ActionResult {
|
|
915
|
+
success: true,
|
|
916
|
+
message: "workflows saved successfully".into(),
|
|
917
|
+
})
|
|
918
|
+
}
|
|
919
|
+
|
|
933
920
|
fn install_tray(app: &AppHandle) -> tauri::Result<()> {
|
|
934
921
|
let show = MenuItem::with_id(app, "show", "Show Mint", true, None::<&str>)?;
|
|
935
922
|
let settings = MenuItem::with_id(app, "settings", "Settings", true, None::<&str>)?;
|
|
@@ -1055,6 +1042,9 @@ pub fn run() {
|
|
|
1055
1042
|
get_profile_value,
|
|
1056
1043
|
set_profile_value,
|
|
1057
1044
|
clear_chat_history,
|
|
1045
|
+
list_learned_skills,
|
|
1046
|
+
add_learned_skill,
|
|
1047
|
+
delete_learned_skill,
|
|
1058
1048
|
list_pictures,
|
|
1059
1049
|
save_pictures,
|
|
1060
1050
|
open_folder,
|
|
@@ -1086,7 +1076,9 @@ pub fn run() {
|
|
|
1086
1076
|
run_next_queued_task,
|
|
1087
1077
|
exit_app,
|
|
1088
1078
|
open_workflows_file,
|
|
1089
|
-
reload_custom_workflows
|
|
1079
|
+
reload_custom_workflows,
|
|
1080
|
+
save_custom_workflows,
|
|
1081
|
+
save_system_interaction
|
|
1090
1082
|
])
|
|
1091
1083
|
.run(tauri::generate_context!())
|
|
1092
1084
|
.expect("error while running Mint desktop");
|