@pheem49/mint 1.8.2 → 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 +297 -107
- 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 +382 -127
- package/crates/mint-cli/src/gmail.rs +22 -2
- package/crates/mint-cli/src/image.rs +8 -8
- package/crates/mint-cli/src/interactive.rs +2888 -0
- package/crates/mint-cli/src/main.rs +391 -2506
- package/crates/mint-cli/src/markdown.rs +294 -0
- package/crates/mint-cli/src/onboard.rs +152 -109
- package/crates/mint-cli/src/setup.rs +143 -143
- 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 +431 -65
- package/crates/mint-core/src/browser.rs +630 -102
- package/crates/mint-core/src/channels.rs +105 -6
- package/crates/mint-core/src/chat.rs +174 -11
- package/crates/mint-core/src/code_tools.rs +47 -31
- package/crates/mint-core/src/config.rs +302 -17
- package/crates/mint-core/src/image_gen.rs +299 -38
- package/crates/mint-core/src/lib.rs +10 -7
- package/crates/mint-core/src/mcp.rs +119 -6
- package/crates/mint-core/src/memory.rs +6 -1
- package/crates/mint-core/src/orchestration.rs +237 -96
- package/crates/mint-core/src/pictures.rs +67 -5
- package/crates/mint-core/src/semantic.rs +1 -1
- package/crates/mint-core/src/shell.rs +1 -1
- package/crates/mint-core/src/skills.rs +128 -25
- package/crates/mint-core/src/video_gen.rs +235 -0
- package/package.json +8 -2
- package/shared-modules/linux-audio/fluidsynth2-static.json +9 -3
- package/shared-modules/linux-audio/fluidsynth2.json +8 -2
- package/shared-modules/linux-audio/lilv.json +26 -19
- package/shared-modules/linux-audio/lv2.json +0 -1
- 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/components/AgentActivityTable.tsx +25 -6
- 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/fileIcons.ts +135 -0
- 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 +486 -69
- 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 +195 -7
- package/src/renderer/src/components/PicturesLibrary.tsx +156 -15
- package/src/renderer/src/components/Settings/AgentsTab.tsx +21 -2
- package/src/renderer/src/components/Settings/AutomationTab.tsx +316 -18
- package/src/renderer/src/components/Settings/GeneralTab.tsx +795 -413
- 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 +106 -3
- package/src/renderer/src/components/VeoStudioPanel.tsx +549 -0
- package/src/renderer/src/components/WorkspacePanel.tsx +7 -125
- 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 +368 -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 +385 -93
- 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 +156 -5
- package/src/renderer/src-web/components/PicturesLibrary.tsx +155 -15
- package/src/renderer/src-web/components/Settings/AgentsTab.tsx +21 -2
- package/src/renderer/src-web/components/Settings/AutomationTab.tsx +316 -18
- package/src/renderer/src-web/components/Settings/GeneralTab.tsx +795 -411
- 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 +110 -3
- 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 +369 -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/desktop.rs +18 -16
- package/src-tauri/src/discord_rpc.rs +1 -1
- package/src-tauri/src/lib.rs +155 -25
- 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,294 @@
|
|
|
1
|
+
pub fn is_table_line(line: &str) -> bool {
|
|
2
|
+
let trimmed = line.trim();
|
|
3
|
+
trimmed.starts_with('|') && trimmed.ends_with('|') && trimmed.len() > 1
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
pub fn process_inline_bold(s: &str) -> String {
|
|
7
|
+
let mut result = String::new();
|
|
8
|
+
let parts = s.split("**");
|
|
9
|
+
let mut is_bold = false;
|
|
10
|
+
for part in parts {
|
|
11
|
+
if is_bold {
|
|
12
|
+
result.push_str("\x1b[1m");
|
|
13
|
+
result.push_str(part);
|
|
14
|
+
result.push_str("\x1b[0m");
|
|
15
|
+
} else {
|
|
16
|
+
result.push_str(part);
|
|
17
|
+
}
|
|
18
|
+
is_bold = !is_bold;
|
|
19
|
+
}
|
|
20
|
+
result
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
pub fn format_line(line: &str) -> String {
|
|
24
|
+
let mut formatted = line.to_string();
|
|
25
|
+
let trimmed = line.trim_start();
|
|
26
|
+
if (trimmed.starts_with("- ") || trimmed.starts_with("* ") || trimmed.starts_with("+ "))
|
|
27
|
+
&& trimmed.len() >= 2
|
|
28
|
+
{
|
|
29
|
+
let leading_len = line.len() - trimmed.len();
|
|
30
|
+
let leading_spaces = &line[..leading_len];
|
|
31
|
+
formatted = format!("{}•{}", leading_spaces, &trimmed[1..]);
|
|
32
|
+
}
|
|
33
|
+
process_inline_bold(&formatted)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
pub fn unicode_width(s: &str) -> usize {
|
|
37
|
+
let mut count = 0;
|
|
38
|
+
for c in s.chars() {
|
|
39
|
+
let val = c as u32;
|
|
40
|
+
if (0x0E31..=0x0E31).contains(&val)
|
|
41
|
+
|| (0x0E34..=0x0E3A).contains(&val)
|
|
42
|
+
|| (0x0E47..=0x0E4E).contains(&val)
|
|
43
|
+
{
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
if is_cjk(c) {
|
|
47
|
+
count += 2;
|
|
48
|
+
} else {
|
|
49
|
+
count += 1;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
count
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
pub fn is_cjk(c: char) -> bool {
|
|
56
|
+
let val = c as u32;
|
|
57
|
+
(0x4E00..=0x9FFF).contains(&val)
|
|
58
|
+
|| (0x3400..=0x4DBF).contains(&val)
|
|
59
|
+
|| (0x20000..=0x2A6DF).contains(&val)
|
|
60
|
+
|| (0xAC00..=0xD7AF).contains(&val)
|
|
61
|
+
|| (0x3040..=0x309F).contains(&val)
|
|
62
|
+
|| (0x30A0..=0x30FF).contains(&val)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
pub fn render_markdown_table(table_lines: &[String]) -> String {
|
|
66
|
+
let mut rows: Vec<Vec<String>> = Vec::new();
|
|
67
|
+
for line in table_lines {
|
|
68
|
+
let trimmed = line.trim();
|
|
69
|
+
if trimmed.is_empty() {
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
let content = &trimmed[1..trimmed.len() - 1];
|
|
73
|
+
let content_escaped = content.replace("\\|", "\u{0000}");
|
|
74
|
+
let cols: Vec<String> = content_escaped
|
|
75
|
+
.split('|')
|
|
76
|
+
.map(|s| s.replace("\u{0000}", "|").trim().to_string())
|
|
77
|
+
.collect();
|
|
78
|
+
rows.push(cols);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if rows.is_empty() {
|
|
82
|
+
return String::new();
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
let mut has_separator = false;
|
|
86
|
+
let mut separator_idx = None;
|
|
87
|
+
for (i, row) in rows.iter().enumerate() {
|
|
88
|
+
if row.iter().all(|col| {
|
|
89
|
+
col.chars()
|
|
90
|
+
.all(|c| c == '-' || c == ':' || c == ' ' || c == '\t')
|
|
91
|
+
}) && !row.is_empty()
|
|
92
|
+
{
|
|
93
|
+
has_separator = true;
|
|
94
|
+
separator_idx = Some(i);
|
|
95
|
+
break;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
let mut data_rows: Vec<Vec<String>> = Vec::new();
|
|
100
|
+
let mut header_row: Option<Vec<String>> = None;
|
|
101
|
+
|
|
102
|
+
if has_separator {
|
|
103
|
+
let sep_idx = separator_idx.unwrap();
|
|
104
|
+
if sep_idx > 0 {
|
|
105
|
+
header_row = Some(rows[sep_idx - 1].clone());
|
|
106
|
+
for (i, row) in rows.iter().enumerate() {
|
|
107
|
+
if i != sep_idx && i != sep_idx - 1 {
|
|
108
|
+
data_rows.push(row.clone());
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
} else {
|
|
112
|
+
for (i, row) in rows.iter().enumerate() {
|
|
113
|
+
if i != sep_idx {
|
|
114
|
+
data_rows.push(row.clone());
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
} else {
|
|
119
|
+
header_row = Some(rows[0].clone());
|
|
120
|
+
data_rows = rows[1..].to_vec();
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
let num_cols = rows.iter().map(|r| r.len()).max().unwrap_or(0);
|
|
124
|
+
let mut col_widths = vec![0; num_cols];
|
|
125
|
+
|
|
126
|
+
if let Some(ref header) = header_row {
|
|
127
|
+
for (i, col) in header.iter().enumerate() {
|
|
128
|
+
if i < num_cols {
|
|
129
|
+
col_widths[i] = col_widths[i].max(unicode_width(col));
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
for row in &data_rows {
|
|
134
|
+
for (i, col) in row.iter().enumerate() {
|
|
135
|
+
if i < num_cols {
|
|
136
|
+
col_widths[i] = col_widths[i].max(unicode_width(col));
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
let mut rendered = String::new();
|
|
142
|
+
|
|
143
|
+
let draw_border =
|
|
144
|
+
|left: &str, mid: &str, right: &str, fill: &str, widths: &[usize]| -> String {
|
|
145
|
+
let mut s = left.to_string();
|
|
146
|
+
for (i, &w) in widths.iter().enumerate() {
|
|
147
|
+
s.push_str(&fill.repeat(w + 2));
|
|
148
|
+
if i < widths.len() - 1 {
|
|
149
|
+
s.push_str(mid);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
s.push_str(right);
|
|
153
|
+
s.push('\n');
|
|
154
|
+
s
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
rendered.push_str(&draw_border("┌", "┬", "┐", "─", &col_widths));
|
|
158
|
+
|
|
159
|
+
if let Some(ref header) = header_row {
|
|
160
|
+
rendered.push('│');
|
|
161
|
+
for (i, col) in header.iter().enumerate() {
|
|
162
|
+
let width = unicode_width(col);
|
|
163
|
+
let padding = col_widths[i] - width;
|
|
164
|
+
rendered.push_str(&format!(
|
|
165
|
+
" \x1b[1;36m{}\x1b[0m{}",
|
|
166
|
+
col,
|
|
167
|
+
" ".repeat(padding + 1)
|
|
168
|
+
));
|
|
169
|
+
rendered.push('│');
|
|
170
|
+
}
|
|
171
|
+
rendered.push('\n');
|
|
172
|
+
rendered.push_str(&draw_border("├", "┼", "┤", "─", &col_widths));
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
for (r_idx, row) in data_rows.iter().enumerate() {
|
|
176
|
+
rendered.push('│');
|
|
177
|
+
for i in 0..num_cols {
|
|
178
|
+
let col_val = row.get(i).cloned().unwrap_or_default();
|
|
179
|
+
let width = unicode_width(&col_val);
|
|
180
|
+
let padding = col_widths[i] - width;
|
|
181
|
+
rendered.push_str(&format!(" {}{}", col_val, " ".repeat(padding + 1)));
|
|
182
|
+
rendered.push('│');
|
|
183
|
+
}
|
|
184
|
+
rendered.push('\n');
|
|
185
|
+
|
|
186
|
+
if r_idx < data_rows.len() - 1 {
|
|
187
|
+
rendered.push_str(&draw_border("├", "┼", "┤", "─", &col_widths));
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
rendered.push_str(draw_border("└", "┴", "┘", "─", &col_widths).trim_end());
|
|
192
|
+
rendered
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
pub struct CliStreamFormatter {
|
|
196
|
+
line_buffer: String,
|
|
197
|
+
table_buffer: Vec<String>,
|
|
198
|
+
in_code_block: bool,
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
impl CliStreamFormatter {
|
|
202
|
+
pub fn new() -> Self {
|
|
203
|
+
Self {
|
|
204
|
+
line_buffer: String::new(),
|
|
205
|
+
table_buffer: Vec::new(),
|
|
206
|
+
in_code_block: false,
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
pub fn process_char(&mut self, c: char, mut print_fn: impl FnMut(char)) {
|
|
211
|
+
self.line_buffer.push(c);
|
|
212
|
+
if c == '\n' {
|
|
213
|
+
let mut line = self.line_buffer.clone();
|
|
214
|
+
self.line_buffer.clear();
|
|
215
|
+
if line.ends_with('\n') {
|
|
216
|
+
line.pop();
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
let trimmed = line.trim();
|
|
220
|
+
if trimmed.starts_with("```") {
|
|
221
|
+
self.in_code_block = !self.in_code_block;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
if self.in_code_block {
|
|
225
|
+
if !self.table_buffer.is_empty() {
|
|
226
|
+
self.flush_table(&mut print_fn);
|
|
227
|
+
}
|
|
228
|
+
for ch in line.chars() {
|
|
229
|
+
print_fn(ch);
|
|
230
|
+
}
|
|
231
|
+
print_fn('\n');
|
|
232
|
+
for ch in " ".chars() {
|
|
233
|
+
print_fn(ch);
|
|
234
|
+
}
|
|
235
|
+
} else {
|
|
236
|
+
if is_table_line(&line) {
|
|
237
|
+
self.table_buffer.push(line);
|
|
238
|
+
} else {
|
|
239
|
+
if !self.table_buffer.is_empty() {
|
|
240
|
+
self.flush_table(&mut print_fn);
|
|
241
|
+
}
|
|
242
|
+
let formatted = format_line(&line);
|
|
243
|
+
for ch in formatted.chars() {
|
|
244
|
+
print_fn(ch);
|
|
245
|
+
}
|
|
246
|
+
print_fn('\n');
|
|
247
|
+
for ch in " ".chars() {
|
|
248
|
+
print_fn(ch);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
pub fn finalize(&mut self, mut print_fn: impl FnMut(char)) {
|
|
256
|
+
if !self.line_buffer.is_empty() {
|
|
257
|
+
let mut line = self.line_buffer.clone();
|
|
258
|
+
self.line_buffer.clear();
|
|
259
|
+
if line.ends_with('\n') {
|
|
260
|
+
line.pop();
|
|
261
|
+
}
|
|
262
|
+
if self.in_code_block {
|
|
263
|
+
if !self.table_buffer.is_empty() {
|
|
264
|
+
self.flush_table(&mut print_fn);
|
|
265
|
+
}
|
|
266
|
+
for ch in line.chars() {
|
|
267
|
+
print_fn(ch);
|
|
268
|
+
}
|
|
269
|
+
} else if is_table_line(&line) {
|
|
270
|
+
self.table_buffer.push(line);
|
|
271
|
+
} else {
|
|
272
|
+
if !self.table_buffer.is_empty() {
|
|
273
|
+
self.flush_table(&mut print_fn);
|
|
274
|
+
}
|
|
275
|
+
let formatted = format_line(&line);
|
|
276
|
+
for ch in formatted.chars() {
|
|
277
|
+
print_fn(ch);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
if !self.table_buffer.is_empty() {
|
|
282
|
+
self.flush_table(&mut print_fn);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
fn flush_table<F: FnMut(char)>(&mut self, mut print_fn: F) {
|
|
287
|
+
let rendered = render_markdown_table(&self.table_buffer);
|
|
288
|
+
for ch in rendered.chars() {
|
|
289
|
+
print_fn(ch);
|
|
290
|
+
}
|
|
291
|
+
print_fn('\n');
|
|
292
|
+
self.table_buffer.clear();
|
|
293
|
+
}
|
|
294
|
+
}
|
|
@@ -75,9 +75,11 @@ const REPLICATE_MODEL_PRESETS: &[&str] = &[
|
|
|
75
75
|
"black-forest-labs/flux-1.1-pro",
|
|
76
76
|
"black-forest-labs/flux-schnell",
|
|
77
77
|
"stability-ai/sdxl",
|
|
78
|
-
"bytedance/sdxl-lightning-4step",
|
|
79
78
|
];
|
|
80
79
|
|
|
80
|
+
// ── Video Generation Providers ──────────────────────────────────────────────
|
|
81
|
+
const VEO_VIDEO_MODEL_PRESETS: &[&str] = &["veo-2.0-generate-001", "veo-2.0-flash-001"];
|
|
82
|
+
|
|
81
83
|
pub async fn run() -> Result<()> {
|
|
82
84
|
let mut config = load_config()?;
|
|
83
85
|
|
|
@@ -280,6 +282,19 @@ pub async fn run() -> Result<()> {
|
|
|
280
282
|
key: "img_replicate",
|
|
281
283
|
enabled: !config.replicate_api_key.is_empty(),
|
|
282
284
|
},
|
|
285
|
+
// ── Video Generation ─────────────────────────────────────────────────
|
|
286
|
+
OnboardService {
|
|
287
|
+
category: "Video Generation",
|
|
288
|
+
name: "Google Veo (Gemini Videos) [uses Gemini key]",
|
|
289
|
+
key: "vid_veo",
|
|
290
|
+
enabled: !config.api_key.is_empty()
|
|
291
|
+
&& config
|
|
292
|
+
.extra
|
|
293
|
+
.get("veoModel")
|
|
294
|
+
.and_then(|v| v.as_str())
|
|
295
|
+
.map(|s| !s.is_empty())
|
|
296
|
+
.unwrap_or(false),
|
|
297
|
+
},
|
|
283
298
|
];
|
|
284
299
|
|
|
285
300
|
let mut cursor = 0;
|
|
@@ -296,71 +311,71 @@ pub async fn run() -> Result<()> {
|
|
|
296
311
|
loop {
|
|
297
312
|
match event::poll(std::time::Duration::from_millis(100)) {
|
|
298
313
|
Ok(true) => {
|
|
299
|
-
if let Event::Key(key_event) = event::read()?
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
314
|
+
if let Event::Key(key_event) = event::read()?
|
|
315
|
+
&& key_event.kind == event::KeyEventKind::Press
|
|
316
|
+
{
|
|
317
|
+
let is_ctrl_c = matches!(key_event.code, KeyCode::Char('c'))
|
|
318
|
+
&& key_event
|
|
319
|
+
.modifiers
|
|
320
|
+
.contains(crossterm::event::KeyModifiers::CONTROL);
|
|
321
|
+
if is_ctrl_c {
|
|
322
|
+
disable_raw_mode()?;
|
|
323
|
+
println!("\n\x1b[31mOnboarding cancelled.\x1b[0m");
|
|
324
|
+
return Ok(());
|
|
325
|
+
}
|
|
310
326
|
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
}
|
|
318
|
-
disable_raw_mode()?;
|
|
319
|
-
print!("\x1b[{}A\x1b[J", service_display_lines(&services));
|
|
320
|
-
print_services(&services, cursor);
|
|
321
|
-
enable_raw_mode()?;
|
|
322
|
-
}
|
|
323
|
-
KeyCode::Down => {
|
|
324
|
-
if cursor < services.len() - 1 {
|
|
325
|
-
cursor += 1;
|
|
326
|
-
} else {
|
|
327
|
-
cursor = 0;
|
|
328
|
-
}
|
|
329
|
-
disable_raw_mode()?;
|
|
330
|
-
print!("\x1b[{}A\x1b[J", service_display_lines(&services));
|
|
331
|
-
print_services(&services, cursor);
|
|
332
|
-
enable_raw_mode()?;
|
|
333
|
-
}
|
|
334
|
-
KeyCode::Char(' ') => {
|
|
335
|
-
services[cursor].enabled = !services[cursor].enabled;
|
|
336
|
-
disable_raw_mode()?;
|
|
337
|
-
print!("\x1b[{}A\x1b[J", service_display_lines(&services));
|
|
338
|
-
print_services(&services, cursor);
|
|
339
|
-
enable_raw_mode()?;
|
|
327
|
+
match key_event.code {
|
|
328
|
+
KeyCode::Up => {
|
|
329
|
+
if cursor > 0 {
|
|
330
|
+
cursor -= 1;
|
|
331
|
+
} else {
|
|
332
|
+
cursor = services.len() - 1;
|
|
340
333
|
}
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
334
|
+
disable_raw_mode()?;
|
|
335
|
+
print!("\x1b[{}A\x1b[J", service_display_lines(&services));
|
|
336
|
+
print_services(&services, cursor);
|
|
337
|
+
enable_raw_mode()?;
|
|
338
|
+
}
|
|
339
|
+
KeyCode::Down => {
|
|
340
|
+
if cursor < services.len() - 1 {
|
|
341
|
+
cursor += 1;
|
|
342
|
+
} else {
|
|
343
|
+
cursor = 0;
|
|
349
344
|
}
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
345
|
+
disable_raw_mode()?;
|
|
346
|
+
print!("\x1b[{}A\x1b[J", service_display_lines(&services));
|
|
347
|
+
print_services(&services, cursor);
|
|
348
|
+
enable_raw_mode()?;
|
|
349
|
+
}
|
|
350
|
+
KeyCode::Char(' ') => {
|
|
351
|
+
services[cursor].enabled = !services[cursor].enabled;
|
|
352
|
+
disable_raw_mode()?;
|
|
353
|
+
print!("\x1b[{}A\x1b[J", service_display_lines(&services));
|
|
354
|
+
print_services(&services, cursor);
|
|
355
|
+
enable_raw_mode()?;
|
|
356
|
+
}
|
|
357
|
+
KeyCode::Char('a') => {
|
|
358
|
+
for svc in &mut services {
|
|
359
|
+
svc.enabled = true;
|
|
358
360
|
}
|
|
359
|
-
|
|
360
|
-
|
|
361
|
+
disable_raw_mode()?;
|
|
362
|
+
print!("\x1b[{}A\x1b[J", service_display_lines(&services));
|
|
363
|
+
print_services(&services, cursor);
|
|
364
|
+
enable_raw_mode()?;
|
|
365
|
+
}
|
|
366
|
+
KeyCode::Char('i') => {
|
|
367
|
+
for svc in &mut services {
|
|
368
|
+
svc.enabled = !svc.enabled;
|
|
361
369
|
}
|
|
362
|
-
|
|
370
|
+
disable_raw_mode()?;
|
|
371
|
+
print!("\x1b[{}A\x1b[J", service_display_lines(&services));
|
|
372
|
+
print_services(&services, cursor);
|
|
373
|
+
enable_raw_mode()?;
|
|
374
|
+
}
|
|
375
|
+
KeyCode::Enter => {
|
|
376
|
+
break;
|
|
363
377
|
}
|
|
378
|
+
_ => {}
|
|
364
379
|
}
|
|
365
380
|
}
|
|
366
381
|
}
|
|
@@ -976,6 +991,34 @@ pub async fn run() -> Result<()> {
|
|
|
976
991
|
config.replicate_api_key = String::new();
|
|
977
992
|
}
|
|
978
993
|
|
|
994
|
+
// Google Veo (Gemini Videos)
|
|
995
|
+
if is_selected("vid_veo", &services) {
|
|
996
|
+
println!("\n\x1b[36m--- Google Veo (Gemini Videos) ---\x1b[0m");
|
|
997
|
+
println!(
|
|
998
|
+
"\x1b[90mUses the same Gemini API key as Step 1. Select the video generation model.\x1b[0m"
|
|
999
|
+
);
|
|
1000
|
+
let current_veo_model = config
|
|
1001
|
+
.extra
|
|
1002
|
+
.get("veoModel")
|
|
1003
|
+
.and_then(|v| v.as_str())
|
|
1004
|
+
.unwrap_or("veo-2.0-generate-001")
|
|
1005
|
+
.to_string();
|
|
1006
|
+
let selected_veo_model = prompt_select_or_custom(
|
|
1007
|
+
"Veo Model",
|
|
1008
|
+
static_model_options(VEO_VIDEO_MODEL_PRESETS),
|
|
1009
|
+
Some(¤t_veo_model),
|
|
1010
|
+
"Custom Veo model...",
|
|
1011
|
+
)?;
|
|
1012
|
+
config.extra.insert(
|
|
1013
|
+
"veoModel".to_string(),
|
|
1014
|
+
serde_json::Value::String(selected_veo_model),
|
|
1015
|
+
);
|
|
1016
|
+
config.extra.insert(
|
|
1017
|
+
"videoGenProvider".to_string(),
|
|
1018
|
+
serde_json::Value::String("veo".to_string()),
|
|
1019
|
+
);
|
|
1020
|
+
}
|
|
1021
|
+
|
|
979
1022
|
save_config(&config)?;
|
|
980
1023
|
println!("\x1b[32m✅ Configuration saved successfully!\x1b[0m");
|
|
981
1024
|
Ok(())
|
|
@@ -1052,52 +1095,52 @@ fn prompt_select_or_custom(
|
|
|
1052
1095
|
loop {
|
|
1053
1096
|
match event::poll(std::time::Duration::from_millis(100)) {
|
|
1054
1097
|
Ok(true) => {
|
|
1055
|
-
if let Event::Key(key_event) = event::read()?
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1098
|
+
if let Event::Key(key_event) = event::read()?
|
|
1099
|
+
&& key_event.kind == event::KeyEventKind::Press
|
|
1100
|
+
{
|
|
1101
|
+
let is_ctrl_c = matches!(key_event.code, KeyCode::Char('c'))
|
|
1102
|
+
&& key_event
|
|
1103
|
+
.modifiers
|
|
1104
|
+
.contains(crossterm::event::KeyModifiers::CONTROL);
|
|
1105
|
+
if is_ctrl_c {
|
|
1106
|
+
disable_raw_mode()?;
|
|
1107
|
+
println!("\n\x1b[31mOnboarding cancelled.\x1b[0m");
|
|
1108
|
+
bail!("onboarding cancelled");
|
|
1109
|
+
}
|
|
1066
1110
|
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
}
|
|
1074
|
-
disable_raw_mode()?;
|
|
1075
|
-
print!("\x1b[{}A\x1b[J", options.len());
|
|
1076
|
-
print_select_options(&options, cursor);
|
|
1077
|
-
enable_raw_mode()?;
|
|
1111
|
+
match key_event.code {
|
|
1112
|
+
KeyCode::Up => {
|
|
1113
|
+
if cursor > 0 {
|
|
1114
|
+
cursor -= 1;
|
|
1115
|
+
} else {
|
|
1116
|
+
cursor = options.len() - 1;
|
|
1078
1117
|
}
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1118
|
+
disable_raw_mode()?;
|
|
1119
|
+
print!("\x1b[{}A\x1b[J", options.len());
|
|
1120
|
+
print_select_options(&options, cursor);
|
|
1121
|
+
enable_raw_mode()?;
|
|
1122
|
+
}
|
|
1123
|
+
KeyCode::Down => {
|
|
1124
|
+
if cursor < options.len() - 1 {
|
|
1125
|
+
cursor += 1;
|
|
1126
|
+
} else {
|
|
1127
|
+
cursor = 0;
|
|
1089
1128
|
}
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1129
|
+
disable_raw_mode()?;
|
|
1130
|
+
print!("\x1b[{}A\x1b[J", options.len());
|
|
1131
|
+
print_select_options(&options, cursor);
|
|
1132
|
+
enable_raw_mode()?;
|
|
1133
|
+
}
|
|
1134
|
+
KeyCode::Enter => {
|
|
1135
|
+
disable_raw_mode()?;
|
|
1136
|
+
println!();
|
|
1137
|
+
let selected = options[cursor].clone();
|
|
1138
|
+
if selected == custom_label {
|
|
1139
|
+
return prompt_input(label, Some(current));
|
|
1098
1140
|
}
|
|
1099
|
-
|
|
1141
|
+
return Ok(selected);
|
|
1100
1142
|
}
|
|
1143
|
+
_ => {}
|
|
1101
1144
|
}
|
|
1102
1145
|
}
|
|
1103
1146
|
}
|
|
@@ -1223,10 +1266,10 @@ fn print_select_options(options: &[String], cursor: usize) {
|
|
|
1223
1266
|
|
|
1224
1267
|
fn prompt_input(label: &str, default: Option<&str>) -> Result<String> {
|
|
1225
1268
|
print!("{}", label);
|
|
1226
|
-
if let Some(d) = default
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
}
|
|
1269
|
+
if let Some(d) = default
|
|
1270
|
+
&& !d.is_empty()
|
|
1271
|
+
{
|
|
1272
|
+
print!(" [\x1b[90m{}\x1b[0m]", d);
|
|
1230
1273
|
}
|
|
1231
1274
|
print!(": ");
|
|
1232
1275
|
io::stdout().flush()?;
|
|
@@ -1234,10 +1277,10 @@ fn prompt_input(label: &str, default: Option<&str>) -> Result<String> {
|
|
|
1234
1277
|
let mut input = String::new();
|
|
1235
1278
|
io::stdin().read_line(&mut input)?;
|
|
1236
1279
|
let trimmed = input.trim();
|
|
1237
|
-
if trimmed.is_empty()
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1280
|
+
if trimmed.is_empty()
|
|
1281
|
+
&& let Some(d) = default
|
|
1282
|
+
{
|
|
1283
|
+
return Ok(d.to_string());
|
|
1241
1284
|
}
|
|
1242
1285
|
Ok(trimmed.to_string())
|
|
1243
1286
|
}
|