@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
|
@@ -128,6 +128,28 @@ export async function getRuntimeStatus(): Promise<RuntimeStatus> {
|
|
|
128
128
|
return invoke<RuntimeStatus>('get_runtime_status')
|
|
129
129
|
}
|
|
130
130
|
|
|
131
|
+
export interface DetectedTools {
|
|
132
|
+
docker: boolean
|
|
133
|
+
git: boolean
|
|
134
|
+
gh: boolean
|
|
135
|
+
node: boolean
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export async function detectSystemTools(): Promise<DetectedTools> {
|
|
139
|
+
if (typeof window === 'undefined' || !(window as any).__TAURI_INTERNALS__) {
|
|
140
|
+
const API_BASE = "http://localhost:3000/api";
|
|
141
|
+
try {
|
|
142
|
+
const res = await fetch(`${API_BASE}/detect-tools`);
|
|
143
|
+
return await res.json();
|
|
144
|
+
} catch (e) {
|
|
145
|
+
console.error("Failed to detect tools from local server:", e);
|
|
146
|
+
return { docker: false, git: false, gh: false, node: false };
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
const { invoke } = await import('@tauri-apps/api/core')
|
|
150
|
+
return invoke<DetectedTools>('detect_system_tools')
|
|
151
|
+
}
|
|
152
|
+
|
|
131
153
|
export async function sendChatMessage(
|
|
132
154
|
message: string,
|
|
133
155
|
imageDataUri?: string | null,
|
|
@@ -426,9 +448,10 @@ export interface LearnedSkill {
|
|
|
426
448
|
sourcePath: string
|
|
427
449
|
content: string
|
|
428
450
|
updatedAt: string
|
|
451
|
+
location?: string
|
|
429
452
|
}
|
|
430
453
|
|
|
431
|
-
export async function listLearnedSkills(): Promise<LearnedSkill[]> {
|
|
454
|
+
export async function listLearnedSkills(workspacePath?: string): Promise<LearnedSkill[]> {
|
|
432
455
|
if (typeof window === 'undefined' || !(window as any).__TAURI_INTERNALS__) {
|
|
433
456
|
try {
|
|
434
457
|
const val = await getProfileValue('learned-skills-web-mock')
|
|
@@ -442,7 +465,7 @@ export async function listLearnedSkills(): Promise<LearnedSkill[]> {
|
|
|
442
465
|
}
|
|
443
466
|
}
|
|
444
467
|
const { invoke } = await import('@tauri-apps/api/core')
|
|
445
|
-
return invoke<LearnedSkill[]>('list_learned_skills')
|
|
468
|
+
return invoke<LearnedSkill[]>('list_learned_skills', { workspacePath })
|
|
446
469
|
}
|
|
447
470
|
|
|
448
471
|
export async function addLearnedSkill(name: string, content: string): Promise<LearnedSkill> {
|
|
@@ -647,6 +670,24 @@ export async function getWorkspaceTree(path?: string | null): Promise<WorkspaceT
|
|
|
647
670
|
return invoke<WorkspaceTreeEntry>('get_workspace_tree', { path })
|
|
648
671
|
}
|
|
649
672
|
|
|
673
|
+
export async function createWorkspaceFile(path: string): Promise<void> {
|
|
674
|
+
if (typeof window === 'undefined' || !(window as any).__TAURI_INTERNALS__) return
|
|
675
|
+
const { invoke } = await import('@tauri-apps/api/core')
|
|
676
|
+
return invoke('create_workspace_file', { path })
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
export async function createWorkspaceFolder(path: string): Promise<void> {
|
|
680
|
+
if (typeof window === 'undefined' || !(window as any).__TAURI_INTERNALS__) return
|
|
681
|
+
const { invoke } = await import('@tauri-apps/api/core')
|
|
682
|
+
return invoke('create_workspace_folder', { path })
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
export async function deleteWorkspaceItem(path: string): Promise<void> {
|
|
686
|
+
if (typeof window === 'undefined' || !(window as any).__TAURI_INTERNALS__) return
|
|
687
|
+
const { invoke } = await import('@tauri-apps/api/core')
|
|
688
|
+
return invoke('delete_workspace_item', { path })
|
|
689
|
+
}
|
|
690
|
+
|
|
650
691
|
export async function selectWorkspaceDirectory(): Promise<string | null> {
|
|
651
692
|
if (typeof window === 'undefined' || !(window as any).__TAURI_INTERNALS__) {
|
|
652
693
|
return null
|
|
@@ -656,12 +697,12 @@ export async function selectWorkspaceDirectory(): Promise<string | null> {
|
|
|
656
697
|
return selected?.trim() || null
|
|
657
698
|
}
|
|
658
699
|
|
|
659
|
-
export async function submitToolApproval(token: string, approved: boolean): Promise<void> {
|
|
700
|
+
export async function submitToolApproval(token: string, approved: boolean, answer?: string): Promise<void> {
|
|
660
701
|
if (typeof window === 'undefined' || !(window as any).__TAURI_INTERNALS__) {
|
|
661
702
|
return;
|
|
662
703
|
}
|
|
663
704
|
const { invoke } = await import('@tauri-apps/api/core')
|
|
664
|
-
return invoke('submit_tool_approval', { token, approved })
|
|
705
|
+
return invoke('submit_tool_approval', { token, approved, answer })
|
|
665
706
|
}
|
|
666
707
|
|
|
667
708
|
export async function proposeCodeEdits(root: string, edits: CodeEdit[]): Promise<CodeEditProposal> {
|
|
@@ -68,15 +68,7 @@ const LOCAL_MODELS = [
|
|
|
68
68
|
'lmstudio-community/gemma-2-9b-it-GGUF'
|
|
69
69
|
]
|
|
70
70
|
|
|
71
|
-
const OLLAMA_MODELS = [
|
|
72
|
-
'llama3:latest',
|
|
73
|
-
'llama3.1:latest',
|
|
74
|
-
'llama3.2:latest',
|
|
75
|
-
'gemma2:latest',
|
|
76
|
-
'mistral:latest',
|
|
77
|
-
'phi3:latest',
|
|
78
|
-
'qwen2.5:latest'
|
|
79
|
-
]
|
|
71
|
+
const OLLAMA_MODELS: string[] = []
|
|
80
72
|
|
|
81
73
|
interface ApprovalDetails {
|
|
82
74
|
title: string
|
|
@@ -122,6 +114,7 @@ interface AgentActivity {
|
|
|
122
114
|
target: string
|
|
123
115
|
kind: 'file' | 'folder' | 'search' | 'terminal' | 'tool'
|
|
124
116
|
state: 'active' | 'done' | 'error'
|
|
117
|
+
action?: string
|
|
125
118
|
}
|
|
126
119
|
|
|
127
120
|
interface AgentActivityView {
|
|
@@ -153,7 +146,20 @@ function describeTool(action: string, input: Record<string, unknown>): AgentActi
|
|
|
153
146
|
const command = activityDetail(input, 'command')
|
|
154
147
|
const name = activityDetail(input, 'name')
|
|
155
148
|
const tool = activityDetail(input, 'tool')
|
|
156
|
-
const
|
|
149
|
+
const rawTarget = path || query || command || name || tool || action.replaceAll('_', ' ')
|
|
150
|
+
|
|
151
|
+
// Append line range for read_file when startLine / endLine are available
|
|
152
|
+
let target = rawTarget
|
|
153
|
+
if (action === 'read_file' && path) {
|
|
154
|
+
const startLine = typeof input.startLine === 'number' ? input.startLine : undefined
|
|
155
|
+
const endLine = typeof input.endLine === 'number' ? input.endLine : undefined
|
|
156
|
+
if (startLine !== undefined && endLine !== undefined) {
|
|
157
|
+
target = `${rawTarget} #L${startLine}-${endLine}`
|
|
158
|
+
} else if (startLine !== undefined) {
|
|
159
|
+
target = `${rawTarget} #L${startLine}`
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
157
163
|
const labels: Record<string, string> = {
|
|
158
164
|
apply_patch: 'Applying patch',
|
|
159
165
|
ask_user: 'Asking user',
|
|
@@ -189,6 +195,7 @@ function describeTool(action: string, input: Record<string, unknown>): AgentActi
|
|
|
189
195
|
target: formatActivityTarget(target),
|
|
190
196
|
kind: activityKind(action, target),
|
|
191
197
|
state: 'active',
|
|
198
|
+
action,
|
|
192
199
|
}
|
|
193
200
|
}
|
|
194
201
|
|
|
@@ -215,7 +222,13 @@ function activitiesFrom(progress: AgentProgress[]): AgentActivityView {
|
|
|
215
222
|
for (let index = activities.length - 1; index >= 0; index -= 1) {
|
|
216
223
|
if (activities[index].state !== 'active') continue
|
|
217
224
|
activities[index].state = event.data.result.startsWith('Error:') ? 'error' : 'done'
|
|
218
|
-
if (activities[index].state === 'error')
|
|
225
|
+
if (activities[index].state === 'error') {
|
|
226
|
+
activities[index].label = 'Failed'
|
|
227
|
+
}
|
|
228
|
+
if (activities[index].action === 'ask_user' && event.data.result.startsWith('User answered:')) {
|
|
229
|
+
const answer = event.data.result.replace('User answered:', '').trim()
|
|
230
|
+
activities[index].target = `(Answered: "${answer}") ${activities[index].target}`
|
|
231
|
+
}
|
|
219
232
|
break
|
|
220
233
|
}
|
|
221
234
|
}
|
|
@@ -224,7 +237,16 @@ function activitiesFrom(progress: AgentProgress[]): AgentActivityView {
|
|
|
224
237
|
return { summary: activitySummary(activities), items }
|
|
225
238
|
}
|
|
226
239
|
|
|
227
|
-
function
|
|
240
|
+
function AgentActivityTable({ activityView }: { activityView: AgentActivityView }) {
|
|
241
|
+
const [expandedIndices, setExpandedIndices] = useState<Record<number, boolean>>({})
|
|
242
|
+
|
|
243
|
+
const toggleExpand = (index: number) => {
|
|
244
|
+
setExpandedIndices(prev => ({
|
|
245
|
+
...prev,
|
|
246
|
+
[index]: !prev[index]
|
|
247
|
+
}))
|
|
248
|
+
}
|
|
249
|
+
|
|
228
250
|
return (
|
|
229
251
|
<div className="agent-activity-list">
|
|
230
252
|
<div className="agent-activity-table-head" aria-hidden="true">
|
|
@@ -233,14 +255,39 @@ function renderAgentActivityTable(activityView: AgentActivityView) {
|
|
|
233
255
|
<span>Target</span>
|
|
234
256
|
<span />
|
|
235
257
|
</div>
|
|
236
|
-
{activityView.items.map((activity, index) =>
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
<
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
258
|
+
{activityView.items.map((activity, index) => {
|
|
259
|
+
const isExpanded = !!expandedIndices[index]
|
|
260
|
+
return (
|
|
261
|
+
<div
|
|
262
|
+
className="agent-activity-item"
|
|
263
|
+
data-kind={activity.kind}
|
|
264
|
+
data-state={activity.state}
|
|
265
|
+
key={`${index}-${activity.label}-${activity.target}`}
|
|
266
|
+
style={{ cursor: 'pointer' }}
|
|
267
|
+
onClick={() => toggleExpand(index)}
|
|
268
|
+
>
|
|
269
|
+
<span className="agent-activity-label">{activity.label}</span>
|
|
270
|
+
<span className="agent-activity-icon" aria-hidden="true" />
|
|
271
|
+
<span
|
|
272
|
+
className="agent-activity-text"
|
|
273
|
+
style={isExpanded ? { whiteSpace: 'normal', wordBreak: 'break-all', overflow: 'visible', textOverflow: 'clip' } : undefined}
|
|
274
|
+
>
|
|
275
|
+
{activity.target}
|
|
276
|
+
</span>
|
|
277
|
+
<span
|
|
278
|
+
className="agent-activity-chevron"
|
|
279
|
+
style={{
|
|
280
|
+
transform: isExpanded ? 'rotate(90deg)' : 'none',
|
|
281
|
+
transition: 'transform 0.2s ease',
|
|
282
|
+
display: 'inline-block'
|
|
283
|
+
}}
|
|
284
|
+
aria-hidden="true"
|
|
285
|
+
>
|
|
286
|
+
>
|
|
287
|
+
</span>
|
|
288
|
+
</div>
|
|
289
|
+
)
|
|
290
|
+
})}
|
|
244
291
|
</div>
|
|
245
292
|
)
|
|
246
293
|
}
|
|
@@ -257,7 +304,7 @@ function renderApprovalDetails(approval: any): ApprovalDetails {
|
|
|
257
304
|
return { title: `Run MCP Tool: ${server}/${tool}`, body: typeof args === 'string' ? args : JSON.stringify(args, null, 2), reason: 'Running external MCP tool.', isDangerous: false }
|
|
258
305
|
}
|
|
259
306
|
if (approval.UserApproval) return { title: approval.UserApproval.title, body: approval.UserApproval.prompt, reason: 'The agent requested explicit approval.', isDangerous: false }
|
|
260
|
-
if (approval.AskUser) return { title: 'Question From Agent', body: approval.AskUser.question, reason: '
|
|
307
|
+
if (approval.AskUser) return { title: 'Question From Agent', body: approval.AskUser.question, reason: 'Type your answer below and submit to respond to the agent.', isDangerous: false }
|
|
261
308
|
return { title: 'Unknown Action', body: JSON.stringify(approval, null, 2), reason: 'Requires approval to proceed.', isDangerous: false }
|
|
262
309
|
}
|
|
263
310
|
|
|
@@ -353,7 +400,7 @@ interface ChatPanelProps {
|
|
|
353
400
|
onSetSmartContext: (enabled: boolean) => void
|
|
354
401
|
onSetAgentMode: (enabled: boolean) => void
|
|
355
402
|
onSetProvider: (provider: string) => void
|
|
356
|
-
onApproval: (approved: boolean, autoApproveSession?: boolean) => void
|
|
403
|
+
onApproval: (approved: boolean, autoApproveSession?: boolean, answer?: string) => void
|
|
357
404
|
onToggleMobileSidebar: () => void
|
|
358
405
|
settingsConfig: any
|
|
359
406
|
onSetModel: (model: string) => void
|
|
@@ -449,7 +496,17 @@ function renderFormattedMessage(text: string) {
|
|
|
449
496
|
</strong>
|
|
450
497
|
)
|
|
451
498
|
}
|
|
452
|
-
|
|
499
|
+
const mentionParts = boldPart.split(/(@[\w\-\.\/]+)/g)
|
|
500
|
+
return mentionParts.map((mentionPart, mentionIndex) => {
|
|
501
|
+
if (mentionIndex % 2 === 1) {
|
|
502
|
+
return (
|
|
503
|
+
<span key={`mention-${mentionIndex}`} className="chat-mention">
|
|
504
|
+
{mentionPart}
|
|
505
|
+
</span>
|
|
506
|
+
)
|
|
507
|
+
}
|
|
508
|
+
return mentionPart
|
|
509
|
+
})
|
|
453
510
|
})
|
|
454
511
|
})
|
|
455
512
|
}
|
|
@@ -975,6 +1032,34 @@ export default function ChatPanel({
|
|
|
975
1032
|
const [openFileDiffs, setOpenFileDiffs] = useState<Record<string, boolean>>({})
|
|
976
1033
|
const [toolMenuOpen, setToolMenuOpen] = useState(false)
|
|
977
1034
|
const toolMenuRef = useRef<HTMLDivElement | null>(null)
|
|
1035
|
+
const [dynamicOllamaModels, setDynamicOllamaModels] = useState<string[]>(OLLAMA_MODELS)
|
|
1036
|
+
const [askAnswer, setAskAnswer] = useState('')
|
|
1037
|
+
|
|
1038
|
+
useEffect(() => {
|
|
1039
|
+
setAskAnswer('')
|
|
1040
|
+
}, [pendingApproval])
|
|
1041
|
+
|
|
1042
|
+
useEffect(() => {
|
|
1043
|
+
const fetchOllamaModels = async () => {
|
|
1044
|
+
if (status?.activeProvider !== 'ollama') return;
|
|
1045
|
+
const host = settingsConfig?.ollamaHost || 'http://localhost:11434';
|
|
1046
|
+
const cleanHost = host.endsWith('/') ? host.slice(0, -1) : host;
|
|
1047
|
+
try {
|
|
1048
|
+
const res = await fetch(`${cleanHost}/api/tags`);
|
|
1049
|
+
if (res.ok) {
|
|
1050
|
+
const data = await res.json();
|
|
1051
|
+
if (data && Array.isArray(data.models)) {
|
|
1052
|
+
setDynamicOllamaModels(data.models.map((m: any) => m.name));
|
|
1053
|
+
return;
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
1056
|
+
} catch (err) {
|
|
1057
|
+
// fallback to default if fetch fails
|
|
1058
|
+
}
|
|
1059
|
+
setDynamicOllamaModels(OLLAMA_MODELS);
|
|
1060
|
+
}
|
|
1061
|
+
fetchOllamaModels();
|
|
1062
|
+
}, [status?.activeProvider, settingsConfig?.ollamaHost])
|
|
978
1063
|
const canSubmit = Boolean(message.trim() || imageAttachments.length > 0 || documentName)
|
|
979
1064
|
const sendingImageMarkers = Array.from({ length: sendingImageCount }, (_, index) => `[Image #${index + 1}]`).join(' ')
|
|
980
1065
|
|
|
@@ -995,7 +1080,7 @@ export default function ChatPanel({
|
|
|
995
1080
|
case 'local_openai':
|
|
996
1081
|
return LOCAL_MODELS
|
|
997
1082
|
case 'ollama':
|
|
998
|
-
return
|
|
1083
|
+
return dynamicOllamaModels
|
|
999
1084
|
default:
|
|
1000
1085
|
return []
|
|
1001
1086
|
}
|
|
@@ -1543,7 +1628,7 @@ export default function ChatPanel({
|
|
|
1543
1628
|
</button>
|
|
1544
1629
|
{isOpen && (
|
|
1545
1630
|
<div className="agent-activity-card agent-activity-card-history">
|
|
1546
|
-
{
|
|
1631
|
+
<AgentActivityTable activityView={activityView} />
|
|
1547
1632
|
</div>
|
|
1548
1633
|
)}
|
|
1549
1634
|
</div>
|
|
@@ -1885,7 +1970,7 @@ export default function ChatPanel({
|
|
|
1885
1970
|
{pendingApproval ? 'Waiting for approval' : 'Working'}
|
|
1886
1971
|
</span>
|
|
1887
1972
|
</div>
|
|
1888
|
-
{
|
|
1973
|
+
<AgentActivityTable activityView={agentActivities} />
|
|
1889
1974
|
</div>
|
|
1890
1975
|
</div>
|
|
1891
1976
|
)}
|
|
@@ -1967,6 +2052,7 @@ export default function ChatPanel({
|
|
|
1967
2052
|
const writeFile = pendingApproval.approval?.WriteFile
|
|
1968
2053
|
const applyPatch = pendingApproval.approval?.ApplyPatch
|
|
1969
2054
|
const diffText = writeFile?.diff || applyPatch?.diff
|
|
2055
|
+
const isAskUser = !!pendingApproval.approval?.AskUser
|
|
1970
2056
|
|
|
1971
2057
|
return (
|
|
1972
2058
|
<div className="message ai-message" style={{ width: '100%' }}>
|
|
@@ -1983,12 +2069,54 @@ export default function ChatPanel({
|
|
|
1983
2069
|
) : (
|
|
1984
2070
|
details.reason && <div className="approval-card-reason" style={{ whiteSpace: 'pre-wrap', fontFamily: 'monospace' }}>{details.reason}</div>
|
|
1985
2071
|
)}
|
|
2072
|
+
{isAskUser && (
|
|
2073
|
+
<div className="approval-card-input-container" style={{ marginTop: '12px', width: '100%' }}>
|
|
2074
|
+
<textarea
|
|
2075
|
+
style={{
|
|
2076
|
+
width: '100%',
|
|
2077
|
+
minHeight: '70px',
|
|
2078
|
+
padding: '10px',
|
|
2079
|
+
background: 'rgba(0, 0, 0, 0.2)',
|
|
2080
|
+
border: '1px solid rgba(255, 255, 255, 0.1)',
|
|
2081
|
+
borderRadius: '6px',
|
|
2082
|
+
color: 'var(--text-main, #f8fafc)',
|
|
2083
|
+
fontSize: '0.9rem',
|
|
2084
|
+
fontFamily: 'inherit',
|
|
2085
|
+
resize: 'vertical',
|
|
2086
|
+
outline: 'none',
|
|
2087
|
+
}}
|
|
2088
|
+
placeholder="Type your answer here..."
|
|
2089
|
+
value={askAnswer}
|
|
2090
|
+
onChange={(e) => setAskAnswer(e.target.value)}
|
|
2091
|
+
/>
|
|
2092
|
+
</div>
|
|
2093
|
+
)}
|
|
1986
2094
|
</div>
|
|
1987
|
-
|
|
1988
|
-
<
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
2095
|
+
{isAskUser ? (
|
|
2096
|
+
<div className="approval-card-actions">
|
|
2097
|
+
<button
|
|
2098
|
+
type="button"
|
|
2099
|
+
className="approval-btn approval-btn-approve"
|
|
2100
|
+
disabled={!askAnswer.trim()}
|
|
2101
|
+
onClick={() => onApproval(true, false, askAnswer)}
|
|
2102
|
+
>
|
|
2103
|
+
Submit Answer
|
|
2104
|
+
</button>
|
|
2105
|
+
<button
|
|
2106
|
+
type="button"
|
|
2107
|
+
className="approval-btn approval-btn-cancel"
|
|
2108
|
+
onClick={() => onApproval(false)}
|
|
2109
|
+
>
|
|
2110
|
+
Decline
|
|
2111
|
+
</button>
|
|
2112
|
+
</div>
|
|
2113
|
+
) : (
|
|
2114
|
+
<div className="approval-card-actions">
|
|
2115
|
+
<button type="button" className="approval-btn approval-btn-approve" onClick={() => onApproval(true)}>Approve</button>
|
|
2116
|
+
<button type="button" className="approval-btn" style={{ backgroundColor: 'rgba(16, 185, 129, 0.22)', borderColor: 'rgba(16, 185, 129, 0.4)', color: '#a7f3d0' }} onClick={() => onApproval(true, true)}>Approve this session</button>
|
|
2117
|
+
<button type="button" className="approval-btn approval-btn-cancel" onClick={() => onApproval(false)}>Cancel</button>
|
|
2118
|
+
</div>
|
|
2119
|
+
)}
|
|
1992
2120
|
</div>
|
|
1993
2121
|
</div>
|
|
1994
2122
|
</div>
|
|
@@ -2134,7 +2262,7 @@ export default function ChatPanel({
|
|
|
2134
2262
|
return <option key={provider} value={provider}>{displayName}</option>
|
|
2135
2263
|
})}
|
|
2136
2264
|
</select>
|
|
2137
|
-
{availableModels.length > 0 && (
|
|
2265
|
+
{(availableModels.length > 0 || activeModel) && (
|
|
2138
2266
|
<select
|
|
2139
2267
|
value={activeModel}
|
|
2140
2268
|
onChange={(event) => onSetModel(event.target.value)}
|
|
@@ -2192,6 +2320,9 @@ export default function ChatPanel({
|
|
|
2192
2320
|
</button>
|
|
2193
2321
|
</form>
|
|
2194
2322
|
</div>
|
|
2323
|
+
<p className="input-disclaimer">
|
|
2324
|
+
Mint Agent is an AI gateway. Responses via third-party APIs. Verify critical info.
|
|
2325
|
+
</p>
|
|
2195
2326
|
</section>
|
|
2196
2327
|
)
|
|
2197
2328
|
}
|
|
@@ -16,6 +16,7 @@ interface ImageStudioPanelProps {
|
|
|
16
16
|
view: string
|
|
17
17
|
onRefreshPictures?: () => void
|
|
18
18
|
onSendToChat?: (dataUri: string, prompt: string) => void
|
|
19
|
+
onToggleMobileSidebar?: () => void
|
|
19
20
|
}
|
|
20
21
|
|
|
21
22
|
const ASPECT_OPTIONS: { value: AspectRatio; label: string; icon: string }[] = [
|
|
@@ -85,7 +86,7 @@ function SkeletonCard() {
|
|
|
85
86
|
)
|
|
86
87
|
}
|
|
87
88
|
|
|
88
|
-
export default function ImageStudioPanel({ view, onRefreshPictures, onSendToChat }: ImageStudioPanelProps) {
|
|
89
|
+
export default function ImageStudioPanel({ view, onRefreshPictures, onSendToChat, onToggleMobileSidebar }: ImageStudioPanelProps) {
|
|
89
90
|
const [prompt, setPrompt] = useState('')
|
|
90
91
|
const [negativePrompt, setNegativePrompt] = useState('')
|
|
91
92
|
const [showNegative, setShowNegative] = useState(false)
|
|
@@ -206,6 +207,15 @@ export default function ImageStudioPanel({ view, onRefreshPictures, onSendToChat
|
|
|
206
207
|
{/* Header */}
|
|
207
208
|
<header className="img-studio-header">
|
|
208
209
|
<div className="img-studio-header-title">
|
|
210
|
+
<button
|
|
211
|
+
className="mobile-menu-btn"
|
|
212
|
+
type="button"
|
|
213
|
+
onClick={onToggleMobileSidebar}
|
|
214
|
+
aria-label="Toggle menu"
|
|
215
|
+
style={{ marginRight: '8px' }}
|
|
216
|
+
>
|
|
217
|
+
☰
|
|
218
|
+
</button>
|
|
209
219
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
|
|
210
220
|
<polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/>
|
|
211
221
|
</svg>
|
|
@@ -384,6 +384,10 @@ export default function MintDashboard() {
|
|
|
384
384
|
}
|
|
385
385
|
|
|
386
386
|
const toggleSidebar = () => {
|
|
387
|
+
if (window.innerWidth <= 760) {
|
|
388
|
+
setMobileSidebarOpen(false)
|
|
389
|
+
return
|
|
390
|
+
}
|
|
387
391
|
const next = !sidebarCollapsed
|
|
388
392
|
window.localStorage.setItem('mint:sidebar-collapsed', String(next))
|
|
389
393
|
setSidebarCollapsed(next)
|
|
@@ -400,10 +404,10 @@ export default function MintDashboard() {
|
|
|
400
404
|
setAgentMode(enabled)
|
|
401
405
|
}
|
|
402
406
|
|
|
403
|
-
async function handleApproval(approved: boolean, _autoApproveSession = false) {
|
|
407
|
+
async function handleApproval(approved: boolean, _autoApproveSession = false, answer?: string) {
|
|
404
408
|
if (!pendingApproval) return
|
|
405
409
|
try {
|
|
406
|
-
await submitToolApproval(pendingApproval.token, approved)
|
|
410
|
+
await submitToolApproval(pendingApproval.token, approved, answer)
|
|
407
411
|
} catch (reason) {
|
|
408
412
|
setError(errorMessage(reason))
|
|
409
413
|
} finally {
|
|
@@ -825,7 +829,7 @@ export default function MintDashboard() {
|
|
|
825
829
|
|
|
826
830
|
return (
|
|
827
831
|
<div className={`app-container ${startupReady ? '' : 'is-loading'}`}>
|
|
828
|
-
<div className={`app-body ${sidebarCollapsed ? 'sidebar-collapsed' : ''} ${view === 'pictures' ? 'pictures-open' : ''} ${mobileSidebarOpen ? 'mobile-sidebar-open' : ''}`}>
|
|
832
|
+
<div className={`app-body ${(sidebarCollapsed && window.innerWidth > 760) ? 'sidebar-collapsed' : ''} ${view === 'pictures' ? 'pictures-open' : ''} ${mobileSidebarOpen ? 'mobile-sidebar-open' : ''}`}>
|
|
829
833
|
{mobileSidebarOpen && (
|
|
830
834
|
<div
|
|
831
835
|
className="sidebar-backdrop"
|
|
@@ -908,6 +912,7 @@ export default function MintDashboard() {
|
|
|
908
912
|
setView('chat')
|
|
909
913
|
setMessage(imgPrompt)
|
|
910
914
|
}}
|
|
915
|
+
onToggleMobileSidebar={() => setMobileSidebarOpen(!mobileSidebarOpen)}
|
|
911
916
|
/>
|
|
912
917
|
</div>
|
|
913
918
|
<div className={`startup-loading ${startupReady ? 'is-hidden' : ''}`} aria-live="polite" aria-busy={!startupReady}>
|