antri_cli 1.49.0 → 1.52.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/dist/cli/shortcuts.d.ts.map +1 -1
- package/dist/cli/shortcuts.js +57 -0
- package/dist/cli/shortcuts.js.map +1 -1
- package/dist/cloud/firestore.d.ts +7 -0
- package/dist/cloud/firestore.d.ts.map +1 -1
- package/dist/cloud/firestore.js +48 -6
- package/dist/cloud/firestore.js.map +1 -1
- package/dist/core/agent.d.ts +1 -0
- package/dist/core/agent.d.ts.map +1 -1
- package/dist/core/agent.js +20 -7
- package/dist/core/agent.js.map +1 -1
- package/dist/core/config.js +1 -1
- package/dist/core/history.d.ts +1 -0
- package/dist/core/history.d.ts.map +1 -1
- package/dist/core/history.js +3 -0
- package/dist/core/history.js.map +1 -1
- package/dist/core/sessionManager.d.ts +23 -0
- package/dist/core/sessionManager.d.ts.map +1 -0
- package/dist/core/sessionManager.js +188 -0
- package/dist/core/sessionManager.js.map +1 -0
- package/dist/core/updater.d.ts +1 -1
- package/dist/core/updater.js +1 -1
- package/dist/desktop/public/app.js +294 -16
- package/dist/desktop/public/index.html +58 -6
- package/dist/desktop/public/style.css +109 -0
- package/dist/desktop/server.d.ts.map +1 -1
- package/dist/desktop/server.js +85 -4
- package/dist/desktop/server.js.map +1 -1
- package/dist/profiles/profileManager.d.ts +8 -1
- package/dist/profiles/profileManager.d.ts.map +1 -1
- package/dist/profiles/profileManager.js +94 -43
- package/dist/profiles/profileManager.js.map +1 -1
- package/dist/types.d.ts +16 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import os from 'os';
|
|
4
|
+
const CHATS_DIR = path.join(os.homedir(), '.antri', 'chats');
|
|
5
|
+
const ACTIVE_SESSION_FILE = path.join(CHATS_DIR, '.active_session');
|
|
6
|
+
export class SessionManager {
|
|
7
|
+
chatsDir;
|
|
8
|
+
activeSessionId = '';
|
|
9
|
+
constructor(customDir) {
|
|
10
|
+
this.chatsDir = customDir || CHATS_DIR;
|
|
11
|
+
this.ensureDirectory();
|
|
12
|
+
this.initActiveSession();
|
|
13
|
+
}
|
|
14
|
+
ensureDirectory() {
|
|
15
|
+
if (!fs.existsSync(this.chatsDir)) {
|
|
16
|
+
fs.mkdirSync(this.chatsDir, { recursive: true });
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
initActiveSession() {
|
|
20
|
+
try {
|
|
21
|
+
if (fs.existsSync(ACTIVE_SESSION_FILE)) {
|
|
22
|
+
const id = fs.readFileSync(ACTIVE_SESSION_FILE, 'utf-8').trim();
|
|
23
|
+
if (id && fs.existsSync(this.getSessionFilePath(id))) {
|
|
24
|
+
this.activeSessionId = id;
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
catch { }
|
|
30
|
+
// Pick first existing session or create a default session
|
|
31
|
+
const sessions = this.listSessions();
|
|
32
|
+
if (sessions.length > 0) {
|
|
33
|
+
this.activeSessionId = sessions[0].id;
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
const newSession = this.createSession('New Chat');
|
|
37
|
+
this.activeSessionId = newSession.id;
|
|
38
|
+
}
|
|
39
|
+
this.saveActiveSessionId();
|
|
40
|
+
}
|
|
41
|
+
getSessionFilePath(id) {
|
|
42
|
+
const cleanId = id.replace(/[^a-zA-Z0-9_-]/g, '_');
|
|
43
|
+
return path.join(this.chatsDir, `${cleanId}.json`);
|
|
44
|
+
}
|
|
45
|
+
saveActiveSessionId() {
|
|
46
|
+
try {
|
|
47
|
+
this.ensureDirectory();
|
|
48
|
+
fs.writeFileSync(ACTIVE_SESSION_FILE, this.activeSessionId, 'utf-8');
|
|
49
|
+
}
|
|
50
|
+
catch { }
|
|
51
|
+
}
|
|
52
|
+
getActiveSessionId() {
|
|
53
|
+
return this.activeSessionId;
|
|
54
|
+
}
|
|
55
|
+
setActiveSessionId(id) {
|
|
56
|
+
const session = this.getSession(id);
|
|
57
|
+
if (session) {
|
|
58
|
+
this.activeSessionId = id;
|
|
59
|
+
this.saveActiveSessionId();
|
|
60
|
+
return session;
|
|
61
|
+
}
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
createSession(title = 'New Chat') {
|
|
65
|
+
this.ensureDirectory();
|
|
66
|
+
const id = `chat_${Date.now()}_${Math.random().toString(36).slice(2, 7)}`;
|
|
67
|
+
const session = {
|
|
68
|
+
id,
|
|
69
|
+
title,
|
|
70
|
+
createdAt: Date.now(),
|
|
71
|
+
updatedAt: Date.now(),
|
|
72
|
+
messages: [],
|
|
73
|
+
};
|
|
74
|
+
this.saveSession(session);
|
|
75
|
+
this.activeSessionId = id;
|
|
76
|
+
this.saveActiveSessionId();
|
|
77
|
+
return session;
|
|
78
|
+
}
|
|
79
|
+
getSession(id) {
|
|
80
|
+
const filePath = this.getSessionFilePath(id);
|
|
81
|
+
if (fs.existsSync(filePath)) {
|
|
82
|
+
try {
|
|
83
|
+
const raw = fs.readFileSync(filePath, 'utf-8');
|
|
84
|
+
return JSON.parse(raw);
|
|
85
|
+
}
|
|
86
|
+
catch { }
|
|
87
|
+
}
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
getActiveSession() {
|
|
91
|
+
const session = this.getSession(this.activeSessionId);
|
|
92
|
+
if (session)
|
|
93
|
+
return session;
|
|
94
|
+
return this.createSession('New Chat');
|
|
95
|
+
}
|
|
96
|
+
saveSession(session) {
|
|
97
|
+
this.ensureDirectory();
|
|
98
|
+
session.updatedAt = Date.now();
|
|
99
|
+
const filePath = this.getSessionFilePath(session.id);
|
|
100
|
+
fs.writeFileSync(filePath, JSON.stringify(session, null, 2), 'utf-8');
|
|
101
|
+
}
|
|
102
|
+
addMessageToActiveSession(message) {
|
|
103
|
+
const session = this.getActiveSession();
|
|
104
|
+
session.messages.push({
|
|
105
|
+
...message,
|
|
106
|
+
timestamp: message.timestamp || Date.now(),
|
|
107
|
+
});
|
|
108
|
+
// Auto-generate title on first user message if title is "New Chat"
|
|
109
|
+
if (session.title === 'New Chat' && message.role === 'user') {
|
|
110
|
+
const cleanPrompt = message.content.replace(/\[Attached File:[^\]]+\]/g, '').trim();
|
|
111
|
+
if (cleanPrompt) {
|
|
112
|
+
session.title = cleanPrompt.length > 35 ? `${cleanPrompt.slice(0, 32)}...` : cleanPrompt;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
this.saveSession(session);
|
|
116
|
+
return session;
|
|
117
|
+
}
|
|
118
|
+
listSessions() {
|
|
119
|
+
this.ensureDirectory();
|
|
120
|
+
try {
|
|
121
|
+
const files = fs.readdirSync(this.chatsDir).filter((f) => f.endsWith('.json') && !f.startsWith('.'));
|
|
122
|
+
const summaries = [];
|
|
123
|
+
for (const file of files) {
|
|
124
|
+
try {
|
|
125
|
+
const filePath = path.join(this.chatsDir, file);
|
|
126
|
+
const raw = fs.readFileSync(filePath, 'utf-8');
|
|
127
|
+
const data = JSON.parse(raw);
|
|
128
|
+
if (data && data.id) {
|
|
129
|
+
const lastUserMsg = data.messages?.filter((m) => m.role === 'user').pop();
|
|
130
|
+
summaries.push({
|
|
131
|
+
id: data.id,
|
|
132
|
+
title: data.title || 'Untitled Chat',
|
|
133
|
+
createdAt: data.createdAt || 0,
|
|
134
|
+
updatedAt: data.updatedAt || 0,
|
|
135
|
+
messageCount: data.messages?.length || 0,
|
|
136
|
+
preview: lastUserMsg?.content?.slice(0, 80) || '',
|
|
137
|
+
isActive: data.id === this.activeSessionId,
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
catch { }
|
|
142
|
+
}
|
|
143
|
+
return summaries.sort((a, b) => b.updatedAt - a.updatedAt);
|
|
144
|
+
}
|
|
145
|
+
catch {
|
|
146
|
+
return [];
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
deleteSession(id) {
|
|
150
|
+
const filePath = this.getSessionFilePath(id);
|
|
151
|
+
try {
|
|
152
|
+
if (fs.existsSync(filePath)) {
|
|
153
|
+
fs.unlinkSync(filePath);
|
|
154
|
+
}
|
|
155
|
+
if (this.activeSessionId === id) {
|
|
156
|
+
const remaining = this.listSessions();
|
|
157
|
+
if (remaining.length > 0) {
|
|
158
|
+
this.activeSessionId = remaining[0].id;
|
|
159
|
+
}
|
|
160
|
+
else {
|
|
161
|
+
const fresh = this.createSession('New Chat');
|
|
162
|
+
this.activeSessionId = fresh.id;
|
|
163
|
+
}
|
|
164
|
+
this.saveActiveSessionId();
|
|
165
|
+
}
|
|
166
|
+
return true;
|
|
167
|
+
}
|
|
168
|
+
catch {
|
|
169
|
+
return false;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
renameSession(id, newTitle) {
|
|
173
|
+
const session = this.getSession(id);
|
|
174
|
+
if (session) {
|
|
175
|
+
session.title = newTitle.trim() || 'Untitled Chat';
|
|
176
|
+
this.saveSession(session);
|
|
177
|
+
return true;
|
|
178
|
+
}
|
|
179
|
+
return false;
|
|
180
|
+
}
|
|
181
|
+
clearActiveSessionMessages() {
|
|
182
|
+
const session = this.getActiveSession();
|
|
183
|
+
session.messages = [];
|
|
184
|
+
this.saveSession(session);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
export const sessionManager = new SessionManager();
|
|
188
|
+
//# sourceMappingURL=sessionManager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sessionManager.js","sourceRoot":"","sources":["../../src/core/sessionManager.ts"],"names":[],"mappings":"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"}
|
package/dist/core/updater.d.ts
CHANGED
package/dist/core/updater.js
CHANGED
|
@@ -5,7 +5,7 @@ import ora from 'ora';
|
|
|
5
5
|
const execPromise = util.promisify(exec);
|
|
6
6
|
export class Updater {
|
|
7
7
|
static PACKAGE_NAME = 'antri_cli';
|
|
8
|
-
static CURRENT_VERSION = '1.
|
|
8
|
+
static CURRENT_VERSION = '1.52.0';
|
|
9
9
|
/**
|
|
10
10
|
* Fetches latest release version directly from registry.npmjs.org (cache-free)
|
|
11
11
|
*/
|
|
@@ -19,6 +19,7 @@ document.addEventListener('DOMContentLoaded', async () => {
|
|
|
19
19
|
await checkAuthStatus();
|
|
20
20
|
await loadStatus();
|
|
21
21
|
await loadCommands();
|
|
22
|
+
await loadChatSessions();
|
|
22
23
|
await loadProfiles();
|
|
23
24
|
await loadSkills();
|
|
24
25
|
await loadMemory();
|
|
@@ -401,6 +402,146 @@ function handleInputKey(event) {
|
|
|
401
402
|
}
|
|
402
403
|
}
|
|
403
404
|
|
|
405
|
+
// ==========================================
|
|
406
|
+
// Multi-Chat Session Management
|
|
407
|
+
// ==========================================
|
|
408
|
+
let currentActiveChatId = '';
|
|
409
|
+
let allChatSessions = [];
|
|
410
|
+
|
|
411
|
+
async function loadChatSessions(renderMessages = true) {
|
|
412
|
+
try {
|
|
413
|
+
const res = await fetch('/api/chats');
|
|
414
|
+
const data = await res.json();
|
|
415
|
+
allChatSessions = data.sessions || [];
|
|
416
|
+
currentActiveChatId = data.activeId || (allChatSessions[0]?.id || '');
|
|
417
|
+
|
|
418
|
+
const select = document.getElementById('select-chat-session');
|
|
419
|
+
if (select) {
|
|
420
|
+
select.innerHTML = '';
|
|
421
|
+
allChatSessions.forEach((s) => {
|
|
422
|
+
const opt = document.createElement('option');
|
|
423
|
+
opt.value = s.id;
|
|
424
|
+
opt.textContent = `${s.title} (${s.messageCount || 0})`;
|
|
425
|
+
if (s.id === currentActiveChatId) opt.selected = true;
|
|
426
|
+
select.appendChild(opt);
|
|
427
|
+
});
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
if (renderMessages && data.activeSession) {
|
|
431
|
+
renderActiveSessionMessages(data.activeSession.messages || []);
|
|
432
|
+
}
|
|
433
|
+
} catch (err) {
|
|
434
|
+
console.error('Failed to load chat sessions:', err);
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
function renderActiveSessionMessages(messages) {
|
|
439
|
+
const container = document.getElementById('chat-messages');
|
|
440
|
+
if (!container) return;
|
|
441
|
+
container.innerHTML = '';
|
|
442
|
+
|
|
443
|
+
if (!messages || messages.length === 0) {
|
|
444
|
+
container.innerHTML = `
|
|
445
|
+
<div class="welcome-card" id="chat-welcome-card">
|
|
446
|
+
<h2>ANTRI Control Plane</h2>
|
|
447
|
+
<p>Minimalist environment for autonomous coding, architectural planning, and self-refinement. State, memory, profiles, and skills are synchronized across CLI and Desktop.</p>
|
|
448
|
+
<div class="quick-action-pills">
|
|
449
|
+
<button onclick="setPrompt('Plan the architecture for a real-time collaborative code editor')">Plan Editor Architecture</button>
|
|
450
|
+
<button onclick="setPrompt('/debate What are the trade-offs between WebSockets vs Server-Sent Events?')">Start Dialectic Debate</button>
|
|
451
|
+
<button onclick="setPrompt('/goal Implement a zero-dependency LRU cache with TTL in TypeScript')">Run Goal Loop</button>
|
|
452
|
+
</div>
|
|
453
|
+
</div>
|
|
454
|
+
`;
|
|
455
|
+
return;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
messages.forEach((msg) => {
|
|
459
|
+
if (msg.role === 'user' || msg.role === 'assistant') {
|
|
460
|
+
appendMessage(msg.role, msg.content);
|
|
461
|
+
}
|
|
462
|
+
});
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
async function createNewChatSession() {
|
|
466
|
+
try {
|
|
467
|
+
const res = await fetch('/api/chats/new', {
|
|
468
|
+
method: 'POST',
|
|
469
|
+
headers: { 'Content-Type': 'application/json' },
|
|
470
|
+
body: JSON.stringify({ title: 'New Chat' }),
|
|
471
|
+
});
|
|
472
|
+
const data = await res.json();
|
|
473
|
+
if (data.success && data.session) {
|
|
474
|
+
currentActiveChatId = data.session.id;
|
|
475
|
+
await loadChatSessions(true);
|
|
476
|
+
showToast('Started new chat session.');
|
|
477
|
+
}
|
|
478
|
+
} catch (err) {
|
|
479
|
+
console.error('Failed to create new chat session:', err);
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
async function onChatSessionSelect(id) {
|
|
484
|
+
try {
|
|
485
|
+
const res = await fetch('/api/chats/select', {
|
|
486
|
+
method: 'POST',
|
|
487
|
+
headers: { 'Content-Type': 'application/json' },
|
|
488
|
+
body: JSON.stringify({ id }),
|
|
489
|
+
});
|
|
490
|
+
const data = await res.json();
|
|
491
|
+
if (data.success && data.session) {
|
|
492
|
+
currentActiveChatId = data.session.id;
|
|
493
|
+
renderActiveSessionMessages(data.session.messages || []);
|
|
494
|
+
await loadChatSessions(false);
|
|
495
|
+
}
|
|
496
|
+
} catch (err) {
|
|
497
|
+
console.error('Failed to select chat session:', err);
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
async function clearCurrentChatContext() {
|
|
502
|
+
if (!confirm('Clear all conversation messages in this chat session?')) return;
|
|
503
|
+
const container = document.getElementById('chat-messages');
|
|
504
|
+
if (container) {
|
|
505
|
+
container.innerHTML = `
|
|
506
|
+
<div class="welcome-card" id="chat-welcome-card">
|
|
507
|
+
<h2>ANTRI Control Plane</h2>
|
|
508
|
+
<p>Minimalist environment for autonomous coding, architectural planning, and self-refinement. State, memory, profiles, and skills are synchronized across CLI and Desktop.</p>
|
|
509
|
+
<div class="quick-action-pills">
|
|
510
|
+
<button onclick="setPrompt('Plan the architecture for a real-time collaborative code editor')">Plan Editor Architecture</button>
|
|
511
|
+
<button onclick="setPrompt('/debate What are the trade-offs between WebSockets vs Server-Sent Events?')">Start Dialectic Debate</button>
|
|
512
|
+
<button onclick="setPrompt('/goal Implement a zero-dependency LRU cache with TTL in TypeScript')">Run Goal Loop</button>
|
|
513
|
+
</div>
|
|
514
|
+
</div>
|
|
515
|
+
`;
|
|
516
|
+
}
|
|
517
|
+
await fetch('/api/chat', {
|
|
518
|
+
method: 'POST',
|
|
519
|
+
headers: { 'Content-Type': 'application/json' },
|
|
520
|
+
body: JSON.stringify({ prompt: '/clear' }),
|
|
521
|
+
}).catch(() => {});
|
|
522
|
+
await loadChatSessions(false);
|
|
523
|
+
showToast('Chat context cleared.');
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
async function deleteCurrentChatSession() {
|
|
527
|
+
if (!currentActiveChatId) return;
|
|
528
|
+
if (!confirm('Are you sure you want to delete this chat session?')) return;
|
|
529
|
+
try {
|
|
530
|
+
const res = await fetch('/api/chats/delete', {
|
|
531
|
+
method: 'POST',
|
|
532
|
+
headers: { 'Content-Type': 'application/json' },
|
|
533
|
+
body: JSON.stringify({ id: currentActiveChatId }),
|
|
534
|
+
});
|
|
535
|
+
const data = await res.json();
|
|
536
|
+
if (data.success) {
|
|
537
|
+
await loadChatSessions(true);
|
|
538
|
+
showToast('Chat session deleted.');
|
|
539
|
+
}
|
|
540
|
+
} catch (err) {
|
|
541
|
+
console.error('Failed to delete chat session:', err);
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
|
|
404
545
|
// Chat Prompt Submission with Real-Time Word-by-Word Side-by-Side Streaming
|
|
405
546
|
async function submitPrompt() {
|
|
406
547
|
const input = document.getElementById('prompt-input');
|
|
@@ -419,6 +560,12 @@ async function submitPrompt() {
|
|
|
419
560
|
input.value = '';
|
|
420
561
|
hidePalette();
|
|
421
562
|
|
|
563
|
+
// Remove welcome card if present
|
|
564
|
+
const welcomeCard = document.getElementById('chat-welcome-card');
|
|
565
|
+
if (welcomeCard) {
|
|
566
|
+
welcomeCard.remove();
|
|
567
|
+
}
|
|
568
|
+
|
|
422
569
|
// Intercept /debate or /goal inside chat
|
|
423
570
|
if (prompt.startsWith('/debate')) {
|
|
424
571
|
showTab('dialectic');
|
|
@@ -502,6 +649,7 @@ async function submitPrompt() {
|
|
|
502
649
|
} finally {
|
|
503
650
|
sendBtn.disabled = false;
|
|
504
651
|
sendBtn.innerHTML = '<span>Send</span>';
|
|
652
|
+
await loadChatSessions(false);
|
|
505
653
|
}
|
|
506
654
|
}
|
|
507
655
|
|
|
@@ -640,13 +788,23 @@ async function startGoalLoop() {
|
|
|
640
788
|
}
|
|
641
789
|
|
|
642
790
|
// ==========================================
|
|
643
|
-
// Thinking Profile Management
|
|
791
|
+
// Thinking Profile & Notes Management
|
|
644
792
|
// ==========================================
|
|
793
|
+
let currentViewedProfile = 'profile_1';
|
|
794
|
+
let currentActiveProfile = 'profile_1';
|
|
795
|
+
let currentNotesScope = 'global';
|
|
796
|
+
let cachedGlobalNotes = '';
|
|
797
|
+
let cachedWorkspaceNotes = '';
|
|
798
|
+
|
|
645
799
|
async function loadProfiles() {
|
|
646
800
|
try {
|
|
647
801
|
const res = await fetch('/api/profiles');
|
|
648
802
|
const data = await res.json();
|
|
649
803
|
|
|
804
|
+
currentActiveProfile = data.activeName || 'profile_1';
|
|
805
|
+
cachedGlobalNotes = data.globalNotes || '';
|
|
806
|
+
cachedWorkspaceNotes = data.workspaceNotes || '';
|
|
807
|
+
|
|
650
808
|
const select = document.getElementById('select-profile');
|
|
651
809
|
const list = document.getElementById('profile-items-list');
|
|
652
810
|
select.innerHTML = '';
|
|
@@ -656,39 +814,156 @@ async function loadProfiles() {
|
|
|
656
814
|
// Dropdown option
|
|
657
815
|
const opt = document.createElement('option');
|
|
658
816
|
opt.value = p.name;
|
|
659
|
-
opt.textContent = p.name
|
|
660
|
-
if (p.name ===
|
|
817
|
+
opt.textContent = `${p.name}.md`;
|
|
818
|
+
if (p.name === currentActiveProfile) opt.selected = true;
|
|
661
819
|
select.appendChild(opt);
|
|
662
820
|
|
|
663
821
|
// List button
|
|
822
|
+
const isCurrentViewed = p.name === currentViewedProfile || (!currentViewedProfile && p.name === currentActiveProfile);
|
|
664
823
|
const btn = document.createElement('button');
|
|
665
|
-
btn.className = `profile-item-btn ${
|
|
824
|
+
btn.className = `profile-item-btn ${isCurrentViewed ? 'active' : ''}`;
|
|
666
825
|
btn.innerHTML = `
|
|
667
826
|
<div style="display:flex;justify-content:space-between;align-items:center;width:100%;">
|
|
668
827
|
<span style="font-weight:600;">${p.name}.md</span>
|
|
669
|
-
<span style="font-size:10px;opacity:0.
|
|
828
|
+
<span style="font-size:10px;opacity:0.8;display:flex;align-items:center;gap:4px;">
|
|
829
|
+
${p.name === currentActiveProfile ? '<span style="color:#10b981;font-weight:700;">● Active</span>' : ''}
|
|
830
|
+
<span>${p.notesCount || 0} notes</span>
|
|
831
|
+
</span>
|
|
670
832
|
</div>
|
|
671
833
|
`;
|
|
672
|
-
btn.onclick = () =>
|
|
834
|
+
btn.onclick = () => viewProfile(p.name);
|
|
673
835
|
list.appendChild(btn);
|
|
674
836
|
});
|
|
675
837
|
|
|
676
|
-
|
|
677
|
-
|
|
838
|
+
if (!currentViewedProfile || !data.profiles.some((p) => p.name === currentViewedProfile)) {
|
|
839
|
+
currentViewedProfile = currentActiveProfile;
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
await viewProfile(currentViewedProfile, false);
|
|
843
|
+
|
|
844
|
+
// Also populate notes editor
|
|
845
|
+
const notesEditor = document.getElementById('notes-editor');
|
|
846
|
+
if (notesEditor) {
|
|
847
|
+
notesEditor.value = currentNotesScope === 'workspace' ? cachedWorkspaceNotes : cachedGlobalNotes;
|
|
848
|
+
}
|
|
678
849
|
} catch (err) {
|
|
679
850
|
console.error('Failed to load profiles:', err);
|
|
680
851
|
}
|
|
681
852
|
}
|
|
682
853
|
|
|
854
|
+
async function viewProfile(name, updateListHighlight = true) {
|
|
855
|
+
try {
|
|
856
|
+
currentViewedProfile = name;
|
|
857
|
+
const res = await fetch(`/api/profile?name=${encodeURIComponent(name)}`);
|
|
858
|
+
const data = await res.json();
|
|
859
|
+
|
|
860
|
+
document.getElementById('active-profile-title').textContent = `${data.name}.md`;
|
|
861
|
+
const subtitle = document.getElementById('active-profile-subtitle');
|
|
862
|
+
const activateBtn = document.getElementById('btn-set-active-profile');
|
|
863
|
+
|
|
864
|
+
if (data.name === currentActiveProfile) {
|
|
865
|
+
if (subtitle) subtitle.textContent = 'Active thinking profile in LLM context';
|
|
866
|
+
if (activateBtn) activateBtn.style.display = 'none';
|
|
867
|
+
} else {
|
|
868
|
+
if (subtitle) subtitle.textContent = 'Viewing profile instructions (Click "Set as Active" to apply)';
|
|
869
|
+
if (activateBtn) activateBtn.style.display = 'inline-block';
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
document.getElementById('profile-editor').value = data.content || '';
|
|
873
|
+
|
|
874
|
+
if (updateListHighlight) {
|
|
875
|
+
document.querySelectorAll('#profile-items-list .profile-item-btn').forEach((btn, idx) => {
|
|
876
|
+
const text = btn.textContent || '';
|
|
877
|
+
if (text.includes(`${name}.md`)) {
|
|
878
|
+
btn.classList.add('active');
|
|
879
|
+
} else {
|
|
880
|
+
btn.classList.remove('active');
|
|
881
|
+
}
|
|
882
|
+
});
|
|
883
|
+
}
|
|
884
|
+
} catch (err) {
|
|
885
|
+
console.error('Failed to view profile:', err);
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
async function activateViewedProfile() {
|
|
890
|
+
if (!currentViewedProfile) return;
|
|
891
|
+
await selectProfile(currentViewedProfile);
|
|
892
|
+
showToast(`Active profile switched to '${currentViewedProfile}.md'`);
|
|
893
|
+
}
|
|
894
|
+
|
|
683
895
|
async function selectProfile(name) {
|
|
684
896
|
await fetch('/api/profile/select', {
|
|
685
897
|
method: 'POST',
|
|
686
898
|
headers: { 'Content-Type': 'application/json' },
|
|
687
899
|
body: JSON.stringify({ name }),
|
|
688
900
|
});
|
|
901
|
+
currentActiveProfile = name;
|
|
902
|
+
currentViewedProfile = name;
|
|
689
903
|
await loadProfiles();
|
|
690
904
|
}
|
|
691
905
|
|
|
906
|
+
function switchProfileSubtab(subtab) {
|
|
907
|
+
const btnProfiles = document.getElementById('btn-subtab-profiles');
|
|
908
|
+
const btnNotes = document.getElementById('btn-subtab-notes');
|
|
909
|
+
const viewProfiles = document.getElementById('subtab-view-profiles');
|
|
910
|
+
const viewNotes = document.getElementById('subtab-view-notes');
|
|
911
|
+
|
|
912
|
+
if (subtab === 'profiles') {
|
|
913
|
+
btnProfiles.classList.add('active');
|
|
914
|
+
btnNotes.classList.remove('active');
|
|
915
|
+
viewProfiles.classList.remove('hidden');
|
|
916
|
+
viewNotes.classList.add('hidden');
|
|
917
|
+
} else {
|
|
918
|
+
btnNotes.classList.add('active');
|
|
919
|
+
btnProfiles.classList.remove('active');
|
|
920
|
+
viewNotes.classList.remove('hidden');
|
|
921
|
+
viewProfiles.classList.add('hidden');
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
|
|
925
|
+
function selectNotesScope(scope) {
|
|
926
|
+
currentNotesScope = scope;
|
|
927
|
+
const btnGlobal = document.getElementById('btn-note-global');
|
|
928
|
+
const btnWorkspace = document.getElementById('btn-note-workspace');
|
|
929
|
+
const title = document.getElementById('notes-scope-title');
|
|
930
|
+
const editor = document.getElementById('notes-editor');
|
|
931
|
+
|
|
932
|
+
if (scope === 'workspace') {
|
|
933
|
+
btnWorkspace.classList.add('active');
|
|
934
|
+
btnGlobal.classList.remove('active');
|
|
935
|
+
if (title) title.textContent = 'Workspace Local Notes (.antri/profiles/notes.md)';
|
|
936
|
+
if (editor) editor.value = cachedWorkspaceNotes;
|
|
937
|
+
} else {
|
|
938
|
+
btnGlobal.classList.add('active');
|
|
939
|
+
btnWorkspace.classList.remove('active');
|
|
940
|
+
if (title) title.textContent = 'Global User Notes (~/.antri/profiles/notes.md)';
|
|
941
|
+
if (editor) editor.value = cachedGlobalNotes;
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
|
|
945
|
+
async function saveNotesContent() {
|
|
946
|
+
const content = document.getElementById('notes-editor').value;
|
|
947
|
+
try {
|
|
948
|
+
const res = await fetch('/api/profile/notes/save', {
|
|
949
|
+
method: 'POST',
|
|
950
|
+
headers: { 'Content-Type': 'application/json' },
|
|
951
|
+
body: JSON.stringify({ type: currentNotesScope, content }),
|
|
952
|
+
});
|
|
953
|
+
const data = await res.json();
|
|
954
|
+
if (data.success) {
|
|
955
|
+
if (currentNotesScope === 'workspace') {
|
|
956
|
+
cachedWorkspaceNotes = content;
|
|
957
|
+
} else {
|
|
958
|
+
cachedGlobalNotes = content;
|
|
959
|
+
}
|
|
960
|
+
showToast(`Notes (${currentNotesScope}) saved & synced.`);
|
|
961
|
+
}
|
|
962
|
+
} catch (err) {
|
|
963
|
+
showToast(`Failed to save notes: ${err.message}`, true);
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
|
|
692
967
|
async function createProfile() {
|
|
693
968
|
const input = document.getElementById('new-profile-name');
|
|
694
969
|
const name = input.value.trim();
|
|
@@ -701,6 +976,7 @@ async function createProfile() {
|
|
|
701
976
|
});
|
|
702
977
|
|
|
703
978
|
input.value = '';
|
|
979
|
+
currentViewedProfile = name.toLowerCase().replace(/[^a-z0-9_-]/g, '_');
|
|
704
980
|
await loadProfiles();
|
|
705
981
|
showToast(`Profile '${name}' created.`);
|
|
706
982
|
}
|
|
@@ -720,6 +996,7 @@ async function handleProfileImport(event) {
|
|
|
720
996
|
});
|
|
721
997
|
const data = await res.json();
|
|
722
998
|
if (data.success) {
|
|
999
|
+
currentViewedProfile = file.name.replace(/\.md$/, '');
|
|
723
1000
|
await loadProfiles();
|
|
724
1001
|
showToast(`Profile '${file.name}' imported successfully.`);
|
|
725
1002
|
}
|
|
@@ -734,9 +1011,9 @@ async function saveActiveProfile() {
|
|
|
734
1011
|
await fetch('/api/profile/save', {
|
|
735
1012
|
method: 'POST',
|
|
736
1013
|
headers: { 'Content-Type': 'application/json' },
|
|
737
|
-
body: JSON.stringify({ content }),
|
|
1014
|
+
body: JSON.stringify({ name: currentViewedProfile, content }),
|
|
738
1015
|
});
|
|
739
|
-
showToast(
|
|
1016
|
+
showToast(`Profile '${currentViewedProfile}.md' saved successfully.`);
|
|
740
1017
|
}
|
|
741
1018
|
|
|
742
1019
|
async function pushProfilesToCloud() {
|
|
@@ -773,7 +1050,7 @@ async function pullProfilesFromCloud() {
|
|
|
773
1050
|
}
|
|
774
1051
|
|
|
775
1052
|
function exportActiveProfile() {
|
|
776
|
-
const activeTitle = document.getElementById('active-profile-title').textContent ||
|
|
1053
|
+
const activeTitle = document.getElementById('active-profile-title').textContent || `${currentViewedProfile}.md`;
|
|
777
1054
|
const content = document.getElementById('profile-editor').value;
|
|
778
1055
|
const blob = new Blob([content], { type: 'text/markdown;charset=utf-8;' });
|
|
779
1056
|
const url = URL.createObjectURL(blob);
|
|
@@ -786,22 +1063,23 @@ function exportActiveProfile() {
|
|
|
786
1063
|
}
|
|
787
1064
|
|
|
788
1065
|
async function deleteActiveProfile() {
|
|
789
|
-
const
|
|
790
|
-
if (
|
|
1066
|
+
const target = currentViewedProfile || 'profile_1';
|
|
1067
|
+
if (target === 'profile_1') {
|
|
791
1068
|
alert('Cannot delete default profile_1.');
|
|
792
1069
|
return;
|
|
793
1070
|
}
|
|
794
|
-
if (!confirm(`Are you sure you want to delete profile '${
|
|
1071
|
+
if (!confirm(`Are you sure you want to delete profile '${target}.md' from both your device and the cloud? It will not be pulled again.`)) return;
|
|
795
1072
|
|
|
796
1073
|
const res = await fetch('/api/profile/delete', {
|
|
797
1074
|
method: 'POST',
|
|
798
1075
|
headers: { 'Content-Type': 'application/json' },
|
|
799
|
-
body: JSON.stringify({ name:
|
|
1076
|
+
body: JSON.stringify({ name: target }),
|
|
800
1077
|
});
|
|
801
1078
|
const data = await res.json();
|
|
802
1079
|
if (data.success) {
|
|
1080
|
+
currentViewedProfile = 'profile_1';
|
|
803
1081
|
await loadProfiles();
|
|
804
|
-
showToast(`Profile '${
|
|
1082
|
+
showToast(`Profile '${target}.md' deleted from device and cloud.`);
|
|
805
1083
|
}
|
|
806
1084
|
}
|
|
807
1085
|
|