@pheem49/mint 1.9.0 → 1.10.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 +55 -3
- package/Cargo.toml +2 -1
- package/Release_Note.md +285 -140
- package/bin/mint +0 -0
- package/crates/mint-cli/Cargo.toml +1 -0
- package/crates/mint-cli/src/actions.rs +233 -0
- package/crates/mint-cli/src/agent.rs +287 -43
- package/crates/mint-cli/src/gmail.rs +22 -2
- package/crates/mint-cli/src/interactive.rs +2888 -0
- package/crates/mint-cli/src/main.rs +336 -2854
- package/crates/mint-cli/src/markdown.rs +294 -0
- package/crates/mint-cli/src/onboard.rs +44 -1
- package/crates/mint-cli/src/updater.rs +48 -6
- package/crates/mint-core/Cargo.toml +1 -0
- package/crates/mint-core/src/agent_loop.rs +1 -0
- package/crates/mint-core/src/api_server.rs +376 -13
- package/crates/mint-core/src/browser.rs +629 -101
- package/crates/mint-core/src/channels.rs +105 -6
- package/crates/mint-core/src/chat.rs +52 -4
- package/crates/mint-core/src/code_tools.rs +29 -11
- package/crates/mint-core/src/config.rs +118 -0
- package/crates/mint-core/src/image_gen.rs +294 -33
- package/crates/mint-core/src/lib.rs +5 -2
- package/crates/mint-core/src/mcp.rs +119 -6
- package/crates/mint-core/src/orchestration.rs +133 -11
- package/crates/mint-core/src/pictures.rs +64 -1
- package/crates/mint-core/src/skills.rs +82 -6
- package/crates/mint-core/src/video_gen.rs +235 -0
- package/package.json +8 -2
- package/shared-modules/luajit/luajit.json +2 -2
- package/skills-lock.json +41 -0
- package/src/renderer/index-web.html +1 -1
- package/src/renderer/index.html +1 -1
- package/src/renderer/shared/constants/slashCommands.ts +27 -0
- package/src/renderer/shared/platform.ts +3 -0
- package/src/renderer/shared/types.ts +4 -0
- package/src/renderer/shared/utils/approval.tsx +86 -7
- package/src/renderer/shared/utils/markdown.tsx +217 -88
- package/src/renderer/shared/utils/slashCommandProcessor.ts +239 -0
- package/src/renderer/src/components/ChatPanel.tsx +461 -64
- package/src/renderer/src/components/DashboardSidebar.tsx +11 -2
- package/src/renderer/src/components/ImageStudioPanel.tsx +87 -4
- package/src/renderer/src/components/MintDashboard.tsx +150 -5
- package/src/renderer/src/components/PicturesLibrary.tsx +156 -15
- package/src/renderer/src/components/Settings/AutomationTab.tsx +316 -18
- package/src/renderer/src/components/Settings/GeneralTab.tsx +762 -634
- package/src/renderer/src/components/Settings/PluginsTab.tsx +304 -70
- package/src/renderer/src/components/Settings/ThemeTab.tsx +6 -1
- package/src/renderer/src/components/SettingsWindow.tsx +38 -2
- package/src/renderer/src/components/VeoStudioPanel.tsx +549 -0
- package/src/renderer/src/css/image-studio.css +106 -0
- package/src/renderer/src/css/settings.css +158 -0
- package/src/renderer/src/css/styles.css +352 -1
- package/src/renderer/src/css/veo-studio.css +626 -0
- package/src/renderer/src/index.css +1 -0
- package/src/renderer/src/tauri.ts +144 -17
- package/src/renderer/src-web/components/ChatPanel.tsx +360 -88
- package/src/renderer/src-web/components/DashboardSidebar.tsx +16 -1
- package/src/renderer/src-web/components/ImageStudioPanel.tsx +87 -4
- package/src/renderer/src-web/components/MintDashboard.tsx +137 -4
- package/src/renderer/src-web/components/PicturesLibrary.tsx +155 -15
- package/src/renderer/src-web/components/Settings/AutomationTab.tsx +316 -18
- package/src/renderer/src-web/components/Settings/GeneralTab.tsx +762 -629
- package/src/renderer/src-web/components/Settings/PluginsTab.tsx +304 -70
- package/src/renderer/src-web/components/Settings/ThemeTab.tsx +6 -1
- package/src/renderer/src-web/components/SettingsWindow.tsx +35 -2
- package/src/renderer/src-web/components/VeoStudioPanel.tsx +549 -0
- package/src/renderer/src-web/css/image-studio.css +106 -0
- package/src/renderer/src-web/css/settings.css +158 -0
- package/src/renderer/src-web/css/styles.css +353 -1
- package/src/renderer/src-web/css/veo-studio.css +650 -0
- package/src/renderer/src-web/index.css +1 -0
- package/src/renderer/src-web/tauri.ts +135 -16
- package/src-tauri/src/lib.rs +114 -8
- package/src-tauri/src/webhooks.rs +45 -31
- package/src-tauri/src/workflows.rs +1 -0
- package/src-tauri/tauri.conf.json +1 -1
- package/temp_gridgeist/AGENTS.md +8 -0
- package/temp_gridgeist/CHANGELOG.md +130 -0
- package/temp_gridgeist/LICENSE +21 -0
- package/temp_gridgeist/README.md +323 -0
- package/temp_gridgeist/README.th.md +325 -0
- package/temp_gridgeist/skills/gridgeist/SKILL.md +88 -0
- package/temp_gridgeist/skills/gridgeist/agents/openai.yaml +9 -0
- package/temp_gridgeist/skills/gridgeist/assets/gridgeist-small.svg +10 -0
- package/temp_gridgeist/skills/gridgeist/assets/gridgeist.png +0 -0
- package/temp_gridgeist/skills/gridgeist/assets/gridgeist.svg +18 -0
- package/temp_gridgeist/skills/gridgeist/references/design-language.md +88 -0
- package/temp_gridgeist/skills/gridgeist/references/review-checklist.md +128 -0
- package/temp_gridgeist/skills/gridgeist/references/system-contract.md +109 -0
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
use base64::{Engine as _, engine::general_purpose::STANDARD};
|
|
2
|
+
use serde::{Deserialize, Serialize};
|
|
3
|
+
use serde_json::{Value, json};
|
|
4
|
+
use thiserror::Error;
|
|
5
|
+
|
|
6
|
+
use crate::{MintConfig, PictureEntry};
|
|
7
|
+
|
|
8
|
+
#[derive(Debug, Clone, Deserialize)]
|
|
9
|
+
#[serde(rename_all = "camelCase")]
|
|
10
|
+
pub struct VideoGenRequest {
|
|
11
|
+
pub prompt: String,
|
|
12
|
+
pub negative_prompt: Option<String>,
|
|
13
|
+
pub aspect_ratio: String,
|
|
14
|
+
pub duration: u32,
|
|
15
|
+
pub model: Option<String>,
|
|
16
|
+
pub provider: String,
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
#[derive(Debug, Clone, Serialize)]
|
|
20
|
+
#[serde(rename_all = "camelCase")]
|
|
21
|
+
pub struct VideoGenResponse {
|
|
22
|
+
pub videos: Vec<PictureEntry>,
|
|
23
|
+
pub model: String,
|
|
24
|
+
pub provider: String,
|
|
25
|
+
pub prompt: String,
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
#[derive(Debug, Error)]
|
|
29
|
+
pub enum VideoGenError {
|
|
30
|
+
#[error("missing Gemini API key — set GEMINI_API_KEY or configure api_key in config")]
|
|
31
|
+
MissingApiKey,
|
|
32
|
+
#[error("API request failed: {0}")]
|
|
33
|
+
Request(#[from] reqwest::Error),
|
|
34
|
+
#[error("unexpected response structure: {0}")]
|
|
35
|
+
UnexpectedResponse(String),
|
|
36
|
+
#[error("video generation timed out")]
|
|
37
|
+
Timeout,
|
|
38
|
+
#[error("failed to save generated video: {0}")]
|
|
39
|
+
SaveError(String),
|
|
40
|
+
#[error(
|
|
41
|
+
"Google Veo model endpoint returned 404 Not Found. Google Veo video generation requires an API Key or Google Cloud project with Veo Model access enabled: {0}"
|
|
42
|
+
)]
|
|
43
|
+
ModelNotFound(String),
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
pub async fn generate_video(
|
|
47
|
+
config: &MintConfig,
|
|
48
|
+
request: &VideoGenRequest,
|
|
49
|
+
) -> Result<VideoGenResponse, VideoGenError> {
|
|
50
|
+
let api_key = if config.api_key.trim().is_empty() {
|
|
51
|
+
std::env::var("GEMINI_API_KEY").unwrap_or_default()
|
|
52
|
+
} else {
|
|
53
|
+
config.api_key.clone()
|
|
54
|
+
};
|
|
55
|
+
if api_key.trim().is_empty() {
|
|
56
|
+
return Err(VideoGenError::MissingApiKey);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
let mut model_owned = request
|
|
60
|
+
.model
|
|
61
|
+
.as_deref()
|
|
62
|
+
.map(str::trim)
|
|
63
|
+
.filter(|m| !m.is_empty())
|
|
64
|
+
.map(str::to_owned)
|
|
65
|
+
.unwrap_or_else(|| {
|
|
66
|
+
config
|
|
67
|
+
.extra
|
|
68
|
+
.get("veoModel")
|
|
69
|
+
.and_then(|v| v.as_str())
|
|
70
|
+
.unwrap_or("veo-2.0-generate-001")
|
|
71
|
+
.to_string()
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
if model_owned == "veo-2.0-flash-exp" {
|
|
75
|
+
model_owned = "veo-2.0-generate-001".to_string();
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
let client = crate::HTTP_CLIENT.clone();
|
|
79
|
+
let mut url = format!(
|
|
80
|
+
"https://generativelanguage.googleapis.com/v1beta/models/{model_owned}:predictLongRunning"
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
let payload = json!({
|
|
84
|
+
"instances": [
|
|
85
|
+
{
|
|
86
|
+
"prompt": request.prompt
|
|
87
|
+
}
|
|
88
|
+
],
|
|
89
|
+
"parameters": {
|
|
90
|
+
"aspectRatio": request.aspect_ratio,
|
|
91
|
+
"durationSeconds": request.duration,
|
|
92
|
+
"sampleCount": 1
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
let mut response_res = client
|
|
97
|
+
.post(&url)
|
|
98
|
+
.header("x-goog-api-key", &api_key)
|
|
99
|
+
.json(&payload)
|
|
100
|
+
.send()
|
|
101
|
+
.await?;
|
|
102
|
+
|
|
103
|
+
if response_res.status() == reqwest::StatusCode::NOT_FOUND && model_owned != "veo-2.0-flash-001"
|
|
104
|
+
{
|
|
105
|
+
model_owned = "veo-2.0-flash-001".to_string();
|
|
106
|
+
url = format!(
|
|
107
|
+
"https://generativelanguage.googleapis.com/v1beta/models/{model_owned}:predictLongRunning"
|
|
108
|
+
);
|
|
109
|
+
response_res = client
|
|
110
|
+
.post(&url)
|
|
111
|
+
.header("x-goog-api-key", &api_key)
|
|
112
|
+
.json(&payload)
|
|
113
|
+
.send()
|
|
114
|
+
.await?;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if !response_res.status().is_success() {
|
|
118
|
+
let status = response_res.status();
|
|
119
|
+
let err_text = response_res.text().await.unwrap_or_default();
|
|
120
|
+
if status == reqwest::StatusCode::NOT_FOUND {
|
|
121
|
+
return Err(VideoGenError::ModelNotFound(format!(
|
|
122
|
+
"404 for model {model_owned} on generativelanguage.googleapis.com API. ({err_text})"
|
|
123
|
+
)));
|
|
124
|
+
}
|
|
125
|
+
return Err(VideoGenError::UnexpectedResponse(format!(
|
|
126
|
+
"HTTP {status}: {err_text}"
|
|
127
|
+
)));
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
let res: Value = response_res.json().await?;
|
|
131
|
+
|
|
132
|
+
let operation_name = res
|
|
133
|
+
.get("name")
|
|
134
|
+
.and_then(|v| v.as_str())
|
|
135
|
+
.ok_or_else(|| VideoGenError::UnexpectedResponse("Missing operation name".to_string()))?;
|
|
136
|
+
|
|
137
|
+
let poll_url = format!("https://generativelanguage.googleapis.com/v1beta/{operation_name}");
|
|
138
|
+
let mut done = false;
|
|
139
|
+
let mut final_response = None;
|
|
140
|
+
|
|
141
|
+
// Poll every 5 seconds, up to 60 times (5 minutes max)
|
|
142
|
+
for _ in 0..60 {
|
|
143
|
+
tokio::time::sleep(tokio::time::Duration::from_secs(5)).await;
|
|
144
|
+
let poll_res: Value = client
|
|
145
|
+
.get(&poll_url)
|
|
146
|
+
.header("x-goog-api-key", &api_key)
|
|
147
|
+
.send()
|
|
148
|
+
.await?
|
|
149
|
+
.error_for_status()?
|
|
150
|
+
.json()
|
|
151
|
+
.await?;
|
|
152
|
+
|
|
153
|
+
if let Some(d) = poll_res.get("done").and_then(|v| v.as_bool()) {
|
|
154
|
+
if d {
|
|
155
|
+
done = true;
|
|
156
|
+
final_response = Some(poll_res);
|
|
157
|
+
break;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if !done {
|
|
163
|
+
return Err(VideoGenError::Timeout);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
let op_res = final_response.ok_or(VideoGenError::Timeout)?;
|
|
167
|
+
|
|
168
|
+
// Check if error occurred in operation
|
|
169
|
+
if let Some(err) = op_res.get("error") {
|
|
170
|
+
let msg = err["message"].as_str().unwrap_or("unknown operation error");
|
|
171
|
+
return Err(VideoGenError::UnexpectedResponse(msg.to_string()));
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
let response_obj = op_res
|
|
175
|
+
.get("response")
|
|
176
|
+
.ok_or_else(|| VideoGenError::UnexpectedResponse("Missing response object".to_string()))?;
|
|
177
|
+
|
|
178
|
+
// Try to find generated samples or outputs
|
|
179
|
+
let generated_samples = response_obj
|
|
180
|
+
.get("generateVideoResponse")
|
|
181
|
+
.and_then(|gvr| gvr.get("generatedSamples"))
|
|
182
|
+
.or_else(|| {
|
|
183
|
+
response_obj
|
|
184
|
+
.get("outputs")
|
|
185
|
+
.and_then(|o| o.as_array()?.first()?.get("generatedVideos"))
|
|
186
|
+
})
|
|
187
|
+
.ok_or_else(|| {
|
|
188
|
+
VideoGenError::UnexpectedResponse(
|
|
189
|
+
"No generated samples or videos in response".to_string(),
|
|
190
|
+
)
|
|
191
|
+
})?;
|
|
192
|
+
|
|
193
|
+
let sample = generated_samples
|
|
194
|
+
.as_array()
|
|
195
|
+
.and_then(|arr| arr.first())
|
|
196
|
+
.ok_or_else(|| {
|
|
197
|
+
VideoGenError::UnexpectedResponse("Generated samples array is empty".to_string())
|
|
198
|
+
})?;
|
|
199
|
+
|
|
200
|
+
let video_uri = sample
|
|
201
|
+
.get("video")
|
|
202
|
+
.and_then(|v| v.get("uri"))
|
|
203
|
+
.and_then(|u| u.as_str())
|
|
204
|
+
.ok_or_else(|| VideoGenError::UnexpectedResponse("Missing video URI".to_string()))?;
|
|
205
|
+
|
|
206
|
+
// Download the video file bytes
|
|
207
|
+
let video_bytes = client
|
|
208
|
+
.get(video_uri)
|
|
209
|
+
.header("x-goog-api-key", &api_key)
|
|
210
|
+
.send()
|
|
211
|
+
.await?
|
|
212
|
+
.error_for_status()?
|
|
213
|
+
.bytes()
|
|
214
|
+
.await?
|
|
215
|
+
.to_vec();
|
|
216
|
+
|
|
217
|
+
// Convert to data URI for saving
|
|
218
|
+
let data_b64 = STANDARD.encode(&video_bytes);
|
|
219
|
+
let data_uri = format!("data:video/mp4;base64,{data_b64}");
|
|
220
|
+
|
|
221
|
+
// Save to the pictures directory and record index entry
|
|
222
|
+
let saved = crate::pictures::save_chat_images(
|
|
223
|
+
vec![data_uri],
|
|
224
|
+
Some("veo".to_string()),
|
|
225
|
+
Some(request.prompt.clone()),
|
|
226
|
+
)
|
|
227
|
+
.map_err(|e| VideoGenError::SaveError(e.to_string()))?;
|
|
228
|
+
|
|
229
|
+
Ok(VideoGenResponse {
|
|
230
|
+
videos: saved,
|
|
231
|
+
model: model_owned,
|
|
232
|
+
provider: "veo".to_string(),
|
|
233
|
+
prompt: request.prompt.clone(),
|
|
234
|
+
})
|
|
235
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pheem49/mint",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.10.0",
|
|
4
4
|
"description": "A native Tauri desktop AI assistant with a Rust backend and React UI.",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"dev:web": "vite --config vite.config.web.ts",
|
|
@@ -56,6 +56,7 @@
|
|
|
56
56
|
"mint": "src/bin/index.js"
|
|
57
57
|
},
|
|
58
58
|
"dependencies": {
|
|
59
|
+
"@pouyanafisi/gmail-mcp": "^2.0.0",
|
|
59
60
|
"@tauri-apps/api": "^2.0.0",
|
|
60
61
|
"@vitejs/plugin-react": "^6.0.1",
|
|
61
62
|
"@xyflow/react": "^12.11.1",
|
|
@@ -71,6 +72,11 @@
|
|
|
71
72
|
"@tauri-apps/cli": "^2.0.0"
|
|
72
73
|
},
|
|
73
74
|
"overrides": {
|
|
74
|
-
"gh-pages": "^5.0.0"
|
|
75
|
+
"gh-pages": "^5.0.0",
|
|
76
|
+
"@modelcontextprotocol/sdk": "^1.24.0",
|
|
77
|
+
"nodemailer": "^9.0.0",
|
|
78
|
+
"uuid": "^11.1.1",
|
|
79
|
+
"brace-expansion": "^5.0.8",
|
|
80
|
+
"@hono/node-server": "^2.0.5"
|
|
75
81
|
}
|
|
76
82
|
}
|
|
@@ -12,8 +12,8 @@
|
|
|
12
12
|
{
|
|
13
13
|
"type": "git",
|
|
14
14
|
"url": "https://github.com/openresty/luajit2.git",
|
|
15
|
-
"tag": "v2.1-
|
|
16
|
-
"commit": "
|
|
15
|
+
"tag": "v2.1-20260717",
|
|
16
|
+
"commit": "1e5c0db1a86c9d0645bdd05e88e3e7a295831074",
|
|
17
17
|
"x-checker-data": {
|
|
18
18
|
"type": "git",
|
|
19
19
|
"tag-pattern": "^v([\\d.-]+)$",
|
package/skills-lock.json
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 1,
|
|
3
|
+
"skills": {
|
|
4
|
+
"debug-mantra": {
|
|
5
|
+
"source": "thananon/9arm-skills",
|
|
6
|
+
"sourceType": "github",
|
|
7
|
+
"skillPath": "skills/engineering/debug-mantra/SKILL.md",
|
|
8
|
+
"computedHash": "7c519ffda61c6a44db8ef12f6a9ed06de920b9b4faa2e7b47b0032b9c78056e3"
|
|
9
|
+
},
|
|
10
|
+
"management-talk": {
|
|
11
|
+
"source": "thananon/9arm-skills",
|
|
12
|
+
"sourceType": "github",
|
|
13
|
+
"skillPath": "skills/productivity/management-talk/SKILL.md",
|
|
14
|
+
"computedHash": "65bb8510492db4e31f465e0ba4cc73570772d1dde75839c49993cd85a8437a54"
|
|
15
|
+
},
|
|
16
|
+
"post-mortem": {
|
|
17
|
+
"source": "thananon/9arm-skills",
|
|
18
|
+
"sourceType": "github",
|
|
19
|
+
"skillPath": "skills/engineering/post-mortem/SKILL.md",
|
|
20
|
+
"computedHash": "124d195540d43cc922ee93d398996ac8c4294528e92e4d3d6ad83deaf5cfe59a"
|
|
21
|
+
},
|
|
22
|
+
"qwen-agent": {
|
|
23
|
+
"source": "thananon/9arm-skills",
|
|
24
|
+
"sourceType": "github",
|
|
25
|
+
"skillPath": "skills/engineering/qwen-agent/SKILL.md",
|
|
26
|
+
"computedHash": "237e1a708655b68ab8066455d16b1e14ccba55ab682764e21843f2e47848f0a3"
|
|
27
|
+
},
|
|
28
|
+
"qwenchance": {
|
|
29
|
+
"source": "thananon/9arm-skills",
|
|
30
|
+
"sourceType": "github",
|
|
31
|
+
"skillPath": "skills/productivity/qwenchance/SKILL.md",
|
|
32
|
+
"computedHash": "116b0e8cc306b2ee891dd9a70160842528420153273e0109933224b49c22adf8"
|
|
33
|
+
},
|
|
34
|
+
"scrutinize": {
|
|
35
|
+
"source": "thananon/9arm-skills",
|
|
36
|
+
"sourceType": "github",
|
|
37
|
+
"skillPath": "skills/engineering/scrutinize/SKILL.md",
|
|
38
|
+
"computedHash": "d82a0b76617a00069f972cd0efd57c591e18b56f7aa0652aec63f6cb37cfd735"
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
<title>Mint Agent (Web)</title>
|
|
7
7
|
<link rel="icon" type="image/png" href="./assets/icon.png">
|
|
8
8
|
<!-- Load font families used in settings -->
|
|
9
|
-
<link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;500;600&family=Mali:wght@400;500;600&family=Prompt:wght@400;500&family=Sarabun:wght@400;500&display=swap" rel="stylesheet">
|
|
9
|
+
<link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;500;600&family=Mali:wght@400;500;600&family=Prompt:wght@400;500;600&family=Sarabun:wght@400;500;600&family=Kanit:wght@400;500;600&family=Mitr:wght@400;500&family=Noto+Sans+Thai:wght@400;500;600&family=Inter:wght@400;500;600&family=Fira+Code:wght@400;500&display=swap" rel="stylesheet">
|
|
10
10
|
<!-- Cubism must be available before the Live2D module is imported. -->
|
|
11
11
|
<script src="./Live2DCubismCore.js"></script>
|
|
12
12
|
</head>
|
package/src/renderer/index.html
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
<title>Mint Agent</title>
|
|
7
7
|
<link rel="icon" type="image/png" href="./assets/icon.png">
|
|
8
8
|
<!-- Load font families used in settings -->
|
|
9
|
-
<link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;500;600&family=Mali:wght@400;500;600&family=Prompt:wght@400;500&family=Sarabun:wght@400;500&display=swap" rel="stylesheet">
|
|
9
|
+
<link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;500;600&family=Mali:wght@400;500;600&family=Prompt:wght@400;500;600&family=Sarabun:wght@400;500;600&family=Kanit:wght@400;500;600&family=Mitr:wght@400;500&family=Noto+Sans+Thai:wght@400;500;600&family=Inter:wght@400;500;600&family=Fira+Code:wght@400;500&display=swap" rel="stylesheet">
|
|
10
10
|
<!-- Cubism must be available before the Live2D module is imported. -->
|
|
11
11
|
<script src="./Live2DCubismCore.js"></script>
|
|
12
12
|
</head>
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export interface SlashCommand {
|
|
2
|
+
command: string
|
|
3
|
+
description: string
|
|
4
|
+
usage?: string
|
|
5
|
+
category?: 'system' | 'workspace' | 'models' | 'tools'
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export const SLASH_COMMANDS: SlashCommand[] = [
|
|
9
|
+
{ command: '/cd', description: 'Change active workspace directory', usage: '/cd <path>', category: 'workspace' },
|
|
10
|
+
{ command: '/code', description: 'Run in code-agent mode', usage: '/code <task>', category: 'tools' },
|
|
11
|
+
{ command: '/fast', description: 'Toggle fast mode (hide thinking traces)', usage: '/fast [on|off]', category: 'system' },
|
|
12
|
+
{ command: '/help', description: 'Show interactive help menu', usage: '/help', category: 'system' },
|
|
13
|
+
{ command: '/image', description: 'Attach image from disk', usage: '/image [path] [prompt]', category: 'tools' },
|
|
14
|
+
{ command: '/image-provider', description: 'List or switch default image gen provider', usage: '/image-provider [name]', category: 'models' },
|
|
15
|
+
{ command: '/learn', description: 'Import persistent skill/instruction file', usage: '/learn [path]', category: 'tools' },
|
|
16
|
+
{ command: '/mcp', description: 'List configured MCP servers & status', usage: '/mcp [list|allow]', category: 'tools' },
|
|
17
|
+
{ command: '/memory', description: 'Manage long-term memory store', usage: '/memory [list|clear|get|set]', category: 'tools' },
|
|
18
|
+
{ command: '/models', description: 'List AI providers or switch active provider/model', usage: '/models [provider/model]', category: 'models' },
|
|
19
|
+
{ command: '/multi-agent', description: 'Toggle Multi-Agent Collaboration system', usage: '/multi-agent [on|off]', category: 'system' },
|
|
20
|
+
{ command: '/paste', description: 'Attach image from clipboard', usage: '/paste [prompt]', category: 'tools' },
|
|
21
|
+
{ command: '/plugins', description: 'List or generate plugins/skills', usage: '/plugins [name]', category: 'tools' },
|
|
22
|
+
{ command: '/skill add', description: 'Add or install global skill file or folder', usage: '/skill add <path>', category: 'tools' },
|
|
23
|
+
{ command: '/stats', description: 'Show session statistics', usage: '/stats', category: 'system' },
|
|
24
|
+
{ command: '/veo', description: 'Generate video using Google Veo', usage: '/veo <prompt>', category: 'tools' },
|
|
25
|
+
{ command: '/video-provider', description: 'List or switch default video gen provider', usage: '/video-provider [name]', category: 'models' },
|
|
26
|
+
]
|
|
27
|
+
|
|
@@ -28,6 +28,7 @@ export interface MintPlatformApi {
|
|
|
28
28
|
message: string,
|
|
29
29
|
imageDataUri?: string | null,
|
|
30
30
|
audioDataUri?: string | null,
|
|
31
|
+
videoDataUri?: string | null,
|
|
31
32
|
documentAttachment?: any | null,
|
|
32
33
|
workspacePath?: string | null,
|
|
33
34
|
chatId?: string | null,
|
|
@@ -38,6 +39,7 @@ export interface MintPlatformApi {
|
|
|
38
39
|
onChunk: (chunk: string) => void,
|
|
39
40
|
imageDataUri?: string | null,
|
|
40
41
|
audioDataUri?: string | null,
|
|
42
|
+
videoDataUri?: string | null,
|
|
41
43
|
systemInstruction?: string,
|
|
42
44
|
onProgress?: (progress: AgentProgress) => void,
|
|
43
45
|
documentAttachment?: any | null,
|
|
@@ -51,6 +53,7 @@ export interface MintPlatformApi {
|
|
|
51
53
|
saveSystemInteraction(
|
|
52
54
|
chatId: string,
|
|
53
55
|
userText: string,
|
|
56
|
+
aiText: string,
|
|
54
57
|
provider: string,
|
|
55
58
|
model: string,
|
|
56
59
|
): Promise<any>
|
|
@@ -8,6 +8,7 @@ export interface RuntimeStatus {
|
|
|
8
8
|
backend: string
|
|
9
9
|
configPath: string
|
|
10
10
|
activeProvider: string
|
|
11
|
+
activeModel?: string
|
|
11
12
|
availableProviders: string[]
|
|
12
13
|
integrations: Record<string, unknown>
|
|
13
14
|
localIp?: string
|
|
@@ -76,6 +77,9 @@ export interface ImageGenRequest {
|
|
|
76
77
|
numImages?: number
|
|
77
78
|
model?: string
|
|
78
79
|
provider?: string
|
|
80
|
+
imageDataUri?: string
|
|
81
|
+
maskDataUri?: string
|
|
82
|
+
mode?: 'generate' | 'edit' | 'inpaint'
|
|
79
83
|
}
|
|
80
84
|
|
|
81
85
|
export interface ImageGenProviders {
|
|
@@ -13,11 +13,30 @@ export interface ApprovalDetails {
|
|
|
13
13
|
isDangerous: boolean
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
+
function parseDiffStats(diffText?: string): string {
|
|
17
|
+
if (!diffText) return ''
|
|
18
|
+
let additions = 0
|
|
19
|
+
let deletions = 0
|
|
20
|
+
const lines = diffText.split('\n')
|
|
21
|
+
for (const line of lines) {
|
|
22
|
+
if (line.startsWith('+++ ') || line.startsWith('--- ')) continue
|
|
23
|
+
if (line.startsWith('+')) additions++
|
|
24
|
+
else if (line.startsWith('-')) deletions++
|
|
25
|
+
}
|
|
26
|
+
return `(+${additions} -${deletions})`
|
|
27
|
+
}
|
|
28
|
+
|
|
16
29
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
17
30
|
export function renderApprovalDetails(approval: any): ApprovalDetails {
|
|
18
31
|
if (!approval) return { title: 'Action Pending Approval', body: 'No action details available.', isDangerous: false }
|
|
19
|
-
if (approval.WriteFile)
|
|
20
|
-
|
|
32
|
+
if (approval.WriteFile) {
|
|
33
|
+
const stats = parseDiffStats(approval.WriteFile.diff)
|
|
34
|
+
return { title: 'Write File', body: `Path: ${approval.WriteFile.path}${stats ? ` ${stats}` : ''}`, reason: approval.WriteFile.diff ? `Diff:\n${approval.WriteFile.diff}` : 'Writing new file content.', isDangerous: false }
|
|
35
|
+
}
|
|
36
|
+
if (approval.ApplyPatch) {
|
|
37
|
+
const stats = parseDiffStats(approval.ApplyPatch.diff)
|
|
38
|
+
return { title: 'Apply Patch', body: `Path: ${approval.ApplyPatch.path}${stats ? ` ${stats}` : ''}`, reason: approval.ApplyPatch.diff ? `Diff:\n${approval.ApplyPatch.diff}` : 'Applying code patch.', isDangerous: false }
|
|
39
|
+
}
|
|
21
40
|
if (approval.RunShell) return { title: 'Run Shell Command', body: approval.RunShell.command, reason: 'Executing shell commands can modify your system.', isDangerous: true }
|
|
22
41
|
if (approval.NoteWrite) return { title: 'Write Note', body: `Path: ${approval.NoteWrite.path}`, reason: 'Creating or updating workspace notes.', isDangerous: false }
|
|
23
42
|
if (approval.RunPlugin) return { title: `Run Plugin: ${approval.RunPlugin.name}`, body: approval.RunPlugin.instruction, reason: 'Executing a native plugin action.', isDangerous: false }
|
|
@@ -30,9 +49,35 @@ export function renderApprovalDetails(approval: any): ApprovalDetails {
|
|
|
30
49
|
return { title: 'Unknown Action', body: JSON.stringify(approval, null, 2), reason: 'Requires approval to proceed.', isDangerous: false }
|
|
31
50
|
}
|
|
32
51
|
|
|
52
|
+
function parseHunkHeader(line: string): { oldStart: number; newStart: number } | null {
|
|
53
|
+
const trimmed = line.trim()
|
|
54
|
+
if (!trimmed.startsWith('@@')) return null
|
|
55
|
+
const parts = trimmed.split('@@')
|
|
56
|
+
if (parts.length < 3) return null
|
|
57
|
+
const headerBody = parts[1].trim()
|
|
58
|
+
let oldStart = 1
|
|
59
|
+
let newStart = 1
|
|
60
|
+
|
|
61
|
+
for (const token of headerBody.split(/\s+/)) {
|
|
62
|
+
if (token.startsWith('-')) {
|
|
63
|
+
const numStr = token.slice(1).split(',')[0]
|
|
64
|
+
const parsed = parseInt(numStr, 10)
|
|
65
|
+
if (!isNaN(parsed)) oldStart = parsed
|
|
66
|
+
} else if (token.startsWith('+')) {
|
|
67
|
+
const numStr = token.slice(1).split(',')[0]
|
|
68
|
+
const parsed = parseInt(numStr, 10)
|
|
69
|
+
if (!isNaN(parsed)) newStart = parsed
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return { oldStart, newStart }
|
|
73
|
+
}
|
|
74
|
+
|
|
33
75
|
export function renderDiff(diffText: string): ReactNode {
|
|
34
76
|
if (!diffText) return null
|
|
35
77
|
const lines = diffText.split('\n')
|
|
78
|
+
let currentOldLine = 1
|
|
79
|
+
let currentNewLine = 1
|
|
80
|
+
|
|
36
81
|
return (
|
|
37
82
|
<div style={{
|
|
38
83
|
background: '#0b0f19',
|
|
@@ -46,18 +91,52 @@ export function renderDiff(diffText: string): ReactNode {
|
|
|
46
91
|
lineHeight: '1.4',
|
|
47
92
|
}}>
|
|
48
93
|
{lines.map((line, idx) => {
|
|
94
|
+
if (line.startsWith('@@')) {
|
|
95
|
+
const hunk = parseHunkHeader(line)
|
|
96
|
+
if (hunk) {
|
|
97
|
+
currentOldLine = hunk.oldStart
|
|
98
|
+
currentNewLine = hunk.newStart
|
|
99
|
+
}
|
|
100
|
+
return (
|
|
101
|
+
<div key={idx} style={{ color: '#64748b', fontWeight: 'bold', padding: '2px 6px' }}>
|
|
102
|
+
{' ' + line}
|
|
103
|
+
</div>
|
|
104
|
+
)
|
|
105
|
+
}
|
|
106
|
+
if (line.startsWith('---') || line.startsWith('+++')) {
|
|
107
|
+
return (
|
|
108
|
+
<div key={idx} style={{ color: '#64748b', fontWeight: 'bold', padding: '2px 6px' }}>
|
|
109
|
+
{' ' + line}
|
|
110
|
+
</div>
|
|
111
|
+
)
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
let lineNum = ''
|
|
49
115
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
50
|
-
let style: any = { whiteSpace: 'pre-wrap', padding: '2px 6px' }
|
|
51
|
-
if (line.startsWith('
|
|
52
|
-
|
|
53
|
-
|
|
116
|
+
let style: any = { whiteSpace: 'pre-wrap', padding: '2px 6px', display: 'flex', alignItems: 'center' }
|
|
117
|
+
if (line.startsWith('+')) {
|
|
118
|
+
lineNum = String(currentNewLine)
|
|
119
|
+
currentNewLine++
|
|
54
120
|
style = { ...style, background: 'rgba(16, 185, 129, 0.12)', borderLeft: '3px solid #10b981', color: '#a7f3d0' }
|
|
55
121
|
} else if (line.startsWith('-')) {
|
|
122
|
+
lineNum = String(currentOldLine)
|
|
123
|
+
currentOldLine++
|
|
56
124
|
style = { ...style, background: 'rgba(239, 68, 68, 0.12)', borderLeft: '3px solid #ef4444', color: '#fca5a5' }
|
|
57
125
|
} else {
|
|
126
|
+
lineNum = String(currentNewLine)
|
|
127
|
+
currentOldLine++
|
|
128
|
+
currentNewLine++
|
|
58
129
|
style = { ...style, color: '#e2e8f0' }
|
|
59
130
|
}
|
|
60
|
-
|
|
131
|
+
|
|
132
|
+
return (
|
|
133
|
+
<div key={idx} style={style}>
|
|
134
|
+
<span style={{ color: '#64748b', marginRight: '12px', userSelect: 'none', display: 'inline-block', width: '36px', textAlign: 'right', flexShrink: 0 }}>
|
|
135
|
+
{lineNum}
|
|
136
|
+
</span>
|
|
137
|
+
<span>{line}</span>
|
|
138
|
+
</div>
|
|
139
|
+
)
|
|
61
140
|
})}
|
|
62
141
|
</div>
|
|
63
142
|
)
|