@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
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useState } from 'react'
|
|
2
2
|
|
|
3
|
-
export type DashboardView = 'chat' | 'pictures' | 'model' | 'imagine' | 'workflows'
|
|
3
|
+
export type DashboardView = 'chat' | 'pictures' | 'model' | 'imagine' | 'workflows' | 'veo'
|
|
4
4
|
|
|
5
5
|
interface ChatSessionItem {
|
|
6
6
|
id: string
|
|
@@ -119,6 +119,21 @@ export default function DashboardSidebar({
|
|
|
119
119
|
</span>
|
|
120
120
|
<span>Image Studio</span>
|
|
121
121
|
</button>
|
|
122
|
+
<button className={`sidebar-top-action ${view === 'veo' ? 'is-active' : ''}`} onClick={() => onSetView('veo')}>
|
|
123
|
+
<span aria-hidden="true" style={{ display: 'inline-flex', alignItems: 'center' }}>
|
|
124
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
|
|
125
|
+
<rect x="2" y="2" width="20" height="20" rx="2.18" ry="2.18"></rect>
|
|
126
|
+
<line x1="7" y1="2" x2="7" y2="22"></line>
|
|
127
|
+
<line x1="17" y1="2" x2="17" y2="22"></line>
|
|
128
|
+
<line x1="2" y1="12" x2="22" y2="12"></line>
|
|
129
|
+
<line x1="2" y1="7" x2="7" y2="7"></line>
|
|
130
|
+
<line x1="2" y1="17" x2="7" y2="17"></line>
|
|
131
|
+
<line x1="17" y1="17" x2="22" y2="17"></line>
|
|
132
|
+
<line x1="17" y1="7" x2="22" y2="7"></line>
|
|
133
|
+
</svg>
|
|
134
|
+
</span>
|
|
135
|
+
<span>Veo Studio</span>
|
|
136
|
+
</button>
|
|
122
137
|
|
|
123
138
|
<div className="sidebar-section">
|
|
124
139
|
<div className="sidebar-section-title">Conversation CLI</div>
|
|
@@ -55,9 +55,10 @@ const PROVIDER_MODELS: Record<string, { value: string; label: string }[]> = {
|
|
|
55
55
|
],
|
|
56
56
|
replicate: [
|
|
57
57
|
{ value: 'black-forest-labs/flux-1.1-pro', label: 'FLUX 1.1 Pro (Default)' },
|
|
58
|
+
{ value: 'timbrooks/instruct-pix2pix', label: 'InstructPix2Pix (Image Editing)' },
|
|
59
|
+
{ value: 'black-forest-labs/flux-fill-dev', label: 'FLUX Fill (Inpainting)' },
|
|
58
60
|
{ value: 'black-forest-labs/flux-schnell', label: 'FLUX Schnell (fast)' },
|
|
59
61
|
{ value: 'stability-ai/sdxl', label: 'SDXL' },
|
|
60
|
-
{ value: 'bytedance/sdxl-lightning-4step', label: 'SDXL Lightning' },
|
|
61
62
|
],
|
|
62
63
|
}
|
|
63
64
|
|
|
@@ -146,6 +147,36 @@ export default function ImageStudioPanel({ view, onRefreshPictures, onSendToChat
|
|
|
146
147
|
|
|
147
148
|
const effectiveModel = selectedModel === 'custom' ? customModel.trim() : selectedModel
|
|
148
149
|
|
|
150
|
+
const [inputImage, setInputImage] = useState<string | null>(null)
|
|
151
|
+
const [isDragging, setIsDragging] = useState(false)
|
|
152
|
+
const fileInputRef = useRef<HTMLInputElement>(null)
|
|
153
|
+
|
|
154
|
+
const handleImageUpload = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
155
|
+
const file = e.target.files?.[0]
|
|
156
|
+
if (!file) return
|
|
157
|
+
const reader = new FileReader()
|
|
158
|
+
reader.onload = (evt) => {
|
|
159
|
+
if (typeof evt.target?.result === 'string') {
|
|
160
|
+
setInputImage(evt.target.result)
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
reader.readAsDataURL(file)
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const handleDrop = (e: React.DragEvent) => {
|
|
167
|
+
e.preventDefault()
|
|
168
|
+
setIsDragging(false)
|
|
169
|
+
const file = e.dataTransfer.files?.[0]
|
|
170
|
+
if (!file || !file.type.startsWith('image/')) return
|
|
171
|
+
const reader = new FileReader()
|
|
172
|
+
reader.onload = (evt) => {
|
|
173
|
+
if (typeof evt.target?.result === 'string') {
|
|
174
|
+
setInputImage(evt.target.result)
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
reader.readAsDataURL(file)
|
|
178
|
+
}
|
|
179
|
+
|
|
149
180
|
const handleGenerate = useCallback(async () => {
|
|
150
181
|
const trimmed = prompt.trim()
|
|
151
182
|
if (!trimmed || generating) return
|
|
@@ -161,6 +192,8 @@ export default function ImageStudioPanel({ view, onRefreshPictures, onSendToChat
|
|
|
161
192
|
numImages,
|
|
162
193
|
model: effectiveModel || undefined,
|
|
163
194
|
provider: selectedProvider,
|
|
195
|
+
imageDataUri: inputImage || undefined,
|
|
196
|
+
mode: inputImage ? 'edit' : 'generate',
|
|
164
197
|
}
|
|
165
198
|
|
|
166
199
|
try {
|
|
@@ -176,7 +209,7 @@ export default function ImageStudioPanel({ view, onRefreshPictures, onSendToChat
|
|
|
176
209
|
} finally {
|
|
177
210
|
setGenerating(false)
|
|
178
211
|
}
|
|
179
|
-
}, [prompt, negativePrompt, aspectRatio, numImages, generating, onRefreshPictures, selectedProvider, effectiveModel])
|
|
212
|
+
}, [prompt, negativePrompt, aspectRatio, numImages, generating, onRefreshPictures, selectedProvider, effectiveModel, inputImage])
|
|
180
213
|
|
|
181
214
|
const handleKeyDown = (e: React.KeyboardEvent) => {
|
|
182
215
|
if ((e.metaKey || e.ctrlKey) && e.key === 'Enter') {
|
|
@@ -299,11 +332,61 @@ export default function ImageStudioPanel({ view, onRefreshPictures, onSendToChat
|
|
|
299
332
|
</div>
|
|
300
333
|
)}
|
|
301
334
|
|
|
335
|
+
{/* Image Editing / Source Image Attachment */}
|
|
336
|
+
<div className="img-studio-field">
|
|
337
|
+
<label className="img-studio-label">
|
|
338
|
+
Source Image (Optional for Image Editing)
|
|
339
|
+
<span className="img-studio-label-hint">Upload an image to edit / inpaint</span>
|
|
340
|
+
</label>
|
|
341
|
+
{inputImage ? (
|
|
342
|
+
<div className="img-studio-preview-card">
|
|
343
|
+
<img src={inputImage} alt="Input source" className="img-studio-preview-thumb" />
|
|
344
|
+
<div className="img-studio-preview-info">
|
|
345
|
+
<div className="img-studio-preview-title">
|
|
346
|
+
<span>Source Image Attached</span>
|
|
347
|
+
</div>
|
|
348
|
+
<div className="img-studio-preview-subtitle">Ready for Image Editing</div>
|
|
349
|
+
</div>
|
|
350
|
+
<button
|
|
351
|
+
type="button"
|
|
352
|
+
className="img-studio-preview-remove"
|
|
353
|
+
onClick={() => setInputImage(null)}
|
|
354
|
+
>
|
|
355
|
+
✕ Remove
|
|
356
|
+
</button>
|
|
357
|
+
</div>
|
|
358
|
+
) : (
|
|
359
|
+
<div
|
|
360
|
+
className={`img-studio-dropzone ${isDragging ? 'is-dragging' : ''}`}
|
|
361
|
+
onClick={() => fileInputRef.current?.click()}
|
|
362
|
+
onDragOver={(e) => { e.preventDefault(); setIsDragging(true); }}
|
|
363
|
+
onDragLeave={() => setIsDragging(false)}
|
|
364
|
+
onDrop={handleDrop}
|
|
365
|
+
>
|
|
366
|
+
<input
|
|
367
|
+
ref={fileInputRef}
|
|
368
|
+
type="file"
|
|
369
|
+
accept="image/*"
|
|
370
|
+
onChange={handleImageUpload}
|
|
371
|
+
disabled={generating}
|
|
372
|
+
style={{ display: 'none' }}
|
|
373
|
+
/>
|
|
374
|
+
<svg className="img-studio-dropzone-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
375
|
+
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/>
|
|
376
|
+
<polyline points="17 8 12 3 7 8"/>
|
|
377
|
+
<line x1="12" y1="3" x2="12" y2="15"/>
|
|
378
|
+
</svg>
|
|
379
|
+
<div className="img-studio-dropzone-text">Click or Drag & Drop image here</div>
|
|
380
|
+
<div className="img-studio-dropzone-subtext">PNG, JPG, WEBP up to 10MB</div>
|
|
381
|
+
</div>
|
|
382
|
+
)}
|
|
383
|
+
</div>
|
|
384
|
+
|
|
302
385
|
{/* Prompt */}
|
|
303
386
|
<div className="img-studio-field">
|
|
304
387
|
<label className="img-studio-label" htmlFor="img-studio-prompt">
|
|
305
|
-
Prompt
|
|
306
|
-
<span className="img-studio-label-hint">Describe the image you want</span>
|
|
388
|
+
{inputImage ? 'Editing Instruction Prompt' : 'Prompt'}
|
|
389
|
+
<span className="img-studio-label-hint">{inputImage ? 'Describe what to edit or remove' : 'Describe the image you want'}</span>
|
|
307
390
|
</label>
|
|
308
391
|
<textarea
|
|
309
392
|
id="img-studio-prompt"
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
getRecentInteractions,
|
|
8
8
|
saveSystemInteraction,
|
|
9
9
|
getRuntimeStatus,
|
|
10
|
+
setActiveModel,
|
|
10
11
|
listChatSessions,
|
|
11
12
|
listSavedPictures,
|
|
12
13
|
saveInteractionAgentActivity,
|
|
@@ -25,6 +26,7 @@ import ChatPanel from './ChatPanel'
|
|
|
25
26
|
import DashboardSidebar, { type DashboardView } from './DashboardSidebar'
|
|
26
27
|
import PicturesLibrary from './PicturesLibrary'
|
|
27
28
|
import ImageStudioPanel from './ImageStudioPanel'
|
|
29
|
+
import VeoStudioPanel from './VeoStudioPanel'
|
|
28
30
|
import {
|
|
29
31
|
errorMessage,
|
|
30
32
|
readImage,
|
|
@@ -32,6 +34,8 @@ import {
|
|
|
32
34
|
createTrimmedImagePreview,
|
|
33
35
|
applyThemeStyles,
|
|
34
36
|
} from '../../shared/utils/ui'
|
|
37
|
+
import { executeSlashCommand } from '../../shared/utils/slashCommandProcessor'
|
|
38
|
+
|
|
35
39
|
|
|
36
40
|
const DEFAULT_CONFIG = {
|
|
37
41
|
theme: 'dark',
|
|
@@ -84,6 +88,7 @@ export default function MintDashboard() {
|
|
|
84
88
|
const [sending, setSending] = useState(false)
|
|
85
89
|
const [sendingMessage, setSendingMessage] = useState('')
|
|
86
90
|
const [sendingImageCount, setSendingImageCount] = useState(0)
|
|
91
|
+
const [sendingVideoCount, setSendingVideoCount] = useState(0)
|
|
87
92
|
const [streamedReply, setStreamedReply] = useState('')
|
|
88
93
|
const [streamedResponse, setStreamedResponse] = useState<ChatResponse | null>(null)
|
|
89
94
|
const [streamingConversationId, setStreamingConversationId] = useState<string | null>(null)
|
|
@@ -92,6 +97,7 @@ export default function MintDashboard() {
|
|
|
92
97
|
const [thinkingExpanded, setThinkingExpanded] = useState<Record<string, boolean>>({})
|
|
93
98
|
const liveThinkingOpenRef = useRef(true)
|
|
94
99
|
const [imageAttachments, setImageAttachments] = useState<Array<{ dataUri: string; name: string; previewDataUri?: string }>>([])
|
|
100
|
+
const [videoAttachments, setVideoAttachments] = useState<Array<{ dataUri: string; name: string }>>([])
|
|
95
101
|
const [documentAttachment, setDocumentAttachment] = useState<DocumentAttachment | null>(null)
|
|
96
102
|
const [pendingApproval, setPendingApproval] = useState<any | null>(null)
|
|
97
103
|
const [sidebarCollapsed, setSidebarCollapsed] = useState(() => window.localStorage.getItem('mint:sidebar-collapsed') === 'true')
|
|
@@ -225,10 +231,25 @@ export default function MintDashboard() {
|
|
|
225
231
|
if (failure?.status === 'rejected') setError(errorMessage(failure.reason))
|
|
226
232
|
setDashboardDataReady(true)
|
|
227
233
|
})
|
|
234
|
+
const handleWindowFocus = () => {
|
|
235
|
+
getRuntimeStatus().then(setStatus).catch(() => {})
|
|
236
|
+
window.settingsApi?.getSettings?.().then((loaded: any) => {
|
|
237
|
+
if (loaded) {
|
|
238
|
+
setSettingsConfig(loaded)
|
|
239
|
+
applyThemeStyles(loaded)
|
|
240
|
+
}
|
|
241
|
+
}).catch(() => {})
|
|
242
|
+
}
|
|
243
|
+
window.addEventListener('focus', handleWindowFocus)
|
|
244
|
+
|
|
228
245
|
window.api?.onSettingsChanged?.((loaded: any) => {
|
|
229
246
|
setSettingsConfig(loaded)
|
|
230
247
|
applyThemeStyles(loaded)
|
|
248
|
+
getRuntimeStatus().then(setStatus).catch(() => {})
|
|
231
249
|
})
|
|
250
|
+
return () => {
|
|
251
|
+
window.removeEventListener('focus', handleWindowFocus)
|
|
252
|
+
}
|
|
232
253
|
}, [])
|
|
233
254
|
|
|
234
255
|
useEffect(() => {
|
|
@@ -304,6 +325,7 @@ export default function MintDashboard() {
|
|
|
304
325
|
promptText: string,
|
|
305
326
|
options: {
|
|
306
327
|
imageAttachments?: Array<{ dataUri: string; name: string; previewDataUri?: string }>
|
|
328
|
+
videoAttachments?: Array<{ dataUri: string; name: string }>
|
|
307
329
|
audioDataUri?: string | null
|
|
308
330
|
documentAttachment?: DocumentAttachment | null
|
|
309
331
|
systemInstruction?: string
|
|
@@ -312,14 +334,17 @@ export default function MintDashboard() {
|
|
|
312
334
|
) {
|
|
313
335
|
if (sending) return
|
|
314
336
|
const outgoingImages = options.imageAttachments ?? []
|
|
337
|
+
const outgoingVideos = options.videoAttachments ?? []
|
|
315
338
|
const outgoingDocument = options.documentAttachment ?? null
|
|
316
339
|
const shouldUseAgentMode = agentMode || promptText.toLowerCase().startsWith('search web:')
|
|
317
340
|
const outgoingImage = outgoingImages.map((img) => img.dataUri).join(' ')
|
|
341
|
+
const outgoingVideo = outgoingVideos.map((vid) => vid.dataUri).join(' ')
|
|
318
342
|
const outgoingImageCount = outgoingImages.length
|
|
319
343
|
setSending(true)
|
|
320
344
|
setStreamingConversationId(conversationId)
|
|
321
345
|
setSendingMessage(promptText)
|
|
322
346
|
setSendingImageCount(outgoingImageCount)
|
|
347
|
+
setSendingVideoCount(outgoingVideos.length)
|
|
323
348
|
setError('')
|
|
324
349
|
setStreamedReply('')
|
|
325
350
|
setStreamedResponse(null)
|
|
@@ -330,6 +355,7 @@ export default function MintDashboard() {
|
|
|
330
355
|
if (options.clearComposer) {
|
|
331
356
|
setMessage('')
|
|
332
357
|
setImageAttachments([])
|
|
358
|
+
setVideoAttachments([])
|
|
333
359
|
setDocumentAttachment(null)
|
|
334
360
|
}
|
|
335
361
|
|
|
@@ -339,6 +365,7 @@ export default function MintDashboard() {
|
|
|
339
365
|
(chunk) => setStreamedReply((current) => `${current}${chunk}`),
|
|
340
366
|
outgoingImage,
|
|
341
367
|
options.audioDataUri ?? null,
|
|
368
|
+
outgoingVideo,
|
|
342
369
|
options.systemInstruction ?? '',
|
|
343
370
|
(progress) => {
|
|
344
371
|
progressSnapshot.push(progress)
|
|
@@ -379,6 +406,7 @@ export default function MintDashboard() {
|
|
|
379
406
|
setAgentActivitySnapshots((current) => mergeActivitySnapshots(current, enrichedHistory))
|
|
380
407
|
await refreshChatSessions()
|
|
381
408
|
await refreshPictures()
|
|
409
|
+
getRuntimeStatus().then(setStatus).catch(() => {})
|
|
382
410
|
setStreamedReply('')
|
|
383
411
|
setStreamedResponse(null)
|
|
384
412
|
} catch (reason) {
|
|
@@ -388,6 +416,7 @@ export default function MintDashboard() {
|
|
|
388
416
|
setStreamingConversationId(null)
|
|
389
417
|
setSendingMessage('')
|
|
390
418
|
setSendingImageCount(0)
|
|
419
|
+
setSendingVideoCount(0)
|
|
391
420
|
}
|
|
392
421
|
}
|
|
393
422
|
|
|
@@ -395,12 +424,77 @@ export default function MintDashboard() {
|
|
|
395
424
|
event.preventDefault()
|
|
396
425
|
const trimmed = message.trim()
|
|
397
426
|
const currentImages = imageAttachments
|
|
427
|
+
const currentVideos = videoAttachments
|
|
398
428
|
const currentDocument = documentAttachment
|
|
399
|
-
const hasAttachments = currentImages.length > 0 || Boolean(currentDocument)
|
|
429
|
+
const hasAttachments = currentImages.length > 0 || currentVideos.length > 0 || Boolean(currentDocument)
|
|
400
430
|
if ((!trimmed && !hasAttachments) || sending) return
|
|
401
|
-
|
|
431
|
+
|
|
432
|
+
if (trimmed.startsWith('/')) {
|
|
433
|
+
const activeP = status?.activeProvider || ''
|
|
434
|
+
const activeM = settingsConfig?.model || ''
|
|
435
|
+
const slashResult = executeSlashCommand(trimmed, {
|
|
436
|
+
activeProvider: activeP,
|
|
437
|
+
activeModel: activeM,
|
|
438
|
+
availableProviders: status?.availableProviders || [],
|
|
439
|
+
interactionsCount: interactions.length,
|
|
440
|
+
fastMode: settingsConfig?.fastMode ?? false,
|
|
441
|
+
multiAgent: settingsConfig?.multiAgent ?? false,
|
|
442
|
+
})
|
|
443
|
+
|
|
444
|
+
if (slashResult.handled) {
|
|
445
|
+
setMessage('')
|
|
446
|
+
setImageAttachments([])
|
|
447
|
+
setVideoAttachments([])
|
|
448
|
+
setDocumentAttachment(null)
|
|
449
|
+
|
|
450
|
+
if (slashResult.action === 'set_agent_mode') {
|
|
451
|
+
setAgentMode(true)
|
|
452
|
+
if (slashResult.payload?.prompt) {
|
|
453
|
+
await sendPrompt(slashResult.payload.prompt, { clearComposer: true })
|
|
454
|
+
}
|
|
455
|
+
return
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
if (slashResult.action === 'open_image_picker') {
|
|
459
|
+
document.getElementById('vision-file-input')?.click()
|
|
460
|
+
return
|
|
461
|
+
} else if (slashResult.action === 'open_plugins') {
|
|
462
|
+
setView('settings')
|
|
463
|
+
return
|
|
464
|
+
} else if (slashResult.action === 'generate_veo') {
|
|
465
|
+
setView('veo_studio')
|
|
466
|
+
return
|
|
467
|
+
} else if (slashResult.action === 'set_provider_model' && slashResult.payload?.target) {
|
|
468
|
+
const target = slashResult.payload.target
|
|
469
|
+
if (status?.availableProviders.includes(target)) {
|
|
470
|
+
setActiveModel(target).then(() => getRuntimeStatus().then(setStatus)).catch(() => {})
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
if (slashResult.systemText) {
|
|
475
|
+
const systemMsg = {
|
|
476
|
+
id: Date.now(),
|
|
477
|
+
userText: trimmed,
|
|
478
|
+
aiText: slashResult.systemText,
|
|
479
|
+
createdAt: new Date().toISOString(),
|
|
480
|
+
provider: 'system',
|
|
481
|
+
model: 'mint-cli',
|
|
482
|
+
}
|
|
483
|
+
setInteractions((prev) => [...prev, systemMsg])
|
|
484
|
+
saveSystemInteraction(conversationId, trimmed, slashResult.systemText || '', 'system', 'mint-cli').catch(() => {})
|
|
485
|
+
}
|
|
486
|
+
return
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
const promptText = trimmed || (
|
|
491
|
+
currentImages.length > 0 ? (currentImages.length > 1 ? 'Describe these images.' : 'Describe this image.') :
|
|
492
|
+
currentVideos.length > 0 ? (currentVideos.length > 1 ? 'Describe these videos.' : 'Describe this video.') :
|
|
493
|
+
'Summarize this document.'
|
|
494
|
+
)
|
|
402
495
|
await sendPrompt(promptText, {
|
|
403
496
|
imageAttachments: currentImages,
|
|
497
|
+
videoAttachments: currentVideos,
|
|
404
498
|
documentAttachment: currentDocument,
|
|
405
499
|
clearComposer: true,
|
|
406
500
|
})
|
|
@@ -430,6 +524,28 @@ export default function MintDashboard() {
|
|
|
430
524
|
event.target.value = ''
|
|
431
525
|
}
|
|
432
526
|
}
|
|
527
|
+
const MAX_VIDEO_BYTES = 25 * 1024 * 1024 // 25 MB
|
|
528
|
+
async function selectVideo(event: ChangeEvent<HTMLInputElement>) {
|
|
529
|
+
const file = event.target.files?.[0]
|
|
530
|
+
if (!file) return
|
|
531
|
+
try {
|
|
532
|
+
if (file.size > MAX_VIDEO_BYTES) {
|
|
533
|
+
setError(`Video is too large. Maximum allowed is ${Math.round(MAX_VIDEO_BYTES / (1024 * 1024))} MB.`)
|
|
534
|
+
return
|
|
535
|
+
}
|
|
536
|
+
const dataUri = await new Promise<string>((resolve, reject) => {
|
|
537
|
+
const reader = new FileReader()
|
|
538
|
+
reader.onload = () => resolve(reader.result as string)
|
|
539
|
+
reader.onerror = reject
|
|
540
|
+
reader.readAsDataURL(file)
|
|
541
|
+
})
|
|
542
|
+
setVideoAttachments((current) => [...current, { dataUri, name: file.name }])
|
|
543
|
+
} catch (reason) {
|
|
544
|
+
setError(errorMessage(reason))
|
|
545
|
+
} finally {
|
|
546
|
+
event.target.value = ''
|
|
547
|
+
}
|
|
548
|
+
}
|
|
433
549
|
|
|
434
550
|
function pasteImage(clipboardData: DataTransfer) {
|
|
435
551
|
let file: File | null = null
|
|
@@ -642,6 +758,7 @@ export default function MintDashboard() {
|
|
|
642
758
|
async function changeProvider(provider: string) {
|
|
643
759
|
try {
|
|
644
760
|
const config = await window.settingsApi.getSettings()
|
|
761
|
+
if (config.aiProvider === provider) return
|
|
645
762
|
config.aiProvider = provider
|
|
646
763
|
await window.settingsApi.saveSettings(config)
|
|
647
764
|
setSettingsConfig(config)
|
|
@@ -650,7 +767,7 @@ export default function MintDashboard() {
|
|
|
650
767
|
// Record system event in chat history
|
|
651
768
|
const activeModel = getActiveModelName(config, provider)
|
|
652
769
|
const displayName = formatProviderChangeText(provider, activeModel)
|
|
653
|
-
await saveSystemInteraction(conversationId, displayName, 'system', 'provider_change')
|
|
770
|
+
await saveSystemInteraction(conversationId, displayName, '', 'system', 'provider_change')
|
|
654
771
|
await refreshHistory()
|
|
655
772
|
} catch (reason) {
|
|
656
773
|
setError(errorMessage(reason))
|
|
@@ -661,6 +778,8 @@ export default function MintDashboard() {
|
|
|
661
778
|
try {
|
|
662
779
|
const config = await window.settingsApi.getSettings()
|
|
663
780
|
const provider = config.aiProvider
|
|
781
|
+
const currentModel = getActiveModelName(config, provider)
|
|
782
|
+
if (currentModel === modelName) return
|
|
664
783
|
if (provider === 'gemini') {
|
|
665
784
|
config.geminiModel = modelName
|
|
666
785
|
} else if (provider === 'openai') {
|
|
@@ -690,7 +809,7 @@ export default function MintDashboard() {
|
|
|
690
809
|
|
|
691
810
|
// Record system event in chat history
|
|
692
811
|
const displayName = formatProviderChangeText(provider, modelName)
|
|
693
|
-
await saveSystemInteraction(conversationId, displayName, 'system', 'provider_change')
|
|
812
|
+
await saveSystemInteraction(conversationId, displayName, '', 'system', 'provider_change')
|
|
694
813
|
await refreshHistory()
|
|
695
814
|
} catch (reason) {
|
|
696
815
|
setError(errorMessage(reason))
|
|
@@ -742,11 +861,13 @@ export default function MintDashboard() {
|
|
|
742
861
|
streamedReply={streamingConversationId === conversationId ? streamedReply : ''}
|
|
743
862
|
streamedResponse={streamingConversationId === conversationId ? streamedResponse : null}
|
|
744
863
|
agentProgress={streamingConversationId === conversationId ? agentProgress : []}
|
|
864
|
+
sendingVideoCount={streamingConversationId === conversationId ? sendingVideoCount : 0}
|
|
745
865
|
agentActivitySnapshots={agentActivitySnapshots}
|
|
746
866
|
thinkingExpanded={thinkingExpanded}
|
|
747
867
|
onThinkingExpandedChange={handleThinkingExpandedChange}
|
|
748
868
|
message={message}
|
|
749
869
|
imageAttachments={imageAttachments}
|
|
870
|
+
videoAttachments={videoAttachments}
|
|
750
871
|
documentName={documentAttachment?.filename ?? ''}
|
|
751
872
|
pendingApproval={streamingConversationId === conversationId ? pendingApproval : null}
|
|
752
873
|
smartContext={smartContext}
|
|
@@ -756,6 +877,7 @@ export default function MintDashboard() {
|
|
|
756
877
|
welcomeInteraction={MOCK_WELCOME_INTERACTION}
|
|
757
878
|
onSubmit={handleSubmit}
|
|
758
879
|
onSelectImage={selectImage}
|
|
880
|
+
onSelectVideo={selectVideo}
|
|
759
881
|
onSelectDocument={selectDocument}
|
|
760
882
|
onPasteImage={pasteImage}
|
|
761
883
|
onSetMessage={setMessage}
|
|
@@ -763,6 +885,9 @@ export default function MintDashboard() {
|
|
|
763
885
|
onRemoveImage={(idx: number) => {
|
|
764
886
|
setImageAttachments((current) => current.filter((_, i) => i !== idx))
|
|
765
887
|
}}
|
|
888
|
+
onRemoveVideo={(idx: number) => {
|
|
889
|
+
setVideoAttachments((current) => current.filter((_, i) => i !== idx))
|
|
890
|
+
}}
|
|
766
891
|
onRemoveDocument={() => setDocumentAttachment(null)}
|
|
767
892
|
onStartWebSearch={startWebSearch}
|
|
768
893
|
onCaptureScreen={captureScreen}
|
|
@@ -786,6 +911,14 @@ export default function MintDashboard() {
|
|
|
786
911
|
}}
|
|
787
912
|
onToggleMobileSidebar={() => setMobileSidebarOpen(!mobileSidebarOpen)}
|
|
788
913
|
/>
|
|
914
|
+
<VeoStudioPanel
|
|
915
|
+
view={view}
|
|
916
|
+
onSendToChat={(imgPrompt) => {
|
|
917
|
+
setView('chat')
|
|
918
|
+
setMessage(imgPrompt)
|
|
919
|
+
}}
|
|
920
|
+
onToggleMobileSidebar={() => setMobileSidebarOpen(!mobileSidebarOpen)}
|
|
921
|
+
/>
|
|
789
922
|
</div>
|
|
790
923
|
<div className={`startup-loading ${startupReady ? 'is-hidden' : ''}`} aria-live="polite" aria-busy={!startupReady}>
|
|
791
924
|
<div className="startup-loading-content">
|