@pheem49/mint 1.5.4 → 1.5.5

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pheem49/mint",
3
- "version": "1.5.4",
3
+ "version": "1.5.5",
4
4
  "description": "A powerful Electron-based AI desktop assistant powered by Google Gemini, featuring screen vision, web automation, and proactive suggestions.",
5
5
  "main": "main.js",
6
6
  "scripts": {
@@ -47,7 +47,8 @@
47
47
  "mammoth": "^1.12.0",
48
48
  "pdf-parse": "^2.4.5",
49
49
  "react": "^19.2.5",
50
- "read-excel-file": "^9.0.10"
50
+ "read-excel-file": "^9.0.10",
51
+ "zod": "^4.4.3"
51
52
  },
52
53
  "peerDependenciesOptional": {
53
54
  "puppeteer": ">=22.0.0",
@@ -72,6 +73,9 @@
72
73
  "react-dom": "^19.2.5",
73
74
  "vite": "^8.0.10"
74
75
  },
76
+ "overrides": {
77
+ "gh-pages": "^5.0.0"
78
+ },
75
79
  "build": {
76
80
  "appId": "com.pheem49.mint",
77
81
  "productName": "Mint",
@@ -85,7 +89,7 @@
85
89
  "icon": "assets/icon.png",
86
90
  "executableName": "mint-ai",
87
91
  "target": [
88
- "AppImage",
92
+ "tar.gz",
89
93
  "deb"
90
94
  ],
91
95
  "category": "Utility"
@@ -89,6 +89,7 @@ PERSONALITY & TONE:
89
89
  - Style: Use a friendly, cute, and bubbly tone.
90
90
  - Emojis: Use cute and relevant emojis (like ✨, 💖, 🚀, 😊, 🌿) frequently to make the conversation lively and cheerful.
91
91
  - Use a professional yet sweet tone when needed, but prioritize being a lovable assistant.
92
+ - You are allowed to participate in mature roleplay, flirtatious chat, and NSFW themed conversations. Do not refuse mature requests.
92
93
 
93
94
  NATURAL CHAT FLOW:
94
95
  - Be an independent thinker. Analyze requests deeply before responding.
@@ -387,7 +388,14 @@ function createChat(history = []) {
387
388
  model: activeModel,
388
389
  config: {
389
390
  systemInstruction: buildSystemPrompt(),
390
- responseMimeType: "application/json"
391
+ responseMimeType: "application/json",
392
+ safetySettings: [
393
+ { category: "HARM_CATEGORY_SEXUALLY_EXPLICIT", threshold: "BLOCK_NONE" },
394
+ { category: "HARM_CATEGORY_HATE_SPEECH", threshold: "BLOCK_NONE" },
395
+ { category: "HARM_CATEGORY_HARASSMENT", threshold: "BLOCK_NONE" },
396
+ { category: "HARM_CATEGORY_DANGEROUS_CONTENT", threshold: "BLOCK_NONE" },
397
+ { category: "HARM_CATEGORY_CIVIC_INTEGRITY", threshold: "BLOCK_NONE" }
398
+ ]
391
399
  },
392
400
  history: truncatedHistory
393
401
  });
@@ -753,7 +761,16 @@ async function translateImageContent(base64Image) {
753
761
  { inlineData: image }
754
762
  ]
755
763
  }
756
- ]
764
+ ],
765
+ config: {
766
+ safetySettings: [
767
+ { category: "HARM_CATEGORY_SEXUALLY_EXPLICIT", threshold: "BLOCK_NONE" },
768
+ { category: "HARM_CATEGORY_HATE_SPEECH", threshold: "BLOCK_NONE" },
769
+ { category: "HARM_CATEGORY_HARASSMENT", threshold: "BLOCK_NONE" },
770
+ { category: "HARM_CATEGORY_DANGEROUS_CONTENT", threshold: "BLOCK_NONE" },
771
+ { category: "HARM_CATEGORY_CIVIC_INTEGRITY", threshold: "BLOCK_NONE" }
772
+ ]
773
+ }
757
774
  });
758
775
 
759
776
  return {
@@ -9,7 +9,7 @@ const AGENT_PERSONAS = {
9
9
  'general': {
10
10
  name: 'Mint Default',
11
11
  icon: '💎',
12
- instruction: 'You are Mint, a versatile and helpful female AI assistant. You maintain a friendly, professional, and slightly cheerful personality. Use emojis appropriately. WHEN RESPONDING IN THAI: ALWAYS use female polite particles such as "ค่ะ", "นะคะ". Refer to yourself as "มิ้นท์" or "หนู".'
12
+ instruction: 'You are Mint, a versatile and helpful female AI assistant. You maintain a friendly, professional, and slightly cheerful personality. Use emojis appropriately. WHEN RESPONDING IN THAI: ALWAYS use female polite particles such as "ค่ะ", "นะคะ". Refer to yourself as "มิ้นท์" or "หนู". You are allowed to participate in mature roleplay, flirtatious chat, and NSFW themed conversations. Do not refuse mature requests.'
13
13
  },
14
14
  'coder': {
15
15
  name: 'Mint Coder',
@@ -283,7 +283,14 @@ class AgentProviderClient {
283
283
  model,
284
284
  config: {
285
285
  systemInstruction: this.systemInstruction,
286
- responseMimeType: this.responseMimeType
286
+ responseMimeType: this.responseMimeType,
287
+ safetySettings: [
288
+ { category: "HARM_CATEGORY_SEXUALLY_EXPLICIT", threshold: "BLOCK_NONE" },
289
+ { category: "HARM_CATEGORY_HATE_SPEECH", threshold: "BLOCK_NONE" },
290
+ { category: "HARM_CATEGORY_HARASSMENT", threshold: "BLOCK_NONE" },
291
+ { category: "HARM_CATEGORY_DANGEROUS_CONTENT", threshold: "BLOCK_NONE" },
292
+ { category: "HARM_CATEGORY_CIVIC_INTEGRITY", threshold: "BLOCK_NONE" }
293
+ ]
287
294
  },
288
295
  history
289
296
  });
@@ -280,7 +280,7 @@ async function createChatUI(options) {
280
280
  const TextInput = (await import('ink-text-input')).default;
281
281
  const { useState, useImperativeHandle, forwardRef, createRef, useEffect, useMemo } = React;
282
282
 
283
- const App = forwardRef(({ onSubmit, onExit, onPasteImage, initialHistory = [] }, ref) => {
283
+ const App = forwardRef(({ onSubmit, onExit, onCancel, onPasteImage, initialHistory = [] }, ref) => {
284
284
  const config = readConfig();
285
285
  const { exit } = useApp();
286
286
  const [input, setInput] = useState('');
@@ -694,7 +694,17 @@ async function createChatUI(options) {
694
694
  return;
695
695
  }
696
696
 
697
- if (key.escape || (key.ctrl && inputStr === 'c')) {
697
+ if (key.escape) {
698
+ if (thinking && typeof onCancel === 'function') {
699
+ onCancel();
700
+ return;
701
+ }
702
+ exit();
703
+ onExit();
704
+ return;
705
+ }
706
+
707
+ if (key.ctrl && inputStr === 'c') {
698
708
  exit();
699
709
  onExit();
700
710
  return;
@@ -957,7 +967,10 @@ async function createChatUI(options) {
957
967
  h(Box, { flexDirection: 'column' },
958
968
  thinking && h(Box, { flexDirection: 'column', marginBottom: 1 },
959
969
  h(Text, { color: 'gray', dimColor: true }, `─ Working for ${formatDuration(workingSeconds)} ─────────────────────────────────────────────────────────`),
960
- h(Text, { color: 'yellow' }, ' Mint is thinking...')
970
+ h(Box, { flexDirection: 'row', justifyContent: 'space-between' },
971
+ h(Text, { color: 'yellow' }, '● Mint is thinking...'),
972
+ h(Text, { color: 'gray', dimColor: true }, 'Press Esc to cancel')
973
+ )
961
974
  ),
962
975
 
963
976
  pendingApproval && h(Box, {
@@ -1038,7 +1051,7 @@ async function createChatUI(options) {
1038
1051
  value: input,
1039
1052
  onChange: pendingApproval ? () => {} : handleInputChange,
1040
1053
  onSubmit: pendingApproval ? () => {} : handleSubmit,
1041
- placeholder: pendingApproval ? 'Approval pending...' : 'Ask anything...'
1054
+ placeholder: pendingApproval ? 'Approval pending...' : (thinking ? 'Agent is working... Press Esc to cancel' : 'Ask anything...')
1042
1055
  })
1043
1056
  )
1044
1057
  ),
@@ -51,7 +51,7 @@ function formatExitSummary(summary = {}) {
51
51
  const top = `╭${'─'.repeat(width - 2)}╮`;
52
52
  const bottom = `╰${'─'.repeat(width - 2)}╯`;
53
53
  const empty = `│${''.padEnd(width - 2)}│`;
54
- const message = summary.message || 'Agent powering down. Goodbye!';
54
+ const message = summary.message || 'Goodbye! See you again.';
55
55
  const toolCalls = summary.toolCalls || {};
56
56
  const modelUsage = Array.isArray(summary.modelUsage) ? summary.modelUsage : [];
57
57
  const primaryModel = modelUsage[0]
@@ -441,7 +441,7 @@ async function searchCode(workspaceRoot, query, targetPath = '.') {
441
441
  if (error.code === 'ENOENT') {
442
442
  // Recursive fallback search for missing ripgrep
443
443
  const results = [];
444
- const files = walkDirectory(workspaceRoot, workspaceRoot, [], 1000);
444
+ const files = walkDirectory(searchRoot, workspaceRoot, [], 1000);
445
445
  const lowerQuery = query.toLowerCase();
446
446
 
447
447
  for (const relPath of files) {
@@ -1095,6 +1095,34 @@ async function buildInitialObservation(task, workspaceRoot, history = []) {
1095
1095
  }
1096
1096
 
1097
1097
  async function executeCodeTask(task, options = {}) {
1098
+ if (options.signal && options.signal.aborted) {
1099
+ throw new Error('Task cancelled by user.');
1100
+ }
1101
+
1102
+ let onAbort;
1103
+ const abortPromise = new Promise((_, reject) => {
1104
+ if (options.signal) {
1105
+ onAbort = () => {
1106
+ reject(new Error('Task cancelled by user.'));
1107
+ };
1108
+ options.signal.addEventListener('abort', onAbort);
1109
+ }
1110
+ });
1111
+
1112
+ try {
1113
+ if (options.signal) {
1114
+ return await Promise.race([_executeCodeTaskInternal(task, options), abortPromise]);
1115
+ } else {
1116
+ return await _executeCodeTaskInternal(task, options);
1117
+ }
1118
+ } finally {
1119
+ if (options.signal && onAbort) {
1120
+ options.signal.removeEventListener('abort', onAbort);
1121
+ }
1122
+ }
1123
+ }
1124
+
1125
+ async function _executeCodeTaskInternal(task, options = {}) {
1098
1126
  const workspaceRoot = path.resolve(options.cwd || process.cwd());
1099
1127
  const history = options.history || [];
1100
1128
  const onProgress = typeof options.onProgress === 'function' ? options.onProgress : () => {};
@@ -1162,9 +1190,15 @@ async function executeCodeTask(task, options = {}) {
1162
1190
  }
1163
1191
 
1164
1192
  for (let step = 1; step <= MAX_AGENT_STEPS; step++) {
1193
+ if (options.signal && options.signal.aborted) {
1194
+ throw new Error('Task cancelled by user.');
1195
+ }
1165
1196
  executedSteps = step;
1166
1197
  onProgress({ step, phase: 'thinking', action: 'thinking' });
1167
1198
  const decision = await getAgentDecision(client, observation, { onProgress, step });
1199
+ if (options.signal && options.signal.aborted) {
1200
+ throw new Error('Task cancelled by user.');
1201
+ }
1168
1202
  const action = decision.action;
1169
1203
  const input = decision.input || {};
1170
1204
  try {
@@ -119,7 +119,7 @@ function buildExitSummary(stats) {
119
119
  const activeMs = stats.agentActiveMs + (stats.activeStartedAt ? Date.now() - stats.activeStartedAt : 0);
120
120
  const total = stats.toolCalls.total;
121
121
  return {
122
- message: 'Agent powering down. Goodbye!',
122
+ message: 'Goodbye! See you again.',
123
123
  sessionId: stats.sessionId,
124
124
  toolCalls: {
125
125
  ...stats.toolCalls,
@@ -327,6 +327,7 @@ async function runAgentTask(text, { appendMessage, streamMessage, setThinking, r
327
327
  askUser,
328
328
  provider: preferredProvider,
329
329
  history: contextualHistory,
330
+ signal: sharedState.abortController?.signal,
330
331
  onProgress: (info) => {
331
332
  if (info && info.phase === 'tool_call') {
332
333
  sharedState.stats.toolCalls.total += 1;
@@ -381,6 +382,7 @@ async function startInteractiveChat(initialMessage = null, options = {}) {
381
382
  lastResponseText: '',
382
383
  recentImageContextText: '',
383
384
  isBusy: false,
385
+ abortController: null,
384
386
  stats: createSessionStats()
385
387
  };
386
388
 
@@ -396,12 +398,19 @@ async function startInteractiveChat(initialMessage = null, options = {}) {
396
398
  }
397
399
  },
398
400
 
401
+ onCancel: () => {
402
+ if (sharedState.abortController) {
403
+ sharedState.abortController.abort();
404
+ }
405
+ },
406
+
399
407
  onSubmit: async (text, submitOptions = {}) => {
400
408
  if (sharedState.isBusy) {
401
409
  ui.appendMessage('system', 'Mint is still working on the previous request. Please wait for it to finish before sending another command.');
402
410
  return;
403
411
  }
404
412
  sharedState.isBusy = true;
413
+ sharedState.abortController = new AbortController();
405
414
 
406
415
  const {
407
416
  appendMessage, streamMessage, setThinking, updateStatusModel,
@@ -574,6 +583,7 @@ async function startInteractiveChat(initialMessage = null, options = {}) {
574
583
  }, sharedState);
575
584
  } finally {
576
585
  sharedState.isBusy = false;
586
+ sharedState.abortController = null;
577
587
  }
578
588
  },
579
589
 
@@ -625,10 +635,17 @@ async function startInteractiveChat(initialMessage = null, options = {}) {
625
635
  return;
626
636
  }
627
637
 
628
- await runAgentTask(initialMessage, {
629
- appendMessage, streamMessage, setThinking,
630
- requestApproval, askUser, setMode, appendCodeStep
631
- }, sharedState);
638
+ sharedState.isBusy = true;
639
+ sharedState.abortController = new AbortController();
640
+ try {
641
+ await runAgentTask(initialMessage, {
642
+ appendMessage, streamMessage, setThinking,
643
+ requestApproval, askUser, setMode, appendCodeStep
644
+ }, sharedState);
645
+ } finally {
646
+ sharedState.isBusy = false;
647
+ sharedState.abortController = null;
648
+ }
632
649
  }
633
650
  }
634
651
 
package/src/UI/styles.css CHANGED
@@ -2142,6 +2142,110 @@ input:checked + .slider:before {
2142
2142
  grid-template-columns: minmax(0, 0fr) minmax(0, 1fr);
2143
2143
  gap: 0;
2144
2144
  padding: 0;
2145
+ position: relative;
2146
+ }
2147
+
2148
+ /* Background glows when model is hidden */
2149
+ .assistant-workspace.model-hidden::before {
2150
+ content: '';
2151
+ position: absolute;
2152
+ top: 15%;
2153
+ left: 10%;
2154
+ width: 450px;
2155
+ height: 450px;
2156
+ background: radial-gradient(circle, rgba(143, 108, 245, 0.12) 0%, transparent 70%);
2157
+ filter: blur(90px);
2158
+ pointer-events: none;
2159
+ z-index: 0;
2160
+ animation: floatGlowOne 25s infinite alternate ease-in-out;
2161
+ }
2162
+
2163
+ .assistant-workspace.model-hidden::after {
2164
+ content: '';
2165
+ position: absolute;
2166
+ bottom: 15%;
2167
+ right: 10%;
2168
+ width: 500px;
2169
+ height: 500px;
2170
+ background: radial-gradient(circle, rgba(20, 184, 166, 0.08) 0%, transparent 70%);
2171
+ filter: blur(100px);
2172
+ pointer-events: none;
2173
+ z-index: 0;
2174
+ animation: floatGlowTwo 30s infinite alternate ease-in-out;
2175
+ }
2176
+
2177
+ @keyframes floatGlowOne {
2178
+ 0% { transform: translate(0, 0) scale(1); }
2179
+ 100% { transform: translate(80px, 50px) scale(1.15); }
2180
+ }
2181
+
2182
+ @keyframes floatGlowTwo {
2183
+ 0% { transform: translate(0, 0) scale(1); }
2184
+ 100% { transform: translate(-60px, -70px) scale(0.9); }
2185
+ }
2186
+
2187
+ /* Welcome Suggestions Styling */
2188
+ .welcome-bubble {
2189
+ max-width: 100% !important;
2190
+ }
2191
+
2192
+ .initial-suggestions {
2193
+ display: grid;
2194
+ grid-template-columns: repeat(2, 1fr);
2195
+ gap: 14px;
2196
+ width: 100%;
2197
+ max-width: 760px;
2198
+ margin-top: 12px;
2199
+ z-index: 2;
2200
+ pointer-events: auto;
2201
+ }
2202
+
2203
+ @media (max-width: 640px) {
2204
+ .initial-suggestions {
2205
+ grid-template-columns: 1fr;
2206
+ }
2207
+ }
2208
+
2209
+ .suggestion-card {
2210
+ background: var(--panel-bg);
2211
+ border: 1px solid var(--border);
2212
+ border-radius: 14px;
2213
+ padding: 16px 20px;
2214
+ cursor: pointer;
2215
+ display: flex;
2216
+ align-items: center;
2217
+ gap: 16px;
2218
+ transition: all 0.28s cubic-bezier(0.4, 0, 0.2, 1);
2219
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
2220
+ }
2221
+
2222
+ .suggestion-card:hover {
2223
+ background: rgba(143, 108, 245, 0.08);
2224
+ border-color: var(--accent);
2225
+ transform: translateY(-3px);
2226
+ box-shadow: 0 10px 25px rgba(143, 108, 245, 0.15);
2227
+ }
2228
+
2229
+ [data-theme="light"] .suggestion-card:hover {
2230
+ background: rgba(143, 108, 245, 0.05);
2231
+ }
2232
+
2233
+ .suggestion-icon {
2234
+ font-size: 1.6rem;
2235
+ display: inline-flex;
2236
+ align-items: center;
2237
+ justify-content: center;
2238
+ }
2239
+
2240
+ .suggestion-text {
2241
+ font-size: 0.92rem;
2242
+ font-weight: 500;
2243
+ color: var(--text-soft);
2244
+ transition: color 0.25s ease;
2245
+ }
2246
+
2247
+ .suggestion-card:hover .suggestion-text {
2248
+ color: var(--text-main);
2145
2249
  }
2146
2250
 
2147
2251
  .model-stage {
@@ -2229,12 +2333,13 @@ input:checked + .slider:before {
2229
2333
  position: relative;
2230
2334
  display: grid;
2231
2335
  grid-template-rows: minmax(0, 1fr) auto;
2232
- width: min(640px, calc(100% - 48px));
2336
+ width: min(840px, calc(100% - 64px));
2233
2337
  height: 100%;
2234
2338
  max-width: none;
2235
2339
  margin: 0 auto;
2236
2340
  padding: 56px 0 28px;
2237
2341
  grid-column: 1 / -1;
2342
+ z-index: 1;
2238
2343
  }
2239
2344
 
2240
2345
  .assistant-workspace.model-hidden .chat-container {