total-recall-brain 3.1.2 → 3.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/extension/background.js +99 -0
- package/extension/content-script.css +16 -0
- package/extension/content-script.js +134 -0
- package/extension/icons/README.md +27 -0
- package/extension/icons/icon-128.png +0 -0
- package/extension/icons/icon-16.png +0 -0
- package/extension/icons/icon-48.png +0 -0
- package/extension/lib/brain-client.js +47 -0
- package/extension/manifest.json +39 -0
- package/extension/options/options.css +218 -0
- package/extension/options/options.html +72 -0
- package/extension/options/options.js +99 -0
- package/extension/popup/popup.css +221 -0
- package/extension/popup/popup.html +44 -0
- package/extension/popup/popup.js +107 -0
- package/extension/sidepanel/sidepanel.css +340 -0
- package/extension/sidepanel/sidepanel.html +38 -0
- package/extension/sidepanel/sidepanel.js +229 -0
- package/frontend/README.md +73 -0
- package/frontend/index.html +17 -0
- package/frontend/package.json +31 -0
- package/frontend/src/App.css +2 -0
- package/frontend/src/App.tsx +390 -0
- package/frontend/src/api.ts +688 -0
- package/frontend/src/assets/hero.png +0 -0
- package/frontend/src/assets/react.svg +1 -0
- package/frontend/src/assets/vite.svg +1 -0
- package/frontend/src/components/BrainSelector.tsx +272 -0
- package/frontend/src/components/DaemonLogsTab.tsx +54 -0
- package/frontend/src/components/Graph3D.tsx +850 -0
- package/frontend/src/components/MarkdownUtils.tsx +99 -0
- package/frontend/src/components/ResearchAgendaTab.tsx +707 -0
- package/frontend/src/components/TaskQueueTab.tsx +185 -0
- package/frontend/src/index.css +1589 -0
- package/frontend/src/main.tsx +10 -0
- package/frontend/src/pages/ApiKeysPage.tsx +288 -0
- package/frontend/src/pages/AutomationsPage.tsx +354 -0
- package/frontend/src/pages/ChatPage.tsx +806 -0
- package/frontend/src/pages/DeploymentsPage.tsx +275 -0
- package/frontend/src/pages/FilesPage.tsx +384 -0
- package/frontend/src/pages/HealthPage.tsx +91 -0
- package/frontend/src/pages/IntegrationsPage.tsx +382 -0
- package/frontend/src/pages/LoginPage.tsx +237 -0
- package/frontend/src/pages/MemoryPage.tsx +1192 -0
- package/frontend/src/pages/SandboxPage.tsx +113 -0
- package/frontend/src/pages/SettingsPage.tsx +662 -0
- package/frontend/src/pages/SkillsPage.tsx +618 -0
- package/frontend/src/pages/TasksPage.tsx +392 -0
- package/frontend/src/pages/UsagePage.tsx +260 -0
- package/frontend/src/types.ts +163 -0
- package/frontend/tsconfig.app.json +25 -0
- package/frontend/tsconfig.json +7 -0
- package/frontend/tsconfig.node.json +24 -0
- package/frontend/vite.config.ts +24 -0
- package/package.json +8 -1
- package/scaffold/.agent/skills/total-recall/memory-vault/concepts/cli-help-reference.md +35 -8
- package/scaffold/.agent/skills/total-recall/memory-vault/invariants/operating-instructions.md +13 -8
- package/scaffold/.agent/skills/total-recall/memory-vault/preferences/always-websearch-gap.md +28 -1
- package/scaffold/.agent/skills/total-recall/memory-vault/preferences/topic-research-sop.md +28 -1
- package/scaffold/.agent/skills/total-recall/skills/ssss/references/ssss-spec.md +830 -81
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
// Total Recall — Options Page Logic
|
|
2
|
+
|
|
3
|
+
(function () {
|
|
4
|
+
'use strict';
|
|
5
|
+
|
|
6
|
+
const brainUrlInput = document.getElementById('brain-url');
|
|
7
|
+
const patInput = document.getElementById('pat-token');
|
|
8
|
+
const btnTest = document.getElementById('btn-test');
|
|
9
|
+
const connectionStatus = document.getElementById('connection-status');
|
|
10
|
+
const blocklistTextarea = document.getElementById('blocklist');
|
|
11
|
+
const btnSave = document.getElementById('btn-save');
|
|
12
|
+
const saveStatus = document.getElementById('save-status');
|
|
13
|
+
|
|
14
|
+
// ---- Load saved settings ----
|
|
15
|
+
async function loadSettings() {
|
|
16
|
+
const settings = await chrome.storage.sync.get([
|
|
17
|
+
'brainUrl',
|
|
18
|
+
'pat',
|
|
19
|
+
'blocklist',
|
|
20
|
+
'captureGranularity'
|
|
21
|
+
]);
|
|
22
|
+
|
|
23
|
+
brainUrlInput.value = settings.brainUrl || 'http://127.0.0.1:3000';
|
|
24
|
+
patInput.value = settings.pat || '';
|
|
25
|
+
|
|
26
|
+
// Blocklist: stored as array, displayed one per line
|
|
27
|
+
if (Array.isArray(settings.blocklist)) {
|
|
28
|
+
blocklistTextarea.value = settings.blocklist.join('\n');
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Granularity radio
|
|
32
|
+
const granularity = settings.captureGranularity || 'minimal';
|
|
33
|
+
const radio = document.querySelector(`input[name="granularity"][value="${granularity}"]`);
|
|
34
|
+
if (radio) radio.checked = true;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// ---- Save settings ----
|
|
38
|
+
btnSave.addEventListener('click', async () => {
|
|
39
|
+
try {
|
|
40
|
+
// Parse blocklist textarea into array
|
|
41
|
+
const blocklistRaw = blocklistTextarea.value.trim();
|
|
42
|
+
const blocklist = blocklistRaw
|
|
43
|
+
? blocklistRaw.split('\n').map((d) => d.trim()).filter(Boolean)
|
|
44
|
+
: [];
|
|
45
|
+
|
|
46
|
+
// Get selected granularity
|
|
47
|
+
const granularityRadio = document.querySelector('input[name="granularity"]:checked');
|
|
48
|
+
const captureGranularity = granularityRadio ? granularityRadio.value : 'minimal';
|
|
49
|
+
|
|
50
|
+
await chrome.storage.sync.set({
|
|
51
|
+
brainUrl: brainUrlInput.value.trim() || 'http://127.0.0.1:3000',
|
|
52
|
+
pat: patInput.value.trim(),
|
|
53
|
+
blocklist,
|
|
54
|
+
captureGranularity
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
showSaveStatus('Settings saved!', false);
|
|
58
|
+
} catch (err) {
|
|
59
|
+
showSaveStatus('Error: ' + err.message, true);
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
// ---- Test connection ----
|
|
64
|
+
btnTest.addEventListener('click', async () => {
|
|
65
|
+
connectionStatus.textContent = 'Testing…';
|
|
66
|
+
connectionStatus.className = 'connection-status';
|
|
67
|
+
|
|
68
|
+
try {
|
|
69
|
+
// Temporarily apply the input values for the test
|
|
70
|
+
const testUrl = brainUrlInput.value.trim() || 'http://127.0.0.1:3000';
|
|
71
|
+
const testPat = patInput.value.trim();
|
|
72
|
+
|
|
73
|
+
const headers = { 'Content-Type': 'application/json' };
|
|
74
|
+
if (testPat) headers['Authorization'] = `Bearer ${testPat}`;
|
|
75
|
+
|
|
76
|
+
const res = await fetch(`${testUrl}/health`, { headers });
|
|
77
|
+
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
78
|
+
|
|
79
|
+
connectionStatus.textContent = '✓ Connected';
|
|
80
|
+
connectionStatus.className = 'connection-status ok';
|
|
81
|
+
} catch (err) {
|
|
82
|
+
connectionStatus.textContent = `✗ ${err.message}`;
|
|
83
|
+
connectionStatus.className = 'connection-status fail';
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
// ---- Save status indicator ----
|
|
88
|
+
function showSaveStatus(message, isError) {
|
|
89
|
+
saveStatus.textContent = message;
|
|
90
|
+
saveStatus.className = `save-status ${isError ? 'error' : 'saved'}`;
|
|
91
|
+
setTimeout(() => {
|
|
92
|
+
saveStatus.textContent = '';
|
|
93
|
+
saveStatus.className = 'save-status';
|
|
94
|
+
}, 3000);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// ---- Init ----
|
|
98
|
+
loadSettings();
|
|
99
|
+
})();
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
/* Total Recall — Popup Styles (Catppuccin Mocha) */
|
|
2
|
+
|
|
3
|
+
* {
|
|
4
|
+
margin: 0;
|
|
5
|
+
padding: 0;
|
|
6
|
+
box-sizing: border-box;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
body {
|
|
10
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
11
|
+
background: #11111b;
|
|
12
|
+
color: #cdd6f4;
|
|
13
|
+
width: 350px;
|
|
14
|
+
overflow: hidden;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.popup {
|
|
18
|
+
padding: 16px;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/* ---- Header ---- */
|
|
22
|
+
.popup-header {
|
|
23
|
+
display: flex;
|
|
24
|
+
align-items: center;
|
|
25
|
+
justify-content: space-between;
|
|
26
|
+
margin-bottom: 16px;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.popup-header h1 {
|
|
30
|
+
font-size: 15px;
|
|
31
|
+
font-weight: 700;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.dot {
|
|
35
|
+
width: 10px;
|
|
36
|
+
height: 10px;
|
|
37
|
+
border-radius: 50%;
|
|
38
|
+
background: #6c7086;
|
|
39
|
+
transition: background 0.3s ease;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.dot.connected {
|
|
43
|
+
background: #a6e3a1;
|
|
44
|
+
box-shadow: 0 0 6px #a6e3a1;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.dot.disconnected {
|
|
48
|
+
background: #f38ba8;
|
|
49
|
+
box-shadow: 0 0 6px #f38ba8;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/* ---- Fields ---- */
|
|
53
|
+
.field-group {
|
|
54
|
+
margin-bottom: 12px;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.field-group label {
|
|
58
|
+
display: block;
|
|
59
|
+
font-size: 11px;
|
|
60
|
+
font-weight: 600;
|
|
61
|
+
text-transform: uppercase;
|
|
62
|
+
letter-spacing: 0.5px;
|
|
63
|
+
color: #585b70;
|
|
64
|
+
margin-bottom: 6px;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
.field-group input,
|
|
68
|
+
.field-group textarea {
|
|
69
|
+
width: 100%;
|
|
70
|
+
padding: 8px 12px;
|
|
71
|
+
background: #1e1e2e;
|
|
72
|
+
border: 1px solid #313244;
|
|
73
|
+
border-radius: 8px;
|
|
74
|
+
color: #cdd6f4;
|
|
75
|
+
font-size: 13px;
|
|
76
|
+
outline: none;
|
|
77
|
+
font-family: inherit;
|
|
78
|
+
transition: border-color 0.2s ease;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.field-group input:focus,
|
|
82
|
+
.field-group textarea:focus {
|
|
83
|
+
border-color: #89b4fa;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
.field-group textarea {
|
|
87
|
+
resize: vertical;
|
|
88
|
+
min-height: 60px;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
.field-group input[readonly] {
|
|
92
|
+
color: #a6adc8;
|
|
93
|
+
cursor: default;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/* ---- Buttons ---- */
|
|
97
|
+
.button-row {
|
|
98
|
+
display: flex;
|
|
99
|
+
gap: 8px;
|
|
100
|
+
margin-bottom: 12px;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
.btn {
|
|
104
|
+
flex: 1;
|
|
105
|
+
padding: 9px 10px;
|
|
106
|
+
font-size: 13px;
|
|
107
|
+
font-weight: 600;
|
|
108
|
+
border: 1px solid #313244;
|
|
109
|
+
border-radius: 10px;
|
|
110
|
+
cursor: pointer;
|
|
111
|
+
transition: background 0.2s ease, border-color 0.2s ease, transform 0.1s ease;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
.btn:active {
|
|
115
|
+
transform: scale(0.97);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
.btn.primary {
|
|
119
|
+
background: #89b4fa;
|
|
120
|
+
color: #11111b;
|
|
121
|
+
border-color: #89b4fa;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
.btn.primary:hover {
|
|
125
|
+
background: #74c7ec;
|
|
126
|
+
border-color: #74c7ec;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
.btn.secondary {
|
|
130
|
+
background: #1e1e2e;
|
|
131
|
+
color: #cdd6f4;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
.btn.secondary:hover {
|
|
135
|
+
background: #313244;
|
|
136
|
+
border-color: #89b4fa;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/* ---- Toggle ---- */
|
|
140
|
+
.toggle-row {
|
|
141
|
+
display: flex;
|
|
142
|
+
align-items: center;
|
|
143
|
+
justify-content: space-between;
|
|
144
|
+
padding: 10px 0;
|
|
145
|
+
border-top: 1px solid #313244;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
.toggle-label {
|
|
149
|
+
font-size: 12px;
|
|
150
|
+
color: #a6adc8;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
.switch {
|
|
154
|
+
position: relative;
|
|
155
|
+
display: inline-block;
|
|
156
|
+
width: 36px;
|
|
157
|
+
height: 20px;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
.switch input {
|
|
161
|
+
opacity: 0;
|
|
162
|
+
width: 0;
|
|
163
|
+
height: 0;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
.slider {
|
|
167
|
+
position: absolute;
|
|
168
|
+
cursor: pointer;
|
|
169
|
+
inset: 0;
|
|
170
|
+
background: #313244;
|
|
171
|
+
border-radius: 20px;
|
|
172
|
+
transition: background 0.3s ease;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
.slider::before {
|
|
176
|
+
content: '';
|
|
177
|
+
position: absolute;
|
|
178
|
+
height: 14px;
|
|
179
|
+
width: 14px;
|
|
180
|
+
left: 3px;
|
|
181
|
+
bottom: 3px;
|
|
182
|
+
background: #6c7086;
|
|
183
|
+
border-radius: 50%;
|
|
184
|
+
transition: transform 0.3s ease, background 0.3s ease;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
.switch input:checked + .slider {
|
|
188
|
+
background: rgba(137, 180, 250, 0.3);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
.switch input:checked + .slider::before {
|
|
192
|
+
transform: translateX(16px);
|
|
193
|
+
background: #89b4fa;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/* ---- Status Message ---- */
|
|
197
|
+
.status-msg {
|
|
198
|
+
margin-top: 10px;
|
|
199
|
+
padding: 8px 12px;
|
|
200
|
+
border-radius: 8px;
|
|
201
|
+
font-size: 12px;
|
|
202
|
+
font-weight: 600;
|
|
203
|
+
text-align: center;
|
|
204
|
+
transition: opacity 0.3s ease;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
.status-msg.success {
|
|
208
|
+
background: rgba(166, 227, 161, 0.1);
|
|
209
|
+
color: #a6e3a1;
|
|
210
|
+
border: 1px solid rgba(166, 227, 161, 0.3);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
.status-msg.error {
|
|
214
|
+
background: rgba(243, 139, 168, 0.1);
|
|
215
|
+
color: #f38ba8;
|
|
216
|
+
border: 1px solid rgba(243, 139, 168, 0.3);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
.hidden {
|
|
220
|
+
display: none !important;
|
|
221
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<title>Total Recall</title>
|
|
6
|
+
<link rel="stylesheet" href="popup.css">
|
|
7
|
+
</head>
|
|
8
|
+
<body>
|
|
9
|
+
<div class="popup">
|
|
10
|
+
<header class="popup-header">
|
|
11
|
+
<h1>🧠Total Recall</h1>
|
|
12
|
+
<div id="connection-dot" class="dot"></div>
|
|
13
|
+
</header>
|
|
14
|
+
|
|
15
|
+
<div class="field-group">
|
|
16
|
+
<label for="url-input">Page URL</label>
|
|
17
|
+
<input type="text" id="url-input" readonly />
|
|
18
|
+
</div>
|
|
19
|
+
|
|
20
|
+
<div class="field-group">
|
|
21
|
+
<label for="note-input">Quick Note</label>
|
|
22
|
+
<textarea id="note-input" placeholder="Add a note about this page…" rows="3"></textarea>
|
|
23
|
+
</div>
|
|
24
|
+
|
|
25
|
+
<div class="button-row">
|
|
26
|
+
<button id="btn-remember" class="btn primary">📌 Remember</button>
|
|
27
|
+
<button id="btn-research" class="btn secondary">🔬 Research</button>
|
|
28
|
+
</div>
|
|
29
|
+
|
|
30
|
+
<div class="toggle-row">
|
|
31
|
+
<span class="toggle-label">Passive tracking</span>
|
|
32
|
+
<label class="switch">
|
|
33
|
+
<input type="checkbox" id="tracking-toggle" />
|
|
34
|
+
<span class="slider"></span>
|
|
35
|
+
</label>
|
|
36
|
+
</div>
|
|
37
|
+
|
|
38
|
+
<div id="status-msg" class="status-msg hidden"></div>
|
|
39
|
+
</div>
|
|
40
|
+
|
|
41
|
+
<script src="../lib/brain-client.js"></script>
|
|
42
|
+
<script src="popup.js"></script>
|
|
43
|
+
</body>
|
|
44
|
+
</html>
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
// Total Recall — Popup Logic
|
|
2
|
+
|
|
3
|
+
(function () {
|
|
4
|
+
'use strict';
|
|
5
|
+
|
|
6
|
+
const dot = document.getElementById('connection-dot');
|
|
7
|
+
const urlInput = document.getElementById('url-input');
|
|
8
|
+
const noteInput = document.getElementById('note-input');
|
|
9
|
+
const btnRemember = document.getElementById('btn-remember');
|
|
10
|
+
const btnResearch = document.getElementById('btn-research');
|
|
11
|
+
const trackingToggle = document.getElementById('tracking-toggle');
|
|
12
|
+
const statusMsg = document.getElementById('status-msg');
|
|
13
|
+
|
|
14
|
+
// ---- Auto-fill current tab URL ----
|
|
15
|
+
async function fillCurrentTab() {
|
|
16
|
+
try {
|
|
17
|
+
const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
|
|
18
|
+
if (tab) urlInput.value = tab.url || '';
|
|
19
|
+
} catch {
|
|
20
|
+
// ignore — might not have activeTab permission yet
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// ---- Connection check ----
|
|
25
|
+
async function checkConnection() {
|
|
26
|
+
try {
|
|
27
|
+
await self.BrainClient.healthCheck();
|
|
28
|
+
dot.className = 'dot connected';
|
|
29
|
+
} catch {
|
|
30
|
+
dot.className = 'dot disconnected';
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// ---- Send share message to background ----
|
|
35
|
+
function sendShare(data) {
|
|
36
|
+
return new Promise((resolve, reject) => {
|
|
37
|
+
chrome.runtime.sendMessage({ type: 'SHARE', data }, (res) => {
|
|
38
|
+
if (chrome.runtime.lastError) return reject(new Error(chrome.runtime.lastError.message));
|
|
39
|
+
if (res && res.success) return resolve(res);
|
|
40
|
+
reject(new Error(res?.error || 'Unknown error'));
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// ---- Show status ----
|
|
46
|
+
function showStatus(message, isError = false) {
|
|
47
|
+
statusMsg.textContent = message;
|
|
48
|
+
statusMsg.className = `status-msg ${isError ? 'error' : 'success'}`;
|
|
49
|
+
statusMsg.classList.remove('hidden');
|
|
50
|
+
setTimeout(() => statusMsg.classList.add('hidden'), 3000);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// ---- Remember ----
|
|
54
|
+
btnRemember.addEventListener('click', async () => {
|
|
55
|
+
try {
|
|
56
|
+
const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
|
|
57
|
+
const payload = {
|
|
58
|
+
url: tab?.url || urlInput.value,
|
|
59
|
+
title: tab?.title || '',
|
|
60
|
+
action: 'remember',
|
|
61
|
+
source: 'chrome-extension-popup'
|
|
62
|
+
};
|
|
63
|
+
const note = noteInput.value.trim();
|
|
64
|
+
if (note) payload.excerpt = note;
|
|
65
|
+
await sendShare(payload);
|
|
66
|
+
showStatus('Page remembered!');
|
|
67
|
+
noteInput.value = '';
|
|
68
|
+
} catch (err) {
|
|
69
|
+
showStatus('Error: ' + err.message, true);
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
// ---- Research ----
|
|
74
|
+
btnResearch.addEventListener('click', async () => {
|
|
75
|
+
try {
|
|
76
|
+
const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
|
|
77
|
+
const payload = {
|
|
78
|
+
url: tab?.url || urlInput.value,
|
|
79
|
+
title: tab?.title || '',
|
|
80
|
+
action: 'research',
|
|
81
|
+
source: 'chrome-extension-popup'
|
|
82
|
+
};
|
|
83
|
+
const note = noteInput.value.trim();
|
|
84
|
+
if (note) payload.excerpt = note;
|
|
85
|
+
await sendShare(payload);
|
|
86
|
+
showStatus('Research queued!');
|
|
87
|
+
noteInput.value = '';
|
|
88
|
+
} catch (err) {
|
|
89
|
+
showStatus('Error: ' + err.message, true);
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
// ---- Tracking toggle ----
|
|
94
|
+
async function loadTrackingPref() {
|
|
95
|
+
const { passiveTracking } = await chrome.storage.sync.get('passiveTracking');
|
|
96
|
+
trackingToggle.checked = passiveTracking || false;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
trackingToggle.addEventListener('change', () => {
|
|
100
|
+
chrome.storage.sync.set({ passiveTracking: trackingToggle.checked });
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
// ---- Init ----
|
|
104
|
+
fillCurrentTab();
|
|
105
|
+
checkConnection();
|
|
106
|
+
loadTrackingPref();
|
|
107
|
+
})();
|