@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
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useState, useEffect, useRef } from 'react'
|
|
2
2
|
|
|
3
|
-
export type DashboardView = 'chat' | 'pictures' | 'model' | 'workspace' | 'imagine' | 'workflows'
|
|
3
|
+
export type DashboardView = 'chat' | 'pictures' | 'model' | 'workspace' | 'imagine' | 'workflows' | 'veo'
|
|
4
4
|
|
|
5
5
|
interface ChatSessionItem {
|
|
6
6
|
id: string
|
|
@@ -181,7 +181,7 @@ export default function DashboardSidebar({
|
|
|
181
181
|
</button>
|
|
182
182
|
|
|
183
183
|
<div className="sidebar-more-container" ref={moreContainerRef}>
|
|
184
|
-
<button className={`sidebar-top-action ${isMoreOpen || view === 'imagine' || view === 'workflows' ? 'is-active' : ''}`} onClick={() => setIsMoreOpen(!isMoreOpen)}>
|
|
184
|
+
<button className={`sidebar-top-action ${isMoreOpen || view === 'imagine' || view === 'workflows' || view === 'veo' ? 'is-active' : ''}`} onClick={() => setIsMoreOpen(!isMoreOpen)}>
|
|
185
185
|
<span aria-hidden="true" style={{ display: 'inline-flex', alignItems: 'center' }}>
|
|
186
186
|
<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">
|
|
187
187
|
<circle cx="12" cy="12" r="1.5"></circle>
|
|
@@ -214,6 +214,15 @@ export default function DashboardSidebar({
|
|
|
214
214
|
</span>
|
|
215
215
|
<span>Workflow (Beta)</span>
|
|
216
216
|
</button>
|
|
217
|
+
<button className={`popover-item ${view === 'veo' ? 'active' : ''}`} onClick={() => { onSetView('veo'); setIsMoreOpen(false); }}>
|
|
218
|
+
<span aria-hidden="true" style={{ display: 'inline-flex', alignItems: 'center' }}>
|
|
219
|
+
<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">
|
|
220
|
+
<polygon points="23 7 16 12 23 17 23 7"></polygon>
|
|
221
|
+
<rect x="1" y="5" width="15" height="14" rx="2" ry="2"></rect>
|
|
222
|
+
</svg>
|
|
223
|
+
</span>
|
|
224
|
+
<span>Veo Studio</span>
|
|
225
|
+
</button>
|
|
217
226
|
</div>
|
|
218
227
|
)}
|
|
219
228
|
</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
|
selectWorkspaceDirectory,
|
|
@@ -15,6 +16,7 @@ import {
|
|
|
15
16
|
cancelChatMessage,
|
|
16
17
|
submitToolApproval,
|
|
17
18
|
listen,
|
|
19
|
+
readClipboardImage,
|
|
18
20
|
type AgentProgress,
|
|
19
21
|
type ChatResponse,
|
|
20
22
|
type ChatSession,
|
|
@@ -22,9 +24,11 @@ import {
|
|
|
22
24
|
type PictureEntry,
|
|
23
25
|
type RuntimeStatus,
|
|
24
26
|
} from '../tauri'
|
|
27
|
+
|
|
25
28
|
import ChatPanel from './ChatPanel'
|
|
26
29
|
import DashboardSidebar, { type DashboardView } from './DashboardSidebar'
|
|
27
30
|
import ImageStudioPanel from './ImageStudioPanel'
|
|
31
|
+
import VeoStudioPanel from './VeoStudioPanel'
|
|
28
32
|
import ModelPanel from './ModelPanel'
|
|
29
33
|
import type { ModelInteraction } from './ModelPanel'
|
|
30
34
|
import PicturesLibrary from './PicturesLibrary'
|
|
@@ -37,6 +41,8 @@ import {
|
|
|
37
41
|
createTrimmedImagePreview,
|
|
38
42
|
applyThemeStyles,
|
|
39
43
|
} from '../../shared/utils/ui'
|
|
44
|
+
import { executeSlashCommand } from '../../shared/utils/slashCommandProcessor'
|
|
45
|
+
|
|
40
46
|
|
|
41
47
|
const EXPRESSIONS = [
|
|
42
48
|
"Default",
|
|
@@ -106,6 +112,7 @@ export default function MintDashboard() {
|
|
|
106
112
|
const [sending, setSending] = useState(false)
|
|
107
113
|
const [sendingMessage, setSendingMessage] = useState('')
|
|
108
114
|
const [sendingImageCount, setSendingImageCount] = useState(0)
|
|
115
|
+
const [sendingVideoCount, setSendingVideoCount] = useState(0)
|
|
109
116
|
const [streamedReply, setStreamedReply] = useState('')
|
|
110
117
|
const [streamedResponse, setStreamedResponse] = useState<ChatResponse | null>(null)
|
|
111
118
|
const [streamingConversationId, setStreamingConversationId] = useState<string | null>(null)
|
|
@@ -114,6 +121,7 @@ export default function MintDashboard() {
|
|
|
114
121
|
const [thinkingExpanded, setThinkingExpanded] = useState<Record<string, boolean>>({})
|
|
115
122
|
const liveThinkingOpenRef = useRef(true)
|
|
116
123
|
const [imageAttachments, setImageAttachments] = useState<Array<{ dataUri: string; name: string; previewDataUri?: string }>>([])
|
|
124
|
+
const [videoAttachments, setVideoAttachments] = useState<Array<{ dataUri: string; name: string }>>([])
|
|
117
125
|
const [documentAttachment, setDocumentAttachment] = useState<DocumentAttachment | null>(null)
|
|
118
126
|
const [pendingApproval, setPendingApproval] = useState<any | null>(null)
|
|
119
127
|
const [sessionAutoApproved, setSessionAutoApproved] = useState(false)
|
|
@@ -138,6 +146,7 @@ export default function MintDashboard() {
|
|
|
138
146
|
const [conversationId, setConversationId] = useState(activeConversationId)
|
|
139
147
|
const [chatSessions, setChatSessions] = useState<ChatSession[]>([])
|
|
140
148
|
const chatEnd = useRef<HTMLDivElement | null>(null)
|
|
149
|
+
const lastNativePasteTimeRef = useRef(0)
|
|
141
150
|
const startupReady = (dashboardDataReady && modelReady) || startupTimedOut
|
|
142
151
|
const [proactiveSuggestion, setProactiveSuggestion] = useState<any>(null)
|
|
143
152
|
|
|
@@ -277,9 +286,21 @@ export default function MintDashboard() {
|
|
|
277
286
|
setImageAttachments((current) => [...current, { dataUri: image, previewDataUri, name: 'Screen capture' }])
|
|
278
287
|
})
|
|
279
288
|
})
|
|
289
|
+
const handleWindowFocus = () => {
|
|
290
|
+
getRuntimeStatus().then(setStatus).catch(() => {})
|
|
291
|
+
window.settingsApi?.getSettings?.().then((loaded: any) => {
|
|
292
|
+
if (loaded) {
|
|
293
|
+
setSettingsConfig(loaded)
|
|
294
|
+
applyThemeStyles(loaded)
|
|
295
|
+
}
|
|
296
|
+
}).catch(() => {})
|
|
297
|
+
}
|
|
298
|
+
window.addEventListener('focus', handleWindowFocus)
|
|
299
|
+
|
|
280
300
|
window.api?.onSettingsChanged?.((loaded: any) => {
|
|
281
301
|
setSettingsConfig(loaded)
|
|
282
302
|
applyThemeStyles(loaded)
|
|
303
|
+
getRuntimeStatus().then(setStatus).catch(() => {})
|
|
283
304
|
})
|
|
284
305
|
|
|
285
306
|
const unlistenPromise = listen<any>('tool-approval-requested', (event) => {
|
|
@@ -295,6 +316,7 @@ export default function MintDashboard() {
|
|
|
295
316
|
setProactiveSuggestion(suggestion)
|
|
296
317
|
})
|
|
297
318
|
return () => {
|
|
319
|
+
window.removeEventListener('focus', handleWindowFocus)
|
|
298
320
|
unlistenPromise?.then?.((unlisten) => unlisten?.())
|
|
299
321
|
unlistenSpotlight?.then?.((unlisten) => unlisten?.())
|
|
300
322
|
unlistenVision?.then?.((unlisten) => unlisten?.())
|
|
@@ -404,6 +426,7 @@ export default function MintDashboard() {
|
|
|
404
426
|
setStreamingConversationId(null)
|
|
405
427
|
setSendingMessage('')
|
|
406
428
|
setSendingImageCount(0)
|
|
429
|
+
setSendingVideoCount(0)
|
|
407
430
|
}
|
|
408
431
|
}
|
|
409
432
|
|
|
@@ -411,6 +434,7 @@ export default function MintDashboard() {
|
|
|
411
434
|
promptText: string,
|
|
412
435
|
options: {
|
|
413
436
|
imageAttachments?: Array<{ dataUri: string; name: string; previewDataUri?: string }>
|
|
437
|
+
videoAttachments?: Array<{ dataUri: string; name: string }>
|
|
414
438
|
audioDataUri?: string | null
|
|
415
439
|
documentAttachment?: DocumentAttachment | null
|
|
416
440
|
systemInstruction?: string
|
|
@@ -419,14 +443,17 @@ export default function MintDashboard() {
|
|
|
419
443
|
) {
|
|
420
444
|
if (sending) return
|
|
421
445
|
const outgoingImages = options.imageAttachments ?? []
|
|
446
|
+
const outgoingVideos = options.videoAttachments ?? []
|
|
422
447
|
const outgoingDocument = options.documentAttachment ?? null
|
|
423
448
|
const shouldUseAgentMode = agentMode || promptText.toLowerCase().startsWith('search web:')
|
|
424
449
|
const outgoingImage = outgoingImages.map((img) => img.dataUri).join(' ')
|
|
450
|
+
const outgoingVideo = outgoingVideos.map((vid) => vid.dataUri).join(' ')
|
|
425
451
|
const outgoingImageCount = outgoingImages.length
|
|
426
452
|
setSending(true)
|
|
427
453
|
setStreamingConversationId(conversationId)
|
|
428
454
|
setSendingMessage(promptText)
|
|
429
455
|
setSendingImageCount(outgoingImageCount)
|
|
456
|
+
setSendingVideoCount(outgoingVideos.length)
|
|
430
457
|
setError('')
|
|
431
458
|
setStreamedReply('')
|
|
432
459
|
setStreamedResponse(null)
|
|
@@ -437,6 +464,7 @@ export default function MintDashboard() {
|
|
|
437
464
|
if (options.clearComposer) {
|
|
438
465
|
setMessage('')
|
|
439
466
|
setImageAttachments([])
|
|
467
|
+
setVideoAttachments([])
|
|
440
468
|
setDocumentAttachment(null)
|
|
441
469
|
}
|
|
442
470
|
|
|
@@ -446,6 +474,7 @@ export default function MintDashboard() {
|
|
|
446
474
|
(chunk) => setStreamedReply((current) => `${current}${chunk}`),
|
|
447
475
|
outgoingImage,
|
|
448
476
|
options.audioDataUri ?? null,
|
|
477
|
+
outgoingVideo,
|
|
449
478
|
options.systemInstruction ?? '',
|
|
450
479
|
(progress) => {
|
|
451
480
|
progressSnapshot.push(progress)
|
|
@@ -486,6 +515,7 @@ export default function MintDashboard() {
|
|
|
486
515
|
setAgentActivitySnapshots((current) => mergeActivitySnapshots(current, enrichedHistory))
|
|
487
516
|
await refreshChatSessions()
|
|
488
517
|
await refreshPictures()
|
|
518
|
+
getRuntimeStatus().then(setStatus).catch(() => {})
|
|
489
519
|
setStreamedReply('')
|
|
490
520
|
setStreamedResponse(null)
|
|
491
521
|
} catch (reason) {
|
|
@@ -495,6 +525,7 @@ export default function MintDashboard() {
|
|
|
495
525
|
setStreamingConversationId(null)
|
|
496
526
|
setSendingMessage('')
|
|
497
527
|
setSendingImageCount(0)
|
|
528
|
+
setSendingVideoCount(0)
|
|
498
529
|
}
|
|
499
530
|
}
|
|
500
531
|
|
|
@@ -513,12 +544,91 @@ export default function MintDashboard() {
|
|
|
513
544
|
event.preventDefault()
|
|
514
545
|
const trimmed = message.trim()
|
|
515
546
|
const currentImages = imageAttachments
|
|
547
|
+
const currentVideos = videoAttachments
|
|
516
548
|
const currentDocument = documentAttachment
|
|
517
|
-
const hasAttachments = currentImages.length > 0 || Boolean(currentDocument)
|
|
549
|
+
const hasAttachments = currentImages.length > 0 || currentVideos.length > 0 || Boolean(currentDocument)
|
|
518
550
|
if ((!trimmed && !hasAttachments) || sending) return
|
|
519
|
-
|
|
551
|
+
|
|
552
|
+
if (trimmed.startsWith('/')) {
|
|
553
|
+
const activeP = status?.activeProvider || ''
|
|
554
|
+
const activeM = settingsConfig?.model || ''
|
|
555
|
+
const slashResult = executeSlashCommand(trimmed, {
|
|
556
|
+
activeProvider: activeP,
|
|
557
|
+
activeModel: activeM,
|
|
558
|
+
availableProviders: status?.availableProviders || [],
|
|
559
|
+
workspacePath,
|
|
560
|
+
interactionsCount: interactions.length,
|
|
561
|
+
fastMode: settingsConfig?.fastMode ?? false,
|
|
562
|
+
multiAgent: settingsConfig?.multiAgent ?? false,
|
|
563
|
+
})
|
|
564
|
+
|
|
565
|
+
if (slashResult.handled) {
|
|
566
|
+
setMessage('')
|
|
567
|
+
setImageAttachments([])
|
|
568
|
+
setVideoAttachments([])
|
|
569
|
+
setDocumentAttachment(null)
|
|
570
|
+
|
|
571
|
+
if (slashResult.action === 'set_agent_mode') {
|
|
572
|
+
setAgentMode(true)
|
|
573
|
+
if (slashResult.payload?.prompt) {
|
|
574
|
+
await sendPrompt(slashResult.payload.prompt, { clearComposer: true })
|
|
575
|
+
}
|
|
576
|
+
return
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
if (slashResult.action === 'change_workspace') {
|
|
580
|
+
if (slashResult.payload?.path) {
|
|
581
|
+
setWorkspacePath(slashResult.payload.path)
|
|
582
|
+
} else {
|
|
583
|
+
selectWorkspaceDirectory().then((res) => {
|
|
584
|
+
if (res) setWorkspacePath(res)
|
|
585
|
+
}).catch(() => {})
|
|
586
|
+
}
|
|
587
|
+
} else if (slashResult.action === 'open_image_picker') {
|
|
588
|
+
document.getElementById('vision-file-input')?.click()
|
|
589
|
+
return
|
|
590
|
+
} else if (slashResult.action === 'paste_image') {
|
|
591
|
+
readClipboardImage().then((uri) => {
|
|
592
|
+
if (uri) setImageAttachments((curr) => [...curr, { dataUri: uri, name: 'Clipboard Image' }])
|
|
593
|
+
}).catch(() => {})
|
|
594
|
+
return
|
|
595
|
+
} else if (slashResult.action === 'open_plugins') {
|
|
596
|
+
setView('settings')
|
|
597
|
+
return
|
|
598
|
+
} else if (slashResult.action === 'generate_veo') {
|
|
599
|
+
setView('veo_studio')
|
|
600
|
+
return
|
|
601
|
+
} else if (slashResult.action === 'set_provider_model' && slashResult.payload?.target) {
|
|
602
|
+
const target = slashResult.payload.target
|
|
603
|
+
if (status?.availableProviders.includes(target)) {
|
|
604
|
+
setActiveModel(target).then(() => getRuntimeStatus().then(setStatus)).catch(() => {})
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
if (slashResult.systemText) {
|
|
609
|
+
const systemMsg = {
|
|
610
|
+
id: Date.now(),
|
|
611
|
+
userText: trimmed,
|
|
612
|
+
aiText: slashResult.systemText,
|
|
613
|
+
createdAt: new Date().toISOString(),
|
|
614
|
+
provider: 'system',
|
|
615
|
+
model: 'mint-cli',
|
|
616
|
+
}
|
|
617
|
+
setInteractions((prev) => [...prev, systemMsg])
|
|
618
|
+
saveSystemInteraction(conversationId, trimmed, slashResult.systemText || '', 'system', 'mint-cli').catch(() => {})
|
|
619
|
+
}
|
|
620
|
+
return
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
const promptText = trimmed || (
|
|
625
|
+
currentImages.length > 0 ? (currentImages.length > 1 ? 'Describe these images.' : 'Describe this image.') :
|
|
626
|
+
currentVideos.length > 0 ? (currentVideos.length > 1 ? 'Describe these videos.' : 'Describe this video.') :
|
|
627
|
+
'Summarize this document.'
|
|
628
|
+
)
|
|
520
629
|
await sendPrompt(promptText, {
|
|
521
630
|
imageAttachments: currentImages,
|
|
631
|
+
videoAttachments: currentVideos,
|
|
522
632
|
documentAttachment: currentDocument,
|
|
523
633
|
clearComposer: true,
|
|
524
634
|
})
|
|
@@ -548,6 +658,28 @@ export default function MintDashboard() {
|
|
|
548
658
|
event.target.value = ''
|
|
549
659
|
}
|
|
550
660
|
}
|
|
661
|
+
const MAX_VIDEO_BYTES = 25 * 1024 * 1024 // 25 MB
|
|
662
|
+
async function selectVideo(event: ChangeEvent<HTMLInputElement>) {
|
|
663
|
+
const file = event.target.files?.[0]
|
|
664
|
+
if (!file) return
|
|
665
|
+
try {
|
|
666
|
+
if (file.size > MAX_VIDEO_BYTES) {
|
|
667
|
+
setError(`Video is too large. Maximum allowed is ${Math.round(MAX_VIDEO_BYTES / (1024 * 1024))} MB.`)
|
|
668
|
+
return
|
|
669
|
+
}
|
|
670
|
+
const dataUri = await new Promise<string>((resolve, reject) => {
|
|
671
|
+
const reader = new FileReader()
|
|
672
|
+
reader.onload = () => resolve(reader.result as string)
|
|
673
|
+
reader.onerror = reject
|
|
674
|
+
reader.readAsDataURL(file)
|
|
675
|
+
})
|
|
676
|
+
setVideoAttachments((current) => [...current, { dataUri, name: file.name }])
|
|
677
|
+
} catch (reason) {
|
|
678
|
+
setError(errorMessage(reason))
|
|
679
|
+
} finally {
|
|
680
|
+
event.target.value = ''
|
|
681
|
+
}
|
|
682
|
+
}
|
|
551
683
|
|
|
552
684
|
function pasteImage(clipboardData: DataTransfer) {
|
|
553
685
|
let file: File | null = null
|
|
@@ -575,7 +707,28 @@ export default function MintDashboard() {
|
|
|
575
707
|
}
|
|
576
708
|
}
|
|
577
709
|
|
|
578
|
-
if (!file)
|
|
710
|
+
if (!file) {
|
|
711
|
+
// Fallback for Tauri desktop app where WebKitGTK/WebView clipboard paste events do not provide file handles
|
|
712
|
+
const now = Date.now()
|
|
713
|
+
if (now - lastNativePasteTimeRef.current > 100) {
|
|
714
|
+
lastNativePasteTimeRef.current = now
|
|
715
|
+
readClipboardImage().then((dataUri) => {
|
|
716
|
+
if (dataUri) {
|
|
717
|
+
const name = 'Pasted image'
|
|
718
|
+
createTrimmedImagePreview(dataUri)
|
|
719
|
+
.catch(() => dataUri)
|
|
720
|
+
.then((previewDataUri) => {
|
|
721
|
+
setImageAttachments((current) => [...current, { dataUri, previewDataUri, name }])
|
|
722
|
+
})
|
|
723
|
+
}
|
|
724
|
+
}).catch((err) => {
|
|
725
|
+
console.warn('Failed to read clipboard image via Tauri API:', err)
|
|
726
|
+
})
|
|
727
|
+
}
|
|
728
|
+
return false
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
|
|
579
732
|
|
|
580
733
|
readImage(file)
|
|
581
734
|
.then((dataUri) => {
|
|
@@ -591,6 +744,7 @@ export default function MintDashboard() {
|
|
|
591
744
|
}
|
|
592
745
|
|
|
593
746
|
|
|
747
|
+
|
|
594
748
|
async function selectDocument(event: ChangeEvent<HTMLInputElement>) {
|
|
595
749
|
const file = event.target.files?.[0]
|
|
596
750
|
if (!file) return
|
|
@@ -736,6 +890,11 @@ export default function MintDashboard() {
|
|
|
736
890
|
else if (provider === 'huggingface') providerName = 'HF'
|
|
737
891
|
else if (provider === 'local_openai') providerName = 'Local'
|
|
738
892
|
else if (provider === 'ollama') providerName = 'Ollama'
|
|
893
|
+
else if (provider.startsWith('custom:')) {
|
|
894
|
+
const id = provider.replace(/^custom:/, '')
|
|
895
|
+
const cp = (settingsConfig?.customProviders ?? []).find((p: any) => p.id === id)
|
|
896
|
+
providerName = cp?.displayName || id
|
|
897
|
+
}
|
|
739
898
|
|
|
740
899
|
if (providerName && providerName === provider) {
|
|
741
900
|
providerName = providerName.charAt(0).toUpperCase() + providerName.slice(1)
|
|
@@ -753,13 +912,21 @@ export default function MintDashboard() {
|
|
|
753
912
|
case 'huggingface': return config.hfModel || 'meta-llama/Meta-Llama-3-8B-Instruct'
|
|
754
913
|
case 'local_openai': return config.localModelName || 'llama3'
|
|
755
914
|
case 'ollama': return config.ollamaModel || 'llama3:latest'
|
|
756
|
-
default:
|
|
915
|
+
default: {
|
|
916
|
+
if (provider.startsWith('custom:')) {
|
|
917
|
+
const id = provider.replace(/^custom:/, '')
|
|
918
|
+
const cp = (config.customProviders ?? []).find((p: any) => p.id === id)
|
|
919
|
+
return (config.customModelSelections ?? {})[id] ?? cp?.models[0]?.modelId ?? ''
|
|
920
|
+
}
|
|
921
|
+
return ''
|
|
922
|
+
}
|
|
757
923
|
}
|
|
758
924
|
}
|
|
759
925
|
|
|
760
926
|
async function changeProvider(provider: string) {
|
|
761
927
|
try {
|
|
762
928
|
const config = await window.settingsApi.getSettings()
|
|
929
|
+
if (config.aiProvider === provider) return
|
|
763
930
|
config.aiProvider = provider
|
|
764
931
|
await window.settingsApi.saveSettings(config)
|
|
765
932
|
setSettingsConfig(config)
|
|
@@ -768,7 +935,7 @@ export default function MintDashboard() {
|
|
|
768
935
|
// Record system event in chat history
|
|
769
936
|
const activeModel = getActiveModelName(config, provider)
|
|
770
937
|
const displayName = formatProviderChangeText(provider, activeModel)
|
|
771
|
-
await saveSystemInteraction(conversationId, displayName, 'system', 'provider_change')
|
|
938
|
+
await saveSystemInteraction(conversationId, displayName, '', 'system', 'provider_change')
|
|
772
939
|
await refreshHistory()
|
|
773
940
|
} catch (reason) {
|
|
774
941
|
setError(errorMessage(reason))
|
|
@@ -779,6 +946,8 @@ export default function MintDashboard() {
|
|
|
779
946
|
try {
|
|
780
947
|
const config = await window.settingsApi.getSettings()
|
|
781
948
|
const provider = config.aiProvider
|
|
949
|
+
const currentModel = getActiveModelName(config, provider)
|
|
950
|
+
if (currentModel === modelName) return
|
|
782
951
|
if (provider === 'gemini') {
|
|
783
952
|
config.geminiModel = modelName
|
|
784
953
|
} else if (provider === 'openai') {
|
|
@@ -795,6 +964,12 @@ export default function MintDashboard() {
|
|
|
795
964
|
config.localModelName = modelName
|
|
796
965
|
} else if (provider === 'ollama') {
|
|
797
966
|
config.ollamaModel = modelName
|
|
967
|
+
} else if (provider.startsWith('custom:')) {
|
|
968
|
+
const id = provider.replace(/^custom:/, '')
|
|
969
|
+
config.customModelSelections = {
|
|
970
|
+
...(config.customModelSelections ?? {}),
|
|
971
|
+
[id]: modelName
|
|
972
|
+
}
|
|
798
973
|
}
|
|
799
974
|
await window.settingsApi.saveSettings(config)
|
|
800
975
|
setSettingsConfig(config)
|
|
@@ -802,7 +977,7 @@ export default function MintDashboard() {
|
|
|
802
977
|
|
|
803
978
|
// Record system event in chat history
|
|
804
979
|
const displayName = formatProviderChangeText(provider, modelName)
|
|
805
|
-
await saveSystemInteraction(conversationId, displayName, 'system', 'provider_change')
|
|
980
|
+
await saveSystemInteraction(conversationId, displayName, '', 'system', 'provider_change')
|
|
806
981
|
await refreshHistory()
|
|
807
982
|
} catch (reason) {
|
|
808
983
|
setError(errorMessage(reason))
|
|
@@ -925,7 +1100,7 @@ export default function MintDashboard() {
|
|
|
925
1100
|
expressionIndex={expressionIndex}
|
|
926
1101
|
accessoryIndex={accessoryIndex}
|
|
927
1102
|
isLocked={isLocked}
|
|
928
|
-
isActive={modelVisible && view !== 'pictures' && view !== 'workspace' && view !== 'workflows'}
|
|
1103
|
+
isActive={modelVisible && view !== 'pictures' && view !== 'workspace' && view !== 'workflows' && view !== 'imagine' && view !== 'veo'}
|
|
929
1104
|
layoutPreset={layoutPreset}
|
|
930
1105
|
sending={sending}
|
|
931
1106
|
interactionEnabled={interactionEnabled}
|
|
@@ -947,11 +1122,13 @@ export default function MintDashboard() {
|
|
|
947
1122
|
streamedReply={streamingConversationId === conversationId ? streamedReply : ''}
|
|
948
1123
|
streamedResponse={streamingConversationId === conversationId ? streamedResponse : null}
|
|
949
1124
|
agentProgress={streamingConversationId === conversationId ? agentProgress : []}
|
|
1125
|
+
sendingVideoCount={streamingConversationId === conversationId ? sendingVideoCount : 0}
|
|
950
1126
|
agentActivitySnapshots={agentActivitySnapshots}
|
|
951
1127
|
thinkingExpanded={thinkingExpanded}
|
|
952
1128
|
onThinkingExpandedChange={handleThinkingExpandedChange}
|
|
953
1129
|
message={message}
|
|
954
1130
|
imageAttachments={imageAttachments}
|
|
1131
|
+
videoAttachments={videoAttachments}
|
|
955
1132
|
documentName={documentAttachment?.filename ?? ''}
|
|
956
1133
|
pendingApproval={streamingConversationId === conversationId ? pendingApproval : null}
|
|
957
1134
|
smartContext={smartContext}
|
|
@@ -962,6 +1139,7 @@ export default function MintDashboard() {
|
|
|
962
1139
|
welcomeInteraction={MOCK_WELCOME_INTERACTION}
|
|
963
1140
|
onSubmit={handleSubmit}
|
|
964
1141
|
onSelectImage={selectImage}
|
|
1142
|
+
onSelectVideo={selectVideo}
|
|
965
1143
|
onSelectDocument={selectDocument}
|
|
966
1144
|
onPasteImage={pasteImage}
|
|
967
1145
|
onSetMessage={setMessage}
|
|
@@ -969,6 +1147,9 @@ export default function MintDashboard() {
|
|
|
969
1147
|
onRemoveImage={(idx: number) => {
|
|
970
1148
|
setImageAttachments((current) => current.filter((_, i) => i !== idx))
|
|
971
1149
|
}}
|
|
1150
|
+
onRemoveVideo={(idx: number) => {
|
|
1151
|
+
setVideoAttachments((current) => current.filter((_, i) => i !== idx))
|
|
1152
|
+
}}
|
|
972
1153
|
onRemoveDocument={() => setDocumentAttachment(null)}
|
|
973
1154
|
onStartWebSearch={startWebSearch}
|
|
974
1155
|
onCaptureScreen={captureScreen}
|
|
@@ -991,6 +1172,13 @@ export default function MintDashboard() {
|
|
|
991
1172
|
setMessage(imgPrompt)
|
|
992
1173
|
}}
|
|
993
1174
|
/>
|
|
1175
|
+
<VeoStudioPanel
|
|
1176
|
+
view={view}
|
|
1177
|
+
onSendToChat={(vidPrompt) => {
|
|
1178
|
+
setView('chat')
|
|
1179
|
+
setMessage(vidPrompt)
|
|
1180
|
+
}}
|
|
1181
|
+
/>
|
|
994
1182
|
<WorkflowBuilderPanel
|
|
995
1183
|
view={view}
|
|
996
1184
|
onShowToast={showToast}
|