@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.
- package/.github/workflows/release.yml +1 -1
- package/Cargo.lock +34 -3
- package/Cargo.toml +2 -2
- package/README.md +31 -0
- package/Release_Note.md +95 -0
- package/assets/Mint_CLI.png +0 -0
- package/crates/mint-cli/Cargo.toml +1 -0
- package/crates/mint-cli/src/agent.rs +142 -48
- package/crates/mint-cli/src/main.rs +231 -22
- package/crates/mint-core/src/api_server.rs +9 -0
- package/crates/mint-core/src/browser.rs +401 -0
- package/crates/mint-core/src/config.rs +69 -1
- package/crates/mint-core/src/lib.rs +5 -0
- package/crates/mint-core/src/orchestration.rs +137 -1
- package/crates/mint-core/src/skills.rs +113 -4
- package/crates/mint-core/src/workflows.rs +1 -22
- package/package.json +1 -1
- package/src/renderer/src/components/ChatPanel.tsx +170 -33
- package/src/renderer/src/components/ImageStudioPanel.tsx +11 -1
- package/src/renderer/src/components/MintDashboard.tsx +40 -2
- package/src/renderer/src/components/Settings/PluginsTab.tsx +221 -41
- package/src/renderer/src/components/SettingsWindow.tsx +1 -9
- package/src/renderer/src/components/WorkspacePanel.tsx +66 -9
- package/src/renderer/src/css/image-studio.css +6 -0
- package/src/renderer/src/css/styles.css +64 -11
- package/src/renderer/src/index.css +12 -1
- package/src/renderer/src/tauri.ts +45 -4
- package/src/renderer/src-web/components/ChatPanel.tsx +163 -32
- package/src/renderer/src-web/components/ImageStudioPanel.tsx +11 -1
- package/src/renderer/src-web/components/MintDashboard.tsx +8 -3
- package/src/renderer/src-web/components/Settings/PluginsTab.tsx +221 -41
- package/src/renderer/src-web/components/SettingsWindow.tsx +1 -9
- package/src/renderer/src-web/css/image-studio.css +6 -0
- package/src/renderer/src-web/css/styles.css +100 -12
- package/src/renderer/src-web/index.css +12 -1
- package/src/renderer/src-web/tauri.ts +24 -2
- package/src-tauri/src/browser.rs +1 -141
- package/src-tauri/src/headless.rs +3 -2
- package/src-tauri/src/lib.rs +117 -26
- package/src-tauri/src/workflows.rs +56 -4
- 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 '../../components/SettingsWindow'
|
|
3
|
-
import { listLearnedSkills, addLearnedSkill, deleteLearnedSkill, LearnedSkill } from '../../../src/tauri'
|
|
3
|
+
import { listLearnedSkills, addLearnedSkill, deleteLearnedSkill, LearnedSkill, detectSystemTools, DetectedTools } from '../../../src/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
|
-
//
|
|
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
|
|
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)
|
|
@@ -121,6 +156,79 @@ export default function PluginsTab({
|
|
|
121
156
|
}
|
|
122
157
|
]
|
|
123
158
|
|
|
159
|
+
const mcpListItems: Array<{
|
|
160
|
+
name: string
|
|
161
|
+
command: string
|
|
162
|
+
args: string[]
|
|
163
|
+
icon: string
|
|
164
|
+
isEnabled: boolean
|
|
165
|
+
isConfigured: boolean
|
|
166
|
+
description: string
|
|
167
|
+
}> = []
|
|
168
|
+
|
|
169
|
+
Object.entries(config.mcpServers || {}).forEach(([name, srv]: [string, any]) => {
|
|
170
|
+
let icon = '⚙';
|
|
171
|
+
if (name === 'docker') icon = '🐳';
|
|
172
|
+
else if (name === 'git' || name === 'gitkraken') icon = '🐙';
|
|
173
|
+
else if (name === 'github') icon = '🐱';
|
|
174
|
+
else if (name === 'node') icon = '🟢';
|
|
175
|
+
|
|
176
|
+
mcpListItems.push({
|
|
177
|
+
name,
|
|
178
|
+
command: srv.command,
|
|
179
|
+
args: srv.args || [],
|
|
180
|
+
icon,
|
|
181
|
+
isEnabled: true,
|
|
182
|
+
isConfigured: true,
|
|
183
|
+
description: `Command: ${srv.command} ${(srv.args || []).join(' ')}`
|
|
184
|
+
});
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
if (detectedTools.docker && !config.mcpServers?.docker) {
|
|
188
|
+
mcpListItems.push({
|
|
189
|
+
name: 'docker',
|
|
190
|
+
command: 'npx',
|
|
191
|
+
args: ['-y', '@proxeus/mcp-docker-server'],
|
|
192
|
+
icon: '🐳',
|
|
193
|
+
isEnabled: false,
|
|
194
|
+
isConfigured: false,
|
|
195
|
+
description: 'Docker MCP Server (Discovered)'
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
if (detectedTools.git && !config.mcpServers?.git) {
|
|
199
|
+
mcpListItems.push({
|
|
200
|
+
name: 'git',
|
|
201
|
+
command: 'npx',
|
|
202
|
+
args: ['-y', '@modelcontextprotocol/server-git'],
|
|
203
|
+
icon: '🐙',
|
|
204
|
+
isEnabled: false,
|
|
205
|
+
isConfigured: false,
|
|
206
|
+
description: 'Git MCP Server (Discovered)'
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
if (detectedTools.gh && !config.mcpServers?.github) {
|
|
210
|
+
mcpListItems.push({
|
|
211
|
+
name: 'github',
|
|
212
|
+
command: 'npx',
|
|
213
|
+
args: ['-y', '@modelcontextprotocol/server-github'],
|
|
214
|
+
icon: '🐱',
|
|
215
|
+
isEnabled: false,
|
|
216
|
+
isConfigured: false,
|
|
217
|
+
description: 'GitHub MCP Server (Discovered)'
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
if (detectedTools.node && !config.mcpServers?.node) {
|
|
221
|
+
mcpListItems.push({
|
|
222
|
+
name: 'node',
|
|
223
|
+
command: 'npx',
|
|
224
|
+
args: ['-y', '@modelcontextprotocol/server-node'],
|
|
225
|
+
icon: '🟢',
|
|
226
|
+
isEnabled: false,
|
|
227
|
+
isConfigured: false,
|
|
228
|
+
description: 'NodeJS Runtime MCP Server (Discovered)'
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
|
|
124
232
|
return (
|
|
125
233
|
<div className="tab-pane active" style={{ display: 'flex', flexDirection: 'column', gap: '24px' }}>
|
|
126
234
|
{/* ── Learned AI Skills ── */}
|
|
@@ -153,7 +261,7 @@ export default function PluginsTab({
|
|
|
153
261
|
{skills.map((s) => (
|
|
154
262
|
<div className="skill-card" key={s.id} style={{
|
|
155
263
|
display: 'flex',
|
|
156
|
-
alignItems: '
|
|
264
|
+
alignItems: 'center',
|
|
157
265
|
justifyContent: 'space-between',
|
|
158
266
|
padding: '16px 20px',
|
|
159
267
|
border: '1px solid var(--border)',
|
|
@@ -162,7 +270,7 @@ export default function PluginsTab({
|
|
|
162
270
|
gap: '16px'
|
|
163
271
|
}}>
|
|
164
272
|
<div style={{ flex: 1, minWidth: 0 }}>
|
|
165
|
-
<div style={{ display: 'flex', alignItems: 'center', gap: '8px'
|
|
273
|
+
<div style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
|
|
166
274
|
<span className="badge" style={{
|
|
167
275
|
background: 'var(--accent)',
|
|
168
276
|
color: '#fff',
|
|
@@ -171,24 +279,31 @@ export default function PluginsTab({
|
|
|
171
279
|
padding: '2px 8px',
|
|
172
280
|
borderRadius: '4px'
|
|
173
281
|
}}>{s.name}</span>
|
|
282
|
+
{s.location && (
|
|
283
|
+
<span className="location-badge" style={{
|
|
284
|
+
background: s.location === 'workspace'
|
|
285
|
+
? 'rgba(16, 185, 129, 0.15)'
|
|
286
|
+
: s.location === 'global'
|
|
287
|
+
? 'rgba(59, 130, 246, 0.15)'
|
|
288
|
+
: 'rgba(139, 92, 246, 0.15)',
|
|
289
|
+
color: s.location === 'workspace'
|
|
290
|
+
? '#10b981'
|
|
291
|
+
: s.location === 'global'
|
|
292
|
+
? '#3b82f6'
|
|
293
|
+
: '#8b5cf6',
|
|
294
|
+
fontSize: '0.7rem',
|
|
295
|
+
fontWeight: '600',
|
|
296
|
+
padding: '2px 6px',
|
|
297
|
+
borderRadius: '4px',
|
|
298
|
+
textTransform: 'capitalize'
|
|
299
|
+
}}>
|
|
300
|
+
{s.location === 'database' ? 'Taught' : s.location}
|
|
301
|
+
</span>
|
|
302
|
+
)}
|
|
174
303
|
<span style={{ fontSize: '0.8rem', color: 'var(--text-muted)', textOverflow: 'ellipsis', overflow: 'hidden', whiteSpace: 'nowrap' }}>
|
|
175
304
|
Source: {s.sourcePath}
|
|
176
305
|
</span>
|
|
177
306
|
</div>
|
|
178
|
-
<div style={{
|
|
179
|
-
fontSize: '0.85rem',
|
|
180
|
-
color: 'var(--text-main)',
|
|
181
|
-
whiteSpace: 'pre-wrap',
|
|
182
|
-
maxHeight: '120px',
|
|
183
|
-
overflowY: 'auto',
|
|
184
|
-
background: 'rgba(0,0,0,0.12)',
|
|
185
|
-
padding: '10px 14px',
|
|
186
|
-
borderRadius: '8px',
|
|
187
|
-
fontFamily: 'monospace',
|
|
188
|
-
lineHeight: '1.4'
|
|
189
|
-
}}>
|
|
190
|
-
{s.content}
|
|
191
|
-
</div>
|
|
192
307
|
</div>
|
|
193
308
|
<button
|
|
194
309
|
className="btn btn-danger"
|
|
@@ -254,33 +369,98 @@ export default function PluginsTab({
|
|
|
254
369
|
</div>
|
|
255
370
|
|
|
256
371
|
<div className="mcp-list">
|
|
257
|
-
{
|
|
258
|
-
<div
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
372
|
+
{mcpListItems.length === 0 ? (
|
|
373
|
+
<div style={{ padding: '20px', textAlign: 'center', color: 'var(--text-muted)' }}>
|
|
374
|
+
No MCP servers configured or discovered.
|
|
375
|
+
</div>
|
|
376
|
+
) : (
|
|
377
|
+
mcpListItems.map((item) => (
|
|
378
|
+
<div
|
|
379
|
+
className="plugin-card"
|
|
380
|
+
key={item.name}
|
|
381
|
+
style={{
|
|
382
|
+
display: 'flex',
|
|
383
|
+
alignItems: 'center',
|
|
384
|
+
justifyContent: 'space-between',
|
|
385
|
+
padding: '16px 20px',
|
|
386
|
+
border: '1px solid var(--border)',
|
|
387
|
+
borderRadius: '12px',
|
|
388
|
+
background: item.isConfigured ? 'var(--surface-bg)' : 'rgba(16, 185, 129, 0.03)',
|
|
389
|
+
opacity: item.isConfigured ? 1 : 0.65,
|
|
390
|
+
marginBottom: '10px',
|
|
391
|
+
transition: 'all 0.2s'
|
|
392
|
+
}}
|
|
393
|
+
>
|
|
394
|
+
<div style={{ display: 'flex', alignItems: 'center', gap: '16px' }}>
|
|
395
|
+
<div className="plugin-icon" style={{ fontSize: '1.5rem', width: '40px', height: '40px', display: 'grid', placeItems: 'center', background: 'var(--surface-strong)', borderRadius: '10px' }}>
|
|
396
|
+
{item.icon}
|
|
397
|
+
</div>
|
|
398
|
+
<div className="plugin-info">
|
|
399
|
+
<div className="plugin-name" style={{ fontWeight: '600', color: 'var(--text-main)' }}>
|
|
400
|
+
{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>}
|
|
401
|
+
</div>
|
|
402
|
+
<div className="plugin-desc" style={{ fontFamily: 'monospace', fontSize: '0.8rem', color: 'var(--text-muted)' }}>
|
|
403
|
+
{item.description}
|
|
404
|
+
</div>
|
|
274
405
|
</div>
|
|
275
406
|
</div>
|
|
407
|
+
<div className="plugin-actions" style={{ display: 'flex', alignItems: 'center', gap: '16px' }}>
|
|
408
|
+
{item.isConfigured && (
|
|
409
|
+
<button
|
|
410
|
+
className="btn-icon-danger"
|
|
411
|
+
onClick={() => handleRemoveMcpServer(item.name)}
|
|
412
|
+
style={{
|
|
413
|
+
background: 'transparent',
|
|
414
|
+
border: 'none',
|
|
415
|
+
color: '#ef4444',
|
|
416
|
+
cursor: 'pointer',
|
|
417
|
+
padding: '6px',
|
|
418
|
+
display: 'flex',
|
|
419
|
+
alignItems: 'center',
|
|
420
|
+
justifyContent: 'center',
|
|
421
|
+
borderRadius: '6px',
|
|
422
|
+
transition: 'background 0.2s'
|
|
423
|
+
}}
|
|
424
|
+
title="Delete MCP Server"
|
|
425
|
+
>
|
|
426
|
+
<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">
|
|
427
|
+
<polyline points="3 6 5 6 21 6"></polyline>
|
|
428
|
+
<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>
|
|
429
|
+
<line x1="10" y1="11" x2="10" y2="17"></line>
|
|
430
|
+
<line x1="14" y1="11" x2="14" y2="17"></line>
|
|
431
|
+
</svg>
|
|
432
|
+
</button>
|
|
433
|
+
)}
|
|
434
|
+
<label className="settings-toggle-switch">
|
|
435
|
+
<input
|
|
436
|
+
type="checkbox"
|
|
437
|
+
checked={item.isEnabled}
|
|
438
|
+
onChange={(e) => {
|
|
439
|
+
if (e.target.checked) {
|
|
440
|
+
handleEnableTool(item.name, item.command, item.args);
|
|
441
|
+
} else {
|
|
442
|
+
handleRemoveMcpServer(item.name);
|
|
443
|
+
}
|
|
444
|
+
}}
|
|
445
|
+
/>
|
|
446
|
+
<span className="settings-toggle-slider" />
|
|
447
|
+
</label>
|
|
448
|
+
</div>
|
|
276
449
|
</div>
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
</div>
|
|
280
|
-
</div>
|
|
281
|
-
))}
|
|
450
|
+
))
|
|
451
|
+
)}
|
|
282
452
|
</div>
|
|
283
453
|
|
|
454
|
+
{/* Save settings alert */}
|
|
455
|
+
{((detectedTools.docker && !config.mcpServers?.docker) ||
|
|
456
|
+
(detectedTools.git && !config.mcpServers?.git) ||
|
|
457
|
+
(detectedTools.gh && !config.mcpServers?.github) ||
|
|
458
|
+
(detectedTools.node && !config.mcpServers?.node)) && (
|
|
459
|
+
<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' }}>
|
|
460
|
+
<span>⚠️ After toggling a plugin, please click the "Save Settings" button at the bottom of the window to persist changes.</span>
|
|
461
|
+
</div>
|
|
462
|
+
)}
|
|
463
|
+
|
|
284
464
|
<div className="add-mcp-box" style={{
|
|
285
465
|
marginTop: '20px',
|
|
286
466
|
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')
|
|
@@ -916,7 +916,7 @@ h1 {
|
|
|
916
916
|
|
|
917
917
|
.assistant-workspace.model-hidden .conversation-panel {
|
|
918
918
|
flex: 1;
|
|
919
|
-
max-width:
|
|
919
|
+
max-width: 1100px;
|
|
920
920
|
margin: 0 auto;
|
|
921
921
|
}
|
|
922
922
|
|
|
@@ -1486,7 +1486,7 @@ h1 {
|
|
|
1486
1486
|
gap: 8px;
|
|
1487
1487
|
margin-bottom: 8px;
|
|
1488
1488
|
margin-top: 0;
|
|
1489
|
-
max-width:
|
|
1489
|
+
max-width: 100%;
|
|
1490
1490
|
}
|
|
1491
1491
|
|
|
1492
1492
|
.agent-activity-toggle {
|
|
@@ -1521,7 +1521,7 @@ h1 {
|
|
|
1521
1521
|
border: 0;
|
|
1522
1522
|
border-radius: 0;
|
|
1523
1523
|
color: var(--text-main);
|
|
1524
|
-
width:
|
|
1524
|
+
width: 100%;
|
|
1525
1525
|
max-width: 100%;
|
|
1526
1526
|
padding: 4px 0;
|
|
1527
1527
|
box-shadow: none;
|
|
@@ -3047,12 +3047,12 @@ input:checked + .slider:before {
|
|
|
3047
3047
|
.assistant-workspace.model-hidden .conversation-panel {
|
|
3048
3048
|
position: relative;
|
|
3049
3049
|
display: grid;
|
|
3050
|
-
grid-template-rows: minmax(0, 1fr) auto;
|
|
3051
|
-
width: min(
|
|
3050
|
+
grid-template-rows: auto minmax(0, 1fr) auto auto;
|
|
3051
|
+
width: min(1100px, calc(100% - 64px));
|
|
3052
3052
|
height: 100%;
|
|
3053
3053
|
max-width: none;
|
|
3054
3054
|
margin: 0 auto;
|
|
3055
|
-
padding:
|
|
3055
|
+
padding: 32px 0 8px;
|
|
3056
3056
|
grid-column: 1 / -1;
|
|
3057
3057
|
z-index: 1;
|
|
3058
3058
|
}
|
|
@@ -3075,13 +3075,13 @@ input:checked + .slider:before {
|
|
|
3075
3075
|
}
|
|
3076
3076
|
|
|
3077
3077
|
.assistant-workspace.model-hidden .conversation-panel.is-empty {
|
|
3078
|
-
grid-template-rows: auto minmax(0, 1fr);
|
|
3079
|
-
align-content:
|
|
3078
|
+
grid-template-rows: auto minmax(0, 1fr) auto auto;
|
|
3079
|
+
align-content: stretch;
|
|
3080
3080
|
justify-content: stretch;
|
|
3081
3081
|
width: min(840px, calc(100% - 64px));
|
|
3082
3082
|
margin: 0 auto;
|
|
3083
|
-
padding-top:
|
|
3084
|
-
padding-bottom:
|
|
3083
|
+
padding-top: 0;
|
|
3084
|
+
padding-bottom: 8px;
|
|
3085
3085
|
}
|
|
3086
3086
|
|
|
3087
3087
|
.conversation-panel.is-empty .chat-container {
|
|
@@ -3643,9 +3643,14 @@ input:checked + .slider:before {
|
|
|
3643
3643
|
|
|
3644
3644
|
.assistant-workspace {
|
|
3645
3645
|
display: flex;
|
|
3646
|
+
flex-direction: column;
|
|
3646
3647
|
margin-left: 0;
|
|
3647
|
-
padding:
|
|
3648
|
-
gap:
|
|
3648
|
+
padding: 0;
|
|
3649
|
+
gap: 0;
|
|
3650
|
+
flex: 1;
|
|
3651
|
+
min-height: 0;
|
|
3652
|
+
height: 100%;
|
|
3653
|
+
overflow: hidden;
|
|
3649
3654
|
}
|
|
3650
3655
|
|
|
3651
3656
|
.assistant-workspace.layout-chat {
|
|
@@ -3654,6 +3659,10 @@ input:checked + .slider:before {
|
|
|
3654
3659
|
|
|
3655
3660
|
.app-body {
|
|
3656
3661
|
grid-template-columns: 1fr;
|
|
3662
|
+
display: flex;
|
|
3663
|
+
flex-direction: column;
|
|
3664
|
+
min-height: 0;
|
|
3665
|
+
flex: 1;
|
|
3657
3666
|
}
|
|
3658
3667
|
|
|
3659
3668
|
.sidebar-toggle {
|
|
@@ -3701,15 +3710,28 @@ input:checked + .slider:before {
|
|
|
3701
3710
|
.conversation-panel {
|
|
3702
3711
|
position: static;
|
|
3703
3712
|
width: 100%;
|
|
3713
|
+
height: 100%;
|
|
3704
3714
|
max-height: none;
|
|
3705
3715
|
transform: none;
|
|
3706
3716
|
flex: 1;
|
|
3707
3717
|
min-width: 0;
|
|
3718
|
+
min-height: 0;
|
|
3719
|
+
border-radius: 0;
|
|
3720
|
+
overflow: hidden;
|
|
3708
3721
|
}
|
|
3709
3722
|
|
|
3710
3723
|
.chat-container {
|
|
3711
3724
|
max-height: none;
|
|
3712
3725
|
}
|
|
3726
|
+
|
|
3727
|
+
.input-area {
|
|
3728
|
+
padding: 10px;
|
|
3729
|
+
}
|
|
3730
|
+
|
|
3731
|
+
#chat-form {
|
|
3732
|
+
min-height: auto;
|
|
3733
|
+
padding: 6px;
|
|
3734
|
+
}
|
|
3713
3735
|
}
|
|
3714
3736
|
|
|
3715
3737
|
/* Chat Bold Highlight styling */
|
|
@@ -3726,6 +3748,19 @@ input:checked + .slider:before {
|
|
|
3726
3748
|
color: #38bdf8; /* Sky-400 for midnight theme */
|
|
3727
3749
|
}
|
|
3728
3750
|
|
|
3751
|
+
/* Input Disclaimer Footer */
|
|
3752
|
+
.input-disclaimer {
|
|
3753
|
+
text-align: center;
|
|
3754
|
+
font-size: 0.7rem;
|
|
3755
|
+
color: var(--text-muted);
|
|
3756
|
+
opacity: 0.55;
|
|
3757
|
+
padding: 6px 20px 10px;
|
|
3758
|
+
margin: 0;
|
|
3759
|
+
flex-shrink: 0;
|
|
3760
|
+
line-height: 1.5;
|
|
3761
|
+
user-select: none;
|
|
3762
|
+
}
|
|
3763
|
+
|
|
3729
3764
|
/* Web-Native Chat Header */
|
|
3730
3765
|
.chat-header {
|
|
3731
3766
|
display: flex;
|
|
@@ -3839,6 +3874,51 @@ input:checked + .slider:before {
|
|
|
3839
3874
|
.mobile-menu-btn {
|
|
3840
3875
|
display: flex;
|
|
3841
3876
|
}
|
|
3877
|
+
|
|
3878
|
+
/* Override desktop grid layout so chat-header (hamburger) is visible on mobile */
|
|
3879
|
+
.assistant-workspace.model-hidden .conversation-panel,
|
|
3880
|
+
.assistant-workspace.model-hidden .conversation-panel.is-empty {
|
|
3881
|
+
display: flex;
|
|
3882
|
+
flex-direction: column;
|
|
3883
|
+
width: 100%;
|
|
3884
|
+
height: 100%;
|
|
3885
|
+
padding: 0;
|
|
3886
|
+
margin: 0;
|
|
3887
|
+
grid-template-rows: none;
|
|
3888
|
+
grid-column: auto;
|
|
3889
|
+
}
|
|
3890
|
+
|
|
3891
|
+
/* Solid header fixed to top with background and blur */
|
|
3892
|
+
.chat-header {
|
|
3893
|
+
position: fixed;
|
|
3894
|
+
top: 0;
|
|
3895
|
+
left: 0;
|
|
3896
|
+
right: 0;
|
|
3897
|
+
z-index: 200;
|
|
3898
|
+
background: var(--chrome-bg);
|
|
3899
|
+
backdrop-filter: var(--glass-blur);
|
|
3900
|
+
-webkit-backdrop-filter: var(--glass-blur);
|
|
3901
|
+
border-bottom: 1px solid var(--border);
|
|
3902
|
+
height: 52px;
|
|
3903
|
+
padding: 10px 16px;
|
|
3904
|
+
}
|
|
3905
|
+
|
|
3906
|
+
.assistant-workspace.model-hidden .chat-header {
|
|
3907
|
+
display: flex;
|
|
3908
|
+
flex-shrink: 0;
|
|
3909
|
+
}
|
|
3910
|
+
|
|
3911
|
+
/* Push chat content below fixed header */
|
|
3912
|
+
.assistant-workspace.model-hidden .chat-container {
|
|
3913
|
+
flex: 1;
|
|
3914
|
+
overflow-y: auto;
|
|
3915
|
+
padding: 64px 12px 8px;
|
|
3916
|
+
}
|
|
3917
|
+
|
|
3918
|
+
.assistant-workspace.model-hidden .input-area {
|
|
3919
|
+
width: 100%;
|
|
3920
|
+
flex-shrink: 0;
|
|
3921
|
+
}
|
|
3842
3922
|
}
|
|
3843
3923
|
|
|
3844
3924
|
/* System Event Divider with Pill */
|
|
@@ -4079,3 +4159,11 @@ input:checked + .slider:before {
|
|
|
4079
4159
|
font-size: 0.9rem;
|
|
4080
4160
|
}
|
|
4081
4161
|
|
|
4162
|
+
/* Allow global text selection everywhere */
|
|
4163
|
+
* {
|
|
4164
|
+
user-select: text !important;
|
|
4165
|
+
-webkit-user-select: text !important;
|
|
4166
|
+
-moz-user-select: text !important;
|
|
4167
|
+
-ms-user-select: text !important;
|
|
4168
|
+
}
|
|
4169
|
+
|
|
@@ -379,7 +379,6 @@ button {
|
|
|
379
379
|
}
|
|
380
380
|
}
|
|
381
381
|
|
|
382
|
-
/* Premium Markdown Styles for Chat Messages */
|
|
383
382
|
.chat-inline-code {
|
|
384
383
|
background: rgba(16, 185, 129, 0.15);
|
|
385
384
|
border: 1px solid rgba(16, 185, 129, 0.25);
|
|
@@ -390,6 +389,18 @@ button {
|
|
|
390
389
|
font-size: 0.88em;
|
|
391
390
|
}
|
|
392
391
|
|
|
392
|
+
.chat-mention {
|
|
393
|
+
background: color-mix(in srgb, var(--accent, #10b981) 12%, transparent);
|
|
394
|
+
border: 1px solid color-mix(in srgb, var(--accent, #10b981) 25%, transparent);
|
|
395
|
+
color: var(--accent, #34d399);
|
|
396
|
+
padding: 2px 6px;
|
|
397
|
+
border-radius: 6px;
|
|
398
|
+
font-weight: 500;
|
|
399
|
+
font-size: 0.9em;
|
|
400
|
+
display: inline-block;
|
|
401
|
+
margin: 0 2px;
|
|
402
|
+
}
|
|
403
|
+
|
|
393
404
|
.chat-code-block-container {
|
|
394
405
|
display: flex;
|
|
395
406
|
flex-direction: column;
|
|
@@ -134,6 +134,28 @@ export async function getRuntimeStatus(): Promise<RuntimeStatus> {
|
|
|
134
134
|
return invoke<RuntimeStatus>('get_runtime_status')
|
|
135
135
|
}
|
|
136
136
|
|
|
137
|
+
export interface DetectedTools {
|
|
138
|
+
docker: boolean
|
|
139
|
+
git: boolean
|
|
140
|
+
gh: boolean
|
|
141
|
+
node: boolean
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export async function detectSystemTools(): Promise<DetectedTools> {
|
|
145
|
+
if (typeof window === 'undefined' || !isTauriRuntime()) {
|
|
146
|
+
const API_BASE = getApiBase();
|
|
147
|
+
try {
|
|
148
|
+
const res = await fetch(`${API_BASE}/detect-tools`);
|
|
149
|
+
return await res.json();
|
|
150
|
+
} catch (e) {
|
|
151
|
+
console.error("Failed to detect tools from local server:", e);
|
|
152
|
+
return { docker: false, git: false, gh: false, node: false };
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
const { invoke } = await import('@tauri-apps/api/core')
|
|
156
|
+
return invoke<DetectedTools>('detect_system_tools')
|
|
157
|
+
}
|
|
158
|
+
|
|
137
159
|
export async function sendChatMessage(
|
|
138
160
|
message: string,
|
|
139
161
|
imageDataUri?: string | null,
|
|
@@ -577,12 +599,12 @@ export async function setDefaultImageProvider(provider: string): Promise<boolean
|
|
|
577
599
|
|
|
578
600
|
|
|
579
601
|
|
|
580
|
-
export async function submitToolApproval(token: string, approved: boolean): Promise<void> {
|
|
602
|
+
export async function submitToolApproval(token: string, approved: boolean, answer?: string): Promise<void> {
|
|
581
603
|
if (typeof window === 'undefined' || !isTauriRuntime()) {
|
|
582
604
|
return;
|
|
583
605
|
}
|
|
584
606
|
const { invoke } = await import('@tauri-apps/api/core')
|
|
585
|
-
return invoke('submit_tool_approval', { token, approved })
|
|
607
|
+
return invoke('submit_tool_approval', { token, approved, answer })
|
|
586
608
|
}
|
|
587
609
|
|
|
588
610
|
export async function proposeCodeEdits(root: string, edits: CodeEdit[]): Promise<CodeEditProposal> {
|