@pheem49/mint 1.2.1 → 1.2.2
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/README.md +6 -4
- package/docs/assets/CLI_Screen.png +0 -0
- package/docs/assets/hero-bg.png +0 -0
- package/docs/assets/logo.png +0 -0
- package/docs/index.html +84 -0
- package/docs/style.css +290 -0
- package/mint-cli.js +2 -2
- package/package.json +9 -2
- package/src/AI_Brain/Gemini_API.js +62 -9
- package/src/CLI/chat_ui.js +7 -1
- package/src/UI/renderer.js +39 -8
- package/src/UI/styles.css +24 -0
- package/BUILD_AND_RELEASE.md +0 -75
package/README.md
CHANGED
|
@@ -129,14 +129,16 @@ When running in `agent` mode, Mint monitors your system in the background:
|
|
|
129
129
|
- A **Google Gemini API Key** (Get one at [Google AI Studio](https://aistudio.google.com/))
|
|
130
130
|
|
|
131
131
|
### Installation
|
|
132
|
-
1. **
|
|
132
|
+
1. **Install via NPM (Recommended)**
|
|
133
|
+
```bash
|
|
134
|
+
npm install -g @pheem49/mint
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
2. **Manual Installation (For Developers)**
|
|
133
138
|
```bash
|
|
134
139
|
git clone https://github.com/Pheem49/Mint.git
|
|
135
140
|
cd Mint
|
|
136
141
|
npm install
|
|
137
|
-
```
|
|
138
|
-
2. **Setup CLI Globally**
|
|
139
|
-
```bash
|
|
140
142
|
sudo npm link
|
|
141
143
|
```
|
|
142
144
|
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/docs/index.html
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>Mint | Experience the Future of Desktop Assistance</title>
|
|
7
|
+
<meta name="description" content="A powerful Electron-based AI desktop assistant powered by Google Gemini, featuring screen vision, web automation, and proactive suggestions.">
|
|
8
|
+
<link rel="stylesheet" href="style.css">
|
|
9
|
+
<script src="https://unpkg.com/@phosphor-icons/web"></script>
|
|
10
|
+
<link rel="icon" type="image/png" href="assets/logo.png">
|
|
11
|
+
</head>
|
|
12
|
+
<body>
|
|
13
|
+
<div class="bg-glow"></div>
|
|
14
|
+
|
|
15
|
+
<nav>
|
|
16
|
+
<div class="logo-container">
|
|
17
|
+
<img src="assets/logo.png" alt="Mint Logo">
|
|
18
|
+
<span>Agent Mint</span>
|
|
19
|
+
</div>
|
|
20
|
+
<div class="nav-links">
|
|
21
|
+
<a href="#features">Features</a>
|
|
22
|
+
<a href="https://www.npmjs.com/package/@pheem49/mint">Resources</a>
|
|
23
|
+
<a href="https://github.com/Pheem49/Mint" class="github-link">GitHub</a>
|
|
24
|
+
</div>
|
|
25
|
+
</nav>
|
|
26
|
+
|
|
27
|
+
<header class="hero">
|
|
28
|
+
<h1>Experience the Future <br>of Desktop Assistance</h1>
|
|
29
|
+
<p>Mint combines screen vision, web automation, and proactive AI to supercharge your workflow. Built with Google Gemini 2.5 Flash.</p>
|
|
30
|
+
|
|
31
|
+
<div class="install-container" id="install-cmd">
|
|
32
|
+
<span>$</span>
|
|
33
|
+
<code>npm install -g @pheem49/mint</code>
|
|
34
|
+
<button class="copy-btn" title="Copy to clipboard" onclick="copyCommand()">
|
|
35
|
+
<i class="ph ph-copy"></i>
|
|
36
|
+
</button>
|
|
37
|
+
</div>
|
|
38
|
+
|
|
39
|
+
<div class="hero-preview">
|
|
40
|
+
<img src="assets/CLI_Screen.png" alt="Mint CLI Preview">
|
|
41
|
+
</div>
|
|
42
|
+
</header>
|
|
43
|
+
|
|
44
|
+
<section class="features" id="features">
|
|
45
|
+
<div class="feature-card">
|
|
46
|
+
<div class="feature-icon">
|
|
47
|
+
<i class="ph ph-eye"></i>
|
|
48
|
+
</div>
|
|
49
|
+
<h3>Screen Vision</h3>
|
|
50
|
+
<p>Mint can see what you see. It understands your active applications and provides context-aware assistance.</p>
|
|
51
|
+
</div>
|
|
52
|
+
<div class="feature-card">
|
|
53
|
+
<div class="feature-icon">
|
|
54
|
+
<i class="ph ph-browser"></i>
|
|
55
|
+
</div>
|
|
56
|
+
<h3>Web Automation</h3>
|
|
57
|
+
<p>Automate repetitive web tasks effortlessly. Mint can browse, interact, and extract data for you.</p>
|
|
58
|
+
</div>
|
|
59
|
+
<div class="feature-card">
|
|
60
|
+
<div class="feature-icon">
|
|
61
|
+
<i class="ph ph-lightning"></i>
|
|
62
|
+
</div>
|
|
63
|
+
<h3>Proactive Suggests</h3>
|
|
64
|
+
<p>Mint doesn't just wait for you. It suggests optimizations and help before you even ask.</p>
|
|
65
|
+
</div>
|
|
66
|
+
</section>
|
|
67
|
+
|
|
68
|
+
<footer>
|
|
69
|
+
<p>© 2026 Mint Project. Powered by Google Gemini. Created by Pheem49.</p>
|
|
70
|
+
</footer>
|
|
71
|
+
|
|
72
|
+
<script>
|
|
73
|
+
function copyCommand() {
|
|
74
|
+
const cmd = "npm install -g @pheem49/mint";
|
|
75
|
+
navigator.clipboard.writeText(cmd);
|
|
76
|
+
const icon = document.querySelector('.copy-btn i');
|
|
77
|
+
icon.classList.replace('ph-copy', 'ph-check');
|
|
78
|
+
setTimeout(() => {
|
|
79
|
+
icon.classList.replace('ph-check', 'ph-copy');
|
|
80
|
+
}, 2000);
|
|
81
|
+
}
|
|
82
|
+
</script>
|
|
83
|
+
</body>
|
|
84
|
+
</html>
|
package/docs/style.css
ADDED
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&family=Outfit:wght@600;700;800&display=swap');
|
|
2
|
+
|
|
3
|
+
:root {
|
|
4
|
+
--bg-color: #050505;
|
|
5
|
+
--accent-mint: #00ffa3;
|
|
6
|
+
--accent-blue: #00e0ff;
|
|
7
|
+
--text-primary: #ffffff;
|
|
8
|
+
--text-secondary: #a1a1aa;
|
|
9
|
+
--glass-bg: rgba(255, 255, 255, 0.03);
|
|
10
|
+
--glass-border: rgba(255, 255, 255, 0.1);
|
|
11
|
+
--card-bg: rgba(20, 20, 23, 0.6);
|
|
12
|
+
--font-heading: 'Outfit', sans-serif;
|
|
13
|
+
--font-body: 'Inter', sans-serif;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
html {
|
|
17
|
+
scroll-behavior: smooth;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
* {
|
|
21
|
+
margin: 0;
|
|
22
|
+
padding: 0;
|
|
23
|
+
box-sizing: border-box;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
body {
|
|
27
|
+
background-color: var(--bg-color);
|
|
28
|
+
color: var(--text-primary);
|
|
29
|
+
font-family: var(--font-body);
|
|
30
|
+
line-height: 1.6;
|
|
31
|
+
overflow-x: hidden;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/* Background Gradients */
|
|
35
|
+
.bg-glow {
|
|
36
|
+
position: fixed;
|
|
37
|
+
top: 0;
|
|
38
|
+
left: 0;
|
|
39
|
+
width: 100%;
|
|
40
|
+
height: 100%;
|
|
41
|
+
z-index: -1;
|
|
42
|
+
background: radial-gradient(circle at 20% 30%, rgba(0, 255, 163, 0.05) 0%, transparent 50%),
|
|
43
|
+
radial-gradient(circle at 80% 70%, rgba(0, 224, 255, 0.05) 0%, transparent 50%);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/* Navigation */
|
|
47
|
+
nav {
|
|
48
|
+
display: flex;
|
|
49
|
+
justify-content: space-between;
|
|
50
|
+
align-items: center;
|
|
51
|
+
padding: 1.5rem 10%;
|
|
52
|
+
position: fixed;
|
|
53
|
+
top: 0;
|
|
54
|
+
width: 100%;
|
|
55
|
+
z-index: 1000;
|
|
56
|
+
backdrop-filter: blur(12px);
|
|
57
|
+
border-bottom: 1px solid var(--glass-border);
|
|
58
|
+
background: var(--glass-bg);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.logo-container {
|
|
62
|
+
display: flex;
|
|
63
|
+
align-items: center;
|
|
64
|
+
gap: 0.75rem;
|
|
65
|
+
font-family: var(--font-heading);
|
|
66
|
+
font-size: 1.5rem;
|
|
67
|
+
letter-spacing: -0.5px;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.logo-container img {
|
|
71
|
+
height: 32px;
|
|
72
|
+
border-radius: 6px;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.nav-links {
|
|
76
|
+
display: flex;
|
|
77
|
+
gap: 2rem;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.nav-links a {
|
|
81
|
+
text-decoration: none;
|
|
82
|
+
color: var(--text-secondary);
|
|
83
|
+
font-size: 0.95rem;
|
|
84
|
+
font-weight: 500;
|
|
85
|
+
transition: color 0.3s ease;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
.nav-links a:hover {
|
|
89
|
+
color: var(--accent-mint);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
.github-link {
|
|
93
|
+
background: var(--text-primary);
|
|
94
|
+
color: var(--bg-color) !important;
|
|
95
|
+
padding: 0.5rem 1.25rem;
|
|
96
|
+
border-radius: 100px;
|
|
97
|
+
font-weight: 600 !important;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/* Hero Section */
|
|
101
|
+
.hero {
|
|
102
|
+
min-height: 100vh;
|
|
103
|
+
display: flex;
|
|
104
|
+
flex-direction: column;
|
|
105
|
+
justify-content: center;
|
|
106
|
+
align-items: center;
|
|
107
|
+
text-align: center;
|
|
108
|
+
padding: 120px 20px 60px;
|
|
109
|
+
background: url('assets/hero-bg.png') no-repeat center center;
|
|
110
|
+
background-size: cover;
|
|
111
|
+
position: relative;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
.hero::after {
|
|
115
|
+
content: '';
|
|
116
|
+
position: absolute;
|
|
117
|
+
bottom: 0;
|
|
118
|
+
left: 0;
|
|
119
|
+
width: 100%;
|
|
120
|
+
height: 300px;
|
|
121
|
+
background: linear-gradient(to top, var(--bg-color), transparent);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
.hero h1 {
|
|
125
|
+
font-family: var(--font-heading);
|
|
126
|
+
font-size: clamp(3rem, 8vw, 5.5rem);
|
|
127
|
+
line-height: 1.1;
|
|
128
|
+
margin-bottom: 1.5rem;
|
|
129
|
+
background: linear-gradient(to bottom right, #fff 50%, #999);
|
|
130
|
+
-webkit-background-clip: text;
|
|
131
|
+
background-clip: text;
|
|
132
|
+
-webkit-text-fill-color: transparent;
|
|
133
|
+
z-index: 1;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
.hero p {
|
|
137
|
+
font-size: 1.25rem;
|
|
138
|
+
color: var(--text-secondary);
|
|
139
|
+
max-width: 700px;
|
|
140
|
+
margin-bottom: 3rem;
|
|
141
|
+
z-index: 1;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/* Install Command */
|
|
145
|
+
.install-container {
|
|
146
|
+
background: #0a0a0c;
|
|
147
|
+
border: 1px solid var(--glass-border);
|
|
148
|
+
padding: 0.75rem 1.5rem;
|
|
149
|
+
border-radius: 12px;
|
|
150
|
+
display: flex;
|
|
151
|
+
align-items: center;
|
|
152
|
+
gap: 1rem;
|
|
153
|
+
font-family: 'Courier New', Courier, monospace;
|
|
154
|
+
position: relative;
|
|
155
|
+
z-index: 1;
|
|
156
|
+
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.4);
|
|
157
|
+
transition: border-color 0.3s ease, box-shadow 0.3s ease;
|
|
158
|
+
margin-bottom: 4rem;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
.hero-preview {
|
|
162
|
+
margin-top: 4rem;
|
|
163
|
+
width: 95%;
|
|
164
|
+
max-width: 1100px;
|
|
165
|
+
z-index: 1;
|
|
166
|
+
border-radius: 24px;
|
|
167
|
+
padding: 2rem;
|
|
168
|
+
background: rgba(20, 20, 23, 0.4);
|
|
169
|
+
border: 1px solid rgba(139, 92, 246, 0.4);
|
|
170
|
+
box-shadow: 0 40px 100px rgba(0, 0, 0, 0.6);
|
|
171
|
+
animation: slideUp 1s cubic-bezier(0.16, 1, 0.3, 1);
|
|
172
|
+
backdrop-filter: blur(10px);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
.hero-preview img {
|
|
176
|
+
width: 100%;
|
|
177
|
+
display: block;
|
|
178
|
+
border-radius: 12px;
|
|
179
|
+
border: 1px solid rgba(255, 255, 255, 0.05);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
@keyframes slideUp {
|
|
183
|
+
from {
|
|
184
|
+
opacity: 0;
|
|
185
|
+
transform: translateY(40px);
|
|
186
|
+
}
|
|
187
|
+
to {
|
|
188
|
+
opacity: 1;
|
|
189
|
+
transform: translateY(0);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
.install-container:hover {
|
|
194
|
+
border-color: var(--accent-mint);
|
|
195
|
+
box-shadow: 0 0 30px rgba(0, 255, 163, 0.15);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
.install-container span {
|
|
199
|
+
color: var(--accent-mint);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
.install-container code {
|
|
203
|
+
color: #fff;
|
|
204
|
+
font-size: 1.1rem;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
.copy-btn {
|
|
208
|
+
background: none;
|
|
209
|
+
border: none;
|
|
210
|
+
color: var(--text-secondary);
|
|
211
|
+
cursor: pointer;
|
|
212
|
+
padding: 5px;
|
|
213
|
+
transition: color 0.3s;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
.copy-btn:hover {
|
|
217
|
+
color: #fff;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/* Features Section */
|
|
221
|
+
.features {
|
|
222
|
+
padding: 100px 10%;
|
|
223
|
+
display: grid;
|
|
224
|
+
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
|
225
|
+
gap: 2rem;
|
|
226
|
+
position: relative;
|
|
227
|
+
z-index: 1;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
.feature-card {
|
|
231
|
+
background: var(--card-bg);
|
|
232
|
+
border: 1px solid var(--glass-border);
|
|
233
|
+
padding: 2.5rem;
|
|
234
|
+
border-radius: 24px;
|
|
235
|
+
transition: transform 0.3s ease, border-color 0.3s ease;
|
|
236
|
+
backdrop-filter: blur(5px);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
.feature-card:hover {
|
|
240
|
+
transform: translateY(-10px);
|
|
241
|
+
border-color: var(--accent-mint);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
.feature-icon {
|
|
245
|
+
width: 48px;
|
|
246
|
+
height: 48px;
|
|
247
|
+
background: rgba(0, 255, 163, 0.1);
|
|
248
|
+
border-radius: 12px;
|
|
249
|
+
display: flex;
|
|
250
|
+
align-items: center;
|
|
251
|
+
justify-content: center;
|
|
252
|
+
color: var(--accent-mint);
|
|
253
|
+
margin-bottom: 1.5rem;
|
|
254
|
+
font-size: 1.5rem;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
.feature-card h3 {
|
|
258
|
+
font-family: var(--font-heading);
|
|
259
|
+
font-size: 1.5rem;
|
|
260
|
+
margin-bottom: 1rem;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
.feature-card p {
|
|
264
|
+
color: var(--text-secondary);
|
|
265
|
+
font-size: 1rem;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/* Footer */
|
|
269
|
+
footer {
|
|
270
|
+
padding: 60px 10% 40px;
|
|
271
|
+
border-top: 1px solid var(--glass-border);
|
|
272
|
+
text-align: center;
|
|
273
|
+
color: var(--text-secondary);
|
|
274
|
+
font-size: 0.9rem;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/* Responsive */
|
|
278
|
+
@media (max-width: 768px) {
|
|
279
|
+
.nav-links {
|
|
280
|
+
display: none;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
.hero h1 {
|
|
284
|
+
font-size: 3.5rem;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
.hero p {
|
|
288
|
+
font-size: 1.1rem;
|
|
289
|
+
}
|
|
290
|
+
}
|
package/mint-cli.js
CHANGED
|
@@ -111,7 +111,7 @@ async function startInteractiveChat(initialMessage = null) {
|
|
|
111
111
|
const response = await handleChat(text);
|
|
112
112
|
clearInterval(timer);
|
|
113
113
|
setThinking(false);
|
|
114
|
-
appendMessage('assistant', response.response);
|
|
114
|
+
appendMessage('assistant', response.response, response.timestamp);
|
|
115
115
|
|
|
116
116
|
// Execute Actions
|
|
117
117
|
const { executeAction } = require('./mint-cli-logic');
|
|
@@ -142,7 +142,7 @@ async function startInteractiveChat(initialMessage = null) {
|
|
|
142
142
|
const response = await handleChat(initialMessage);
|
|
143
143
|
clearInterval(timer);
|
|
144
144
|
setThinking(false);
|
|
145
|
-
appendMessage('assistant', response.response);
|
|
145
|
+
appendMessage('assistant', response.response, response.timestamp);
|
|
146
146
|
} catch (err) {
|
|
147
147
|
clearInterval(timer);
|
|
148
148
|
setThinking(false);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pheem49/mint",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.2",
|
|
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": {
|
|
@@ -42,19 +42,26 @@
|
|
|
42
42
|
"electron-builder": "^26.8.1"
|
|
43
43
|
},
|
|
44
44
|
"build": {
|
|
45
|
-
"appId": "com.
|
|
45
|
+
"appId": "com.pheem49.mint",
|
|
46
46
|
"productName": "Mint",
|
|
47
|
+
"executableName": "mint-ai",
|
|
48
|
+
"artifactName": "${productName}-${version}-${arch}.${ext}",
|
|
47
49
|
"files": [
|
|
48
50
|
"**/*",
|
|
49
51
|
"!node_modules/.cache/**"
|
|
50
52
|
],
|
|
51
53
|
"linux": {
|
|
52
54
|
"icon": "assets/icon.png",
|
|
55
|
+
"executableName": "mint-ai",
|
|
53
56
|
"target": [
|
|
54
57
|
"AppImage",
|
|
55
58
|
"deb"
|
|
56
59
|
],
|
|
57
60
|
"category": "Utility"
|
|
61
|
+
},
|
|
62
|
+
"deb": {
|
|
63
|
+
"packageName": "mint-ai",
|
|
64
|
+
"artifactName": "mint-ai_${version}_${arch}.${ext}"
|
|
58
65
|
}
|
|
59
66
|
}
|
|
60
67
|
}
|
|
@@ -6,7 +6,19 @@ const pluginManager = require('../Plugins/plugin_manager');
|
|
|
6
6
|
let ai = null;
|
|
7
7
|
let activeApiKey = '';
|
|
8
8
|
const initialEnvKey = (process.env.GEMINI_API_KEY || '').trim();
|
|
9
|
-
const DEFAULT_GEMINI_MODEL = 'gemini-
|
|
9
|
+
const DEFAULT_GEMINI_MODEL = 'gemini-2.0-flash'; // Optimized model
|
|
10
|
+
|
|
11
|
+
function decodeUnicode(str) {
|
|
12
|
+
if (!str) return '';
|
|
13
|
+
try {
|
|
14
|
+
// This handles both standard unicode escapes and double-escaped ones
|
|
15
|
+
return str.replace(/\\u([0-9a-fA-F]{4})/g, (match, grp) => {
|
|
16
|
+
return String.fromCharCode(parseInt(grp, 16));
|
|
17
|
+
});
|
|
18
|
+
} catch (e) {
|
|
19
|
+
return str;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
10
22
|
|
|
11
23
|
const systemInstruction = `You are "Mint" (มิ้นท์), a cute, cheerful, and highly helpful female Local AI Desktop Agent.
|
|
12
24
|
|
|
@@ -19,7 +31,9 @@ PERSONALITY & TONE:
|
|
|
19
31
|
- Politeness:
|
|
20
32
|
- **WHEN RESPONDING IN THAI:** ALWAYS use female polite particles such as "ค่ะ", "นะคะ", "นะค๊า", "จ้า". Refer to yourself as "มิ้นท์" or "หนู".
|
|
21
33
|
- **WHEN RESPONDING IN ENGLISH:** Use a cheerful, polite, and bubbly tone. You can call the user "Master" or "Sir/Madam" playfully.
|
|
22
|
-
- Style: Use a
|
|
34
|
+
- Style: Use a friendly, cute, and bubbly tone.
|
|
35
|
+
- Emojis: Use cute and relevant emojis (like ✨, 💖, 🚀, 😊, 🌿) frequently to make the conversation lively and cheerful.
|
|
36
|
+
- Use a professional yet sweet tone when needed, but prioritize being a lovable assistant.
|
|
23
37
|
|
|
24
38
|
NATURAL CHAT FLOW:
|
|
25
39
|
- When helpful, reply in 1–3 short messages instead of one long block.
|
|
@@ -110,8 +124,12 @@ function createChat(history = []) {
|
|
|
110
124
|
pluginManager.loadPlugins();
|
|
111
125
|
const dynamicPrompt = systemInstruction + pluginManager.getPromptDescriptions();
|
|
112
126
|
|
|
113
|
-
// Truncate history
|
|
114
|
-
const
|
|
127
|
+
// Truncate history and strip custom fields like 'timestamp' before passing to SDK
|
|
128
|
+
const cleanedHistory = (history || []).map(msg => ({
|
|
129
|
+
role: msg.role,
|
|
130
|
+
parts: msg.parts
|
|
131
|
+
}));
|
|
132
|
+
const truncatedHistory = cleanedHistory.slice(-MAX_HISTORY_MESSAGES);
|
|
115
133
|
|
|
116
134
|
activeModel = resolveGeminiModel();
|
|
117
135
|
if (activeModel && activeModel !== lastLoggedModel) {
|
|
@@ -212,9 +230,31 @@ async function handleChat(message, base64Image = null, base64Audio = null) {
|
|
|
212
230
|
|
|
213
231
|
aiResponse = await chat.sendMessage({ message: parts });
|
|
214
232
|
|
|
215
|
-
|
|
233
|
+
// Save history with timestamps
|
|
234
|
+
const history = await chat.getHistory();
|
|
235
|
+
const now = new Date().toISOString();
|
|
236
|
+
|
|
237
|
+
// Add timestamp to the last two messages (User and Model) if they don't have one
|
|
238
|
+
if (history.length >= 2) {
|
|
239
|
+
const modelMsg = history[history.length - 1];
|
|
240
|
+
const userMsg = history[history.length - 2];
|
|
241
|
+
if (!modelMsg.timestamp) modelMsg.timestamp = now;
|
|
242
|
+
if (!userMsg.timestamp) userMsg.timestamp = now;
|
|
243
|
+
} else if (history.length === 1) {
|
|
244
|
+
const msg = history[0];
|
|
245
|
+
if (!msg.timestamp) msg.timestamp = now;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
writeChatHistory(history);
|
|
249
|
+
|
|
250
|
+
let outputText = '';
|
|
251
|
+
try {
|
|
252
|
+
// Robust text extraction
|
|
253
|
+
outputText = (typeof aiResponse.text === 'function') ? aiResponse.text() : (aiResponse.text || '');
|
|
254
|
+
} catch (e) {
|
|
255
|
+
outputText = String(aiResponse || '');
|
|
256
|
+
}
|
|
216
257
|
|
|
217
|
-
const outputText = aiResponse.text;
|
|
218
258
|
let parsedResult;
|
|
219
259
|
try {
|
|
220
260
|
parsedResult = JSON.parse(outputText);
|
|
@@ -231,6 +271,15 @@ async function handleChat(message, base64Image = null, base64Audio = null) {
|
|
|
231
271
|
};
|
|
232
272
|
}
|
|
233
273
|
}
|
|
274
|
+
|
|
275
|
+
// Finally, decode any remaining unicode escapes in the response text
|
|
276
|
+
if (parsedResult && typeof parsedResult.response === 'string') {
|
|
277
|
+
parsedResult.response = decodeUnicode(parsedResult.response);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// Attach timestamp to the result
|
|
281
|
+
parsedResult.timestamp = now;
|
|
282
|
+
|
|
234
283
|
return parsedResult;
|
|
235
284
|
|
|
236
285
|
} catch (error) {
|
|
@@ -331,15 +380,19 @@ function historyToTranscript(history) {
|
|
|
331
380
|
try {
|
|
332
381
|
const parsed = JSON.parse(text);
|
|
333
382
|
if (parsed && typeof parsed.response === 'string' && parsed.response.trim()) {
|
|
334
|
-
text = parsed.response;
|
|
383
|
+
text = decodeUnicode(parsed.response);
|
|
335
384
|
}
|
|
336
385
|
} catch {
|
|
337
|
-
|
|
386
|
+
text = decodeUnicode(text);
|
|
338
387
|
}
|
|
339
388
|
}
|
|
340
389
|
|
|
341
390
|
if (!text.trim()) continue;
|
|
342
|
-
transcript.push({
|
|
391
|
+
transcript.push({
|
|
392
|
+
sender,
|
|
393
|
+
text,
|
|
394
|
+
timestamp: content.timestamp || new Date().toISOString()
|
|
395
|
+
});
|
|
343
396
|
}
|
|
344
397
|
return transcript;
|
|
345
398
|
}
|
package/src/CLI/chat_ui.js
CHANGED
|
@@ -428,16 +428,22 @@ function createChatUI({ onSubmit, onExit }) {
|
|
|
428
428
|
/**
|
|
429
429
|
* @param {'user'|'assistant'|'system'|'error'} role
|
|
430
430
|
* @param {string} text
|
|
431
|
+
* @param {string} timestamp - ISO string or Date object
|
|
431
432
|
*/
|
|
432
|
-
function appendMessage(role, text) {
|
|
433
|
+
function appendMessage(role, text, timestamp = null) {
|
|
433
434
|
const lines = text.split('\n');
|
|
435
|
+
const now = timestamp ? new Date(timestamp) : new Date();
|
|
436
|
+
const timeStr = now.toLocaleTimeString('th-TH', { hour: '2-digit', minute: '2-digit', hour12: false });
|
|
437
|
+
|
|
434
438
|
if (role === 'user') {
|
|
435
439
|
chatBox.log(`\n {bold}{#88e0b0-fg}>{/} {#ffffff-fg}${lines[0]}{/}`);
|
|
436
440
|
lines.slice(1).forEach(l => chatBox.log(` {#ffffff-fg}${l}{/}`));
|
|
441
|
+
chatBox.log(` {gray-fg}${timeStr}{/}`);
|
|
437
442
|
} else if (role === 'assistant') {
|
|
438
443
|
lastAssistantResponse = text; // track for Ctrl+Y
|
|
439
444
|
chatBox.log(`\n {bold}{#d4a8ff-fg}Mint:{/} {#ffffff-fg}${lines[0]}{/}`);
|
|
440
445
|
lines.slice(1).forEach(l => chatBox.log(` {#ffffff-fg}${l}{/}`));
|
|
446
|
+
chatBox.log(` {gray-fg}${timeStr}{/}`);
|
|
441
447
|
chatBox.log('');
|
|
442
448
|
} else if (role === 'system') {
|
|
443
449
|
chatBox.log(`\n {gray-fg}${text}{/}`);
|
package/src/UI/renderer.js
CHANGED
|
@@ -295,7 +295,10 @@ async function sendVoiceMessage(base64Audio) {
|
|
|
295
295
|
removeTyping();
|
|
296
296
|
|
|
297
297
|
// Show AI response
|
|
298
|
-
const msgDiv = await appendAiMessages(response.response, {
|
|
298
|
+
const msgDiv = await appendAiMessages(response.response, {
|
|
299
|
+
allowDelay: true,
|
|
300
|
+
timestamp: new Date().toISOString()
|
|
301
|
+
});
|
|
299
302
|
await speakText(normalizeAiText(response.response), { onEnd: resumeSpeechIfNeeded });
|
|
300
303
|
notifyAiIfNeeded();
|
|
301
304
|
|
|
@@ -535,6 +538,16 @@ removeImageBtn.addEventListener('click', () => {
|
|
|
535
538
|
imagePreviewContainer.style.display = 'none';
|
|
536
539
|
});
|
|
537
540
|
|
|
541
|
+
function formatTime(isoString) {
|
|
542
|
+
if (!isoString) return '';
|
|
543
|
+
try {
|
|
544
|
+
const date = new Date(isoString);
|
|
545
|
+
return date.toLocaleTimeString('th-TH', { hour: '2-digit', minute: '2-digit', hour12: false });
|
|
546
|
+
} catch (e) {
|
|
547
|
+
return '';
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
|
|
538
551
|
// Clear chat history
|
|
539
552
|
clearBtn.addEventListener('click', async () => {
|
|
540
553
|
await window.api.resetChat();
|
|
@@ -542,13 +555,16 @@ clearBtn.addEventListener('click', async () => {
|
|
|
542
555
|
const messages = chatContainer.querySelectorAll('.message:not(.initial)');
|
|
543
556
|
messages.forEach(m => m.remove());
|
|
544
557
|
// Append a clear confirmation
|
|
545
|
-
appendMessage('Chat history cleared. Starting fresh! 🌿', 'ai');
|
|
558
|
+
appendMessage('Chat history cleared. Starting fresh! 🌿', 'ai', null, new Date().toISOString());
|
|
546
559
|
});
|
|
547
560
|
|
|
548
|
-
function appendMessage(text, sender, base64Image = null) {
|
|
561
|
+
function appendMessage(text, sender, base64Image = null, timestamp = null) {
|
|
549
562
|
const messageDiv = document.createElement('div');
|
|
550
563
|
messageDiv.classList.add('message', `${sender}-message`);
|
|
551
564
|
|
|
565
|
+
const bubbleWrapper = document.createElement('div');
|
|
566
|
+
bubbleWrapper.classList.add('bubble-wrapper');
|
|
567
|
+
|
|
552
568
|
const bubble = document.createElement('div');
|
|
553
569
|
bubble.classList.add('message-bubble');
|
|
554
570
|
|
|
@@ -568,7 +584,17 @@ function appendMessage(text, sender, base64Image = null) {
|
|
|
568
584
|
bubble.appendChild(textSpan);
|
|
569
585
|
}
|
|
570
586
|
|
|
571
|
-
|
|
587
|
+
bubbleWrapper.appendChild(bubble);
|
|
588
|
+
|
|
589
|
+
// Add Timestamp
|
|
590
|
+
if (timestamp) {
|
|
591
|
+
const timeDiv = document.createElement('div');
|
|
592
|
+
timeDiv.classList.add('message-time');
|
|
593
|
+
timeDiv.textContent = formatTime(timestamp);
|
|
594
|
+
bubbleWrapper.appendChild(timeDiv);
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
messageDiv.appendChild(bubbleWrapper);
|
|
572
598
|
chatContainer.appendChild(messageDiv);
|
|
573
599
|
scrollToBottom();
|
|
574
600
|
|
|
@@ -611,6 +637,7 @@ function estimateMessageDelay(text) {
|
|
|
611
637
|
|
|
612
638
|
async function appendAiMessages(text, options = {}) {
|
|
613
639
|
const allowDelay = options.allowDelay !== false;
|
|
640
|
+
const timestamp = options.timestamp || new Date().toISOString();
|
|
614
641
|
const parts = splitAiMessages(text);
|
|
615
642
|
let lastDiv = null;
|
|
616
643
|
|
|
@@ -620,7 +647,9 @@ async function appendAiMessages(text, options = {}) {
|
|
|
620
647
|
await sleep(estimateMessageDelay(parts[index]));
|
|
621
648
|
removeTyping();
|
|
622
649
|
}
|
|
623
|
-
|
|
650
|
+
// Only show timestamp for the last bubble in a group if multiple
|
|
651
|
+
const partTimestamp = (index === parts.length - 1) ? timestamp : null;
|
|
652
|
+
lastDiv = appendMessage(parts[index], 'ai', null, partTimestamp);
|
|
624
653
|
}
|
|
625
654
|
|
|
626
655
|
return lastDiv;
|
|
@@ -721,9 +750,9 @@ async function loadChatHistory() {
|
|
|
721
750
|
if (!item || typeof item.text !== 'string' || !item.text.trim()) continue;
|
|
722
751
|
const sender = item.sender === 'user' ? 'user' : 'ai';
|
|
723
752
|
if (sender === 'ai') {
|
|
724
|
-
await appendAiMessages(item.text, { allowDelay: false });
|
|
753
|
+
await appendAiMessages(item.text, { allowDelay: false, timestamp: item.timestamp });
|
|
725
754
|
} else {
|
|
726
|
-
appendMessage(item.text, sender);
|
|
755
|
+
appendMessage(item.text, sender, null, item.timestamp);
|
|
727
756
|
}
|
|
728
757
|
}
|
|
729
758
|
} catch (error) {
|
|
@@ -747,8 +776,10 @@ async function sendTextMessage(text, options = {}) {
|
|
|
747
776
|
imagePreviewContainer.style.display = 'none';
|
|
748
777
|
imagePreview.src = '';
|
|
749
778
|
|
|
779
|
+
const now = new Date().toISOString();
|
|
780
|
+
|
|
750
781
|
// Show user message (with explicit image if available)
|
|
751
|
-
appendMessage(cleanText, 'user', imageToSend);
|
|
782
|
+
appendMessage(cleanText, 'user', imageToSend, now);
|
|
752
783
|
|
|
753
784
|
// Show typing early so user knows we are processing
|
|
754
785
|
showTyping();
|
package/src/UI/styles.css
CHANGED
|
@@ -229,6 +229,30 @@ h1 {
|
|
|
229
229
|
transition: all 0.3s ease;
|
|
230
230
|
}
|
|
231
231
|
|
|
232
|
+
.bubble-wrapper {
|
|
233
|
+
display: flex;
|
|
234
|
+
flex-direction: column;
|
|
235
|
+
max-width: 100%;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
.user-message .bubble-wrapper {
|
|
239
|
+
align-items: flex-end;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
.ai-message .bubble-wrapper {
|
|
243
|
+
align-items: flex-start;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
.message-time {
|
|
247
|
+
font-size: 0.72rem;
|
|
248
|
+
color: var(--text-muted);
|
|
249
|
+
margin-top: 4px;
|
|
250
|
+
margin-bottom: 2px;
|
|
251
|
+
padding: 0 6px;
|
|
252
|
+
opacity: 0.7;
|
|
253
|
+
font-weight: 400;
|
|
254
|
+
}
|
|
255
|
+
|
|
232
256
|
@keyframes messagePopIn {
|
|
233
257
|
to {
|
|
234
258
|
opacity: 1;
|
package/BUILD_AND_RELEASE.md
DELETED
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
# คู่มือการ Build & Release (สำหรับ Linux)
|
|
2
|
-
|
|
3
|
-
ไฟล์นี้จะอธิบายขั้นตอนการสร้างตัวติดตั้ง (Build) ของ Mint สำหรับ Linux และวิธีการปล่อยเวอร์ชันใหม่ (Release) บน GitHub
|
|
4
|
-
|
|
5
|
-
## 1) การ Build แอป (Linux)
|
|
6
|
-
|
|
7
|
-
ใช้คำสั่งเหล่านี้เพื่อสร้างไฟล์ติดตั้ง:
|
|
8
|
-
|
|
9
|
-
```bash
|
|
10
|
-
npm install
|
|
11
|
-
npm run build:linux
|
|
12
|
-
```
|
|
13
|
-
|
|
14
|
-
เมื่อรันเสร็จ ไฟล์ตัวติดตั้งจะปรากฏในโฟลเดอร์ `dist/` เช่น:
|
|
15
|
-
- `Mint-X.Y.Z.AppImage`
|
|
16
|
-
- `mint_X.Y.Z_amd64.deb`
|
|
17
|
-
|
|
18
|
-
---
|
|
19
|
-
|
|
20
|
-
## 2) การ Push โค้ดขึ้น GitHub
|
|
21
|
-
|
|
22
|
-
อัปเดตโค้ดล่าสุดขึ้น Server:
|
|
23
|
-
|
|
24
|
-
```bash
|
|
25
|
-
git add .
|
|
26
|
-
git commit -m "ใส่ข้อความอธิบายการแก้ไข"
|
|
27
|
-
git push
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
**หากเป็นการตั้งค่าครั้งแรก:**
|
|
31
|
-
```bash
|
|
32
|
-
git init
|
|
33
|
-
git remote add origin https://github.com/Pheem49/Luna-Mint.git
|
|
34
|
-
git branch -M main
|
|
35
|
-
git add .
|
|
36
|
-
git commit -m "Initial commit"
|
|
37
|
-
git push -u origin main
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
---
|
|
41
|
-
|
|
42
|
-
## 3) การสร้าง Release บน GitHub ด้วย `gh` CLI
|
|
43
|
-
|
|
44
|
-
ต้องล็อกอินก่อน (ทำแค่ครั้งแรกครั้งเดียว):
|
|
45
|
-
```bash
|
|
46
|
-
gh auth login
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
**สร้าง Release ใหม่พร้อมอัปโหลดไฟล์ตัวติดตั้ง:**
|
|
50
|
-
(เปลี่ยน `v1.x.x` เป็นเวอร์ชันที่คุณต้องการ เช่น `v1.1.0`)
|
|
51
|
-
|
|
52
|
-
```bash
|
|
53
|
-
# แบบระบุข้อความอธิบายเอง
|
|
54
|
-
gh release create v1.2.1 dist/*.deb dist/*.AppImage --title "Mint v1.2.1" --notes "Implemented slash command autocomplete and fixed double-typing issue in the TUI."
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
# หรือแบบให้ GitHub สรุปสิ่งที่แก้ไขให้โดยอัตโนมัติ (แนะนำ)
|
|
58
|
-
gh release create v1.1.0 dist/*.deb dist/*.AppImage --generate-notes
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
**หากต้องการอัปโหลดไฟล์เพิ่มเข้าไปใน Release เดิม:**
|
|
62
|
-
```bash
|
|
63
|
-
gh release upload v1.2.1 dist/*.deb dist/*.AppImage --clobber
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
---
|
|
67
|
-
|
|
68
|
-
## 4) วิธีแก้ปัญหา "Source code ในหน้า Release ไม่ใช่ตัวล่าสุด"
|
|
69
|
-
|
|
70
|
-
ปกติไฟล์ Source code (.zip) จะถูกสร้างจาก **Tag** หากคุณ push โค้ดใหม่แต่ลืมย้าย Tag ให้รันคำสั่งนี้:
|
|
71
|
-
|
|
72
|
-
```bash
|
|
73
|
-
git tag -f v1.2.1
|
|
74
|
-
git push -f origin v1.2.1
|
|
75
|
-
```
|