@pheem49/mint 1.6.3 → 1.7.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 +3 -3
- package/Cargo.toml +1 -1
- package/README.md +82 -21
- package/assets/Mint_CLI.png +0 -0
- package/assets/Mint_Desktop.png +0 -0
- package/assets/Mint_Web.png +0 -0
- package/assets/Mint_WorkSpace.png +0 -0
- package/assets/Setting.png +0 -0
- package/assets/bridges.svg +4 -0
- package/assets/chat.svg +3 -0
- package/assets/code.svg +4 -0
- package/assets/features.svg +3 -0
- package/assets/imagegen.svg +5 -0
- package/assets/live2d.svg +6 -0
- package/assets/memory.svg +5 -0
- package/assets/screencapture.svg +4 -0
- package/assets/setup.svg +3 -0
- package/assets/tools.svg +4 -0
- package/bin/mint +0 -0
- package/crates/mint-cli/src/agent.rs +47 -15
- package/crates/mint-cli/src/main.rs +534 -7
- package/crates/mint-cli/src/onboard.rs +134 -1
- package/crates/mint-cli/src/setup.rs +173 -32
- package/crates/mint-core/src/api_server.rs +147 -3
- package/crates/mint-core/src/config.rs +22 -3
- package/crates/mint-core/src/image_gen.rs +895 -0
- package/crates/mint-core/src/lib.rs +4 -0
- package/crates/mint-core/src/memory.rs +27 -1
- package/crates/mint-core/src/orchestration.rs +1 -1
- package/crates/mint-core/src/plugins.rs +32 -0
- package/crates/mint-core/tests/memory_persistence.rs +28 -0
- package/package.json +1 -1
- package/src/bin/index.js +0 -0
- package/src/renderer/src/agentProgress.ts +39 -0
- package/src/renderer/src/components/ChatPanel.tsx +547 -47
- package/src/renderer/src/components/DashboardSidebar.tsx +12 -4
- package/src/renderer/src/components/ImageStudioPanel.tsx +638 -0
- package/src/renderer/src/components/MintDashboard.tsx +84 -31
- package/src/renderer/src/components/ModelPanel.tsx +2 -2
- package/src/renderer/src/components/PicturesLibrary.tsx +5 -1
- package/src/renderer/src/components/SettingsWindow.tsx +29 -20
- package/src/renderer/src/components/SpotlightWindow.tsx +8 -8
- package/src/renderer/src/components/WorkspacePanel.tsx +1 -1
- package/src/renderer/src/css/image-studio.css +722 -0
- package/src/renderer/src/css/settings.css +13 -3
- package/src/renderer/src/css/styles.css +245 -43
- package/src/renderer/src/index.css +152 -0
- package/src/renderer/src/tauri.ts +148 -0
- package/src/renderer/src-web/App.tsx +16 -1
- package/src/renderer/src-web/agentProgress.ts +39 -0
- package/src/renderer/src-web/components/ChatPanel.tsx +568 -46
- package/src/renderer/src-web/components/DashboardSidebar.tsx +10 -2
- package/src/renderer/src-web/components/ImageStudioPanel.tsx +638 -0
- package/src/renderer/src-web/components/MintDashboard.tsx +65 -19
- package/src/renderer/src-web/components/PicturesLibrary.tsx +5 -1
- package/src/renderer/src-web/components/SettingsWindow.tsx +29 -20
- package/src/renderer/src-web/css/image-studio.css +722 -0
- package/src/renderer/src-web/css/settings.css +14 -4
- package/src/renderer/src-web/css/styles.css +292 -44
- package/src/renderer/src-web/index.css +152 -0
- package/src/renderer/src-web/tauri.ts +149 -0
- package/src-tauri/src/lib.rs +65 -5
- package/src-tauri/tauri.conf.json +1 -1
|
@@ -42,6 +42,7 @@ export interface InteractionMemory {
|
|
|
42
42
|
model: string
|
|
43
43
|
fallbackProvider?: string | null
|
|
44
44
|
createdAt: string
|
|
45
|
+
agentActivity?: AgentProgress[] | null
|
|
45
46
|
}
|
|
46
47
|
|
|
47
48
|
export interface ChatSession {
|
|
@@ -65,6 +66,29 @@ export interface PictureEntry {
|
|
|
65
66
|
thumbnailUrl?: string
|
|
66
67
|
}
|
|
67
68
|
|
|
69
|
+
export interface ImageGenRequest {
|
|
70
|
+
prompt: string
|
|
71
|
+
negativePrompt?: string
|
|
72
|
+
aspectRatio?: '1:1' | '16:9' | '9:16' | '4:3'
|
|
73
|
+
numImages?: number
|
|
74
|
+
model?: string
|
|
75
|
+
/** Which image generation provider to use. Falls back to config default when omitted. */
|
|
76
|
+
provider?: string
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export interface ImageGenProviders {
|
|
80
|
+
active: string
|
|
81
|
+
available: string[]
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export interface ImageGenResponse {
|
|
85
|
+
images: PictureEntry[]
|
|
86
|
+
model: string
|
|
87
|
+
provider: string
|
|
88
|
+
prompt: string
|
|
89
|
+
description?: string | null
|
|
90
|
+
}
|
|
91
|
+
|
|
68
92
|
export interface WorkspaceTreeEntry {
|
|
69
93
|
name: string
|
|
70
94
|
path: string
|
|
@@ -258,6 +282,30 @@ export async function getRecentInteractions(limit = 50, chatId?: string | null):
|
|
|
258
282
|
return invoke<InteractionMemory[]>('get_recent_interactions', { limit, chatId })
|
|
259
283
|
}
|
|
260
284
|
|
|
285
|
+
export async function saveInteractionAgentActivity(
|
|
286
|
+
interactionId: number,
|
|
287
|
+
activity: AgentProgress[],
|
|
288
|
+
): Promise<void> {
|
|
289
|
+
if (typeof window === 'undefined' || !(window as any).__TAURI_INTERNALS__) {
|
|
290
|
+
const API_BASE = "http://localhost:3000/api";
|
|
291
|
+
try {
|
|
292
|
+
const res = await fetch(`${API_BASE}/interactions/agent-activity`, {
|
|
293
|
+
method: 'POST',
|
|
294
|
+
headers: { 'Content-Type': 'application/json' },
|
|
295
|
+
body: JSON.stringify({ interactionId, activity }),
|
|
296
|
+
});
|
|
297
|
+
if (!res.ok) {
|
|
298
|
+
throw new Error(`HTTP ${res.status}`);
|
|
299
|
+
}
|
|
300
|
+
} catch (error) {
|
|
301
|
+
console.error('Failed to persist agent activity:', error);
|
|
302
|
+
}
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
const { invoke } = await import('@tauri-apps/api/core')
|
|
306
|
+
await invoke('save_interaction_agent_activity', { interactionId, activity })
|
|
307
|
+
}
|
|
308
|
+
|
|
261
309
|
export async function listChatSessions(): Promise<ChatSession[]> {
|
|
262
310
|
if (typeof window === 'undefined' || !(window as any).__TAURI_INTERNALS__) {
|
|
263
311
|
const API_BASE = "http://localhost:3000/api";
|
|
@@ -394,6 +442,106 @@ export async function listSavedPictures(): Promise<PictureEntry[]> {
|
|
|
394
442
|
return invoke<PictureEntry[]>('list_pictures')
|
|
395
443
|
}
|
|
396
444
|
|
|
445
|
+
export async function generateImages(
|
|
446
|
+
request: ImageGenRequest
|
|
447
|
+
): Promise<ImageGenResponse> {
|
|
448
|
+
if (typeof window === 'undefined' || !(window as any).__TAURI_INTERNALS__) {
|
|
449
|
+
const API_BASE = 'http://localhost:3000/api'
|
|
450
|
+
const res = await fetch(`${API_BASE}/image-generate`, {
|
|
451
|
+
method: 'POST',
|
|
452
|
+
headers: { 'Content-Type': 'application/json' },
|
|
453
|
+
body: JSON.stringify({
|
|
454
|
+
prompt: request.prompt,
|
|
455
|
+
negativePrompt: request.negativePrompt,
|
|
456
|
+
aspectRatio: request.aspectRatio ?? '1:1',
|
|
457
|
+
numImages: request.numImages ?? 1,
|
|
458
|
+
model: request.model,
|
|
459
|
+
provider: request.provider,
|
|
460
|
+
}),
|
|
461
|
+
})
|
|
462
|
+
if (!res.ok) {
|
|
463
|
+
const err = await res.json().catch(() => null)
|
|
464
|
+
throw new Error(err?.error || `Image generation failed: HTTP ${res.status}`)
|
|
465
|
+
}
|
|
466
|
+
const data = await res.json()
|
|
467
|
+
// Normalize image URLs for web mode
|
|
468
|
+
const images: PictureEntry[] = Array.isArray(data.images)
|
|
469
|
+
? data.images.map((pic: PictureEntry) => {
|
|
470
|
+
const pictureUrl = pic.url ? `http://localhost:3000${pic.url}` : pic.url
|
|
471
|
+
return { ...pic, url: pictureUrl, thumbnailUrl: pictureUrl }
|
|
472
|
+
})
|
|
473
|
+
: []
|
|
474
|
+
return { ...data, images }
|
|
475
|
+
}
|
|
476
|
+
// Desktop / Tauri mode
|
|
477
|
+
const { invoke } = await import('@tauri-apps/api/core')
|
|
478
|
+
return invoke<ImageGenResponse>('generate_images', { request })
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
/** Fetch which image-generation providers are currently configured on the backend. */
|
|
482
|
+
export async function getImageGenProviders(): Promise<ImageGenProviders> {
|
|
483
|
+
if (typeof window === 'undefined' || !(window as any).__TAURI_INTERNALS__) {
|
|
484
|
+
const API_BASE = 'http://localhost:3000/api'
|
|
485
|
+
try {
|
|
486
|
+
const res = await fetch(`${API_BASE}/image-gen/providers`)
|
|
487
|
+
if (res.ok) return await res.json()
|
|
488
|
+
} catch (_) { /* ignore */ }
|
|
489
|
+
return { active: 'nanobanana', available: ['nanobanana'] }
|
|
490
|
+
}
|
|
491
|
+
// Desktop / Tauri: read config to know which keys are set
|
|
492
|
+
try {
|
|
493
|
+
const { invoke } = await import('@tauri-apps/api/core')
|
|
494
|
+
const config = await invoke<Record<string, string>>('get_config')
|
|
495
|
+
const available: string[] = []
|
|
496
|
+
if (config.api_key) available.push('nanobanana')
|
|
497
|
+
if (config.openai_api_key) available.push('dalle')
|
|
498
|
+
if (config.stability_api_key) available.push('stability')
|
|
499
|
+
if (config.ideogram_api_key) available.push('ideogram')
|
|
500
|
+
if (config.replicate_api_key) available.push('replicate')
|
|
501
|
+
if (available.length === 0) available.push('nanobanana')
|
|
502
|
+
const active = available.includes(config.image_gen_provider)
|
|
503
|
+
? config.image_gen_provider
|
|
504
|
+
: available[0]
|
|
505
|
+
return { active, available }
|
|
506
|
+
} catch (_) {
|
|
507
|
+
return { active: 'nanobanana', available: ['nanobanana'] }
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
/** Updates the default image generation provider in the configuration. */
|
|
512
|
+
export async function setDefaultImageProvider(provider: string): Promise<boolean> {
|
|
513
|
+
if (typeof window === 'undefined' || !(window as any).__TAURI_INTERNALS__) {
|
|
514
|
+
const API_BASE = "http://localhost:3000/api";
|
|
515
|
+
try {
|
|
516
|
+
const getRes = await fetch(`${API_BASE}/config`)
|
|
517
|
+
if (!getRes.ok) return false
|
|
518
|
+
const config = await getRes.json()
|
|
519
|
+
config.image_gen_provider = provider
|
|
520
|
+
const saveRes = await fetch(`${API_BASE}/config`, {
|
|
521
|
+
method: 'POST',
|
|
522
|
+
headers: { 'Content-Type': 'application/json' },
|
|
523
|
+
body: JSON.stringify(config)
|
|
524
|
+
})
|
|
525
|
+
return saveRes.ok
|
|
526
|
+
} catch (_) {
|
|
527
|
+
return false
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
// Desktop / Tauri
|
|
532
|
+
try {
|
|
533
|
+
const { invoke } = await import('@tauri-apps/api/core')
|
|
534
|
+
const config = await invoke<any>('get_config')
|
|
535
|
+
config.image_gen_provider = provider
|
|
536
|
+
await invoke('update_config', { config })
|
|
537
|
+
return true
|
|
538
|
+
} catch (_) {
|
|
539
|
+
return false
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
|
|
544
|
+
|
|
397
545
|
export async function getWorkspaceTree(path?: string | null): Promise<WorkspaceTreeEntry> {
|
|
398
546
|
if (typeof window === 'undefined' || !(window as any).__TAURI_INTERNALS__) {
|
|
399
547
|
return {
|
|
@@ -19,7 +19,22 @@ export default function App() {
|
|
|
19
19
|
|
|
20
20
|
let content = <MintDashboard />
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
// For web, show settings as a centered modal overlay instead of a full-page route
|
|
23
|
+
if (route.startsWith('/settings')) {
|
|
24
|
+
content = (
|
|
25
|
+
<>
|
|
26
|
+
<MintDashboard />
|
|
27
|
+
<div
|
|
28
|
+
className="settings-modal-overlay"
|
|
29
|
+
onClick={() => window.settingsApi?.closeSettings?.()}
|
|
30
|
+
>
|
|
31
|
+
<div className="settings-modal" onClick={(e) => e.stopPropagation()}>
|
|
32
|
+
<SettingsWindow />
|
|
33
|
+
</div>
|
|
34
|
+
</div>
|
|
35
|
+
</>
|
|
36
|
+
)
|
|
37
|
+
}
|
|
23
38
|
|
|
24
39
|
return <Suspense fallback={null}>{content}</Suspense>
|
|
25
40
|
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { AgentProgress, InteractionMemory } from './tauri'
|
|
2
|
+
|
|
3
|
+
export function thoughtsFrom(progress: AgentProgress[]): string[] {
|
|
4
|
+
return progress
|
|
5
|
+
.filter((event) => event.type === 'Thought')
|
|
6
|
+
.map((event) => (event as Extract<AgentProgress, { type: 'Thought' }>).data.thought)
|
|
7
|
+
.filter(Boolean)
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function trimAgentProgress(progress: AgentProgress[], maxNonThought = 20): AgentProgress[] {
|
|
11
|
+
if (progress.length === 0) return progress
|
|
12
|
+
const keptNonThought = new Set(
|
|
13
|
+
progress.filter((event) => event.type !== 'Thought').slice(-maxNonThought),
|
|
14
|
+
)
|
|
15
|
+
return progress.filter((event) => event.type === 'Thought' || keptNonThought.has(event))
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function hasAgentToolActivity(progress: AgentProgress[]): boolean {
|
|
19
|
+
return progress.some((event) => event.type === 'ToolStart' || event.type === 'ToolEnd')
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function mergeActivitySnapshots(
|
|
23
|
+
current: Record<string, AgentProgress[]>,
|
|
24
|
+
interactions: InteractionMemory[],
|
|
25
|
+
): Record<string, AgentProgress[]> {
|
|
26
|
+
const merged = { ...current }
|
|
27
|
+
for (const interaction of interactions) {
|
|
28
|
+
const key = String(interaction.id)
|
|
29
|
+
if (interaction.agentActivity?.length && !merged[key]) {
|
|
30
|
+
merged[key] = interaction.agentActivity
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return merged
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function parseAgentActivity(value: unknown): AgentProgress[] | undefined {
|
|
37
|
+
if (!Array.isArray(value)) return undefined
|
|
38
|
+
return value as AgentProgress[]
|
|
39
|
+
}
|