@pheem49/mint 1.6.2 → 1.6.4
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/.github/workflows/ci.yml +14 -0
- package/Cargo.lock +3 -3
- package/Cargo.toml +1 -1
- package/README.md +82 -21
- package/assets/Mint_CLI.png +0 -0
- package/assets/Mint_Desktop.png +0 -0
- package/assets/Mint_Web.png +0 -0
- package/assets/Mint_WorkSpace.png +0 -0
- package/assets/Setting.png +0 -0
- package/assets/bridges.svg +4 -0
- package/assets/chat.svg +3 -0
- package/assets/code.svg +4 -0
- package/assets/features.svg +3 -0
- package/assets/imagegen.svg +5 -0
- package/assets/live2d.svg +6 -0
- package/assets/memory.svg +5 -0
- package/assets/screencapture.svg +4 -0
- package/assets/setup.svg +3 -0
- package/assets/tools.svg +4 -0
- package/bin/mint +0 -0
- package/crates/mint-cli/src/agent.rs +6 -2
- package/crates/mint-cli/src/main.rs +145 -5
- package/crates/mint-cli/src/onboard.rs +134 -1
- package/crates/mint-core/src/api_server.rs +108 -3
- package/crates/mint-core/src/config.rs +20 -1
- package/crates/mint-core/src/image_gen.rs +895 -0
- package/crates/mint-core/src/lib.rs +4 -0
- package/package.json +3 -3
- package/src/bin/index.js +0 -0
- package/src/renderer/index-web.html +1 -1
- package/src/renderer/index.html +1 -1
- package/src/renderer/src/components/ChatPanel.tsx +252 -26
- package/src/renderer/src/components/DashboardSidebar.tsx +11 -3
- package/src/renderer/src/components/ImageStudioPanel.tsx +638 -0
- package/src/renderer/src/components/MintDashboard.tsx +14 -5
- package/src/renderer/src/components/SettingsWindow.tsx +29 -20
- package/src/renderer/src/css/image-studio.css +722 -0
- package/src/renderer/src/css/settings.css +13 -3
- package/src/renderer/src/css/styles.css +50 -26
- package/src/renderer/src/index.css +106 -0
- package/src/renderer/src/tauri.ts +123 -0
- package/src/renderer/src-web/components/ChatPanel.tsx +273 -25
- package/src/renderer/src-web/components/DashboardSidebar.tsx +11 -3
- package/src/renderer/src-web/components/ImageStudioPanel.tsx +638 -0
- package/src/renderer/src-web/components/MintDashboard.tsx +13 -4
- package/src/renderer/src-web/components/SettingsWindow.tsx +29 -20
- package/src/renderer/src-web/css/image-studio.css +722 -0
- package/src/renderer/src-web/css/settings.css +14 -4
- package/src/renderer/src-web/css/styles.css +50 -26
- package/src/renderer/src-web/index.css +106 -0
- package/src/renderer/src-web/tauri.ts +124 -0
- package/src-tauri/Cargo.toml +29 -0
- package/src-tauri/build.rs +3 -0
- package/src-tauri/capabilities/main.json +7 -0
- package/src-tauri/gen/schemas/acl-manifests.json +1 -0
- package/src-tauri/gen/schemas/capabilities.json +1 -0
- package/src-tauri/gen/schemas/desktop-schema.json +2412 -0
- package/src-tauri/gen/schemas/linux-schema.json +2412 -0
- package/src-tauri/src/browser.rs +141 -0
- package/src-tauri/src/desktop.rs +392 -0
- package/src-tauri/src/discord_rpc.rs +99 -0
- package/src-tauri/src/events.rs +70 -0
- package/src-tauri/src/headless.rs +222 -0
- package/src-tauri/src/integrations.rs +126 -0
- package/src-tauri/src/lib.rs +1076 -0
- package/src-tauri/src/main.rs +3 -0
- package/src-tauri/src/plugins.rs +16 -0
- package/src-tauri/src/proactive.rs +254 -0
- package/src-tauri/src/system.rs +250 -0
- package/src-tauri/src/updater.rs +148 -0
- package/src-tauri/src/webhooks.rs +255 -0
- package/src-tauri/src/workflows.rs +70 -0
- package/src-tauri/tauri.conf.json +48 -0
- package/tsconfig.json +1 -1
|
@@ -60,6 +60,24 @@ const HUGGINGFACE_MODEL_PRESETS: &[&str] = &[
|
|
|
60
60
|
"google/gemma-3-27b-it",
|
|
61
61
|
];
|
|
62
62
|
|
|
63
|
+
// ── Image Generation Providers ──────────────────────────────────────────────
|
|
64
|
+
const NANOBANANA_IMAGE_MODEL_PRESETS: &[&str] =
|
|
65
|
+
&["gemini-2.5-flash-image", "gemini-2.0-flash-image"];
|
|
66
|
+
|
|
67
|
+
const DALLE_MODEL_PRESETS: &[&str] = &["dall-e-3", "gpt-image-1", "dall-e-2"];
|
|
68
|
+
|
|
69
|
+
const STABILITY_MODEL_PRESETS: &[&str] =
|
|
70
|
+
&["sd3.5-large", "sd3.5-large-turbo", "sd3-medium", "core"];
|
|
71
|
+
|
|
72
|
+
const IDEOGRAM_MODEL_PRESETS: &[&str] = &["V_3", "V_2", "V_2_TURBO"];
|
|
73
|
+
|
|
74
|
+
const REPLICATE_MODEL_PRESETS: &[&str] = &[
|
|
75
|
+
"black-forest-labs/flux-1.1-pro",
|
|
76
|
+
"black-forest-labs/flux-schnell",
|
|
77
|
+
"stability-ai/sdxl",
|
|
78
|
+
"bytedance/sdxl-lightning-4step",
|
|
79
|
+
];
|
|
80
|
+
|
|
63
81
|
pub async fn run() -> Result<()> {
|
|
64
82
|
let mut config = load_config()?;
|
|
65
83
|
|
|
@@ -231,6 +249,37 @@ pub async fn run() -> Result<()> {
|
|
|
231
249
|
.and_then(|v| v.as_bool())
|
|
232
250
|
.unwrap_or(false),
|
|
233
251
|
},
|
|
252
|
+
// ── Image Generation ─────────────────────────────────────────────────
|
|
253
|
+
OnboardService {
|
|
254
|
+
category: "Image Generation",
|
|
255
|
+
name: "Google NanoBanana (Gemini Images) [uses Gemini key]",
|
|
256
|
+
key: "img_nanobanana",
|
|
257
|
+
enabled: !config.api_key.is_empty() && !config.nanobanana_model.is_empty(),
|
|
258
|
+
},
|
|
259
|
+
OnboardService {
|
|
260
|
+
category: "Image Generation",
|
|
261
|
+
name: "OpenAI DALL·E [uses OpenAI key]",
|
|
262
|
+
key: "img_dalle",
|
|
263
|
+
enabled: !config.openai_api_key.is_empty() && !config.dalle_model.is_empty(),
|
|
264
|
+
},
|
|
265
|
+
OnboardService {
|
|
266
|
+
category: "Image Generation",
|
|
267
|
+
name: "Stability AI (Stable Diffusion)",
|
|
268
|
+
key: "img_stability",
|
|
269
|
+
enabled: !config.stability_api_key.is_empty(),
|
|
270
|
+
},
|
|
271
|
+
OnboardService {
|
|
272
|
+
category: "Image Generation",
|
|
273
|
+
name: "Ideogram v3",
|
|
274
|
+
key: "img_ideogram",
|
|
275
|
+
enabled: !config.ideogram_api_key.is_empty(),
|
|
276
|
+
},
|
|
277
|
+
OnboardService {
|
|
278
|
+
category: "Image Generation",
|
|
279
|
+
name: "Replicate (FLUX / SDXL / custom)",
|
|
280
|
+
key: "img_replicate",
|
|
281
|
+
enabled: !config.replicate_api_key.is_empty(),
|
|
282
|
+
},
|
|
234
283
|
];
|
|
235
284
|
|
|
236
285
|
let mut cursor = 0;
|
|
@@ -842,7 +891,91 @@ pub async fn run() -> Result<()> {
|
|
|
842
891
|
);
|
|
843
892
|
}
|
|
844
893
|
|
|
845
|
-
|
|
894
|
+
// ────────────────────────────────────────────────────────────────────────
|
|
895
|
+
// Image Generation providers
|
|
896
|
+
// ────────────────────────────────────────────────────────────────────────
|
|
897
|
+
|
|
898
|
+
// NanoBanana (Gemini Images)
|
|
899
|
+
if is_selected("img_nanobanana", &services) {
|
|
900
|
+
println!("\n\x1b[36m--- Google NanoBanana (Gemini Images) ---\x1b[0m");
|
|
901
|
+
println!(
|
|
902
|
+
"\x1b[90mUses the same Gemini API key as Step 1. Select the image generation model.\x1b[0m"
|
|
903
|
+
);
|
|
904
|
+
config.nanobanana_model = prompt_select_or_custom(
|
|
905
|
+
"NanoBanana Model",
|
|
906
|
+
static_model_options(NANOBANANA_IMAGE_MODEL_PRESETS),
|
|
907
|
+
Some(&config.nanobanana_model),
|
|
908
|
+
"Custom NanoBanana model...",
|
|
909
|
+
)?;
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
// DALL·E
|
|
913
|
+
if is_selected("img_dalle", &services) {
|
|
914
|
+
println!("\n\x1b[36m--- OpenAI DALL·E ---\x1b[0m");
|
|
915
|
+
println!(
|
|
916
|
+
"\x1b[90mUses the same OpenAI API key. DALL·E 3 supports only 1 image per request; DALL·E 2 supports up to 10.\x1b[0m"
|
|
917
|
+
);
|
|
918
|
+
config.dalle_model = prompt_select_or_custom(
|
|
919
|
+
"DALL·E Model",
|
|
920
|
+
static_model_options(DALLE_MODEL_PRESETS),
|
|
921
|
+
Some(&config.dalle_model),
|
|
922
|
+
"Custom DALL·E model...",
|
|
923
|
+
)?;
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
// Stability AI
|
|
927
|
+
if is_selected("img_stability", &services) {
|
|
928
|
+
println!("\n\x1b[36m--- Stability AI ---\x1b[0m");
|
|
929
|
+
println!(
|
|
930
|
+
"\x1b[90mGet your API key at https://platform.stability.ai/. Supports SD3.5 Large, SD3 Medium, and Stable Image Core.\x1b[0m"
|
|
931
|
+
);
|
|
932
|
+
config.stability_api_key =
|
|
933
|
+
prompt_sensitive("Stability AI API Key", &config.stability_api_key)?;
|
|
934
|
+
config.stability_model = prompt_select_or_custom(
|
|
935
|
+
"Stability Model",
|
|
936
|
+
static_model_options(STABILITY_MODEL_PRESETS),
|
|
937
|
+
Some(&config.stability_model),
|
|
938
|
+
"Custom Stability model...",
|
|
939
|
+
)?;
|
|
940
|
+
} else {
|
|
941
|
+
config.stability_api_key = String::new();
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
// Ideogram
|
|
945
|
+
if is_selected("img_ideogram", &services) {
|
|
946
|
+
println!("\n\x1b[36m--- Ideogram ---\x1b[0m");
|
|
947
|
+
println!(
|
|
948
|
+
"\x1b[90mGet your API key at https://ideogram.ai/api. Supports V_3, V_2, and V_2_TURBO.\x1b[0m"
|
|
949
|
+
);
|
|
950
|
+
config.ideogram_api_key = prompt_sensitive("Ideogram API Key", &config.ideogram_api_key)?;
|
|
951
|
+
config.ideogram_model = prompt_select_or_custom(
|
|
952
|
+
"Ideogram Model",
|
|
953
|
+
static_model_options(IDEOGRAM_MODEL_PRESETS),
|
|
954
|
+
Some(&config.ideogram_model),
|
|
955
|
+
"Custom Ideogram model...",
|
|
956
|
+
)?;
|
|
957
|
+
} else {
|
|
958
|
+
config.ideogram_api_key = String::new();
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
// Replicate
|
|
962
|
+
if is_selected("img_replicate", &services) {
|
|
963
|
+
println!("\n\x1b[36m--- Replicate ---\x1b[0m");
|
|
964
|
+
println!(
|
|
965
|
+
"\x1b[90mGet your API token at https://replicate.com/account/api-tokens. Works with FLUX, SDXL, and any public image model.\x1b[0m"
|
|
966
|
+
);
|
|
967
|
+
config.replicate_api_key =
|
|
968
|
+
prompt_sensitive("Replicate API Token", &config.replicate_api_key)?;
|
|
969
|
+
config.replicate_model = prompt_select_or_custom(
|
|
970
|
+
"Replicate Model (owner/model-name)",
|
|
971
|
+
static_model_options(REPLICATE_MODEL_PRESETS),
|
|
972
|
+
Some(&config.replicate_model),
|
|
973
|
+
"Custom Replicate model...",
|
|
974
|
+
)?;
|
|
975
|
+
} else {
|
|
976
|
+
config.replicate_api_key = String::new();
|
|
977
|
+
}
|
|
978
|
+
|
|
846
979
|
save_config(&config)?;
|
|
847
980
|
println!("\x1b[32m✅ Configuration saved successfully!\x1b[0m");
|
|
848
981
|
Ok(())
|
|
@@ -11,9 +11,10 @@ use tokio::net::TcpListener;
|
|
|
11
11
|
|
|
12
12
|
use crate::{
|
|
13
13
|
AgentProgress, ApprovalOutcome, ChatRequest, ChatResponse, DEFAULT_CONVERSATION_ID,
|
|
14
|
-
MemoryStore, MintConfig, config_path, create_folder, find_paths,
|
|
15
|
-
load_config, orchestrate_agent_loop,
|
|
16
|
-
orchestrate_chat_with_fallback, save_chat_images,
|
|
14
|
+
ImageGenRequest, MemoryStore, MintConfig, config_path, create_folder, find_paths,
|
|
15
|
+
generate_images, list_saved_pictures, load_config, orchestrate_agent_loop,
|
|
16
|
+
orchestrate_chat_stream_with_fallback, orchestrate_chat_with_fallback, save_chat_images,
|
|
17
|
+
save_config, weather,
|
|
17
18
|
};
|
|
18
19
|
|
|
19
20
|
const MAX_API_REQUEST_BYTES: usize = 32 * 1024 * 1024;
|
|
@@ -663,6 +664,110 @@ pub async fn start_api_server(port: u16) -> Result<(), std::io::Error> {
|
|
|
663
664
|
)
|
|
664
665
|
.await;
|
|
665
666
|
}
|
|
667
|
+
("POST", "/api/image-generate") => {
|
|
668
|
+
#[derive(serde::Deserialize)]
|
|
669
|
+
#[serde(rename_all = "camelCase")]
|
|
670
|
+
struct ImageGenApiRequest {
|
|
671
|
+
prompt: String,
|
|
672
|
+
#[serde(default)]
|
|
673
|
+
negative_prompt: Option<String>,
|
|
674
|
+
#[serde(default)]
|
|
675
|
+
aspect_ratio: Option<String>,
|
|
676
|
+
#[serde(default)]
|
|
677
|
+
num_images: Option<u8>,
|
|
678
|
+
#[serde(default)]
|
|
679
|
+
model: Option<String>,
|
|
680
|
+
/// Which image provider to use (overrides config.image_gen_provider).
|
|
681
|
+
#[serde(default)]
|
|
682
|
+
provider: Option<String>,
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
if let Ok(req) = serde_json::from_str::<ImageGenApiRequest>(body) {
|
|
686
|
+
let config = load_config().unwrap_or_default();
|
|
687
|
+
let gen_request = ImageGenRequest {
|
|
688
|
+
prompt: req.prompt.clone(),
|
|
689
|
+
negative_prompt: req.negative_prompt,
|
|
690
|
+
aspect_ratio: req.aspect_ratio,
|
|
691
|
+
num_images: req.num_images,
|
|
692
|
+
model: req.model,
|
|
693
|
+
provider: req.provider,
|
|
694
|
+
};
|
|
695
|
+
match generate_images(&config, &gen_request).await {
|
|
696
|
+
Ok(result) => {
|
|
697
|
+
let data_uris: Vec<String> = result
|
|
698
|
+
.images
|
|
699
|
+
.iter()
|
|
700
|
+
.map(|img| img.data_uri.clone())
|
|
701
|
+
.collect();
|
|
702
|
+
let mut saved = save_chat_images(
|
|
703
|
+
data_uris,
|
|
704
|
+
Some(result.provider.clone()),
|
|
705
|
+
Some(req.prompt.clone()),
|
|
706
|
+
)
|
|
707
|
+
.unwrap_or_default();
|
|
708
|
+
for picture in &mut saved {
|
|
709
|
+
picture.url =
|
|
710
|
+
Some(format!("/api/pictures/{}", picture.filename));
|
|
711
|
+
picture.thumbnail_url =
|
|
712
|
+
Some(format!("/api/pictures/{}", picture.filename));
|
|
713
|
+
}
|
|
714
|
+
let response = json!({
|
|
715
|
+
"images": saved,
|
|
716
|
+
"model": result.model,
|
|
717
|
+
"provider": result.provider,
|
|
718
|
+
"prompt": result.prompt,
|
|
719
|
+
"description": result.description
|
|
720
|
+
});
|
|
721
|
+
send_json_response(socket, "200 OK", &response.to_string()).await;
|
|
722
|
+
}
|
|
723
|
+
Err(e) => {
|
|
724
|
+
let err = json!({ "error": e.to_string() });
|
|
725
|
+
send_json_response(
|
|
726
|
+
socket,
|
|
727
|
+
"500 Internal Server Error",
|
|
728
|
+
&err.to_string(),
|
|
729
|
+
)
|
|
730
|
+
.await;
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
} else {
|
|
734
|
+
send_json_response(
|
|
735
|
+
socket,
|
|
736
|
+
"400 Bad Request",
|
|
737
|
+
"{\"error\":\"invalid image generation request body\"}",
|
|
738
|
+
)
|
|
739
|
+
.await;
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
("GET", "/api/image-gen/providers") => {
|
|
743
|
+
let config = load_config().unwrap_or_default();
|
|
744
|
+
let mut available: Vec<String> = Vec::new();
|
|
745
|
+
if !config.api_key.trim().is_empty() {
|
|
746
|
+
available.push("nanobanana".into());
|
|
747
|
+
}
|
|
748
|
+
if !config.openai_api_key.trim().is_empty() {
|
|
749
|
+
available.push("dalle".into());
|
|
750
|
+
}
|
|
751
|
+
if !config.stability_api_key.trim().is_empty() {
|
|
752
|
+
available.push("stability".into());
|
|
753
|
+
}
|
|
754
|
+
if !config.ideogram_api_key.trim().is_empty() {
|
|
755
|
+
available.push("ideogram".into());
|
|
756
|
+
}
|
|
757
|
+
if !config.replicate_api_key.trim().is_empty() {
|
|
758
|
+
available.push("replicate".into());
|
|
759
|
+
}
|
|
760
|
+
let active = if available.contains(&config.image_gen_provider) {
|
|
761
|
+
config.image_gen_provider.clone()
|
|
762
|
+
} else {
|
|
763
|
+
available
|
|
764
|
+
.first()
|
|
765
|
+
.cloned()
|
|
766
|
+
.unwrap_or_else(|| "nanobanana".into())
|
|
767
|
+
};
|
|
768
|
+
let response = json!({ "active": active, "available": available });
|
|
769
|
+
send_json_response(socket, "200 OK", &response.to_string()).await;
|
|
770
|
+
}
|
|
666
771
|
_ => {
|
|
667
772
|
send_json_response(socket, "404 Not Found", "{\"error\":\"Not Found\"}").await;
|
|
668
773
|
}
|
|
@@ -60,6 +60,16 @@ pub struct MintConfig {
|
|
|
60
60
|
pub local_model_name: String,
|
|
61
61
|
pub ollama_host: String,
|
|
62
62
|
pub ollama_model: String,
|
|
63
|
+
pub nanobanana_model: String,
|
|
64
|
+
/// Active image-generation provider: "nanobanana" | "dalle" | "stability" | "ideogram" | "replicate"
|
|
65
|
+
pub image_gen_provider: String,
|
|
66
|
+
pub dalle_model: String,
|
|
67
|
+
pub stability_api_key: String,
|
|
68
|
+
pub stability_model: String,
|
|
69
|
+
pub ideogram_api_key: String,
|
|
70
|
+
pub ideogram_model: String,
|
|
71
|
+
pub replicate_api_key: String,
|
|
72
|
+
pub replicate_model: String,
|
|
63
73
|
pub show_desktop_widget: bool,
|
|
64
74
|
pub safety_enabled: bool,
|
|
65
75
|
pub sandbox_mode: String,
|
|
@@ -89,7 +99,7 @@ impl Default for MintConfig {
|
|
|
89
99
|
];
|
|
90
100
|
Self {
|
|
91
101
|
theme: "dark".into(),
|
|
92
|
-
accent_color: "#
|
|
102
|
+
accent_color: "#10b981".into(),
|
|
93
103
|
language: "th-TH".into(),
|
|
94
104
|
assistant_mode: "chat".into(),
|
|
95
105
|
ai_provider: "gemini".into(),
|
|
@@ -109,6 +119,15 @@ impl Default for MintConfig {
|
|
|
109
119
|
local_model_name: "local-model".into(),
|
|
110
120
|
ollama_host: String::new(),
|
|
111
121
|
ollama_model: "llama3:latest".into(),
|
|
122
|
+
nanobanana_model: "gemini-2.5-flash-image".into(),
|
|
123
|
+
image_gen_provider: "nanobanana".into(),
|
|
124
|
+
dalle_model: "dall-e-3".into(),
|
|
125
|
+
stability_api_key: String::new(),
|
|
126
|
+
stability_model: "sd3.5-large".into(),
|
|
127
|
+
ideogram_api_key: String::new(),
|
|
128
|
+
ideogram_model: "V_3".into(),
|
|
129
|
+
replicate_api_key: String::new(),
|
|
130
|
+
replicate_model: "black-forest-labs/flux-1.1-pro".into(),
|
|
112
131
|
show_desktop_widget: true,
|
|
113
132
|
safety_enabled: true,
|
|
114
133
|
sandbox_mode: "prefer".into(),
|