@pheem49/mint 1.8.0 → 1.8.1

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 (41) hide show
  1. package/.github/workflows/release.yml +1 -1
  2. package/Cargo.lock +34 -3
  3. package/Cargo.toml +2 -2
  4. package/README.md +31 -0
  5. package/Release_Note.md +95 -0
  6. package/assets/Mint_CLI.png +0 -0
  7. package/crates/mint-cli/Cargo.toml +1 -0
  8. package/crates/mint-cli/src/agent.rs +142 -48
  9. package/crates/mint-cli/src/main.rs +231 -22
  10. package/crates/mint-core/src/api_server.rs +9 -0
  11. package/crates/mint-core/src/browser.rs +401 -0
  12. package/crates/mint-core/src/config.rs +69 -1
  13. package/crates/mint-core/src/lib.rs +5 -0
  14. package/crates/mint-core/src/orchestration.rs +137 -1
  15. package/crates/mint-core/src/skills.rs +113 -4
  16. package/crates/mint-core/src/workflows.rs +1 -22
  17. package/package.json +1 -1
  18. package/src/renderer/src/components/ChatPanel.tsx +170 -33
  19. package/src/renderer/src/components/ImageStudioPanel.tsx +11 -1
  20. package/src/renderer/src/components/MintDashboard.tsx +40 -2
  21. package/src/renderer/src/components/Settings/PluginsTab.tsx +221 -41
  22. package/src/renderer/src/components/SettingsWindow.tsx +1 -9
  23. package/src/renderer/src/components/WorkspacePanel.tsx +66 -9
  24. package/src/renderer/src/css/image-studio.css +6 -0
  25. package/src/renderer/src/css/styles.css +64 -11
  26. package/src/renderer/src/index.css +12 -1
  27. package/src/renderer/src/tauri.ts +45 -4
  28. package/src/renderer/src-web/components/ChatPanel.tsx +163 -32
  29. package/src/renderer/src-web/components/ImageStudioPanel.tsx +11 -1
  30. package/src/renderer/src-web/components/MintDashboard.tsx +8 -3
  31. package/src/renderer/src-web/components/Settings/PluginsTab.tsx +221 -41
  32. package/src/renderer/src-web/components/SettingsWindow.tsx +1 -9
  33. package/src/renderer/src-web/css/image-studio.css +6 -0
  34. package/src/renderer/src-web/css/styles.css +100 -12
  35. package/src/renderer/src-web/index.css +12 -1
  36. package/src/renderer/src-web/tauri.ts +24 -2
  37. package/src-tauri/src/browser.rs +1 -141
  38. package/src-tauri/src/headless.rs +3 -2
  39. package/src-tauri/src/lib.rs +117 -26
  40. package/src-tauri/src/workflows.rs +56 -4
  41. package/src-tauri/tauri.conf.json +1 -1
@@ -1,6 +1,6 @@
1
1
  import React, { useState, useEffect } from 'react'
2
2
  import { DEFAULT_CONFIG } from '../SettingsWindow'
3
- import { listLearnedSkills, addLearnedSkill, deleteLearnedSkill, LearnedSkill } from '../../tauri'
3
+ import { listLearnedSkills, addLearnedSkill, deleteLearnedSkill, LearnedSkill, detectSystemTools, DetectedTools } from '../../tauri'
4
4
 
5
5
  interface PluginsTabProps {
6
6
  config: typeof DEFAULT_CONFIG
@@ -40,14 +40,49 @@ export default function PluginsTab({
40
40
  const [skillsLoading, setSkillsLoading] = useState(false)
41
41
  const [skillsError, setSkillsError] = useState('')
42
42
 
43
- // Load skills on mount
43
+ // Local state for auto-detected tools
44
+ const [detectedTools, setDetectedTools] = useState<DetectedTools>({
45
+ docker: false,
46
+ git: false,
47
+ gh: false,
48
+ node: false
49
+ })
50
+ const [detecting, setDetecting] = useState(false)
51
+
52
+ // Load skills and detect tools on mount
44
53
  useEffect(() => {
45
54
  fetchSkills()
55
+ detectTools()
46
56
  }, [])
47
57
 
58
+ const detectTools = async () => {
59
+ setDetecting(true)
60
+ try {
61
+ const tools = await detectSystemTools()
62
+ setDetectedTools(tools)
63
+ } catch (e) {
64
+ console.error("Failed to detect tools:", e)
65
+ } finally {
66
+ setDetecting(false)
67
+ }
68
+ }
69
+
70
+ const handleEnableTool = (name: string, command: string, args: string[]) => {
71
+ const updatedMcp = {
72
+ ...(config.mcpServers || {}),
73
+ [name]: {
74
+ command,
75
+ args,
76
+ env: {}
77
+ }
78
+ }
79
+ updateField('mcpServers', updatedMcp)
80
+ }
81
+
48
82
  const fetchSkills = async () => {
49
83
  try {
50
- const list = await listLearnedSkills()
84
+ const activeWorkspace = window.localStorage.getItem('mint:last-workspace-path') || undefined
85
+ const list = await listLearnedSkills(activeWorkspace)
51
86
  setSkills(list)
52
87
  } catch (e) {
53
88
  console.error("Failed to load skills:", e)
@@ -122,6 +157,79 @@ export default function PluginsTab({
122
157
  }
123
158
  ]
124
159
 
160
+ const mcpListItems: Array<{
161
+ name: string
162
+ command: string
163
+ args: string[]
164
+ icon: string
165
+ isEnabled: boolean
166
+ isConfigured: boolean
167
+ description: string
168
+ }> = []
169
+
170
+ Object.entries(config.mcpServers || {}).forEach(([name, srv]: [string, any]) => {
171
+ let icon = '⚙';
172
+ if (name === 'docker') icon = '🐳';
173
+ else if (name === 'git' || name === 'gitkraken') icon = '🐙';
174
+ else if (name === 'github') icon = '🐱';
175
+ else if (name === 'node') icon = '🟢';
176
+
177
+ mcpListItems.push({
178
+ name,
179
+ command: srv.command,
180
+ args: srv.args || [],
181
+ icon,
182
+ isEnabled: true,
183
+ isConfigured: true,
184
+ description: `Command: ${srv.command} ${(srv.args || []).join(' ')}`
185
+ });
186
+ });
187
+
188
+ if (detectedTools.docker && !config.mcpServers?.docker) {
189
+ mcpListItems.push({
190
+ name: 'docker',
191
+ command: 'npx',
192
+ args: ['-y', '@proxeus/mcp-docker-server'],
193
+ icon: '🐳',
194
+ isEnabled: false,
195
+ isConfigured: false,
196
+ description: 'Docker MCP Server (Discovered)'
197
+ });
198
+ }
199
+ if (detectedTools.git && !config.mcpServers?.git) {
200
+ mcpListItems.push({
201
+ name: 'git',
202
+ command: 'npx',
203
+ args: ['-y', '@modelcontextprotocol/server-git'],
204
+ icon: '🐙',
205
+ isEnabled: false,
206
+ isConfigured: false,
207
+ description: 'Git MCP Server (Discovered)'
208
+ });
209
+ }
210
+ if (detectedTools.gh && !config.mcpServers?.github) {
211
+ mcpListItems.push({
212
+ name: 'github',
213
+ command: 'npx',
214
+ args: ['-y', '@modelcontextprotocol/server-github'],
215
+ icon: '🐱',
216
+ isEnabled: false,
217
+ isConfigured: false,
218
+ description: 'GitHub MCP Server (Discovered)'
219
+ });
220
+ }
221
+ if (detectedTools.node && !config.mcpServers?.node) {
222
+ mcpListItems.push({
223
+ name: 'node',
224
+ command: 'npx',
225
+ args: ['-y', '@modelcontextprotocol/server-node'],
226
+ icon: '🟢',
227
+ isEnabled: false,
228
+ isConfigured: false,
229
+ description: 'NodeJS Runtime MCP Server (Discovered)'
230
+ });
231
+ }
232
+
125
233
  return (
126
234
  <div className="tab-pane active" style={{ display: 'flex', flexDirection: 'column', gap: '24px' }}>
127
235
  {/* ── Learned AI Skills ── */}
@@ -154,7 +262,7 @@ export default function PluginsTab({
154
262
  {skills.map((s) => (
155
263
  <div className="skill-card" key={s.id} style={{
156
264
  display: 'flex',
157
- alignItems: 'flex-start',
265
+ alignItems: 'center',
158
266
  justifyContent: 'space-between',
159
267
  padding: '16px 20px',
160
268
  border: '1px solid var(--border)',
@@ -163,7 +271,7 @@ export default function PluginsTab({
163
271
  gap: '16px'
164
272
  }}>
165
273
  <div style={{ flex: 1, minWidth: 0 }}>
166
- <div style={{ display: 'flex', alignItems: 'center', gap: '8px', marginBottom: '6px' }}>
274
+ <div style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
167
275
  <span className="badge" style={{
168
276
  background: 'var(--accent)',
169
277
  color: '#fff',
@@ -172,24 +280,31 @@ export default function PluginsTab({
172
280
  padding: '2px 8px',
173
281
  borderRadius: '4px'
174
282
  }}>{s.name}</span>
283
+ {s.location && (
284
+ <span className="location-badge" style={{
285
+ background: s.location === 'workspace'
286
+ ? 'rgba(16, 185, 129, 0.15)'
287
+ : s.location === 'global'
288
+ ? 'rgba(59, 130, 246, 0.15)'
289
+ : 'rgba(139, 92, 246, 0.15)',
290
+ color: s.location === 'workspace'
291
+ ? '#10b981'
292
+ : s.location === 'global'
293
+ ? '#3b82f6'
294
+ : '#8b5cf6',
295
+ fontSize: '0.7rem',
296
+ fontWeight: '600',
297
+ padding: '2px 6px',
298
+ borderRadius: '4px',
299
+ textTransform: 'capitalize'
300
+ }}>
301
+ {s.location === 'database' ? 'Taught' : s.location}
302
+ </span>
303
+ )}
175
304
  <span style={{ fontSize: '0.8rem', color: 'var(--text-muted)', textOverflow: 'ellipsis', overflow: 'hidden', whiteSpace: 'nowrap' }}>
176
305
  Source: {s.sourcePath}
177
306
  </span>
178
307
  </div>
179
- <div style={{
180
- fontSize: '0.85rem',
181
- color: 'var(--text-main)',
182
- whiteSpace: 'pre-wrap',
183
- maxHeight: '120px',
184
- overflowY: 'auto',
185
- background: 'rgba(0,0,0,0.12)',
186
- padding: '10px 14px',
187
- borderRadius: '8px',
188
- fontFamily: 'monospace',
189
- lineHeight: '1.4'
190
- }}>
191
- {s.content}
192
- </div>
193
308
  </div>
194
309
  <button
195
310
  className="btn btn-danger"
@@ -255,33 +370,98 @@ export default function PluginsTab({
255
370
  </div>
256
371
 
257
372
  <div className="mcp-list">
258
- {Object.entries(config.mcpServers || {}).map(([name, srv]: [string, any]) => (
259
- <div className="plugin-card" key={name} style={{
260
- display: 'flex',
261
- alignItems: 'center',
262
- justifyContent: 'space-between',
263
- padding: '16px 20px',
264
- border: '1px solid var(--border)',
265
- borderRadius: '12px',
266
- background: 'var(--surface-bg)',
267
- marginBottom: '10px'
268
- }}>
269
- <div style={{ display: 'flex', alignItems: 'center', gap: '16px' }}>
270
- <div className="plugin-icon" style={{ fontSize: '1.5rem', width: '40px', height: '40px', display: 'grid', placeItems: 'center', background: 'var(--surface-strong)', borderRadius: '10px' }}>⚙</div>
271
- <div className="plugin-info">
272
- <div className="plugin-name" style={{ fontWeight: '600', color: 'var(--text-main)' }}>{name}</div>
273
- <div className="plugin-desc" style={{ fontFamily: 'monospace', fontSize: '0.8rem', color: 'var(--text-muted)' }}>
274
- Command: {srv.command} {srv.args?.join(' ')}
373
+ {mcpListItems.length === 0 ? (
374
+ <div style={{ padding: '20px', textAlign: 'center', color: 'var(--text-muted)' }}>
375
+ No MCP servers configured or discovered.
376
+ </div>
377
+ ) : (
378
+ mcpListItems.map((item) => (
379
+ <div
380
+ className="plugin-card"
381
+ key={item.name}
382
+ style={{
383
+ display: 'flex',
384
+ alignItems: 'center',
385
+ justifyContent: 'space-between',
386
+ padding: '16px 20px',
387
+ border: '1px solid var(--border)',
388
+ borderRadius: '12px',
389
+ background: item.isConfigured ? 'var(--surface-bg)' : 'rgba(16, 185, 129, 0.03)',
390
+ opacity: item.isConfigured ? 1 : 0.65,
391
+ marginBottom: '10px',
392
+ transition: 'all 0.2s'
393
+ }}
394
+ >
395
+ <div style={{ display: 'flex', alignItems: 'center', gap: '16px' }}>
396
+ <div className="plugin-icon" style={{ fontSize: '1.5rem', width: '40px', height: '40px', display: 'grid', placeItems: 'center', background: 'var(--surface-strong)', borderRadius: '10px' }}>
397
+ {item.icon}
398
+ </div>
399
+ <div className="plugin-info">
400
+ <div className="plugin-name" style={{ fontWeight: '600', color: 'var(--text-main)' }}>
401
+ {item.name} {!item.isConfigured && <span style={{ fontSize: '0.75rem', padding: '2px 6px', background: 'rgba(16, 185, 129, 0.15)', color: '#10b981', borderRadius: '4px', marginLeft: '6px' }}>Discovered</span>}
402
+ </div>
403
+ <div className="plugin-desc" style={{ fontFamily: 'monospace', fontSize: '0.8rem', color: 'var(--text-muted)' }}>
404
+ {item.description}
405
+ </div>
275
406
  </div>
276
407
  </div>
408
+ <div className="plugin-actions" style={{ display: 'flex', alignItems: 'center', gap: '16px' }}>
409
+ {item.isConfigured && (
410
+ <button
411
+ className="btn-icon-danger"
412
+ onClick={() => handleRemoveMcpServer(item.name)}
413
+ style={{
414
+ background: 'transparent',
415
+ border: 'none',
416
+ color: '#ef4444',
417
+ cursor: 'pointer',
418
+ padding: '6px',
419
+ display: 'flex',
420
+ alignItems: 'center',
421
+ justifyContent: 'center',
422
+ borderRadius: '6px',
423
+ transition: 'background 0.2s'
424
+ }}
425
+ title="Delete MCP Server"
426
+ >
427
+ <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
428
+ <polyline points="3 6 5 6 21 6"></polyline>
429
+ <path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path>
430
+ <line x1="10" y1="11" x2="10" y2="17"></line>
431
+ <line x1="14" y1="11" x2="14" y2="17"></line>
432
+ </svg>
433
+ </button>
434
+ )}
435
+ <label className="settings-toggle-switch">
436
+ <input
437
+ type="checkbox"
438
+ checked={item.isEnabled}
439
+ onChange={(e) => {
440
+ if (e.target.checked) {
441
+ handleEnableTool(item.name, item.command, item.args);
442
+ } else {
443
+ handleRemoveMcpServer(item.name);
444
+ }
445
+ }}
446
+ />
447
+ <span className="settings-toggle-slider" />
448
+ </label>
449
+ </div>
277
450
  </div>
278
- <div className="plugin-actions">
279
- <button className="btn btn-danger" onClick={() => handleRemoveMcpServer(name)} style={{ padding: '6px 12px', fontSize: '0.8rem', height: '34px' }}>Remove</button>
280
- </div>
281
- </div>
282
- ))}
451
+ ))
452
+ )}
283
453
  </div>
284
454
 
455
+ {/* Save settings alert */}
456
+ {((detectedTools.docker && !config.mcpServers?.docker) ||
457
+ (detectedTools.git && !config.mcpServers?.git) ||
458
+ (detectedTools.gh && !config.mcpServers?.github) ||
459
+ (detectedTools.node && !config.mcpServers?.node)) && (
460
+ <div style={{ marginTop: '12px', padding: '12px 16px', background: 'rgba(255,193,7,0.1)', border: '1px solid rgba(255,193,7,0.3)', borderRadius: '8px', fontSize: '0.8rem', color: '#ffb300', fontWeight: '500', display: 'flex', alignItems: 'center', gap: '8px' }}>
461
+ <span>⚠️ After toggling a plugin, please click the "Save Settings" button at the bottom of the window to persist changes.</span>
462
+ </div>
463
+ )}
464
+
285
465
  <div className="add-mcp-box" style={{
286
466
  marginTop: '20px',
287
467
  padding: '20px',
@@ -157,15 +157,7 @@ export const LOCAL_MODELS = [
157
157
  'lmstudio-community/gemma-2-9b-it-GGUF'
158
158
  ]
159
159
 
160
- export const OLLAMA_MODELS = [
161
- 'llama3:latest',
162
- 'llama3.1:latest',
163
- 'llama3.2:latest',
164
- 'gemma2:latest',
165
- 'mistral:latest',
166
- 'phi3:latest',
167
- 'qwen2.5:latest'
168
- ]
160
+ export const OLLAMA_MODELS: string[] = []
169
161
 
170
162
  export default function SettingsWindow() {
171
163
  const [activeTab, setActiveTab] = useState<TabType>('sect-general')
@@ -1,5 +1,5 @@
1
- import { useEffect, useState } from 'react'
2
- import { getWorkspaceTree, type WorkspaceTreeEntry } from '../tauri'
1
+ import React, { useEffect, useState } from 'react'
2
+ import { getWorkspaceTree, type WorkspaceTreeEntry, createWorkspaceFile, createWorkspaceFolder, deleteWorkspaceItem } from '../tauri'
3
3
  import folderIcon from 'material-icon-theme/icons/folder.svg?url'
4
4
  import folderOpenIcon from 'material-icon-theme/icons/folder-open.svg?url'
5
5
  import folderComponentsIcon from 'material-icon-theme/icons/folder-components.svg?url'
@@ -147,7 +147,7 @@ function materialFileIcon(name: string, fileExtension: string) {
147
147
  return FILE_ICONS_BY_NAME[name.toLowerCase()] || FILE_ICONS_BY_EXTENSION[fileExtension] || documentIcon
148
148
  }
149
149
 
150
- function TreeNode({ entry, level }: { entry: WorkspaceTreeEntry; level: number; key?: string }) {
150
+ function TreeNode({ entry, level, onRefresh, workspacePath }: { entry: WorkspaceTreeEntry; level: number; onRefresh: () => void; workspacePath: string; key?: string }) {
151
151
  const [open, setOpen] = useState(level < 1)
152
152
  const isDirectory = entry.kind === 'directory'
153
153
  const hasChildren = entry.children.length > 0
@@ -156,6 +156,20 @@ function TreeNode({ entry, level }: { entry: WorkspaceTreeEntry; level: number;
156
156
  const materialIcon = isDirectory ? materialFolderIcon(entry.name, open) : materialFileIcon(entry.name, fileExtension)
157
157
  const dragText = `@${entry.path}`
158
158
 
159
+ const handleContextMenu = async (event: React.MouseEvent) => {
160
+ event.preventDefault()
161
+ const confirmed = confirm(`Are you sure you want to delete "${entry.name}"?`)
162
+ if (!confirmed) return
163
+
164
+ try {
165
+ const absolutePath = `${workspacePath}/${entry.path}`
166
+ await deleteWorkspaceItem(absolutePath)
167
+ onRefresh()
168
+ } catch (err) {
169
+ alert(`Failed to delete item: ${err instanceof Error ? err.message : String(err)}`)
170
+ }
171
+ }
172
+
159
173
  return (
160
174
  <div className="workspace-tree-node">
161
175
  <button
@@ -163,6 +177,7 @@ function TreeNode({ entry, level }: { entry: WorkspaceTreeEntry; level: number;
163
177
  className={`workspace-tree-row ${isDirectory ? 'is-directory' : 'is-file'}`}
164
178
  style={{ paddingLeft: `${level * 16 + 8}px` }}
165
179
  onClick={() => isDirectory && hasChildren && setOpen((current) => !current)}
180
+ onContextMenu={handleContextMenu}
166
181
  title={entry.path}
167
182
  draggable
168
183
  onDragStart={(event) => {
@@ -186,7 +201,7 @@ function TreeNode({ entry, level }: { entry: WorkspaceTreeEntry; level: number;
186
201
  {isDirectory && open && hasChildren && (
187
202
  <div className="workspace-tree-children">
188
203
  {entry.children.map((child) => (
189
- <TreeNode key={child.path} entry={child} level={level + 1} />
204
+ <TreeNode key={child.path} entry={child} level={level + 1} onRefresh={onRefresh} workspacePath={workspacePath} />
190
205
  ))}
191
206
  </div>
192
207
  )}
@@ -217,11 +232,52 @@ export default function WorkspacePanel({ agentMode, sending, workspacePath, onEn
217
232
 
218
233
  useEffect(() => {
219
234
  refresh()
235
+
236
+ // Refresh when the window gains focus (e.g., switching back from VS Code)
237
+ const handleFocus = () => {
238
+ refresh()
239
+ }
240
+ window.addEventListener('focus', handleFocus)
241
+
242
+ // Poll every 15 seconds to catch edits/updates in real-time
243
+ const interval = setInterval(() => {
244
+ refresh()
245
+ }, 15000)
246
+
247
+ return () => {
248
+ window.removeEventListener('focus', handleFocus)
249
+ clearInterval(interval)
250
+ }
220
251
  }, [workspacePath])
221
252
 
222
- const startWorkspacePrompt = () => {
223
- onEnableAgentMode()
224
- onSetMessage('Review this workspace and help plan next steps')
253
+ const handleCreateFile = async () => {
254
+ if (!workspacePath.trim()) return
255
+ const name = prompt('Enter name of new file:')
256
+ if (!name || !name.trim()) return
257
+
258
+ try {
259
+ setError('')
260
+ const fullPath = `${workspacePath}/${name.trim()}`
261
+ await createWorkspaceFile(fullPath)
262
+ await refresh()
263
+ } catch (err) {
264
+ setError(err instanceof Error ? err.message : String(err))
265
+ }
266
+ }
267
+
268
+ const handleCreateFolder = async () => {
269
+ if (!workspacePath.trim()) return
270
+ const name = prompt('Enter name of new folder:')
271
+ if (!name || !name.trim()) return
272
+
273
+ try {
274
+ setError('')
275
+ const fullPath = `${workspacePath}/${name.trim()}`
276
+ await createWorkspaceFolder(fullPath)
277
+ await refresh()
278
+ } catch (err) {
279
+ setError(err instanceof Error ? err.message : String(err))
280
+ }
225
281
  }
226
282
 
227
283
  return (
@@ -237,7 +293,8 @@ export default function WorkspacePanel({ agentMode, sending, workspacePath, onEn
237
293
  </header>
238
294
 
239
295
  <div className="workspace-panel-actions">
240
- <button type="button" onClick={startWorkspacePrompt}>Use Agent</button>
296
+ <button type="button" onClick={handleCreateFile} disabled={!workspacePath.trim()}>New File</button>
297
+ <button type="button" onClick={handleCreateFolder} disabled={!workspacePath.trim()}>New Folder</button>
241
298
  <button type="button" onClick={refresh} disabled={!workspacePath.trim()}>Refresh</button>
242
299
  </div>
243
300
 
@@ -255,7 +312,7 @@ export default function WorkspacePanel({ agentMode, sending, workspacePath, onEn
255
312
  </div>
256
313
  <div className="workspace-tree">
257
314
  {tree.children.map((entry) => (
258
- <TreeNode key={entry.path} entry={entry} level={0} />
315
+ <TreeNode key={entry.path} entry={entry} level={0} onRefresh={refresh} workspacePath={workspacePath} />
259
316
  ))}
260
317
  </div>
261
318
  </>
@@ -725,3 +725,9 @@
725
725
  overflow-y: auto;
726
726
  }
727
727
  }
728
+
729
+ @media (max-width: 760px) {
730
+ .img-studio {
731
+ left: 0 !important;
732
+ }
733
+ }
@@ -873,7 +873,7 @@ h1 {
873
873
 
874
874
  .assistant-workspace.model-hidden .conversation-panel {
875
875
  flex: 1;
876
- max-width: 900px;
876
+ max-width: 1100px;
877
877
  margin: 0 auto;
878
878
  }
879
879
 
@@ -1167,6 +1167,51 @@ h1 {
1167
1167
  box-shadow: 0 -4px 20px rgba(0, 0, 0, 0.1);
1168
1168
  }
1169
1169
 
1170
+ .input-disclaimer {
1171
+ text-align: center;
1172
+ font-size: 0.7rem;
1173
+ color: var(--text-muted);
1174
+ opacity: 0.55;
1175
+ padding: 4px 20px 10px;
1176
+ margin: 0;
1177
+ flex-shrink: 0;
1178
+ line-height: 1.5;
1179
+ user-select: none;
1180
+ }
1181
+
1182
+
1183
+ /* Chat Header (Mint Agent title bar inside panel) */
1184
+ .chat-header {
1185
+ display: flex;
1186
+ align-items: center;
1187
+ gap: 12px;
1188
+ padding: 10px 18px;
1189
+ background: var(--chrome-bg);
1190
+ backdrop-filter: var(--glass-blur);
1191
+ -webkit-backdrop-filter: var(--glass-blur);
1192
+ border-bottom: 1px solid var(--border);
1193
+ position: sticky;
1194
+ top: 0;
1195
+ z-index: 50;
1196
+ height: 52px;
1197
+ flex-shrink: 0;
1198
+ }
1199
+
1200
+ .chat-header-title {
1201
+ display: flex;
1202
+ align-items: center;
1203
+ gap: 8px;
1204
+ font-weight: 600;
1205
+ color: var(--text-main);
1206
+ font-size: 0.92rem;
1207
+ }
1208
+
1209
+ .chat-header-logo {
1210
+ width: 20px;
1211
+ height: 20px;
1212
+ object-fit: contain;
1213
+ }
1214
+
1170
1215
  #chat-form {
1171
1216
  display: flex;
1172
1217
  gap: 12px;
@@ -1428,7 +1473,7 @@ h1 {
1428
1473
  gap: 8px;
1429
1474
  margin-bottom: 8px;
1430
1475
  margin-top: 0;
1431
- max-width: min(680px, 100%);
1476
+ max-width: 100%;
1432
1477
  }
1433
1478
 
1434
1479
  .agent-activity-toggle {
@@ -1463,7 +1508,7 @@ h1 {
1463
1508
  border: 0;
1464
1509
  border-radius: 0;
1465
1510
  color: var(--text-main);
1466
- width: min(680px, 100%);
1511
+ width: 100%;
1467
1512
  max-width: 100%;
1468
1513
  padding: 4px 0;
1469
1514
  box-shadow: none;
@@ -2705,7 +2750,7 @@ input:checked + .slider:before {
2705
2750
 
2706
2751
  .workspace-panel-actions {
2707
2752
  display: grid;
2708
- grid-template-columns: 1fr 1fr;
2753
+ grid-template-columns: repeat(3, 1fr);
2709
2754
  gap: 8px;
2710
2755
  padding: 0 10px 12px 0;
2711
2756
  }
@@ -3321,12 +3366,12 @@ input:checked + .slider:before {
3321
3366
  .assistant-workspace.model-hidden .conversation-panel {
3322
3367
  position: relative;
3323
3368
  display: grid;
3324
- grid-template-rows: minmax(0, 1fr) auto;
3325
- width: min(840px, calc(100% - 64px));
3369
+ grid-template-rows: auto minmax(0, 1fr) auto auto;
3370
+ width: min(1100px, calc(100% - 64px));
3326
3371
  height: 100%;
3327
3372
  max-width: none;
3328
3373
  margin: 0 auto;
3329
- padding: 56px 0 28px;
3374
+ padding: 0 0 8px;
3330
3375
  grid-column: 1 / -1;
3331
3376
  z-index: 1;
3332
3377
  }
@@ -3349,13 +3394,13 @@ input:checked + .slider:before {
3349
3394
  }
3350
3395
 
3351
3396
  .assistant-workspace.model-hidden .conversation-panel.is-empty {
3352
- grid-template-rows: minmax(0, 1fr);
3353
- align-content: center;
3397
+ grid-template-rows: auto minmax(0, 1fr) auto auto;
3398
+ align-content: stretch;
3354
3399
  justify-content: stretch;
3355
- width: min(840px, calc(100% - 64px));
3400
+ width: min(1100px, calc(100% - 64px));
3356
3401
  margin: 0 auto;
3357
3402
  padding-top: 0;
3358
- padding-bottom: 0;
3403
+ padding-bottom: 8px;
3359
3404
  }
3360
3405
 
3361
3406
  .conversation-panel.is-empty .chat-container {
@@ -4239,3 +4284,11 @@ input:checked + .slider:before {
4239
4284
  font-size: 0.9rem;
4240
4285
  }
4241
4286
 
4287
+ /* Allow global text selection everywhere */
4288
+ * {
4289
+ user-select: text !important;
4290
+ -webkit-user-select: text !important;
4291
+ -moz-user-select: text !important;
4292
+ -ms-user-select: text !important;
4293
+ }
4294
+
@@ -380,7 +380,6 @@ button {
380
380
  }
381
381
  }
382
382
 
383
- /* Premium Markdown Styles for Chat Messages */
384
383
  .chat-inline-code {
385
384
  background: rgba(16, 185, 129, 0.15);
386
385
  border: 1px solid rgba(16, 185, 129, 0.25);
@@ -391,6 +390,18 @@ button {
391
390
  font-size: 0.88em;
392
391
  }
393
392
 
393
+ .chat-mention {
394
+ background: color-mix(in srgb, var(--accent, #10b981) 12%, transparent);
395
+ border: 1px solid color-mix(in srgb, var(--accent, #10b981) 25%, transparent);
396
+ color: var(--accent, #34d399);
397
+ padding: 2px 6px;
398
+ border-radius: 6px;
399
+ font-weight: 500;
400
+ font-size: 0.9em;
401
+ display: inline-block;
402
+ margin: 0 2px;
403
+ }
404
+
394
405
  .chat-code-block-container {
395
406
  display: flex;
396
407
  flex-direction: column;