@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.
Files changed (63) hide show
  1. package/Cargo.lock +3 -3
  2. package/Cargo.toml +1 -1
  3. package/README.md +82 -21
  4. package/assets/Mint_CLI.png +0 -0
  5. package/assets/Mint_Desktop.png +0 -0
  6. package/assets/Mint_Web.png +0 -0
  7. package/assets/Mint_WorkSpace.png +0 -0
  8. package/assets/Setting.png +0 -0
  9. package/assets/bridges.svg +4 -0
  10. package/assets/chat.svg +3 -0
  11. package/assets/code.svg +4 -0
  12. package/assets/features.svg +3 -0
  13. package/assets/imagegen.svg +5 -0
  14. package/assets/live2d.svg +6 -0
  15. package/assets/memory.svg +5 -0
  16. package/assets/screencapture.svg +4 -0
  17. package/assets/setup.svg +3 -0
  18. package/assets/tools.svg +4 -0
  19. package/bin/mint +0 -0
  20. package/crates/mint-cli/src/agent.rs +47 -15
  21. package/crates/mint-cli/src/main.rs +534 -7
  22. package/crates/mint-cli/src/onboard.rs +134 -1
  23. package/crates/mint-cli/src/setup.rs +173 -32
  24. package/crates/mint-core/src/api_server.rs +147 -3
  25. package/crates/mint-core/src/config.rs +22 -3
  26. package/crates/mint-core/src/image_gen.rs +895 -0
  27. package/crates/mint-core/src/lib.rs +4 -0
  28. package/crates/mint-core/src/memory.rs +27 -1
  29. package/crates/mint-core/src/orchestration.rs +1 -1
  30. package/crates/mint-core/src/plugins.rs +32 -0
  31. package/crates/mint-core/tests/memory_persistence.rs +28 -0
  32. package/package.json +1 -1
  33. package/src/bin/index.js +0 -0
  34. package/src/renderer/src/agentProgress.ts +39 -0
  35. package/src/renderer/src/components/ChatPanel.tsx +547 -47
  36. package/src/renderer/src/components/DashboardSidebar.tsx +12 -4
  37. package/src/renderer/src/components/ImageStudioPanel.tsx +638 -0
  38. package/src/renderer/src/components/MintDashboard.tsx +84 -31
  39. package/src/renderer/src/components/ModelPanel.tsx +2 -2
  40. package/src/renderer/src/components/PicturesLibrary.tsx +5 -1
  41. package/src/renderer/src/components/SettingsWindow.tsx +29 -20
  42. package/src/renderer/src/components/SpotlightWindow.tsx +8 -8
  43. package/src/renderer/src/components/WorkspacePanel.tsx +1 -1
  44. package/src/renderer/src/css/image-studio.css +722 -0
  45. package/src/renderer/src/css/settings.css +13 -3
  46. package/src/renderer/src/css/styles.css +245 -43
  47. package/src/renderer/src/index.css +152 -0
  48. package/src/renderer/src/tauri.ts +148 -0
  49. package/src/renderer/src-web/App.tsx +16 -1
  50. package/src/renderer/src-web/agentProgress.ts +39 -0
  51. package/src/renderer/src-web/components/ChatPanel.tsx +568 -46
  52. package/src/renderer/src-web/components/DashboardSidebar.tsx +10 -2
  53. package/src/renderer/src-web/components/ImageStudioPanel.tsx +638 -0
  54. package/src/renderer/src-web/components/MintDashboard.tsx +65 -19
  55. package/src/renderer/src-web/components/PicturesLibrary.tsx +5 -1
  56. package/src/renderer/src-web/components/SettingsWindow.tsx +29 -20
  57. package/src/renderer/src-web/css/image-studio.css +722 -0
  58. package/src/renderer/src-web/css/settings.css +14 -4
  59. package/src/renderer/src-web/css/styles.css +292 -44
  60. package/src/renderer/src-web/index.css +152 -0
  61. package/src/renderer/src-web/tauri.ts +149 -0
  62. package/src-tauri/src/lib.rs +65 -5
  63. 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
- if (route.startsWith('/settings')) content = <SettingsWindow />
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
+ }