@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,
@@ -8,6 +9,7 @@ import {
8
9
  listChatSessions,
9
10
  listSavedPictures,
10
11
  selectWorkspaceDirectory,
12
+ saveInteractionAgentActivity,
11
13
  streamChatMessage,
12
14
  submitToolApproval,
13
15
  listen,
@@ -28,19 +30,19 @@ import PicturesLibrary from './PicturesLibrary'
28
30
  import WorkspacePanel from './WorkspacePanel'
29
31
 
30
32
  const EXPRESSIONS = [
31
- "ปกติ (Default)",
32
- "呆猫 (Dumb Cat)",
33
- "呆猫眼珠摇晃 (Dumb Cat Eye Roll)",
34
- "拍照 (Take Photo)",
35
- "点一下 (Poke)",
36
- "猫咪滤镜 (Cat Filter)",
33
+ "Default",
34
+ "Dumb Cat",
35
+ "Dumb Cat Eye Roll",
36
+ "Take Photo",
37
+ "Poke",
38
+ "Cat Filter",
37
39
  ]
38
40
 
39
41
  const ACCESSORIES = [
40
- "ปกติ (None)",
41
- "ผ้ากันเปื้อน (Apron)",
42
- "แว่นตา (Glasses)",
43
- "ท่าถือปากกา (Hold Pen)",
42
+ "None",
43
+ "Apron",
44
+ "Glasses",
45
+ "Hold Pen",
44
46
  ]
45
47
 
46
48
  const DEFAULT_CONFIG = {
@@ -78,7 +80,7 @@ function activeConversationId() {
78
80
  const MOCK_WELCOME_INTERACTION = {
79
81
  id: -1,
80
82
  userText: '',
81
- aiText: `มิ้นท์กำลังรอสแตนด์บายเตรียมพร้อมช่วยคุณทีมอยู่เลยค่ะ! ✨ แล้วก็แอบนั่งจัดระเบียบข้อมูลนิดๆ หน่อยๆ ให้พร้อมใช้ด้วยค่ะ 😊💖\n\nแต่พอคุณทีมทักมา มิ้นท์ก็วางมือจากทุกอย่างมาคุยกับคุณทีมก่อนเลยนะคะค้าา! ช่วงนี้มีอะไรให้มิ้นท์ช่วยดูแล หรืออยากชวนคุยเรื่องไหนเป็นพิเศษไหมคะ มิ้นท์พร้อมมว๊ากกกค่ะ! 🚀🎯`,
83
+ 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! 🚀💪`,
82
84
  provider: 'gemini',
83
85
  model: 'gemini-3-flash-preview',
84
86
  createdAt: new Date().toISOString(),
@@ -233,8 +235,11 @@ export default function MintDashboard() {
233
235
  const [sendingImageCount, setSendingImageCount] = useState(0)
234
236
  const [streamedReply, setStreamedReply] = useState('')
235
237
  const [streamedResponse, setStreamedResponse] = useState<ChatResponse | null>(null)
238
+ const [streamingConversationId, setStreamingConversationId] = useState<string | null>(null)
236
239
  const [agentProgress, setAgentProgress] = useState<AgentProgress[]>([])
237
240
  const [agentActivitySnapshots, setAgentActivitySnapshots] = useState<Record<string, AgentProgress[]>>({})
241
+ const [thinkingExpanded, setThinkingExpanded] = useState<Record<string, boolean>>({})
242
+ const liveThinkingOpenRef = useRef(true)
238
243
  const [imageAttachments, setImageAttachments] = useState<Array<{ dataUri: string; name: string; previewDataUri?: string }>>([])
239
244
  const [documentAttachment, setDocumentAttachment] = useState<DocumentAttachment | null>(null)
240
245
  const [pendingApproval, setPendingApproval] = useState<any | null>(null)
@@ -264,7 +269,9 @@ export default function MintDashboard() {
264
269
 
265
270
  async function refreshHistory() {
266
271
  const history = await getRecentInteractions(50, conversationId)
267
- setInteractions(history.reverse())
272
+ const reversed = history.reverse()
273
+ setInteractions(reversed)
274
+ setAgentActivitySnapshots((current) => mergeActivitySnapshots(current, reversed))
268
275
  }
269
276
 
270
277
  async function refreshChatSessions(nextActiveId = conversationId) {
@@ -286,8 +293,15 @@ export default function MintDashboard() {
286
293
  )
287
294
  }
288
295
 
296
+ const [picturesRefreshing, setPicturesRefreshing] = useState(false)
297
+
289
298
  async function refreshPictures() {
290
- setPictures(await listSavedPictures())
299
+ setPicturesRefreshing(true)
300
+ try {
301
+ setPictures(await listSavedPictures())
302
+ } finally {
303
+ setPicturesRefreshing(false)
304
+ }
291
305
  }
292
306
 
293
307
  useEffect(() => {
@@ -445,12 +459,15 @@ export default function MintDashboard() {
445
459
  const outgoingImage = outgoingImages.map((img) => img.dataUri).join(' ')
446
460
  const outgoingImageCount = outgoingImages.length
447
461
  setSending(true)
462
+ setStreamingConversationId(conversationId)
448
463
  setSendingMessage(promptText)
449
464
  setSendingImageCount(outgoingImageCount)
450
465
  setError('')
451
466
  setStreamedReply('')
452
467
  setStreamedResponse(null)
453
468
  setAgentProgress([])
469
+ liveThinkingOpenRef.current = true
470
+ setThinkingExpanded((current) => ({ ...current, live: true }))
454
471
  const progressSnapshot: AgentProgress[] = []
455
472
  if (options.clearComposer) {
456
473
  setMessage('')
@@ -467,7 +484,7 @@ export default function MintDashboard() {
467
484
  options.systemInstruction ?? '',
468
485
  (progress) => {
469
486
  progressSnapshot.push(progress)
470
- setAgentProgress((current) => [...current, progress].slice(-24))
487
+ setAgentProgress((current) => trimAgentProgress([...current, progress]))
471
488
  },
472
489
  outgoingDocument,
473
490
  workspacePath || null,
@@ -475,18 +492,33 @@ export default function MintDashboard() {
475
492
  )
476
493
  setStreamedResponse(response)
477
494
  const history = (await getRecentInteractions(50, conversationId)).reverse()
495
+ let enrichedHistory = history
478
496
  if (progressSnapshot.length > 0) {
479
497
  const newestInteraction = [...history]
480
498
  .reverse()
481
499
  .find((interaction) => interaction.aiText === response.text || interaction.userText === promptText) ?? history[history.length - 1]
482
500
  if (newestInteraction?.id != null) {
501
+ const interactionKey = String(newestInteraction.id)
502
+ enrichedHistory = history.map((interaction) =>
503
+ interaction.id === newestInteraction.id
504
+ ? { ...interaction, agentActivity: progressSnapshot }
505
+ : interaction,
506
+ )
483
507
  setAgentActivitySnapshots((current) => ({
484
508
  ...current,
485
- [String(newestInteraction.id)]: progressSnapshot.slice(),
509
+ [interactionKey]: progressSnapshot.slice(),
486
510
  }))
511
+ if (liveThinkingOpenRef.current) {
512
+ setThinkingExpanded((current) => ({
513
+ ...current,
514
+ [interactionKey]: true,
515
+ }))
516
+ }
517
+ await saveInteractionAgentActivity(newestInteraction.id, progressSnapshot)
487
518
  }
488
519
  }
489
- setInteractions(history)
520
+ setInteractions(enrichedHistory)
521
+ setAgentActivitySnapshots((current) => mergeActivitySnapshots(current, enrichedHistory))
490
522
  await refreshChatSessions()
491
523
  await refreshPictures()
492
524
  setStreamedReply('')
@@ -495,6 +527,7 @@ export default function MintDashboard() {
495
527
  setError(errorMessage(reason))
496
528
  } finally {
497
529
  setSending(false)
530
+ setStreamingConversationId(null)
498
531
  setSendingMessage('')
499
532
  setSendingImageCount(0)
500
533
  }
@@ -516,7 +549,7 @@ export default function MintDashboard() {
516
549
  }
517
550
 
518
551
  async function sendVoiceMessage(transcript: string, audioDataUri?: string | null) {
519
- const promptText = transcript.trim() || 'ข้อความเสียง'
552
+ const promptText = transcript.trim() || 'Voice message'
520
553
  if (!promptText || sending) return
521
554
  await sendPrompt(promptText, {
522
555
  audioDataUri,
@@ -678,6 +711,11 @@ export default function MintDashboard() {
678
711
  }
679
712
  }
680
713
 
714
+ function handleThinkingExpandedChange(key: string, open: boolean) {
715
+ if (key === 'live') liveThinkingOpenRef.current = open
716
+ setThinkingExpanded((current) => ({ ...current, [key]: open }))
717
+ }
718
+
681
719
  async function selectConversation(id: string) {
682
720
  if (id === conversationId) {
683
721
  setView('chat')
@@ -693,7 +731,9 @@ export default function MintDashboard() {
693
731
  setDocumentAttachment(null)
694
732
  setAgentProgress([])
695
733
  const history = await getRecentInteractions(50, id)
696
- setInteractions(history.reverse())
734
+ const reversed = history.reverse()
735
+ setInteractions(reversed)
736
+ setAgentActivitySnapshots((current) => mergeActivitySnapshots(current, reversed))
697
737
  }
698
738
 
699
739
  async function deleteConversation(id: string) {
@@ -714,7 +754,9 @@ export default function MintDashboard() {
714
754
  setConversationId(nextActive)
715
755
  setAgentProgress([])
716
756
  const history = await getRecentInteractions(50, nextActive)
717
- setInteractions(history.reverse())
757
+ const reversed = history.reverse()
758
+ setInteractions(reversed)
759
+ setAgentActivitySnapshots((current) => mergeActivitySnapshots(current, reversed))
718
760
  }
719
761
 
720
762
  await refreshChatSessions(nextActive)
@@ -888,17 +930,19 @@ export default function MintDashboard() {
888
930
  />
889
931
  <ChatPanel
890
932
  interactions={interactions}
891
- sending={sending}
892
- sendingMessage={sendingMessage}
893
- sendingImageCount={sendingImageCount}
894
- streamedReply={streamedReply}
895
- streamedResponse={streamedResponse}
896
- agentProgress={agentProgress}
933
+ sending={sending && streamingConversationId === conversationId}
934
+ sendingMessage={streamingConversationId === conversationId ? sendingMessage : ''}
935
+ sendingImageCount={streamingConversationId === conversationId ? sendingImageCount : 0}
936
+ streamedReply={streamingConversationId === conversationId ? streamedReply : ''}
937
+ streamedResponse={streamingConversationId === conversationId ? streamedResponse : null}
938
+ agentProgress={streamingConversationId === conversationId ? agentProgress : []}
897
939
  agentActivitySnapshots={agentActivitySnapshots}
940
+ thinkingExpanded={thinkingExpanded}
941
+ onThinkingExpandedChange={handleThinkingExpandedChange}
898
942
  message={message}
899
943
  imageAttachments={imageAttachments}
900
944
  documentName={documentAttachment?.filename ?? ''}
901
- pendingApproval={pendingApproval}
945
+ pendingApproval={streamingConversationId === conversationId ? pendingApproval : null}
902
946
  smartContext={smartContext}
903
947
  agentMode={agentMode}
904
948
  status={status}
@@ -927,7 +971,7 @@ export default function MintDashboard() {
927
971
  onApproval={handleApproval}
928
972
  />
929
973
  </main>
930
- <PicturesLibrary view={view} pictures={pictures} onSetView={setView} />
974
+ <PicturesLibrary view={view} pictures={pictures} onSetView={setView} onRefreshPictures={refreshPictures} />
931
975
  <ImageStudioPanel
932
976
  view={view}
933
977
  onRefreshPictures={refreshPictures}
@@ -98,13 +98,13 @@ export default function ModelPanel({
98
98
  </svg>
99
99
  </button>
100
100
  <div className="layout-preset-group">
101
- <button className={`layout-preset-btn ${layoutPreset === 'chat-wide' ? 'is-active' : ''}`} onClick={() => onChangeLayoutPreset('chat-wide')} title="ขยายหน้าต่างแชท (ย่อโมเดล)">
101
+ <button className={`layout-preset-btn ${layoutPreset === 'chat-wide' ? 'is-active' : ''}`} onClick={() => onChangeLayoutPreset('chat-wide')} title="Expand chat panel (minimize model)">
102
102
  <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
103
103
  <rect x="3" y="3" width="18" height="18" rx="2" />
104
104
  <path d="M9 3v18" />
105
105
  </svg>
106
106
  </button>
107
- <button className={`layout-preset-btn ${layoutPreset === 'model-wide' ? 'is-active' : ''}`} onClick={() => onChangeLayoutPreset('model-wide')} title="ขยายหน้าต่างโมเดล (ย่อแชท)">
107
+ <button className={`layout-preset-btn ${layoutPreset === 'model-wide' ? 'is-active' : ''}`} onClick={() => onChangeLayoutPreset('model-wide')} title="Expand model panel (minimize chat)">
108
108
  <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
109
109
  <rect x="3" y="3" width="18" height="18" rx="2" />
110
110
  <path d="M15 3v18" />
@@ -5,9 +5,10 @@ interface PicturesLibraryProps {
5
5
  view: DashboardView
6
6
  pictures: PictureEntry[]
7
7
  onSetView: (view: DashboardView) => void
8
+ onRefreshPictures?: () => Promise<void>
8
9
  }
9
10
 
10
- export default function PicturesLibrary({ view, pictures, onSetView }: PicturesLibraryProps) {
11
+ export default function PicturesLibrary({ view, pictures, onSetView, onRefreshPictures }: PicturesLibraryProps) {
11
12
  if (view !== 'pictures') return null
12
13
 
13
14
  return (
@@ -16,6 +17,9 @@ export default function PicturesLibrary({ view, pictures, onSetView }: PicturesL
16
17
  <div><span className="pictures-kicker">Gallery</span><h2>Saved Pictures</h2></div>
17
18
  <div className="pictures-header-actions">
18
19
  <button className="pictures-close-btn" onClick={() => onSetView('chat')}>Close Gallery</button>
20
+ <button type="button" className="picture-refresh-btn" title="Refresh" onClick={() => onRefreshPictures?.()}>
21
+ <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>
22
+ </button>
19
23
  <button type="button" className="picture-folder-btn" disabled={pictures.length === 0} onClick={() => window.settingsApi?.openFolder(pictures[0]?.path || '')}>
20
24
  Open Folder
21
25
  </button>
@@ -2,14 +2,14 @@ import React, { useState, useEffect, useRef } from 'react'
2
2
  import { evaluateArithmetic } from '../calculator'
3
3
 
4
4
  const COMMANDS = [
5
- { label: 'Open YouTube', desc: 'เปิดเว็บไซต์ YouTube', icon: '📺', action: { type: 'open_url', target: 'https://youtube.com' } },
6
- { label: 'Open Facebook', desc: 'เปิดเว็บไซต์ Facebook', icon: '📘', action: { type: 'open_url', target: 'https://facebook.com' } },
7
- { label: 'Open Instagram', desc: 'เปิดเว็บไซต์ Instagram', icon: '📸', action: { type: 'open_url', target: 'https://instagram.com' } },
8
- { label: 'Open GitHub', desc: 'เปิดเว็บไซต์ GitHub', icon: '🐙', action: { type: 'open_url', target: 'https://github.com' } },
9
- { label: 'System Info', desc: 'ดูข้อมูลระบบ', icon: '💻', action: { type: 'chat', query: 'ขอข้อมูลระบบหน่อย' } },
10
- { label: 'Weather', desc: 'เช็คสภาพอากาศ', icon: '🌤️', action: { type: 'chat', query: 'อากาศที่กรุงเทพเป็นยังไง' } },
11
- { label: 'Open Spotify', desc: 'เปิดโปรแกรม Spotify', icon: '🎵', action: { type: 'open_app', target: 'spotify' } },
12
- { label: 'Open VS Code', desc: 'เปิดโปรแกรม VS Code', icon: '💻', action: { type: 'open_app', target: 'code' } },
5
+ { label: 'Open YouTube', desc: 'Open YouTube website', icon: '📺', action: { type: 'open_url', target: 'https://youtube.com' } },
6
+ { label: 'Open Facebook', desc: 'Open Facebook website', icon: '📘', action: { type: 'open_url', target: 'https://facebook.com' } },
7
+ { label: 'Open Instagram', desc: 'Open Instagram website', icon: '📸', action: { type: 'open_url', target: 'https://instagram.com' } },
8
+ { label: 'Open GitHub', desc: 'Open GitHub website', icon: '🐙', action: { type: 'open_url', target: 'https://github.com' } },
9
+ { label: 'System Info', desc: 'Show system information', icon: '💻', action: { type: 'chat', query: 'Show me system information' } },
10
+ { label: 'Weather', desc: 'Check the weather', icon: '🌤️', action: { type: 'chat', query: 'What is the current weather' } },
11
+ { label: 'Open Spotify', desc: 'Open Spotify app', icon: '🎵', action: { type: 'open_app', target: 'spotify' } },
12
+ { label: 'Open VS Code', desc: 'Open VS Code app', icon: '💻', action: { type: 'open_app', target: 'code' } },
13
13
  ]
14
14
 
15
15
  function getCommandIcon(iconName: string) {
@@ -221,7 +221,7 @@ export default function WorkspacePanel({ agentMode, sending, workspacePath, onEn
221
221
 
222
222
  const startWorkspacePrompt = () => {
223
223
  onEnableAgentMode()
224
- onSetMessage('ดู workspace นี้แล้วช่วยวางแผนงานต่อไปให้หน่อย')
224
+ onSetMessage('Review this workspace and help plan next steps')
225
225
  }
226
226
 
227
227
  return (
@@ -397,6 +397,30 @@ h1 {
397
397
  opacity: 1;
398
398
  }
399
399
 
400
+ /* pictures header actions */
401
+
402
+ .picture-refresh-btn {
403
+ -webkit-app-region: no-drag;
404
+ height: 28px;
405
+ min-width: 0;
406
+ padding: 0 8px;
407
+ display: inline-flex;
408
+ align-items: center;
409
+ justify-content: center;
410
+ gap: 8px;
411
+ border-radius: 6px;
412
+ background: transparent;
413
+ color: var(--text-muted);
414
+ border: 1px solid transparent;
415
+ cursor: pointer;
416
+ }
417
+
418
+ .picture-refresh-btn:hover {
419
+ background: color-mix(in srgb, var(--accent) 10%, transparent);
420
+ border-color: color-mix(in srgb, var(--accent) 16%, transparent);
421
+ color: var(--accent);
422
+ }
423
+
400
424
  .interaction-guide-btn:hover {
401
425
  background: rgba(34, 211, 238, 0.14);
402
426
  border-color: rgba(34, 211, 238, 0.2);
@@ -829,8 +853,9 @@ h1 {
829
853
  }
830
854
 
831
855
  .conversation-panel {
832
- flex: 0 1 460px;
833
- min-width: 400px;
856
+ /* Make conversation panel wider by default */
857
+ flex: 0 1 720px;
858
+ min-width: 600px;
834
859
  display: flex;
835
860
  flex-direction: column;
836
861
  min-height: 0;
@@ -877,7 +902,7 @@ h1 {
877
902
 
878
903
  .message {
879
904
  display: flex;
880
- max-width: 85%;
905
+ max-width: 95%;
881
906
  animation: messagePopIn 0.5s cubic-bezier(0.2, 0.9, 0.3, 1.2) forwards;
882
907
  opacity: 0;
883
908
  transform: translateY(15px) scale(0.95);
@@ -1066,24 +1091,26 @@ h1 {
1066
1091
  }
1067
1092
 
1068
1093
  .ai-message .message-bubble {
1069
- background: var(--panel-bg);
1070
- backdrop-filter: var(--glass-blur);
1071
- -webkit-backdrop-filter: var(--glass-blur);
1072
- border: 1px solid var(--border);
1073
- border-top: 1px solid var(--border-light);
1074
- border-left: 1px solid var(--border-light);
1075
- border-bottom-left-radius: 4px;
1076
- box-shadow: 0 6px 20px rgba(0, 0, 0, 0.1);
1094
+ /* Render AI replies without a framed bubble — plain inline text */
1095
+ background: transparent;
1096
+ backdrop-filter: none;
1097
+ -webkit-backdrop-filter: none;
1098
+ border: none;
1099
+ border-radius: 0;
1100
+ box-shadow: none;
1077
1101
  color: var(--text-main);
1102
+ padding: 0; /* keep spacing handled by wrapper */
1078
1103
  }
1079
1104
 
1080
1105
  .message.ai-message.thinking-message .message-bubble {
1081
- background: color-mix(in srgb, var(--panel-bg) 75%, #000000) !important;
1082
- border-color: color-mix(in srgb, var(--accent) 30%, var(--border-light)) !important;
1083
- box-shadow: 0 4px 15px rgba(0, 0, 0, 0.25) !important;
1106
+ /* Keep thinking state unframed too */
1107
+ background: transparent !important;
1108
+ border: none !important;
1109
+ box-shadow: none !important;
1084
1110
  color: var(--text-muted) !important;
1085
1111
  backdrop-filter: none !important;
1086
1112
  -webkit-backdrop-filter: none !important;
1113
+ padding: 0 !important;
1087
1114
  }
1088
1115
 
1089
1116
 
@@ -1702,6 +1729,146 @@ h1 {
1702
1729
  line-height: 1;
1703
1730
  }
1704
1731
 
1732
+ /* ── Thinking Block ─────────────────────────────────────── */
1733
+ .thinking-block {
1734
+ margin-bottom: 6px;
1735
+ border-radius: 8px;
1736
+ border: 1px solid rgba(148, 163, 184, 0.18);
1737
+ background: rgba(148, 163, 184, 0.04);
1738
+ overflow: hidden;
1739
+ animation: agent-activity-slide-up-fade 0.25s cubic-bezier(0.16, 1, 0.3, 1) forwards;
1740
+ }
1741
+
1742
+ .thinking-block-header {
1743
+ display: flex;
1744
+ align-items: center;
1745
+ gap: 7px;
1746
+ width: 100%;
1747
+ padding: 6px 10px;
1748
+ background: transparent;
1749
+ border: 0;
1750
+ color: #94a3b8;
1751
+ font-size: 0.76rem;
1752
+ font-weight: 500;
1753
+ cursor: pointer;
1754
+ text-align: left;
1755
+ transition: background 0.15s ease, color 0.15s ease;
1756
+ user-select: none;
1757
+ }
1758
+
1759
+ .thinking-block-header:hover {
1760
+ background: rgba(148, 163, 184, 0.08);
1761
+ color: #cbd5e1;
1762
+ }
1763
+
1764
+ .thinking-block-label {
1765
+ flex: 1;
1766
+ min-width: 0;
1767
+ }
1768
+
1769
+ .thinking-block-live-dot {
1770
+ width: 6px;
1771
+ height: 6px;
1772
+ border-radius: 50%;
1773
+ background: #94a3b8;
1774
+ flex-shrink: 0;
1775
+ animation: thinkingDotPulse 1.4s ease-in-out infinite;
1776
+ }
1777
+
1778
+ @keyframes thinkingDotPulse {
1779
+ 0%, 100% {
1780
+ opacity: 0.4;
1781
+ transform: scale(0.85);
1782
+ }
1783
+ 50% {
1784
+ opacity: 1;
1785
+ transform: scale(1.1);
1786
+ }
1787
+ }
1788
+
1789
+ .thinking-block-chevron {
1790
+ color: rgba(148, 163, 184, 0.6);
1791
+ flex-shrink: 0;
1792
+ display: inline-flex;
1793
+ align-items: center;
1794
+ transition: transform 0.2s ease;
1795
+ }
1796
+
1797
+ .thinking-block-chevron.is-open {
1798
+ transform: rotate(90deg);
1799
+ }
1800
+
1801
+ .thinking-block-body {
1802
+ border-top: 1px solid rgba(148, 163, 184, 0.12);
1803
+ animation: thinkingBodyIn 0.18s ease;
1804
+ }
1805
+
1806
+ @keyframes thinkingBodyIn {
1807
+ from {
1808
+ opacity: 0;
1809
+ transform: translateY(-4px);
1810
+ }
1811
+ to {
1812
+ opacity: 1;
1813
+ transform: translateY(0);
1814
+ }
1815
+ }
1816
+
1817
+ .thinking-block-content {
1818
+ padding: 8px 12px 10px;
1819
+ font-size: 0.76rem;
1820
+ line-height: 1.6;
1821
+ color: #64748b;
1822
+ white-space: pre-wrap;
1823
+ max-height: 260px;
1824
+ overflow-y: auto;
1825
+ user-select: text;
1826
+ }
1827
+
1828
+ .thinking-block-step + .thinking-block-step {
1829
+ margin-top: 10px;
1830
+ padding-top: 10px;
1831
+ border-top: 1px dashed rgba(148, 163, 184, 0.16);
1832
+ }
1833
+
1834
+ .thinking-block-step-label {
1835
+ font-size: 0.68rem;
1836
+ font-weight: 600;
1837
+ letter-spacing: 0.02em;
1838
+ color: #94a3b8;
1839
+ margin-bottom: 4px;
1840
+ font-style: normal;
1841
+ }
1842
+
1843
+ .thinking-block-step-body {
1844
+ font-style: italic;
1845
+ }
1846
+
1847
+ .thinking-block-empty-state {
1848
+ padding: 6px 10px;
1849
+ }
1850
+
1851
+ .thinking-block-empty {
1852
+ display: block;
1853
+ font-size: 0.74rem;
1854
+ line-height: 1.5;
1855
+ color: #64748b;
1856
+ font-style: italic;
1857
+ }
1858
+
1859
+ .thinking-block-content::-webkit-scrollbar {
1860
+ width: 3px;
1861
+ }
1862
+
1863
+ .thinking-block-content::-webkit-scrollbar-track {
1864
+ background: transparent;
1865
+ }
1866
+
1867
+ .thinking-block-content::-webkit-scrollbar-thumb {
1868
+ background: rgba(148, 163, 184, 0.22);
1869
+ border-radius: 2px;
1870
+ }
1871
+
1705
1872
  /* Smart Context Toggle */
1706
1873
  .smart-context-bar {
1707
1874
  display: flex;
@@ -3155,13 +3322,15 @@ input:checked + .slider:before {
3155
3322
 
3156
3323
  .ai-message .message-bubble,
3157
3324
  .typing-indicator {
3158
- background: var(--surface-bg);
3159
- border: 1px solid var(--border);
3160
- border-radius: 8px;
3325
+ /* Keep AI messages unframed here as well */
3326
+ background: transparent;
3327
+ border: none;
3328
+ border-radius: 0;
3161
3329
  color: var(--text-main);
3162
3330
  backdrop-filter: none;
3163
3331
  -webkit-backdrop-filter: none;
3164
3332
  box-shadow: none;
3333
+ padding: 0; /* spacing handled by wrapper */
3165
3334
  }
3166
3335
 
3167
3336
  .user-message .message-bubble {
@@ -3176,6 +3345,15 @@ input:checked + .slider:before {
3176
3345
  color: var(--accent-hover);
3177
3346
  }
3178
3347
 
3348
+ /* Force AI replies to render as plain inline text (no bubble/frame). */
3349
+ .message.ai-message .message-bubble {
3350
+ background: transparent !important;
3351
+ border: none !important;
3352
+ box-shadow: none !important;
3353
+ border-radius: 0 !important;
3354
+ padding: 0 !important;
3355
+ }
3356
+
3179
3357
  .input-area {
3180
3358
  flex: 0 0 auto;
3181
3359
  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
+ }
@@ -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 {
@@ -281,6 +282,30 @@ export async function getRecentInteractions(limit = 50, chatId?: string | null):
281
282
  return invoke<InteractionMemory[]>('get_recent_interactions', { limit, chatId })
282
283
  }
283
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
+
284
309
  export async function listChatSessions(): Promise<ChatSession[]> {
285
310
  if (typeof window === 'undefined' || !(window as any).__TAURI_INTERNALS__) {
286
311
  const API_BASE = "http://localhost:3000/api";