@pheem49/mint 1.6.2 → 1.6.4

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 (74) hide show
  1. package/.github/workflows/ci.yml +14 -0
  2. package/Cargo.lock +3 -3
  3. package/Cargo.toml +1 -1
  4. package/README.md +82 -21
  5. package/assets/Mint_CLI.png +0 -0
  6. package/assets/Mint_Desktop.png +0 -0
  7. package/assets/Mint_Web.png +0 -0
  8. package/assets/Mint_WorkSpace.png +0 -0
  9. package/assets/Setting.png +0 -0
  10. package/assets/bridges.svg +4 -0
  11. package/assets/chat.svg +3 -0
  12. package/assets/code.svg +4 -0
  13. package/assets/features.svg +3 -0
  14. package/assets/imagegen.svg +5 -0
  15. package/assets/live2d.svg +6 -0
  16. package/assets/memory.svg +5 -0
  17. package/assets/screencapture.svg +4 -0
  18. package/assets/setup.svg +3 -0
  19. package/assets/tools.svg +4 -0
  20. package/bin/mint +0 -0
  21. package/crates/mint-cli/src/agent.rs +6 -2
  22. package/crates/mint-cli/src/main.rs +145 -5
  23. package/crates/mint-cli/src/onboard.rs +134 -1
  24. package/crates/mint-core/src/api_server.rs +108 -3
  25. package/crates/mint-core/src/config.rs +20 -1
  26. package/crates/mint-core/src/image_gen.rs +895 -0
  27. package/crates/mint-core/src/lib.rs +4 -0
  28. package/package.json +3 -3
  29. package/src/bin/index.js +0 -0
  30. package/src/renderer/index-web.html +1 -1
  31. package/src/renderer/index.html +1 -1
  32. package/src/renderer/src/components/ChatPanel.tsx +252 -26
  33. package/src/renderer/src/components/DashboardSidebar.tsx +11 -3
  34. package/src/renderer/src/components/ImageStudioPanel.tsx +638 -0
  35. package/src/renderer/src/components/MintDashboard.tsx +14 -5
  36. package/src/renderer/src/components/SettingsWindow.tsx +29 -20
  37. package/src/renderer/src/css/image-studio.css +722 -0
  38. package/src/renderer/src/css/settings.css +13 -3
  39. package/src/renderer/src/css/styles.css +50 -26
  40. package/src/renderer/src/index.css +106 -0
  41. package/src/renderer/src/tauri.ts +123 -0
  42. package/src/renderer/src-web/components/ChatPanel.tsx +273 -25
  43. package/src/renderer/src-web/components/DashboardSidebar.tsx +11 -3
  44. package/src/renderer/src-web/components/ImageStudioPanel.tsx +638 -0
  45. package/src/renderer/src-web/components/MintDashboard.tsx +13 -4
  46. package/src/renderer/src-web/components/SettingsWindow.tsx +29 -20
  47. package/src/renderer/src-web/css/image-studio.css +722 -0
  48. package/src/renderer/src-web/css/settings.css +14 -4
  49. package/src/renderer/src-web/css/styles.css +50 -26
  50. package/src/renderer/src-web/index.css +106 -0
  51. package/src/renderer/src-web/tauri.ts +124 -0
  52. package/src-tauri/Cargo.toml +29 -0
  53. package/src-tauri/build.rs +3 -0
  54. package/src-tauri/capabilities/main.json +7 -0
  55. package/src-tauri/gen/schemas/acl-manifests.json +1 -0
  56. package/src-tauri/gen/schemas/capabilities.json +1 -0
  57. package/src-tauri/gen/schemas/desktop-schema.json +2412 -0
  58. package/src-tauri/gen/schemas/linux-schema.json +2412 -0
  59. package/src-tauri/src/browser.rs +141 -0
  60. package/src-tauri/src/desktop.rs +392 -0
  61. package/src-tauri/src/discord_rpc.rs +99 -0
  62. package/src-tauri/src/events.rs +70 -0
  63. package/src-tauri/src/headless.rs +222 -0
  64. package/src-tauri/src/integrations.rs +126 -0
  65. package/src-tauri/src/lib.rs +1076 -0
  66. package/src-tauri/src/main.rs +3 -0
  67. package/src-tauri/src/plugins.rs +16 -0
  68. package/src-tauri/src/proactive.rs +254 -0
  69. package/src-tauri/src/system.rs +250 -0
  70. package/src-tauri/src/updater.rs +148 -0
  71. package/src-tauri/src/webhooks.rs +255 -0
  72. package/src-tauri/src/workflows.rs +70 -0
  73. package/src-tauri/tauri.conf.json +48 -0
  74. package/tsconfig.json +1 -1
@@ -260,6 +260,64 @@ function renderApprovalDetails(approval: any): ApprovalDetails {
260
260
  return { title: 'Unknown Action', body: JSON.stringify(approval, null, 2), reason: 'Requires approval to proceed.', isDangerous: false }
261
261
  }
262
262
 
263
+ function renderDiff(diffText: string) {
264
+ if (!diffText) return null
265
+ const lines = diffText.split('\n')
266
+ return (
267
+ <div style={{
268
+ background: '#0b0f19',
269
+ borderRadius: '6px',
270
+ padding: '8px',
271
+ border: '1px solid rgba(255, 255, 255, 0.08)',
272
+ overflowX: 'auto',
273
+ maxHeight: '400px',
274
+ fontFamily: 'monospace',
275
+ fontSize: '0.74rem',
276
+ lineHeight: '1.4',
277
+ }}>
278
+ {lines.map((line, idx) => {
279
+ let style: any = {
280
+ whiteSpace: 'pre-wrap',
281
+ padding: '2px 6px',
282
+ }
283
+
284
+ if (line.startsWith('@@') || line.startsWith('---') || line.startsWith('+++')) {
285
+ style = {
286
+ ...style,
287
+ color: '#64748b',
288
+ fontWeight: 'bold',
289
+ }
290
+ } else if (line.startsWith('+')) {
291
+ style = {
292
+ ...style,
293
+ background: 'rgba(16, 185, 129, 0.12)',
294
+ borderLeft: '3px solid #10b981',
295
+ color: '#a7f3d0'
296
+ }
297
+ } else if (line.startsWith('-')) {
298
+ style = {
299
+ ...style,
300
+ background: 'rgba(239, 68, 68, 0.12)',
301
+ borderLeft: '3px solid #ef4444',
302
+ color: '#fca5a5'
303
+ }
304
+ } else {
305
+ style = {
306
+ ...style,
307
+ color: '#e2e8f0',
308
+ }
309
+ }
310
+
311
+ return (
312
+ <div key={idx} style={style}>
313
+ {line}
314
+ </div>
315
+ )
316
+ })}
317
+ </div>
318
+ )
319
+ }
320
+
263
321
  interface ChatPanelProps {
264
322
  interactions: any[]
265
323
  sending: boolean
@@ -298,55 +356,171 @@ interface ChatPanelProps {
298
356
  onSetModel: (model: string) => void
299
357
  }
300
358
 
359
+ function ChatCodeBlock({ code, language }: { code: string; language: string; key?: any }) {
360
+ const [copied, setCopied] = useState(false)
361
+
362
+ const handleCopy = async () => {
363
+ try {
364
+ await navigator.clipboard.writeText(code)
365
+ setCopied(true)
366
+ setTimeout(() => setCopied(false), 2000)
367
+ } catch (err) {
368
+ console.error('Failed to copy code: ', err)
369
+ }
370
+ }
371
+
372
+ const handleDownload = () => {
373
+ try {
374
+ const blob = new Blob([code], { type: 'text/plain;charset=utf-8' })
375
+ const url = URL.createObjectURL(blob)
376
+ const link = document.createElement('a')
377
+ link.href = url
378
+ link.download = `code.${language === 'plaintext' ? 'txt' : language}`
379
+ link.click()
380
+ URL.revokeObjectURL(url)
381
+ } catch (err) {
382
+ console.error('Failed to download code: ', err)
383
+ }
384
+ }
385
+
386
+ const displayLang = language.charAt(0).toUpperCase() + language.slice(1).toLowerCase()
387
+
388
+ return (
389
+ <div className="chat-code-block-container" style={{ whiteSpace: 'normal' }}>
390
+ <div className="chat-code-block-header">
391
+ <span className="chat-code-block-lang">{displayLang}</span>
392
+ <div className="chat-code-block-actions">
393
+ <button type="button" onClick={handleDownload} title="Download code" className="chat-code-action-btn">
394
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
395
+ <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" />
396
+ <polyline points="7 10 12 15 17 10" />
397
+ <line x1="12" y1="15" x2="12" y2="3" />
398
+ </svg>
399
+ </button>
400
+ <button type="button" onClick={handleCopy} title="Copy code" className="chat-code-action-btn">
401
+ {copied ? (
402
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="var(--accent)" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round">
403
+ <polyline points="20 6 9 17 4 12" />
404
+ </svg>
405
+ ) : (
406
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
407
+ <rect x="9" y="9" width="13" height="13" rx="2" ry="2" />
408
+ <path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" />
409
+ </svg>
410
+ )}
411
+ </button>
412
+ </div>
413
+ </div>
414
+ <pre className="chat-code-block-body">
415
+ <code>{code}</code>
416
+ </pre>
417
+ </div>
418
+ )
419
+ }
420
+
301
421
  function renderFormattedMessage(text: string) {
302
422
  const displayText = readableAssistantText(text)
303
423
  if (!displayText) return null
304
-
305
- const lines = displayText.split('\n')
306
- return lines.map((line, lineIndex) => {
307
- const headerMatch = line.match(/^(#{1,6})\s+(.*)$/)
308
-
309
- const formatInline = (str: string) => {
310
- const parts = str.split(/\*\*([\s\S]*?)\*\*/g)
311
- return parts.map((part, partIndex) => {
312
- if (partIndex % 2 === 1) {
424
+
425
+ const formatInline = (str: string) => {
426
+ const codeParts = str.split(/`([\s\S]*?)`/g)
427
+ return codeParts.map((codePart, codeIndex) => {
428
+ if (codeIndex % 2 === 1) {
429
+ return (
430
+ <code key={`code-${codeIndex}`} className="chat-inline-code">
431
+ {codePart}
432
+ </code>
433
+ )
434
+ }
435
+ const boldParts = codePart.split(/\*\*([\s\S]*?)\*\*/g)
436
+ return boldParts.map((boldPart, boldIndex) => {
437
+ if (boldIndex % 2 === 1) {
313
438
  return (
314
- <strong key={partIndex} className="chat-bold-highlight">
315
- {part}
439
+ <strong key={`bold-${boldIndex}`} className="chat-bold-highlight">
440
+ {boldPart}
316
441
  </strong>
317
442
  )
318
443
  }
319
- return part
444
+ return boldPart
320
445
  })
446
+ })
447
+ }
448
+
449
+ const lines = displayText.split('\n')
450
+ const items: any[] = []
451
+
452
+ let inCodeBlock = false
453
+ let codeBlockLang = ''
454
+ let codeBlockLines: string[] = []
455
+
456
+ for (let i = 0; i < lines.length; i++) {
457
+ const line = lines[i]
458
+
459
+ if (line.trim().startsWith('```')) {
460
+ if (inCodeBlock) {
461
+ const codeText = codeBlockLines.join('\n')
462
+ items.push(
463
+ <ChatCodeBlock
464
+ key={`code-block-${i}`}
465
+ code={codeText}
466
+ language={codeBlockLang}
467
+ />
468
+ )
469
+ inCodeBlock = false
470
+ codeBlockLines = []
471
+ } else {
472
+ inCodeBlock = true
473
+ codeBlockLang = line.trim().slice(3).trim() || 'plaintext'
474
+ }
475
+ continue
321
476
  }
322
477
 
478
+ if (inCodeBlock) {
479
+ codeBlockLines.push(line)
480
+ continue
481
+ }
482
+
483
+ const headerMatch = line.match(/^(#{1,6})\s+(.*)$/)
323
484
  if (headerMatch) {
324
485
  const level = headerMatch[1].length
325
486
  const content = headerMatch[2]
326
-
487
+
327
488
  const style = {
328
489
  fontWeight: 'bold',
329
490
  display: 'block',
330
491
  marginTop: level === 1 ? '16px' : level === 2 ? '14px' : '10px',
331
492
  marginBottom: '6px',
332
493
  fontSize: level === 1 ? '1.25em' : level === 2 ? '1.15em' : '1.05em',
333
- color: 'var(--text-main)'
494
+ color: 'var(--text-main)',
334
495
  }
335
-
336
- return (
337
- <span key={lineIndex} style={style}>
496
+
497
+ items.push(
498
+ <span key={`line-${i}`} style={style}>
338
499
  {formatInline(content)}
339
500
  </span>
340
501
  )
502
+ } else {
503
+ items.push(
504
+ <Fragment key={`line-${i}`}>
505
+ {formatInline(line)}
506
+ {i < lines.length - 1 && '\n'}
507
+ </Fragment>
508
+ )
341
509
  }
510
+ }
342
511
 
343
- return (
344
- <Fragment key={lineIndex}>
345
- {formatInline(line)}
346
- {lineIndex < lines.length - 1 && '\n'}
347
- </Fragment>
512
+ if (inCodeBlock && codeBlockLines.length > 0) {
513
+ const codeText = codeBlockLines.join('\n')
514
+ items.push(
515
+ <ChatCodeBlock
516
+ key={`code-block-end`}
517
+ code={codeText}
518
+ language={codeBlockLang}
519
+ />
348
520
  )
349
- })
521
+ }
522
+
523
+ return items
350
524
  }
351
525
 
352
526
  function readableAssistantText(text: string) {
@@ -607,6 +781,25 @@ export default function ChatPanel({
607
781
  }
608
782
  const activeModel = getActiveModel(activeProvider)
609
783
 
784
+ const [elapsedSeconds, setElapsedSeconds] = useState(0)
785
+
786
+ useEffect(() => {
787
+ if (!sending) {
788
+ setElapsedSeconds(0)
789
+ return
790
+ }
791
+
792
+ const startTime = Date.now()
793
+ setElapsedSeconds(0)
794
+
795
+ const timer = setInterval(() => {
796
+ const elapsed = Math.round((Date.now() - startTime) / 1000)
797
+ setElapsedSeconds(elapsed)
798
+ }, 1000)
799
+
800
+ return () => clearInterval(timer)
801
+ }, [sending])
802
+
610
803
  const [isRecording, setIsRecording] = useState(false)
611
804
  const [voiceMode, setVoiceMode] = useState(false)
612
805
  const [voiceTranscript, setVoiceTranscript] = useState('')
@@ -1319,7 +1512,7 @@ export default function ChatPanel({
1319
1512
  </button>
1320
1513
  <div className="chat-header-title">
1321
1514
  <img src="./assets/icon.png" alt="Logo" className="chat-header-logo" />
1322
- <span>Agent Mint</span>
1515
+ <span>Mint Agent</span>
1323
1516
  </div>
1324
1517
  </div>
1325
1518
 
@@ -1425,7 +1618,30 @@ export default function ChatPanel({
1425
1618
  {renderActiveFileChanges()}
1426
1619
  <div className="message ai-message thinking-message">
1427
1620
  <div className="bubble-wrapper">
1428
- <div className="message-bubble"><span>{streamedReply ? renderFormattedMessage(streamedReply) : 'Thinking...'}</span></div>
1621
+ <div className="message-bubble">
1622
+ <span>
1623
+ {streamedReply ? (
1624
+ renderFormattedMessage(streamedReply)
1625
+ ) : (
1626
+ <div style={{ display: 'flex', alignItems: 'center', gap: '8px', color: 'var(--text-soft, #94a3b8)' }}>
1627
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" strokeWidth="3" strokeLinecap="round" strokeLinejoin="round" style={{ display: 'inline-block', flexShrink: 0 }}>
1628
+ <circle cx="12" cy="12" r="10" stroke="rgba(255, 255, 255, 0.12)" />
1629
+ <path d="M12 2a10 10 0 0 1 10 10" stroke="var(--accent)" strokeLinecap="round">
1630
+ <animateTransform
1631
+ attributeName="transform"
1632
+ type="rotate"
1633
+ from="0 12 12"
1634
+ to="360 12 12"
1635
+ dur="0.9s"
1636
+ repeatCount="indefinite"
1637
+ />
1638
+ </path>
1639
+ </svg>
1640
+ <span>Thinking for {elapsedSeconds}s</span>
1641
+ </div>
1642
+ )}
1643
+ </span>
1644
+ </div>
1429
1645
  {streamedResponse && (
1430
1646
  <div className="message-time" style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
1431
1647
  <button className="provider-badge">{badge(streamedResponse.provider, streamedResponse.model)}</button>
@@ -1459,6 +1675,38 @@ export default function ChatPanel({
1459
1675
  </div>
1460
1676
  )}
1461
1677
 
1678
+ {pendingApproval && (() => {
1679
+ const details = renderApprovalDetails(pendingApproval.approval)
1680
+ const writeFile = pendingApproval.approval?.WriteFile
1681
+ const applyPatch = pendingApproval.approval?.ApplyPatch
1682
+ const diffText = writeFile?.diff || applyPatch?.diff
1683
+
1684
+ return (
1685
+ <div className="message ai-message" style={{ width: '100%' }}>
1686
+ <div className="bubble-wrapper" style={{ width: '100%' }}>
1687
+ <div className="action-card approval-card" data-tier={details.isDangerous ? 'dangerous' : undefined} style={{ width: '100%' }}>
1688
+ <div className="approval-card-content" style={{ width: '100%' }}>
1689
+ <div className="approval-card-title">{details.title}</div>
1690
+ <div className="approval-card-body" style={{ whiteSpace: 'pre-wrap', fontFamily: 'monospace' }}>{details.body}</div>
1691
+ {diffText ? (
1692
+ <div className="approval-card-diff-container" style={{ marginTop: '8px', width: '100%' }}>
1693
+ <div style={{ fontSize: '0.8rem', fontWeight: 600, color: 'var(--text-soft, #94a3b8)', marginBottom: '4px' }}>Diff:</div>
1694
+ {renderDiff(diffText)}
1695
+ </div>
1696
+ ) : (
1697
+ details.reason && <div className="approval-card-reason" style={{ whiteSpace: 'pre-wrap', fontFamily: 'monospace' }}>{details.reason}</div>
1698
+ )}
1699
+ </div>
1700
+ <div className="approval-card-actions">
1701
+ <button type="button" className="approval-btn approval-btn-approve" onClick={() => onApproval(true)}>Approve</button>
1702
+ <button type="button" className="approval-btn" style={{ backgroundColor: 'rgba(16, 185, 129, 0.22)', borderColor: 'rgba(16, 185, 129, 0.4)', color: '#a7f3d0' }} onClick={() => onApproval(true, true)}>Approve this session</button>
1703
+ <button type="button" className="approval-btn approval-btn-cancel" onClick={() => onApproval(false)}>Cancel</button>
1704
+ </div>
1705
+ </div>
1706
+ </div>
1707
+ </div>
1708
+ )
1709
+ })()}
1462
1710
  <div ref={chatEnd} />
1463
1711
  </div>
1464
1712
 
@@ -1,6 +1,6 @@
1
1
  import { useState } from 'react'
2
2
 
3
- export type DashboardView = 'chat' | 'pictures' | 'model'
3
+ export type DashboardView = 'chat' | 'pictures' | 'model' | 'imagine'
4
4
 
5
5
  interface ChatSessionItem {
6
6
  id: string
@@ -63,8 +63,8 @@ export default function DashboardSidebar({
63
63
  if (event.key === 'Enter' || event.key === ' ') onToggleSidebar()
64
64
  }}
65
65
  >
66
- <img src="./assets/icon.png" alt="Agent Mint Logo" className="sidebar-logo" />
67
- <span className="sidebar-brand-name">Agent Mint</span>
66
+ <img src="./assets/icon.png" alt="Mint Agent Logo" className="sidebar-logo" />
67
+ <span className="sidebar-brand-name">Mint Agent</span>
68
68
  </div>
69
69
 
70
70
  <button className="sidebar-new-chat" onClick={() => onClearHistory('New chat')}>
@@ -95,6 +95,14 @@ export default function DashboardSidebar({
95
95
  </span>
96
96
  <span>Pictures</span>
97
97
  </button>
98
+ <button className={`sidebar-top-action ${view === 'imagine' ? 'is-active' : ''}`} onClick={() => onSetView('imagine')}>
99
+ <span aria-hidden="true" style={{ display: 'inline-flex', alignItems: 'center' }}>
100
+ <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">
101
+ <polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"></polygon>
102
+ </svg>
103
+ </span>
104
+ <span>Image Studio</span>
105
+ </button>
98
106
 
99
107
  <div className="sidebar-section">
100
108
  <div className="sidebar-section-title">Conversation CLI</div>