antri_cli 1.27.0 → 1.29.0
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 +89 -42
- package/assets/desktop_panel.png +0 -0
- package/assets/home.png +0 -0
- package/dist/cli/promptToolkit.d.ts.map +1 -1
- package/dist/cli/promptToolkit.js +2 -0
- package/dist/cli/promptToolkit.js.map +1 -1
- package/dist/cli/shortcuts.d.ts.map +1 -1
- package/dist/cli/shortcuts.js +36 -0
- package/dist/cli/shortcuts.js.map +1 -1
- package/dist/cloud/firestore.d.ts +35 -0
- package/dist/cloud/firestore.d.ts.map +1 -0
- package/dist/cloud/firestore.js +131 -0
- package/dist/cloud/firestore.js.map +1 -0
- package/dist/core/config.js +1 -1
- package/dist/core/updater.d.ts +1 -1
- package/dist/core/updater.js +1 -1
- package/dist/desktop/public/app.js +248 -8
- package/dist/desktop/public/index.html +24 -4
- package/dist/desktop/public/style.css +152 -2
- package/dist/desktop/server.d.ts.map +1 -1
- package/dist/desktop/server.js +70 -5
- package/dist/desktop/server.js.map +1 -1
- package/dist/index.js +58 -1
- package/dist/index.js.map +1 -1
- package/dist/mobile/public/app.js +449 -0
- package/dist/mobile/public/index.html +287 -0
- package/dist/mobile/public/manifest.json +16 -0
- package/dist/mobile/public/style.css +589 -0
- package/dist/mobile/server.d.ts +14 -0
- package/dist/mobile/server.d.ts.map +1 -0
- package/dist/mobile/server.js +232 -0
- package/dist/mobile/server.js.map +1 -0
- package/package.json +3 -2
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import os from 'os';
|
|
4
|
+
import https from 'https';
|
|
5
|
+
import { profileManager } from '../profiles/profileManager.js';
|
|
6
|
+
export class FirestoreSyncManager {
|
|
7
|
+
static getSyncConfigFile() {
|
|
8
|
+
return path.join(os.homedir(), '.antri', 'cloud_sync.json');
|
|
9
|
+
}
|
|
10
|
+
static getSyncConfig() {
|
|
11
|
+
const filePath = this.getSyncConfigFile();
|
|
12
|
+
if (fs.existsSync(filePath)) {
|
|
13
|
+
try {
|
|
14
|
+
return JSON.parse(fs.readFileSync(filePath, 'utf-8'));
|
|
15
|
+
}
|
|
16
|
+
catch (_) { }
|
|
17
|
+
}
|
|
18
|
+
return {
|
|
19
|
+
projectId: process.env.GOOGLE_CLOUD_PROJECT || process.env.GCP_PROJECT || '',
|
|
20
|
+
syncKey: 'default_user',
|
|
21
|
+
apiKey: process.env.GEMINI_API_KEY || '',
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
static saveSyncConfig(projectId, syncKey = 'default_user', apiKey = '') {
|
|
25
|
+
const dir = path.join(os.homedir(), '.antri');
|
|
26
|
+
if (!fs.existsSync(dir))
|
|
27
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
28
|
+
fs.writeFileSync(this.getSyncConfigFile(), JSON.stringify({ projectId, syncKey, apiKey, lastSynced: new Date().toISOString() }, null, 2), 'utf-8');
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Push local profiles from ~/.antri/profiles/ to Google Cloud Firestore
|
|
32
|
+
*/
|
|
33
|
+
static async pushToFirestore() {
|
|
34
|
+
const { projectId, syncKey, apiKey } = this.getSyncConfig();
|
|
35
|
+
if (!projectId) {
|
|
36
|
+
return { success: false, count: 0, error: 'Google Cloud Project ID is not configured. Run "antri sync config <project-id>"' };
|
|
37
|
+
}
|
|
38
|
+
const profiles = profileManager.listProfiles();
|
|
39
|
+
let synced = 0;
|
|
40
|
+
const keyParam = apiKey ? `?key=${apiKey}` : '';
|
|
41
|
+
for (const p of profiles) {
|
|
42
|
+
const filePath = path.join(os.homedir(), '.antri', 'profiles', `${p.name}.md`);
|
|
43
|
+
let content = '';
|
|
44
|
+
if (fs.existsSync(filePath)) {
|
|
45
|
+
content = fs.readFileSync(filePath, 'utf-8');
|
|
46
|
+
}
|
|
47
|
+
const url = `https://firestore.googleapis.com/v1/projects/${projectId}/databases/(default)/documents/antri_sync/${syncKey}/profiles/${p.name}${keyParam}`;
|
|
48
|
+
const payload = JSON.stringify({
|
|
49
|
+
fields: {
|
|
50
|
+
name: { stringValue: p.name },
|
|
51
|
+
content: { stringValue: content },
|
|
52
|
+
updatedAt: { stringValue: new Date().toISOString() },
|
|
53
|
+
},
|
|
54
|
+
});
|
|
55
|
+
try {
|
|
56
|
+
await this.httpRequest(url, 'PATCH', payload);
|
|
57
|
+
synced++;
|
|
58
|
+
}
|
|
59
|
+
catch (err) {
|
|
60
|
+
// Continue attempting others
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
this.saveSyncConfig(projectId, syncKey, apiKey);
|
|
64
|
+
return { success: true, count: synced };
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Pull profiles from Google Cloud Firestore to ~/.antri/profiles/
|
|
68
|
+
*/
|
|
69
|
+
static async pullFromFirestore() {
|
|
70
|
+
const { projectId, syncKey } = this.getSyncConfig();
|
|
71
|
+
if (!projectId) {
|
|
72
|
+
return { success: false, count: 0, error: 'Google Cloud Project ID is not configured.' };
|
|
73
|
+
}
|
|
74
|
+
const url = `https://firestore.googleapis.com/v1/projects/${projectId}/databases/(default)/documents/antri_sync/${syncKey}/profiles`;
|
|
75
|
+
try {
|
|
76
|
+
const raw = await this.httpRequest(url, 'GET');
|
|
77
|
+
const data = JSON.parse(raw);
|
|
78
|
+
const docs = data.documents || [];
|
|
79
|
+
const dir = path.join(os.homedir(), '.antri', 'profiles');
|
|
80
|
+
if (!fs.existsSync(dir))
|
|
81
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
82
|
+
let count = 0;
|
|
83
|
+
for (const doc of docs) {
|
|
84
|
+
const fields = doc.fields || {};
|
|
85
|
+
const name = fields.name?.stringValue;
|
|
86
|
+
const content = fields.content?.stringValue;
|
|
87
|
+
if (name && content) {
|
|
88
|
+
fs.writeFileSync(path.join(dir, `${name}.md`), content, 'utf-8');
|
|
89
|
+
count++;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
this.saveSyncConfig(projectId, syncKey);
|
|
93
|
+
return { success: true, count };
|
|
94
|
+
}
|
|
95
|
+
catch (err) {
|
|
96
|
+
return { success: false, count: 0, error: err.message };
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
static httpRequest(urlStr, method, body) {
|
|
100
|
+
return new Promise((resolve, reject) => {
|
|
101
|
+
const url = new URL(urlStr);
|
|
102
|
+
const options = {
|
|
103
|
+
hostname: url.hostname,
|
|
104
|
+
port: 443,
|
|
105
|
+
path: url.pathname + url.search,
|
|
106
|
+
method,
|
|
107
|
+
headers: {
|
|
108
|
+
'Content-Type': 'application/json',
|
|
109
|
+
'Content-Length': body ? Buffer.byteLength(body) : 0,
|
|
110
|
+
},
|
|
111
|
+
};
|
|
112
|
+
const req = https.request(options, (res) => {
|
|
113
|
+
let resBody = '';
|
|
114
|
+
res.on('data', (d) => (resBody += d));
|
|
115
|
+
res.on('end', () => {
|
|
116
|
+
if (res.statusCode && res.statusCode >= 200 && res.statusCode < 300) {
|
|
117
|
+
resolve(resBody);
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
reject(new Error(`HTTP ${res.statusCode}: ${resBody}`));
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
req.on('error', (e) => reject(e));
|
|
125
|
+
if (body)
|
|
126
|
+
req.write(body);
|
|
127
|
+
req.end();
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
//# sourceMappingURL=firestore.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"firestore.js","sourceRoot":"","sources":["../../src/cloud/firestore.ts"],"names":[],"mappings":"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"}
|
package/dist/core/config.js
CHANGED
package/dist/core/updater.d.ts
CHANGED
package/dist/core/updater.js
CHANGED
|
@@ -4,7 +4,7 @@ import chalk from 'chalk';
|
|
|
4
4
|
import ora from 'ora';
|
|
5
5
|
const execPromise = util.promisify(exec);
|
|
6
6
|
export class Updater {
|
|
7
|
-
static CURRENT_VERSION = '1.
|
|
7
|
+
static CURRENT_VERSION = '1.29.0';
|
|
8
8
|
static PACKAGE_NAME = 'antri_cli';
|
|
9
9
|
/**
|
|
10
10
|
* Checks for available updates and self-updates the global antri CLI
|
|
@@ -2,10 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
let currentConfig = null;
|
|
4
4
|
let activeTab = 'chat';
|
|
5
|
+
let attachedFiles = [];
|
|
6
|
+
let availableCommands = [];
|
|
7
|
+
let activePaletteMatches = [];
|
|
8
|
+
let paletteSelectedIndex = 0;
|
|
9
|
+
let activePaletteMode = null; // 'slash' | 'file' | null
|
|
5
10
|
|
|
6
11
|
// Initialize on Load
|
|
7
12
|
document.addEventListener('DOMContentLoaded', async () => {
|
|
8
13
|
await loadStatus();
|
|
14
|
+
await loadCommands();
|
|
9
15
|
await loadProfiles();
|
|
10
16
|
await loadSkills();
|
|
11
17
|
await loadMemory();
|
|
@@ -34,6 +40,17 @@ async function loadStatus() {
|
|
|
34
40
|
}
|
|
35
41
|
}
|
|
36
42
|
|
|
43
|
+
// Load Slash Commands for Prompt Toolkit
|
|
44
|
+
async function loadCommands() {
|
|
45
|
+
try {
|
|
46
|
+
const res = await fetch('/api/commands');
|
|
47
|
+
const data = await res.json();
|
|
48
|
+
availableCommands = data.commands || [];
|
|
49
|
+
} catch (err) {
|
|
50
|
+
console.error('Failed to load commands:', err);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
37
54
|
// Load Models for Provider
|
|
38
55
|
async function loadModels() {
|
|
39
56
|
try {
|
|
@@ -132,13 +149,243 @@ function showTab(tabName) {
|
|
|
132
149
|
if (navButtons[navIndex]) navButtons[navIndex].classList.add('active');
|
|
133
150
|
}
|
|
134
151
|
|
|
152
|
+
// File Upload Handler (Images & Files)
|
|
153
|
+
async function handleFileSelected(event) {
|
|
154
|
+
const files = event.target.files;
|
|
155
|
+
if (!files || files.length === 0) return;
|
|
156
|
+
|
|
157
|
+
for (const file of files) {
|
|
158
|
+
const reader = new FileReader();
|
|
159
|
+
reader.onload = async (e) => {
|
|
160
|
+
const data = e.target.result;
|
|
161
|
+
try {
|
|
162
|
+
const res = await fetch('/api/upload', {
|
|
163
|
+
method: 'POST',
|
|
164
|
+
headers: { 'Content-Type': 'application/json' },
|
|
165
|
+
body: JSON.stringify({
|
|
166
|
+
fileName: file.name,
|
|
167
|
+
fileType: file.type || 'text/plain',
|
|
168
|
+
data,
|
|
169
|
+
}),
|
|
170
|
+
});
|
|
171
|
+
const uploadRes = await res.json();
|
|
172
|
+
if (uploadRes.success) {
|
|
173
|
+
attachedFiles.push({
|
|
174
|
+
name: file.name,
|
|
175
|
+
path: uploadRes.filePath,
|
|
176
|
+
isImage: uploadRes.isImage,
|
|
177
|
+
dataUrl: uploadRes.isImage ? data : null,
|
|
178
|
+
});
|
|
179
|
+
renderAttachmentChips();
|
|
180
|
+
}
|
|
181
|
+
} catch (err) {
|
|
182
|
+
console.error('File upload failed:', err);
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
if (file.type.startsWith('image/')) {
|
|
186
|
+
reader.readAsDataURL(file);
|
|
187
|
+
} else {
|
|
188
|
+
reader.readAsText(file);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Reset input
|
|
193
|
+
event.target.value = '';
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function renderAttachmentChips() {
|
|
197
|
+
const tray = document.getElementById('attachment-preview-tray');
|
|
198
|
+
if (!tray) return;
|
|
199
|
+
|
|
200
|
+
if (attachedFiles.length === 0) {
|
|
201
|
+
tray.classList.add('hidden');
|
|
202
|
+
tray.innerHTML = '';
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
tray.classList.remove('hidden');
|
|
207
|
+
tray.innerHTML = '';
|
|
208
|
+
|
|
209
|
+
attachedFiles.forEach((file, index) => {
|
|
210
|
+
const chip = document.createElement('div');
|
|
211
|
+
chip.className = 'attachment-chip';
|
|
212
|
+
if (file.isImage && file.dataUrl) {
|
|
213
|
+
chip.innerHTML = `
|
|
214
|
+
<img src="${file.dataUrl}" alt="preview" />
|
|
215
|
+
<span>${file.name}</span>
|
|
216
|
+
<button class="remove-chip-btn" onclick="removeAttachment(${index})">×</button>
|
|
217
|
+
`;
|
|
218
|
+
} else {
|
|
219
|
+
chip.innerHTML = `
|
|
220
|
+
<span>${file.name}</span>
|
|
221
|
+
<button class="remove-chip-btn" onclick="removeAttachment(${index})">×</button>
|
|
222
|
+
`;
|
|
223
|
+
}
|
|
224
|
+
tray.appendChild(chip);
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function removeAttachment(index) {
|
|
229
|
+
attachedFiles.splice(index, 1);
|
|
230
|
+
renderAttachmentChips();
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// Prompt Toolkit Text & Key Handler
|
|
234
|
+
async function handleInputText(event) {
|
|
235
|
+
const val = event.target.value;
|
|
236
|
+
const cursorPos = event.target.selectionStart;
|
|
237
|
+
|
|
238
|
+
// 1. Slash command mode
|
|
239
|
+
if (val.startsWith('/')) {
|
|
240
|
+
const query = val.toLowerCase();
|
|
241
|
+
activePaletteMatches = availableCommands.filter((cmd) => {
|
|
242
|
+
const baseName = cmd.name.split(' ')[0].toLowerCase();
|
|
243
|
+
return baseName.startsWith(query) || cmd.name.toLowerCase().startsWith(query);
|
|
244
|
+
});
|
|
245
|
+
activePaletteMode = 'slash';
|
|
246
|
+
renderPalette('Commands', activePaletteMatches);
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// 2. Attachment file mode (@)
|
|
251
|
+
const lastAt = val.lastIndexOf('@', cursorPos - 1);
|
|
252
|
+
if (lastAt !== -1 && (lastAt === 0 || val[lastAt - 1] === ' ')) {
|
|
253
|
+
const query = val.slice(lastAt + 1, cursorPos);
|
|
254
|
+
if (!query.includes(' ')) {
|
|
255
|
+
try {
|
|
256
|
+
const res = await fetch(`/api/files?query=${encodeURIComponent(query)}`);
|
|
257
|
+
const data = await res.json();
|
|
258
|
+
activePaletteMatches = data.items.map((item) => ({
|
|
259
|
+
name: item.name,
|
|
260
|
+
description: item.isDirectory ? 'Directory' : item.relativePath,
|
|
261
|
+
relativePath: item.relativePath,
|
|
262
|
+
isDirectory: item.isDirectory,
|
|
263
|
+
}));
|
|
264
|
+
activePaletteMode = 'file';
|
|
265
|
+
renderPalette(`Files: ${data.currentDir}`, activePaletteMatches);
|
|
266
|
+
return;
|
|
267
|
+
} catch (e) {}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
hidePalette();
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
function renderPalette(title, items) {
|
|
275
|
+
const palette = document.getElementById('prompt-toolkit-palette');
|
|
276
|
+
const header = document.getElementById('palette-header');
|
|
277
|
+
const list = document.getElementById('palette-list');
|
|
278
|
+
|
|
279
|
+
if (!palette || !items || items.length === 0) {
|
|
280
|
+
hidePalette();
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
header.textContent = title;
|
|
285
|
+
list.innerHTML = '';
|
|
286
|
+
paletteSelectedIndex = Math.min(paletteSelectedIndex, items.length - 1);
|
|
287
|
+
if (paletteSelectedIndex < 0) paletteSelectedIndex = 0;
|
|
288
|
+
|
|
289
|
+
items.slice(0, 10).forEach((item, idx) => {
|
|
290
|
+
const el = document.createElement('div');
|
|
291
|
+
el.className = `palette-item ${idx === paletteSelectedIndex ? 'active' : ''}`;
|
|
292
|
+
el.innerHTML = `
|
|
293
|
+
<span class="palette-name">${item.name}</span>
|
|
294
|
+
<span class="palette-desc">${item.description || ''}</span>
|
|
295
|
+
${item.isDirectory ? '<span class="palette-tag">dir</span>' : ''}
|
|
296
|
+
`;
|
|
297
|
+
el.onclick = () => selectPaletteItem(idx);
|
|
298
|
+
list.appendChild(el);
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
palette.classList.remove('hidden');
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
function hidePalette() {
|
|
305
|
+
const palette = document.getElementById('prompt-toolkit-palette');
|
|
306
|
+
if (palette) palette.classList.add('hidden');
|
|
307
|
+
activePaletteMode = null;
|
|
308
|
+
activePaletteMatches = [];
|
|
309
|
+
paletteSelectedIndex = 0;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
function selectPaletteItem(index) {
|
|
313
|
+
const item = activePaletteMatches[index];
|
|
314
|
+
if (!item) return;
|
|
315
|
+
|
|
316
|
+
const input = document.getElementById('prompt-input');
|
|
317
|
+
|
|
318
|
+
if (activePaletteMode === 'slash') {
|
|
319
|
+
const rawCmd = item.name.split(' ')[0];
|
|
320
|
+
input.value = rawCmd + ' ';
|
|
321
|
+
hidePalette();
|
|
322
|
+
input.focus();
|
|
323
|
+
} else if (activePaletteMode === 'file') {
|
|
324
|
+
const val = input.value;
|
|
325
|
+
const cursorPos = input.selectionStart;
|
|
326
|
+
const lastAt = val.lastIndexOf('@', cursorPos - 1);
|
|
327
|
+
if (lastAt !== -1) {
|
|
328
|
+
input.value = val.slice(0, lastAt) + '@' + item.relativePath + ' ' + val.slice(cursorPos);
|
|
329
|
+
}
|
|
330
|
+
hidePalette();
|
|
331
|
+
input.focus();
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
function handleInputKey(event) {
|
|
336
|
+
const palette = document.getElementById('prompt-toolkit-palette');
|
|
337
|
+
const isPaletteVisible = palette && !palette.classList.contains('hidden');
|
|
338
|
+
|
|
339
|
+
if (isPaletteVisible && activePaletteMatches.length > 0) {
|
|
340
|
+
if (event.key === 'ArrowUp') {
|
|
341
|
+
event.preventDefault();
|
|
342
|
+
paletteSelectedIndex = (paletteSelectedIndex - 1 + activePaletteMatches.length) % activePaletteMatches.length;
|
|
343
|
+
renderPalette(document.getElementById('palette-header').textContent, activePaletteMatches);
|
|
344
|
+
return;
|
|
345
|
+
}
|
|
346
|
+
if (event.key === 'ArrowDown') {
|
|
347
|
+
event.preventDefault();
|
|
348
|
+
paletteSelectedIndex = (paletteSelectedIndex + 1) % activePaletteMatches.length;
|
|
349
|
+
renderPalette(document.getElementById('palette-header').textContent, activePaletteMatches);
|
|
350
|
+
return;
|
|
351
|
+
}
|
|
352
|
+
if (event.key === 'Tab' || event.key === 'Enter') {
|
|
353
|
+
if (!event.ctrlKey && !event.metaKey) {
|
|
354
|
+
event.preventDefault();
|
|
355
|
+
selectPaletteItem(paletteSelectedIndex);
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
if (event.key === 'Escape') {
|
|
360
|
+
hidePalette();
|
|
361
|
+
return;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
// Ctrl + Enter to submit prompt
|
|
366
|
+
if (event.key === 'Enter' && (event.ctrlKey || event.metaKey)) {
|
|
367
|
+
event.preventDefault();
|
|
368
|
+
submitPrompt();
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
|
|
135
372
|
// Chat Prompt Submission with SSE Streaming
|
|
136
373
|
async function submitPrompt() {
|
|
137
374
|
const input = document.getElementById('prompt-input');
|
|
138
|
-
|
|
375
|
+
let prompt = input.value.trim();
|
|
376
|
+
|
|
377
|
+
// Attach any uploaded files to prompt
|
|
378
|
+
if (attachedFiles.length > 0) {
|
|
379
|
+
const attachmentsText = attachedFiles.map((f) => `\n[Attached File: ${f.name} (${f.path})]`).join('');
|
|
380
|
+
prompt = prompt + '\n' + attachmentsText;
|
|
381
|
+
attachedFiles = [];
|
|
382
|
+
renderAttachmentChips();
|
|
383
|
+
}
|
|
384
|
+
|
|
139
385
|
if (!prompt) return;
|
|
140
386
|
|
|
141
387
|
input.value = '';
|
|
388
|
+
hidePalette();
|
|
142
389
|
|
|
143
390
|
// Intercept /debate or /goal inside chat
|
|
144
391
|
if (prompt.startsWith('/debate')) {
|
|
@@ -232,13 +479,6 @@ function setPrompt(text) {
|
|
|
232
479
|
input.focus();
|
|
233
480
|
}
|
|
234
481
|
|
|
235
|
-
function handleInputKey(event) {
|
|
236
|
-
if (event.key === 'Enter' && (event.ctrlKey || event.metaKey)) {
|
|
237
|
-
event.preventDefault();
|
|
238
|
-
submitPrompt();
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
|
|
242
482
|
// Dialectic Debate Runner
|
|
243
483
|
async function startDebate() {
|
|
244
484
|
const input = document.getElementById('debate-query-input');
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
<header class="app-header">
|
|
16
16
|
<div class="brand-zone">
|
|
17
17
|
<span class="logo-mark">ANTRI</span>
|
|
18
|
-
<span class="version-tag">v1.
|
|
18
|
+
<span class="version-tag">v1.29.0</span>
|
|
19
19
|
</div>
|
|
20
20
|
|
|
21
21
|
<!-- Mode Toggle -->
|
|
@@ -116,12 +116,32 @@
|
|
|
116
116
|
</div>
|
|
117
117
|
</div>
|
|
118
118
|
|
|
119
|
-
<!-- Bottom Prompt Box -->
|
|
119
|
+
<!-- Bottom Prompt Box & Prompt Toolkit -->
|
|
120
120
|
<div class="chat-input-bar">
|
|
121
|
+
<!-- Prompt Toolkit Dropdown Palette -->
|
|
122
|
+
<div id="prompt-toolkit-palette" class="prompt-toolkit-palette hidden">
|
|
123
|
+
<div class="palette-header" id="palette-header">Commands</div>
|
|
124
|
+
<div class="palette-list" id="palette-list">
|
|
125
|
+
<!-- Dynamically populated -->
|
|
126
|
+
</div>
|
|
127
|
+
</div>
|
|
128
|
+
|
|
129
|
+
<!-- Attached Files Preview Tray -->
|
|
130
|
+
<div id="attachment-preview-tray" class="attachment-preview-tray hidden"></div>
|
|
131
|
+
|
|
121
132
|
<div class="input-container">
|
|
122
|
-
|
|
133
|
+
<!-- Hidden File Input -->
|
|
134
|
+
<input type="file" id="file-upload-input" multiple accept="image/*,.txt,.ts,.js,.json,.py,.md,.csv,.html,.css" style="display:none;" onchange="handleFileSelected(event)" />
|
|
135
|
+
|
|
136
|
+
<div class="input-row">
|
|
137
|
+
<button id="btn-attach-file" class="attach-btn" title="Upload image or file" onclick="document.getElementById('file-upload-input').click()">
|
|
138
|
+
+
|
|
139
|
+
</button>
|
|
140
|
+
<textarea id="prompt-input" placeholder="Ask ANTRI, type / for commands, @ for files..." rows="2" onkeydown="handleInputKey(event)" oninput="handleInputText(event)"></textarea>
|
|
141
|
+
</div>
|
|
142
|
+
|
|
123
143
|
<div class="input-actions">
|
|
124
|
-
<span class="shortcut-tip">Press Ctrl + Enter to send ·
|
|
144
|
+
<span class="shortcut-tip">Press <b>Ctrl + Enter</b> to send · <b>/</b> commands · <b>@</b> files</span>
|
|
125
145
|
<button id="send-btn" class="send-btn" onclick="submitPrompt()">
|
|
126
146
|
<span>Send</span>
|
|
127
147
|
</button>
|
|
@@ -394,25 +394,175 @@ body.antri-app {
|
|
|
394
394
|
display: inline-block;
|
|
395
395
|
}
|
|
396
396
|
|
|
397
|
-
/* Chat Input Bar */
|
|
397
|
+
/* Chat Input Bar & Attachments */
|
|
398
398
|
.chat-input-bar {
|
|
399
399
|
position: absolute;
|
|
400
400
|
bottom: 1.25rem;
|
|
401
401
|
left: 2.75rem;
|
|
402
402
|
right: 2.75rem;
|
|
403
|
+
z-index: 50;
|
|
403
404
|
}
|
|
404
405
|
|
|
405
406
|
.input-container {
|
|
406
407
|
background: var(--bg-card);
|
|
407
408
|
border: 1px solid var(--border-main);
|
|
408
409
|
border-radius: var(--radius-lg);
|
|
409
|
-
padding:
|
|
410
|
+
padding: 0.9rem 1.25rem;
|
|
410
411
|
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.04);
|
|
411
412
|
display: flex;
|
|
412
413
|
flex-direction: column;
|
|
414
|
+
gap: 0.5rem;
|
|
415
|
+
position: relative;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
.input-row {
|
|
419
|
+
display: flex;
|
|
420
|
+
align-items: flex-start;
|
|
413
421
|
gap: 0.75rem;
|
|
414
422
|
}
|
|
415
423
|
|
|
424
|
+
.attach-btn {
|
|
425
|
+
width: 32px;
|
|
426
|
+
height: 32px;
|
|
427
|
+
min-width: 32px;
|
|
428
|
+
border-radius: var(--radius-sm);
|
|
429
|
+
border: 1px solid var(--border-main);
|
|
430
|
+
background: var(--bg-subtle);
|
|
431
|
+
color: var(--text-primary);
|
|
432
|
+
font-size: 1.15rem;
|
|
433
|
+
font-weight: 500;
|
|
434
|
+
display: flex;
|
|
435
|
+
align-items: center;
|
|
436
|
+
justify-content: center;
|
|
437
|
+
cursor: pointer;
|
|
438
|
+
transition: all 0.15s ease;
|
|
439
|
+
margin-top: 2px;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
.attach-btn:hover {
|
|
443
|
+
background: var(--bg-hover);
|
|
444
|
+
border-color: var(--text-primary);
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
.attachment-preview-tray {
|
|
448
|
+
display: flex;
|
|
449
|
+
flex-wrap: wrap;
|
|
450
|
+
gap: 0.5rem;
|
|
451
|
+
padding: 0.4rem 0.2rem;
|
|
452
|
+
border-bottom: 1px solid var(--border-light);
|
|
453
|
+
margin-bottom: 0.25rem;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
.attachment-chip {
|
|
457
|
+
display: flex;
|
|
458
|
+
align-items: center;
|
|
459
|
+
gap: 0.4rem;
|
|
460
|
+
background: var(--bg-subtle);
|
|
461
|
+
border: 1px solid var(--border-main);
|
|
462
|
+
padding: 0.25rem 0.6rem;
|
|
463
|
+
border-radius: var(--radius-xs);
|
|
464
|
+
font-size: 0.75rem;
|
|
465
|
+
font-family: var(--font-mono);
|
|
466
|
+
color: var(--text-primary);
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
.attachment-chip img {
|
|
470
|
+
width: 18px;
|
|
471
|
+
height: 18px;
|
|
472
|
+
object-fit: cover;
|
|
473
|
+
border-radius: 2px;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
.attachment-chip .remove-chip-btn {
|
|
477
|
+
background: transparent;
|
|
478
|
+
border: none;
|
|
479
|
+
color: var(--text-tertiary);
|
|
480
|
+
font-weight: 700;
|
|
481
|
+
cursor: pointer;
|
|
482
|
+
padding: 0 0.2rem;
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
.attachment-chip .remove-chip-btn:hover {
|
|
486
|
+
color: var(--accent-warning);
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
/* Prompt Toolkit Autocomplete Palette */
|
|
490
|
+
.prompt-toolkit-palette {
|
|
491
|
+
position: absolute;
|
|
492
|
+
bottom: calc(100% + 0.5rem);
|
|
493
|
+
left: 0;
|
|
494
|
+
right: 0;
|
|
495
|
+
background: var(--bg-card);
|
|
496
|
+
border: 1px solid var(--border-main);
|
|
497
|
+
border-radius: var(--radius-md);
|
|
498
|
+
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.08);
|
|
499
|
+
max-height: 280px;
|
|
500
|
+
overflow-y: auto;
|
|
501
|
+
display: flex;
|
|
502
|
+
flex-direction: column;
|
|
503
|
+
z-index: 100;
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
.palette-header {
|
|
507
|
+
padding: 0.5rem 0.9rem;
|
|
508
|
+
background: var(--bg-sidebar);
|
|
509
|
+
border-bottom: 1px solid var(--border-main);
|
|
510
|
+
font-size: 0.7rem;
|
|
511
|
+
text-transform: uppercase;
|
|
512
|
+
letter-spacing: 0.05em;
|
|
513
|
+
font-weight: 700;
|
|
514
|
+
color: var(--text-tertiary);
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
.palette-list {
|
|
518
|
+
display: flex;
|
|
519
|
+
flex-direction: column;
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
.palette-item {
|
|
523
|
+
display: flex;
|
|
524
|
+
align-items: center;
|
|
525
|
+
justify-content: space-between;
|
|
526
|
+
padding: 0.6rem 1rem;
|
|
527
|
+
cursor: pointer;
|
|
528
|
+
border-bottom: 1px solid var(--border-light);
|
|
529
|
+
transition: all 0.1s ease;
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
.palette-item:last-child {
|
|
533
|
+
border-bottom: none;
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
.palette-item.active,
|
|
537
|
+
.palette-item:hover {
|
|
538
|
+
background: var(--bg-subtle);
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
.palette-name {
|
|
542
|
+
font-family: var(--font-mono);
|
|
543
|
+
font-size: 0.84rem;
|
|
544
|
+
font-weight: 600;
|
|
545
|
+
color: var(--text-primary);
|
|
546
|
+
min-width: 140px;
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
.palette-desc {
|
|
550
|
+
font-size: 0.78rem;
|
|
551
|
+
color: var(--text-secondary);
|
|
552
|
+
flex: 1;
|
|
553
|
+
margin-left: 1rem;
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
.palette-tag {
|
|
557
|
+
font-size: 0.7rem;
|
|
558
|
+
color: var(--text-tertiary);
|
|
559
|
+
font-family: var(--font-mono);
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
.hidden {
|
|
563
|
+
display: none !important;
|
|
564
|
+
}
|
|
565
|
+
|
|
416
566
|
.input-container textarea {
|
|
417
567
|
background: transparent;
|
|
418
568
|
border: none;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/desktop/server.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/desktop/server.ts"],"names":[],"mappings":"AAoCA,qBAAa,aAAa;IACxB,OAAO,CAAC,MAAM,CAA4B;IAC1C,OAAO,CAAC,IAAI,CAAQ;IACpB,OAAO,CAAC,WAAW,CAAa;;IAMnB,KAAK,IAAI,OAAO,CAAC,MAAM,CAAC;IAqExB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;YAUpB,SAAS;WA8QH,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC;CAyBnD"}
|