@pheem49/mint 1.6.4 → 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 (34) hide show
  1. package/Cargo.toml +1 -1
  2. package/bin/mint +0 -0
  3. package/crates/mint-cli/src/agent.rs +42 -14
  4. package/crates/mint-cli/src/main.rs +389 -2
  5. package/crates/mint-cli/src/setup.rs +173 -32
  6. package/crates/mint-core/src/api_server.rs +39 -0
  7. package/crates/mint-core/src/config.rs +2 -2
  8. package/crates/mint-core/src/memory.rs +27 -1
  9. package/crates/mint-core/src/orchestration.rs +1 -1
  10. package/crates/mint-core/src/plugins.rs +32 -0
  11. package/crates/mint-core/tests/memory_persistence.rs +28 -0
  12. package/package.json +1 -1
  13. package/src/renderer/src/agentProgress.ts +39 -0
  14. package/src/renderer/src/components/ChatPanel.tsx +308 -34
  15. package/src/renderer/src/components/DashboardSidebar.tsx +3 -3
  16. package/src/renderer/src/components/MintDashboard.tsx +71 -27
  17. package/src/renderer/src/components/ModelPanel.tsx +2 -2
  18. package/src/renderer/src/components/PicturesLibrary.tsx +5 -1
  19. package/src/renderer/src/components/SpotlightWindow.tsx +8 -8
  20. package/src/renderer/src/components/WorkspacePanel.tsx +1 -1
  21. package/src/renderer/src/css/styles.css +195 -17
  22. package/src/renderer/src/index.css +46 -0
  23. package/src/renderer/src/tauri.ts +25 -0
  24. package/src/renderer/src-web/App.tsx +16 -1
  25. package/src/renderer/src-web/agentProgress.ts +39 -0
  26. package/src/renderer/src-web/components/ChatPanel.tsx +309 -35
  27. package/src/renderer/src-web/components/DashboardSidebar.tsx +1 -1
  28. package/src/renderer/src-web/components/MintDashboard.tsx +53 -16
  29. package/src/renderer/src-web/components/PicturesLibrary.tsx +5 -1
  30. package/src/renderer/src-web/css/styles.css +242 -18
  31. package/src/renderer/src-web/index.css +46 -0
  32. package/src/renderer/src-web/tauri.ts +25 -0
  33. package/src-tauri/src/lib.rs +17 -0
  34. package/src-tauri/tauri.conf.json +1 -1
@@ -1,4 +1,5 @@
1
1
  import { type ChangeEvent, type FormEvent, useEffect, useRef, useState } from 'react'
2
+ import { mergeActivitySnapshots, trimAgentProgress } from '../agentProgress'
2
3
  import {
3
4
  clearChatHistory,
4
5
  deleteChatSession,
@@ -7,6 +8,7 @@ import {
7
8
  getRuntimeStatus,
8
9
  listChatSessions,
9
10
  listSavedPictures,
11
+ saveInteractionAgentActivity,
10
12
  streamChatMessage,
11
13
  submitToolApproval,
12
14
  listen,
@@ -57,7 +59,7 @@ function activeConversationId() {
57
59
  const MOCK_WELCOME_INTERACTION = {
58
60
  id: -1,
59
61
  userText: '',
60
- aiText: `มิ้นท์กำลังรอสแตนด์บายเตรียมพร้อมช่วยคุณทีมอยู่เลยค่ะ! ✨ แล้วก็แอบนั่งจัดระเบียบข้อมูลนิดๆ หน่อยๆ ให้พร้อมใช้ด้วยค่ะ 😊💖\n\nแต่พอคุณทีมทักมา มิ้นท์ก็วางมือจากทุกอย่างมาคุยกับคุณทีมก่อนเลยนะคะค้าา! ช่วงนี้มีอะไรให้มิ้นท์ช่วยดูแล หรืออยากชวนคุยเรื่องไหนเป็นพิเศษไหมคะ มิ้นท์พร้อมมว๊ากกกค่ะ! 🚀🎯`,
62
+ aiText: `Hi there! I'm Mint, your AI assistant! 🎯✨ I'm here and ready to help you with whatever you need. I've been organizing some background data to make things smoother for you. 💖\n\nBut the moment you start chatting with me, I'll put everything aside and focus on you! Is there something I can help you with today, or would you like to chat about something special? Let's do this! 🚀💪`,
61
63
  provider: 'gemini',
62
64
  model: 'gemini-3-flash-preview',
63
65
  createdAt: new Date().toISOString(),
@@ -212,8 +214,11 @@ export default function MintDashboard() {
212
214
  const [sendingImageCount, setSendingImageCount] = useState(0)
213
215
  const [streamedReply, setStreamedReply] = useState('')
214
216
  const [streamedResponse, setStreamedResponse] = useState<ChatResponse | null>(null)
217
+ const [streamingConversationId, setStreamingConversationId] = useState<string | null>(null)
215
218
  const [agentProgress, setAgentProgress] = useState<AgentProgress[]>([])
216
219
  const [agentActivitySnapshots, setAgentActivitySnapshots] = useState<Record<string, AgentProgress[]>>({})
220
+ const [thinkingExpanded, setThinkingExpanded] = useState<Record<string, boolean>>({})
221
+ const liveThinkingOpenRef = useRef(true)
217
222
  const [imageAttachments, setImageAttachments] = useState<Array<{ dataUri: string; name: string; previewDataUri?: string }>>([])
218
223
  const [documentAttachment, setDocumentAttachment] = useState<DocumentAttachment | null>(null)
219
224
  const [pendingApproval, setPendingApproval] = useState<any | null>(null)
@@ -232,7 +237,9 @@ export default function MintDashboard() {
232
237
 
233
238
  async function refreshHistory() {
234
239
  const history = await getRecentInteractions(50, conversationId)
235
- setInteractions(history.reverse())
240
+ const reversed = history.reverse()
241
+ setInteractions(reversed)
242
+ setAgentActivitySnapshots((current) => mergeActivitySnapshots(current, reversed))
236
243
  }
237
244
 
238
245
  async function refreshChatSessions(nextActiveId = conversationId) {
@@ -347,12 +354,15 @@ export default function MintDashboard() {
347
354
  const outgoingImage = outgoingImages.map((img) => img.dataUri).join(' ')
348
355
  const outgoingImageCount = outgoingImages.length
349
356
  setSending(true)
357
+ setStreamingConversationId(conversationId)
350
358
  setSendingMessage(promptText)
351
359
  setSendingImageCount(outgoingImageCount)
352
360
  setError('')
353
361
  setStreamedReply('')
354
362
  setStreamedResponse(null)
355
363
  setAgentProgress([])
364
+ liveThinkingOpenRef.current = true
365
+ setThinkingExpanded((current) => ({ ...current, live: true }))
356
366
  const progressSnapshot: AgentProgress[] = []
357
367
  if (options.clearComposer) {
358
368
  setMessage('')
@@ -369,7 +379,7 @@ export default function MintDashboard() {
369
379
  options.systemInstruction ?? '',
370
380
  (progress) => {
371
381
  progressSnapshot.push(progress)
372
- setAgentProgress((current) => [...current, progress].slice(-24))
382
+ setAgentProgress((current) => trimAgentProgress([...current, progress]))
373
383
  },
374
384
  outgoingDocument,
375
385
  null,
@@ -377,18 +387,33 @@ export default function MintDashboard() {
377
387
  )
378
388
  setStreamedResponse(response)
379
389
  const history = (await getRecentInteractions(50, conversationId)).reverse()
390
+ let enrichedHistory = history
380
391
  if (progressSnapshot.length > 0) {
381
392
  const newestInteraction = [...history]
382
393
  .reverse()
383
394
  .find((interaction) => interaction.aiText === response.text || interaction.userText === promptText) ?? history[history.length - 1]
384
395
  if (newestInteraction?.id != null) {
396
+ const interactionKey = String(newestInteraction.id)
397
+ enrichedHistory = history.map((interaction) =>
398
+ interaction.id === newestInteraction.id
399
+ ? { ...interaction, agentActivity: progressSnapshot }
400
+ : interaction,
401
+ )
385
402
  setAgentActivitySnapshots((current) => ({
386
403
  ...current,
387
- [String(newestInteraction.id)]: progressSnapshot.slice(),
404
+ [interactionKey]: progressSnapshot.slice(),
388
405
  }))
406
+ if (liveThinkingOpenRef.current) {
407
+ setThinkingExpanded((current) => ({
408
+ ...current,
409
+ [interactionKey]: true,
410
+ }))
411
+ }
412
+ await saveInteractionAgentActivity(newestInteraction.id, progressSnapshot)
389
413
  }
390
414
  }
391
- setInteractions(history)
415
+ setInteractions(enrichedHistory)
416
+ setAgentActivitySnapshots((current) => mergeActivitySnapshots(current, enrichedHistory))
392
417
  await refreshChatSessions()
393
418
  await refreshPictures()
394
419
  setStreamedReply('')
@@ -397,6 +422,7 @@ export default function MintDashboard() {
397
422
  setError(errorMessage(reason))
398
423
  } finally {
399
424
  setSending(false)
425
+ setStreamingConversationId(null)
400
426
  setSendingMessage('')
401
427
  setSendingImageCount(0)
402
428
  }
@@ -418,7 +444,7 @@ export default function MintDashboard() {
418
444
  }
419
445
 
420
446
  async function sendVoiceMessage(transcript: string, audioDataUri?: string | null) {
421
- const promptText = transcript.trim() || 'ข้อความเสียง'
447
+ const promptText = transcript.trim() || 'Voice message'
422
448
  if (!promptText || sending) return
423
449
  await sendPrompt(promptText, {
424
450
  audioDataUri,
@@ -568,6 +594,11 @@ export default function MintDashboard() {
568
594
  }
569
595
  }
570
596
 
597
+ function handleThinkingExpandedChange(key: string, open: boolean) {
598
+ if (key === 'live') liveThinkingOpenRef.current = open
599
+ setThinkingExpanded((current) => ({ ...current, [key]: open }))
600
+ }
601
+
571
602
  async function selectConversation(id: string) {
572
603
  if (id === conversationId) {
573
604
  changeView('chat')
@@ -583,7 +614,9 @@ export default function MintDashboard() {
583
614
  setDocumentAttachment(null)
584
615
  setAgentProgress([])
585
616
  const history = await getRecentInteractions(50, id)
586
- setInteractions(history.reverse())
617
+ const reversed = history.reverse()
618
+ setInteractions(reversed)
619
+ setAgentActivitySnapshots((current) => mergeActivitySnapshots(current, reversed))
587
620
  }
588
621
 
589
622
  async function deleteConversation(id: string) {
@@ -604,7 +637,9 @@ export default function MintDashboard() {
604
637
  setConversationId(nextActive)
605
638
  setAgentProgress([])
606
639
  const history = await getRecentInteractions(50, nextActive)
607
- setInteractions(history.reverse())
640
+ const reversed = history.reverse()
641
+ setInteractions(reversed)
642
+ setAgentActivitySnapshots((current) => mergeActivitySnapshots(current, reversed))
608
643
  }
609
644
 
610
645
  await refreshChatSessions(nextActive)
@@ -707,17 +742,19 @@ export default function MintDashboard() {
707
742
  <main className="assistant-workspace model-hidden">
708
743
  <ChatPanel
709
744
  interactions={interactions}
710
- sending={sending}
711
- sendingMessage={sendingMessage}
712
- sendingImageCount={sendingImageCount}
713
- streamedReply={streamedReply}
714
- streamedResponse={streamedResponse}
715
- agentProgress={agentProgress}
745
+ sending={sending && streamingConversationId === conversationId}
746
+ sendingMessage={streamingConversationId === conversationId ? sendingMessage : ''}
747
+ sendingImageCount={streamingConversationId === conversationId ? sendingImageCount : 0}
748
+ streamedReply={streamingConversationId === conversationId ? streamedReply : ''}
749
+ streamedResponse={streamingConversationId === conversationId ? streamedResponse : null}
750
+ agentProgress={streamingConversationId === conversationId ? agentProgress : []}
716
751
  agentActivitySnapshots={agentActivitySnapshots}
752
+ thinkingExpanded={thinkingExpanded}
753
+ onThinkingExpandedChange={handleThinkingExpandedChange}
717
754
  message={message}
718
755
  imageAttachments={imageAttachments}
719
756
  documentName={documentAttachment?.filename ?? ''}
720
- pendingApproval={pendingApproval}
757
+ pendingApproval={streamingConversationId === conversationId ? pendingApproval : null}
721
758
  smartContext={smartContext}
722
759
  agentMode={agentMode}
723
760
  status={status}
@@ -745,7 +782,7 @@ export default function MintDashboard() {
745
782
  onToggleMobileSidebar={() => setMobileSidebarOpen(!mobileSidebarOpen)}
746
783
  />
747
784
  </main>
748
- <PicturesLibrary view={view} pictures={pictures} onSetView={changeView} />
785
+ <PicturesLibrary view={view} pictures={pictures} onSetView={changeView} onRefreshPictures={refreshPictures} />
749
786
  <ImageStudioPanel
750
787
  view={view}
751
788
  onRefreshPictures={refreshPictures}
@@ -10,9 +10,10 @@ interface PicturesLibraryProps {
10
10
  view: DashboardView
11
11
  pictures: PictureEntry[]
12
12
  onSetView: (view: DashboardView) => void
13
+ onRefreshPictures?: () => Promise<void>
13
14
  }
14
15
 
15
- export default function PicturesLibrary({ view, pictures, onSetView }: PicturesLibraryProps) {
16
+ export default function PicturesLibrary({ view, pictures, onSetView, onRefreshPictures }: PicturesLibraryProps) {
16
17
  const [visibleCount, setVisibleCount] = useState(INITIAL_VISIBLE_PICTURES)
17
18
  const visiblePictures = useMemo(
18
19
  () => pictures.slice(0, visibleCount),
@@ -46,6 +47,9 @@ export default function PicturesLibrary({ view, pictures, onSetView }: PicturesL
46
47
  <div><span className="pictures-kicker">Gallery</span><h2>Saved Pictures</h2></div>
47
48
  <div className="pictures-header-actions">
48
49
  <button className="pictures-close-btn" onClick={() => onSetView('chat')}>Close Gallery</button>
50
+ <button type="button" className="picture-refresh-btn" title="Refresh" onClick={() => onRefreshPictures?.()}>
51
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><polyline points="23 4 23 10 17 10"/><polyline points="1 20 1 14 7 14"/><path d="M3.51 9a9 9 0 0 1 14.13-3.36L23 10"/><path d="M20.49 15a9 9 0 0 1-14.13 3.36L1 14"/></svg>
52
+ </button>
49
53
  </div>
50
54
  </header>
51
55
  {pictures.length === 0 ? (
@@ -406,7 +406,27 @@ h1 {
406
406
  .interaction-guide-btn.active {
407
407
  background: rgba(34, 211, 238, 0.22);
408
408
  border-color: rgba(34, 211, 238, 0.34);
409
- color: #67e8f9;
409
+ }
410
+
411
+ .picture-refresh-btn {
412
+ height: 28px;
413
+ min-width: 0;
414
+ padding: 0 8px;
415
+ display: inline-flex;
416
+ align-items: center;
417
+ justify-content: center;
418
+ gap: 8px;
419
+ border-radius: 6px;
420
+ background: transparent;
421
+ color: var(--text-muted);
422
+ border: 1px solid transparent;
423
+ cursor: pointer;
424
+ }
425
+
426
+ .picture-refresh-btn:hover {
427
+ background: color-mix(in srgb, var(--accent) 10%, transparent);
428
+ border-color: color-mix(in srgb, var(--accent) 16%, transparent);
429
+ color: var(--accent);
410
430
  }
411
431
 
412
432
  .maximize-btn:hover {
@@ -829,6 +849,41 @@ h1 {
829
849
  transform: scaleX(1.2);
830
850
  }
831
851
 
852
+ /* Settings modal overlay (web) */
853
+ .settings-modal-overlay {
854
+ position: fixed;
855
+ inset: 0;
856
+ display: flex;
857
+ align-items: center;
858
+ justify-content: center;
859
+ background: rgba(0,0,0,0.45);
860
+ z-index: 80;
861
+ padding: 24px;
862
+ }
863
+
864
+ .settings-modal {
865
+ width: min(920px, 96vw);
866
+ max-height: 92vh;
867
+ overflow: hidden;
868
+ border-radius: 12px;
869
+ box-shadow: 0 30px 80px rgba(0,0,0,0.6);
870
+ }
871
+
872
+ /* When rendered inside modal, make settings-container adapt to modal size */
873
+ .settings-modal .settings-container {
874
+ height: auto !important;
875
+ width: 100% !important;
876
+ max-height: 92vh !important;
877
+ overflow: hidden !important;
878
+ border-radius: 12px !important;
879
+ }
880
+
881
+ /* Make settings content scroll internally */
882
+ .settings-modal .settings-content {
883
+ max-height: calc(92vh - 160px);
884
+ overflow-y: auto;
885
+ }
886
+
832
887
  @keyframes modelFloat {
833
888
  0%,
834
889
  100% {
@@ -841,8 +896,9 @@ h1 {
841
896
  }
842
897
 
843
898
  .conversation-panel {
844
- flex: 0 1 460px;
845
- min-width: 400px;
899
+ /* Make conversation panel wider by default */
900
+ flex: 0 1 720px;
901
+ min-width: 600px;
846
902
  display: flex;
847
903
  flex-direction: column;
848
904
  min-height: 0;
@@ -889,7 +945,7 @@ h1 {
889
945
 
890
946
  .message {
891
947
  display: flex;
892
- max-width: 85%;
948
+ max-width: 95%;
893
949
  animation: messagePopIn 0.5s cubic-bezier(0.2, 0.9, 0.3, 1.2) forwards;
894
950
  opacity: 0;
895
951
  transform: translateY(15px) scale(0.95);
@@ -897,6 +953,21 @@ h1 {
897
953
  transition: all 0.3s ease;
898
954
  }
899
955
 
956
+ /* Allow selecting text inside the main chat and settings areas despite global user-select:none */
957
+ .chat-container,
958
+ .chat-container *,
959
+ .message-bubble,
960
+ .message-bubble *,
961
+ .bubble-wrapper,
962
+ .bubble-wrapper *,
963
+ .settings-content,
964
+ .settings-content * {
965
+ -webkit-user-select: text !important;
966
+ -moz-user-select: text !important;
967
+ -ms-user-select: text !important;
968
+ user-select: text !important;
969
+ }
970
+
900
971
  .bubble-wrapper {
901
972
  display: flex;
902
973
  flex-direction: column;
@@ -1078,24 +1149,26 @@ h1 {
1078
1149
  }
1079
1150
 
1080
1151
  .ai-message .message-bubble {
1081
- background: var(--panel-bg);
1082
- backdrop-filter: var(--glass-blur);
1083
- -webkit-backdrop-filter: var(--glass-blur);
1084
- border: 1px solid var(--border);
1085
- border-top: 1px solid var(--border-light);
1086
- border-left: 1px solid var(--border-light);
1087
- border-bottom-left-radius: 4px;
1088
- box-shadow: 0 6px 20px rgba(0, 0, 0, 0.1);
1152
+ /* Render AI replies without a framed bubble — plain inline text */
1153
+ background: transparent;
1154
+ backdrop-filter: none;
1155
+ -webkit-backdrop-filter: none;
1156
+ border: none;
1157
+ border-radius: 0;
1158
+ box-shadow: none;
1089
1159
  color: var(--text-main);
1160
+ padding: 0; /* keep spacing handled by wrapper */
1090
1161
  }
1091
1162
 
1092
1163
  .message.ai-message.thinking-message .message-bubble {
1093
- background: color-mix(in srgb, var(--panel-bg) 75%, #000000) !important;
1094
- border-color: color-mix(in srgb, var(--accent) 30%, var(--border-light)) !important;
1095
- box-shadow: 0 4px 15px rgba(0, 0, 0, 0.25) !important;
1164
+ /* Keep thinking state unframed too */
1165
+ background: transparent !important;
1166
+ border: none !important;
1167
+ box-shadow: none !important;
1096
1168
  color: var(--text-muted) !important;
1097
1169
  backdrop-filter: none !important;
1098
1170
  -webkit-backdrop-filter: none !important;
1171
+ padding: 0 !important;
1099
1172
  }
1100
1173
 
1101
1174
 
@@ -1714,6 +1787,146 @@ h1 {
1714
1787
  line-height: 1;
1715
1788
  }
1716
1789
 
1790
+ /* ── Thinking Block ─────────────────────────────────────── */
1791
+ .thinking-block {
1792
+ margin-bottom: 6px;
1793
+ border-radius: 8px;
1794
+ border: 1px solid rgba(148, 163, 184, 0.18);
1795
+ background: rgba(148, 163, 184, 0.04);
1796
+ overflow: hidden;
1797
+ animation: agent-activity-slide-up-fade 0.25s cubic-bezier(0.16, 1, 0.3, 1) forwards;
1798
+ }
1799
+
1800
+ .thinking-block-header {
1801
+ display: flex;
1802
+ align-items: center;
1803
+ gap: 7px;
1804
+ width: 100%;
1805
+ padding: 6px 10px;
1806
+ background: transparent;
1807
+ border: 0;
1808
+ color: #94a3b8;
1809
+ font-size: 0.76rem;
1810
+ font-weight: 500;
1811
+ cursor: pointer;
1812
+ text-align: left;
1813
+ transition: background 0.15s ease, color 0.15s ease;
1814
+ user-select: none;
1815
+ }
1816
+
1817
+ .thinking-block-header:hover {
1818
+ background: rgba(148, 163, 184, 0.08);
1819
+ color: #cbd5e1;
1820
+ }
1821
+
1822
+ .thinking-block-label {
1823
+ flex: 1;
1824
+ min-width: 0;
1825
+ }
1826
+
1827
+ .thinking-block-live-dot {
1828
+ width: 6px;
1829
+ height: 6px;
1830
+ border-radius: 50%;
1831
+ background: #94a3b8;
1832
+ flex-shrink: 0;
1833
+ animation: thinkingDotPulse 1.4s ease-in-out infinite;
1834
+ }
1835
+
1836
+ @keyframes thinkingDotPulse {
1837
+ 0%, 100% {
1838
+ opacity: 0.4;
1839
+ transform: scale(0.85);
1840
+ }
1841
+ 50% {
1842
+ opacity: 1;
1843
+ transform: scale(1.1);
1844
+ }
1845
+ }
1846
+
1847
+ .thinking-block-chevron {
1848
+ color: rgba(148, 163, 184, 0.6);
1849
+ flex-shrink: 0;
1850
+ display: inline-flex;
1851
+ align-items: center;
1852
+ transition: transform 0.2s ease;
1853
+ }
1854
+
1855
+ .thinking-block-chevron.is-open {
1856
+ transform: rotate(90deg);
1857
+ }
1858
+
1859
+ .thinking-block-body {
1860
+ border-top: 1px solid rgba(148, 163, 184, 0.12);
1861
+ animation: thinkingBodyIn 0.18s ease;
1862
+ }
1863
+
1864
+ @keyframes thinkingBodyIn {
1865
+ from {
1866
+ opacity: 0;
1867
+ transform: translateY(-4px);
1868
+ }
1869
+ to {
1870
+ opacity: 1;
1871
+ transform: translateY(0);
1872
+ }
1873
+ }
1874
+
1875
+ .thinking-block-content {
1876
+ padding: 8px 12px 10px;
1877
+ font-size: 0.76rem;
1878
+ line-height: 1.6;
1879
+ color: #64748b;
1880
+ white-space: pre-wrap;
1881
+ max-height: 260px;
1882
+ overflow-y: auto;
1883
+ user-select: text;
1884
+ }
1885
+
1886
+ .thinking-block-step + .thinking-block-step {
1887
+ margin-top: 10px;
1888
+ padding-top: 10px;
1889
+ border-top: 1px dashed rgba(148, 163, 184, 0.16);
1890
+ }
1891
+
1892
+ .thinking-block-step-label {
1893
+ font-size: 0.68rem;
1894
+ font-weight: 600;
1895
+ letter-spacing: 0.02em;
1896
+ color: #94a3b8;
1897
+ margin-bottom: 4px;
1898
+ font-style: normal;
1899
+ }
1900
+
1901
+ .thinking-block-step-body {
1902
+ font-style: italic;
1903
+ }
1904
+
1905
+ .thinking-block-empty-state {
1906
+ padding: 6px 10px;
1907
+ }
1908
+
1909
+ .thinking-block-empty {
1910
+ display: block;
1911
+ font-size: 0.74rem;
1912
+ line-height: 1.5;
1913
+ color: #64748b;
1914
+ font-style: italic;
1915
+ }
1916
+
1917
+ .thinking-block-content::-webkit-scrollbar {
1918
+ width: 3px;
1919
+ }
1920
+
1921
+ .thinking-block-content::-webkit-scrollbar-track {
1922
+ background: transparent;
1923
+ }
1924
+
1925
+ .thinking-block-content::-webkit-scrollbar-thumb {
1926
+ background: rgba(148, 163, 184, 0.22);
1927
+ border-radius: 2px;
1928
+ }
1929
+
1717
1930
  /* Smart Context Toggle */
1718
1931
  .smart-context-bar {
1719
1932
  display: flex;
@@ -2896,13 +3109,15 @@ input:checked + .slider:before {
2896
3109
 
2897
3110
  .ai-message .message-bubble,
2898
3111
  .typing-indicator {
2899
- background: var(--surface-bg);
2900
- border: 1px solid var(--border);
2901
- border-radius: 8px;
3112
+ /* Keep AI messages unframed here as well */
3113
+ background: transparent;
3114
+ border: none;
3115
+ border-radius: 0;
2902
3116
  color: var(--text-main);
2903
3117
  backdrop-filter: none;
2904
3118
  -webkit-backdrop-filter: none;
2905
3119
  box-shadow: none;
3120
+ padding: 0; /* spacing handled by wrapper */
2906
3121
  }
2907
3122
 
2908
3123
  .user-message .message-bubble {
@@ -2917,6 +3132,15 @@ input:checked + .slider:before {
2917
3132
  color: var(--accent-hover);
2918
3133
  }
2919
3134
 
3135
+ /* Force AI replies to render as plain inline text (no bubble/frame). */
3136
+ .message.ai-message .message-bubble {
3137
+ background: transparent !important;
3138
+ border: none !important;
3139
+ box-shadow: none !important;
3140
+ border-radius: 0 !important;
3141
+ padding: 0 !important;
3142
+ }
3143
+
2920
3144
  .input-area {
2921
3145
  flex: 0 0 auto;
2922
3146
  padding: 0;
@@ -483,3 +483,49 @@ button {
483
483
  font-family: 'Fira Code', 'Courier New', Courier, monospace;
484
484
  font-size: 0.9em;
485
485
  }
486
+
487
+ /* Premium Table Styles for Chat Panel */
488
+ .chat-table-container {
489
+ overflow-x: auto;
490
+ margin: 14px 0;
491
+ width: 100%;
492
+ border-radius: 8px;
493
+ border: 1px solid rgba(255, 255, 255, 0.12);
494
+ background: rgba(30, 41, 59, 0.35);
495
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
496
+ }
497
+
498
+ .chat-table {
499
+ width: 100%;
500
+ border-collapse: collapse;
501
+ font-size: 0.86rem;
502
+ text-align: left;
503
+ line-height: 1.5;
504
+ }
505
+
506
+ .chat-table th {
507
+ padding: 12px 16px;
508
+ font-weight: 700;
509
+ color: var(--accent);
510
+ background: rgba(255, 255, 255, 0.04);
511
+ border-bottom: 2px solid rgba(255, 255, 255, 0.15);
512
+ font-family: ui-sans-serif, system-ui, sans-serif;
513
+ }
514
+
515
+ .chat-table td {
516
+ padding: 12px 16px;
517
+ color: var(--text-main, #e2e8f0);
518
+ border-bottom: 1px solid rgba(255, 255, 255, 0.08);
519
+ }
520
+
521
+ .chat-table tbody tr {
522
+ transition: background 0.15s ease;
523
+ }
524
+
525
+ .chat-table tbody tr:hover {
526
+ background: rgba(255, 255, 255, 0.03);
527
+ }
528
+
529
+ .chat-table tbody tr:last-child td {
530
+ border-bottom: none;
531
+ }
@@ -43,6 +43,7 @@ export interface InteractionMemory {
43
43
  model: string
44
44
  fallbackProvider?: string | null
45
45
  createdAt: string
46
+ agentActivity?: AgentProgress[] | null
46
47
  }
47
48
 
48
49
  export interface ChatSession {
@@ -292,6 +293,30 @@ export async function getRecentInteractions(limit = 50, chatId?: string | null):
292
293
  return invoke<InteractionMemory[]>('get_recent_interactions', { limit, chatId })
293
294
  }
294
295
 
296
+ export async function saveInteractionAgentActivity(
297
+ interactionId: number,
298
+ activity: AgentProgress[],
299
+ ): Promise<void> {
300
+ if (typeof window === 'undefined' || !isTauriRuntime()) {
301
+ const API_BASE = getApiBase();
302
+ try {
303
+ const res = await fetch(`${API_BASE}/interactions/agent-activity`, {
304
+ method: 'POST',
305
+ headers: { 'Content-Type': 'application/json' },
306
+ body: JSON.stringify({ interactionId, activity }),
307
+ });
308
+ if (!res.ok) {
309
+ throw new Error(`HTTP ${res.status}`);
310
+ }
311
+ } catch (error) {
312
+ console.error('Failed to persist agent activity:', error);
313
+ }
314
+ return;
315
+ }
316
+ const { invoke } = await import('@tauri-apps/api/core')
317
+ await invoke('save_interaction_agent_activity', { interactionId, activity })
318
+ }
319
+
295
320
  export async function listChatSessions(): Promise<ChatSession[]> {
296
321
  if (typeof window === 'undefined' || !isTauriRuntime()) {
297
322
  const API_BASE = getApiBase();
@@ -547,6 +547,22 @@ async fn stream_chat_message(
547
547
  })
548
548
  }
549
549
 
550
+ #[tauri::command]
551
+ fn save_interaction_agent_activity(
552
+ interaction_id: i64,
553
+ activity: Vec<AgentProgress>,
554
+ ) -> Result<(), String> {
555
+ let activity_json =
556
+ serde_json::to_string(&activity).map_err(|error| error.to_string())?;
557
+ MemoryStore::open_default()
558
+ .and_then(|memory| {
559
+ memory
560
+ .set_interaction_agent_activity_json(interaction_id, &activity_json)
561
+ .map(|_| ())
562
+ })
563
+ .map_err(|error| error.to_string())
564
+ }
565
+
550
566
  #[tauri::command]
551
567
  fn get_recent_interactions(
552
568
  limit: Option<usize>,
@@ -1032,6 +1048,7 @@ pub fn run() {
1032
1048
  stream_chat_message,
1033
1049
  submit_tool_approval,
1034
1050
  get_recent_interactions,
1051
+ save_interaction_agent_activity,
1035
1052
  list_chat_sessions,
1036
1053
  delete_chat_session,
1037
1054
  rename_chat_session,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://schema.tauri.app/config/2",
3
3
  "productName": "Mint",
4
- "version": "1.6.4",
4
+ "version": "1.7.0",
5
5
  "identifier": "com.pheem49.mint",
6
6
  "build": {
7
7
  "beforeDevCommand": "npm run dev:desktop",