total-recall-brain 3.28.2 → 3.30.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/bin/total-recall.mjs +24 -1
- package/docs/ARCHITECTURE.md +26 -18
- package/extension/background.js +259 -84
- package/extension/content-script.css +7 -13
- package/extension/content-script.js +252 -477
- package/extension/icons/icon-128.png +0 -0
- package/extension/icons/icon-16.png +0 -0
- package/extension/icons/icon-32.png +0 -0
- package/extension/icons/icon-48.png +0 -0
- package/extension/icons/mark.svg +6 -0
- package/extension/lib/brain-client.js +160 -73
- package/extension/lib/tokens.css +187 -0
- package/extension/lib/ui-helpers.js +229 -0
- package/extension/lib/ui-helpers.spec.mjs +94 -0
- package/extension/manifest.json +20 -5
- package/extension/options/options.css +64 -218
- package/extension/options/options.html +91 -43
- package/extension/options/options.js +208 -85
- package/extension/sidepanel/sidepanel.css +268 -970
- package/extension/sidepanel/sidepanel.html +200 -166
- package/extension/sidepanel/sidepanel.js +788 -942
- package/frontend/dist/assets/index-DGMNZi_1.js +5742 -0
- package/frontend/dist/index.html +1 -1
- package/frontend/src/api/plugins.ts +124 -92
- package/frontend/src/components/ResearchAgendaTab.spec.tsx +11 -0
- package/frontend/src/components/ResearchAgendaTab.tsx +19 -14
- package/frontend/src/pages/PluginsPage.spec.tsx +87 -16
- package/frontend/src/pages/PluginsPage.tsx +454 -1218
- package/package.json +2 -1
- package/plugins/code-quality/README.md +25 -0
- package/plugins/code-quality/cli.mjs +25 -0
- package/plugins/code-quality/plugin.json +17 -0
- package/plugins/git-sentinel/README.md +27 -0
- package/{scaffold/.agent/plugins → plugins}/git-sentinel/cli.mjs +8 -5
- package/plugins/git-sentinel/plugin.json +18 -0
- package/plugins/system-monitor/README.md +26 -0
- package/{scaffold/.agent/plugins → plugins}/system-monitor/cli.mjs +5 -4
- package/{scaffold/.agent/plugins → plugins}/system-monitor/generator.mjs +4 -5
- package/plugins/system-monitor/plugin.json +29 -0
- package/scaffold/.agent/skills/meta-harness/SKILL.md +4 -4
- package/scaffold/.agent/skills/plugins/SKILL.md +53 -0
- package/scaffold/.agent/skills/total-recall/scripts/brain-state.json +1 -0
- package/src/cli/agent.mjs +1 -1
- package/src/cli/app/index.mjs +493 -0
- package/src/cli/brain.mjs +33 -3
- package/src/cli/command.mjs +80 -41
- package/src/cli/harness.mjs +1 -1
- package/src/cli/init.mjs +3 -1
- package/src/cli/mesh.mjs +118 -12
- package/src/cli/plugin/create.mjs +144 -25
- package/src/cli/plugin/index.mjs +83 -53
- package/src/cli/plugin/install.mjs +10 -127
- package/src/cli/plugin/list.mjs +44 -44
- package/src/cli/plugin/remove.mjs +23 -30
- package/src/cli/plugin/scaffold-cmd.mjs +30 -0
- package/src/cli/plugin/search.mjs +108 -23
- package/src/cli/rebuild.mjs +37 -1
- package/src/cli/recall.mjs +17 -2
- package/src/cli/research.mjs +4 -6
- package/src/cli/share.mjs +2 -0
- package/src/core/app-deploy/apply.mjs +386 -0
- package/src/core/app-deploy/design-tokens.mjs +217 -0
- package/src/core/app-deploy/plan.mjs +249 -0
- package/src/core/app-deploy/resolve.mjs +295 -0
- package/src/core/app-deploy/source.mjs +337 -0
- package/src/core/app-deploy/standalone.mjs +248 -0
- package/src/core/app-deploy/upgrade.mjs +256 -0
- package/src/core/app-deploy/verify.mjs +213 -0
- package/src/core/bound-hosts.mjs +1 -1
- package/src/core/brain-registry.mjs +146 -0
- package/src/core/brain-state.json +1 -0
- package/src/core/daemon-loop.mjs +30 -26
- package/src/core/dream.mjs +5 -17
- package/src/core/fact-seeker.mjs +53 -368
- package/src/core/lan-discovery.mjs +3 -3
- package/src/core/mesh-access-sync.mjs +134 -0
- package/src/core/mesh-access.mjs +18 -3
- package/src/core/mesh-discover.mjs +173 -0
- package/src/core/mesh.mjs +131 -32
- package/src/core/optimizer.mjs +4 -41
- package/src/core/plugin-bundle.mjs +167 -0
- package/src/core/plugin-context.mjs +8 -13
- package/src/core/plugin-loader.mjs +272 -68
- package/src/core/plugin-peers.mjs +163 -0
- package/src/core/plugin-public.mjs +85 -0
- package/src/core/plugin-runner-child.mjs +24 -0
- package/src/core/plugin-runner.mjs +102 -0
- package/src/core/plugin-store.mjs +384 -0
- package/src/core/plugin-tasks.mjs +212 -0
- package/src/core/post-mortem.mjs +40 -9
- package/src/core/research-gate.mjs +182 -0
- package/src/core/research-queue.mjs +47 -7
- package/src/core/research-surface.mjs +211 -0
- package/src/core/research.mjs +25 -32
- package/src/core/scheduler.mjs +67 -153
- package/src/core/schema.mjs +129 -0
- package/src/core/search.mjs +12 -1
- package/src/core/secret-integration-research.mjs +12 -8
- package/src/core/secrets-store.mjs +32 -2
- package/src/core/session-watcher.mjs +6 -0
- package/src/core/surface.mjs +64 -3
- package/src/core/task-executors.mjs +8 -45
- package/src/core/vault-cache.mjs +70 -4
- package/src/server/api.mjs +21 -0
- package/src/server/rest.mjs +2 -0
- package/src/server/route-manifest.json +21 -5
- package/src/server/routes/collab.mjs +38 -11
- package/src/server/routes/extension.mjs +3 -2
- package/src/server/routes/memory.mjs +24 -2
- package/src/server/routes/mesh.mjs +7 -7
- package/src/server/routes/plugins-mesh.mjs +49 -0
- package/src/server/routes/plugins.mjs +91 -426
- package/src/server/routes/research.mjs +5 -2
- package/src/server/routes/share.mjs +2 -0
- package/src/server/tools.mjs +29 -2
- package/extension/icons/README.md +0 -27
- package/extension/lib/preconfigured.js +0 -5
- package/extension/popup/popup.css +0 -227
- package/extension/popup/popup.html +0 -50
- package/extension/popup/popup.js +0 -175
- package/frontend/dist/assets/index-U7o-qU99.js +0 -5742
- package/scaffold/.agent/plugins/.gitkeep +0 -0
- package/scaffold/.agent/plugins/code-quality/README.md +0 -16
- package/scaffold/.agent/plugins/code-quality/cli.mjs +0 -15
- package/scaffold/.agent/plugins/code-quality/plugin.json +0 -34
- package/scaffold/.agent/plugins/git-sentinel/README.md +0 -16
- package/scaffold/.agent/plugins/git-sentinel/plugin.json +0 -34
- package/scaffold/.agent/plugins/meta-harness/README.md +0 -16
- package/scaffold/.agent/plugins/meta-harness/cli.mjs +0 -34
- package/scaffold/.agent/plugins/meta-harness/plugin.json +0 -34
- package/scaffold/.agent/plugins/system-monitor/README.md +0 -16
- package/scaffold/.agent/plugins/system-monitor/plugin.json +0 -38
|
@@ -79,6 +79,8 @@ router.post('/api/share', requireAuth, requireScope('memory:write'), async (req,
|
|
|
79
79
|
topic,
|
|
80
80
|
priority: 'medium',
|
|
81
81
|
notes: excerpt || '',
|
|
82
|
+
origin: 'user',
|
|
83
|
+
requested_via: String(source || '').includes('extension') ? 'extension' : 'share',
|
|
82
84
|
});
|
|
83
85
|
|
|
84
86
|
return res.status(201).json({
|
package/src/server/tools.mjs
CHANGED
|
@@ -524,9 +524,35 @@ export async function meshMintPreAuthKey(options = {}) {
|
|
|
524
524
|
}
|
|
525
525
|
}
|
|
526
526
|
|
|
527
|
+
/**
|
|
528
|
+
* The user asked, in chat, for background research. Recorded as a user request
|
|
529
|
+
* (runs ahead of autonomous research, no budget applies).
|
|
530
|
+
*/
|
|
531
|
+
export async function queueResearch(topic, notes) {
|
|
532
|
+
const { requestResearch } = await import('../core/research-gate.mjs');
|
|
533
|
+
const item = requestResearch({ topic, notes, via: 'chat' });
|
|
534
|
+
const state = item.status === 'done' ? 'already researched' : item.status === 'pending' ? 'queued' : item.status;
|
|
535
|
+
return JSON.stringify({ id: item.id, topic: item.topic, status: state, note: 'Runs in the background; findings will appear in memory and future context.' });
|
|
536
|
+
}
|
|
537
|
+
|
|
527
538
|
// ─── Tool Definitions ─────────────────────────────────────────────────────────
|
|
528
539
|
|
|
529
540
|
export const AVAILABLE_TOOLS = [
|
|
541
|
+
{
|
|
542
|
+
type: 'function',
|
|
543
|
+
function: {
|
|
544
|
+
name: 'queue_research',
|
|
545
|
+
description: 'Queue deep background research on a topic. Use ONLY when the user explicitly asks you to research something in depth or to look into it later. Returns immediately; the findings land in memory and show up in future context. For an answer right now, use search_web instead.',
|
|
546
|
+
parameters: {
|
|
547
|
+
type: 'object',
|
|
548
|
+
properties: {
|
|
549
|
+
topic: { type: 'string', description: 'Specific, searchable topic, e.g. "Stripe Node SDK v17 webhook signature verification".' },
|
|
550
|
+
notes: { type: 'string', description: 'Optional: what the user wants to learn and why.' },
|
|
551
|
+
},
|
|
552
|
+
required: ['topic'],
|
|
553
|
+
},
|
|
554
|
+
},
|
|
555
|
+
},
|
|
530
556
|
{
|
|
531
557
|
type: 'function',
|
|
532
558
|
function: {
|
|
@@ -787,7 +813,7 @@ export const AVAILABLE_TOOLS = [
|
|
|
787
813
|
parameters: {
|
|
788
814
|
type: 'object',
|
|
789
815
|
properties: {
|
|
790
|
-
node: { type: 'string', description: 'Target node hostname, slug, or mesh IP (e.g. "
|
|
816
|
+
node: { type: 'string', description: 'Target node hostname, slug, or mesh IP (e.g. "build-box", "cloud", "100.64.0.10").' },
|
|
791
817
|
command: { type: 'string', description: 'Shell command to execute on the remote node (e.g. "uptime", "git status", "docker ps").' },
|
|
792
818
|
timeoutMs: { type: 'integer', description: 'Timeout in milliseconds (default 60000).' },
|
|
793
819
|
},
|
|
@@ -804,7 +830,7 @@ export const AVAILABLE_TOOLS = [
|
|
|
804
830
|
type: 'object',
|
|
805
831
|
properties: {
|
|
806
832
|
node: { type: 'string', description: 'Target node hostname, slug, or mesh IP.' },
|
|
807
|
-
ssh_user: { type: 'string', description: 'Username to log in as (e.g. "
|
|
833
|
+
ssh_user: { type: 'string', description: 'Username to log in as (e.g. "admin", "root", "deploy").' },
|
|
808
834
|
ssh_port: { type: 'integer', description: 'SSH port (default 22).' },
|
|
809
835
|
identity_file: { type: 'string', description: 'Path to private SSH key file (e.g. "~/.ssh/id_ed25519").' },
|
|
810
836
|
ssh_host: { type: 'string', description: 'Custom host or IP override if not using the mesh IP.' },
|
|
@@ -851,6 +877,7 @@ export async function handleToolCall(toolCall) {
|
|
|
851
877
|
try {
|
|
852
878
|
const args = JSON.parse(argsString);
|
|
853
879
|
switch (name) {
|
|
880
|
+
case 'queue_research': return await queueResearch(args.topic, args.notes);
|
|
854
881
|
case 'search_web': return await executeWebSearch(args.query);
|
|
855
882
|
case 'browser_navigate': return await browserNavigate(args.url);
|
|
856
883
|
case 'browser_click': return await browserClick(args.selector);
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
# Extension Icons
|
|
2
|
-
|
|
3
|
-
This directory should contain the following PNG icon files for the Total Recall Brain Extension:
|
|
4
|
-
|
|
5
|
-
| File | Size | Usage |
|
|
6
|
-
|-----------------|---------|---------------------------------------------|
|
|
7
|
-
| `icon-16.png` | 16×16 | Favicon, toolbar (small) |
|
|
8
|
-
| `icon-48.png` | 48×48 | Extensions management page |
|
|
9
|
-
| `icon-128.png` | 128×128 | Chrome Web Store, install dialog |
|
|
10
|
-
|
|
11
|
-
## Requirements
|
|
12
|
-
|
|
13
|
-
- Format: PNG with transparency
|
|
14
|
-
- Design: Should match Total Recall branding (brain / recall motif)
|
|
15
|
-
- The manifest references these exact filenames — do not rename without updating `manifest.json`
|
|
16
|
-
|
|
17
|
-
## Generating placeholder icons
|
|
18
|
-
|
|
19
|
-
You can generate minimal placeholder PNGs with ImageMagick:
|
|
20
|
-
|
|
21
|
-
```bash
|
|
22
|
-
convert -size 16x16 xc:'#6366f1' icon-16.png
|
|
23
|
-
convert -size 48x48 xc:'#6366f1' icon-48.png
|
|
24
|
-
convert -size 128x128 xc:'#6366f1' icon-128.png
|
|
25
|
-
```
|
|
26
|
-
|
|
27
|
-
Replace these with proper branded icons before publishing.
|
|
@@ -1,227 +0,0 @@
|
|
|
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:disabled, .btn.loading {
|
|
119
|
-
opacity: 0.6;
|
|
120
|
-
cursor: not-allowed;
|
|
121
|
-
transform: none !important;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
.btn.primary {
|
|
125
|
-
background: #89b4fa;
|
|
126
|
-
color: #11111b;
|
|
127
|
-
border-color: #89b4fa;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
.btn.primary:hover {
|
|
131
|
-
background: #74c7ec;
|
|
132
|
-
border-color: #74c7ec;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
.btn.secondary {
|
|
136
|
-
background: #1e1e2e;
|
|
137
|
-
color: #cdd6f4;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
.btn.secondary:hover {
|
|
141
|
-
background: #313244;
|
|
142
|
-
border-color: #89b4fa;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
/* ---- Toggle ---- */
|
|
146
|
-
.toggle-row {
|
|
147
|
-
display: flex;
|
|
148
|
-
align-items: center;
|
|
149
|
-
justify-content: space-between;
|
|
150
|
-
padding: 10px 0;
|
|
151
|
-
border-top: 1px solid #313244;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
.toggle-label {
|
|
155
|
-
font-size: 12px;
|
|
156
|
-
color: #a6adc8;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
.switch {
|
|
160
|
-
position: relative;
|
|
161
|
-
display: inline-block;
|
|
162
|
-
width: 36px;
|
|
163
|
-
height: 20px;
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
.switch input {
|
|
167
|
-
opacity: 0;
|
|
168
|
-
width: 0;
|
|
169
|
-
height: 0;
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
.slider {
|
|
173
|
-
position: absolute;
|
|
174
|
-
cursor: pointer;
|
|
175
|
-
inset: 0;
|
|
176
|
-
background: #313244;
|
|
177
|
-
border-radius: 20px;
|
|
178
|
-
transition: background 0.3s ease;
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
.slider::before {
|
|
182
|
-
content: '';
|
|
183
|
-
position: absolute;
|
|
184
|
-
height: 14px;
|
|
185
|
-
width: 14px;
|
|
186
|
-
left: 3px;
|
|
187
|
-
bottom: 3px;
|
|
188
|
-
background: #6c7086;
|
|
189
|
-
border-radius: 50%;
|
|
190
|
-
transition: transform 0.3s ease, background 0.3s ease;
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
.switch input:checked + .slider {
|
|
194
|
-
background: rgba(137, 180, 250, 0.3);
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
.switch input:checked + .slider::before {
|
|
198
|
-
transform: translateX(16px);
|
|
199
|
-
background: #89b4fa;
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
/* ---- Status Message ---- */
|
|
203
|
-
.status-msg {
|
|
204
|
-
margin-top: 10px;
|
|
205
|
-
padding: 8px 12px;
|
|
206
|
-
border-radius: 8px;
|
|
207
|
-
font-size: 12px;
|
|
208
|
-
font-weight: 600;
|
|
209
|
-
text-align: center;
|
|
210
|
-
transition: opacity 0.3s ease;
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
.status-msg.success {
|
|
214
|
-
background: rgba(166, 227, 161, 0.1);
|
|
215
|
-
color: #a6e3a1;
|
|
216
|
-
border: 1px solid rgba(166, 227, 161, 0.3);
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
.status-msg.error {
|
|
220
|
-
background: rgba(243, 139, 168, 0.1);
|
|
221
|
-
color: #f38ba8;
|
|
222
|
-
border: 1px solid rgba(243, 139, 168, 0.3);
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
.hidden {
|
|
226
|
-
display: none !important;
|
|
227
|
-
}
|
|
@@ -1,50 +0,0 @@
|
|
|
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
|
-
|
|
40
|
-
<footer class="popup-footer" style="margin-top: 16px; padding-top: 8px; border-top: 1px solid rgba(255, 255, 255, 0.05); display: flex; justify-content: space-between; font-size: 10px; color: #6c7086;">
|
|
41
|
-
<span>Total Recall Brain Connection</span>
|
|
42
|
-
<span id="version-label">v0.1.0</span>
|
|
43
|
-
</footer>
|
|
44
|
-
</div>
|
|
45
|
-
|
|
46
|
-
<script src="../lib/preconfigured.js"></script>
|
|
47
|
-
<script src="../lib/brain-client.js"></script>
|
|
48
|
-
<script src="popup.js"></script>
|
|
49
|
-
</body>
|
|
50
|
-
</html>
|
package/extension/popup/popup.js
DELETED
|
@@ -1,175 +0,0 @@
|
|
|
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
|
-
|
|
30
|
-
// Check for extension updates
|
|
31
|
-
try {
|
|
32
|
-
const status = await self.BrainClient.brainFetch('/api/extension/status');
|
|
33
|
-
const clientVersion = chrome.runtime.getManifest().version;
|
|
34
|
-
if (status && status.version && status.version !== clientVersion) {
|
|
35
|
-
const config = await self.BrainClient.getConfig();
|
|
36
|
-
const brainUrl = config.brainUrl || 'http://127.0.0.1:3000';
|
|
37
|
-
const footer = document.querySelector('.popup-footer');
|
|
38
|
-
if (footer) {
|
|
39
|
-
footer.innerHTML = `
|
|
40
|
-
<span style="color: #f38ba8; font-weight: bold; display: flex; align-items: center; gap: 4px;">⚠️ Update Available (v${status.version})</span>
|
|
41
|
-
<a href="${brainUrl}/health" target="_blank" style="color: #89b4fa; text-decoration: none; font-weight: bold;">Download ZIP</a>
|
|
42
|
-
`;
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
} catch (err) {
|
|
46
|
-
console.error('Failed to check extension update status:', err);
|
|
47
|
-
}
|
|
48
|
-
} catch {
|
|
49
|
-
dot.className = 'dot disconnected';
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
// ---- Send share message to background ----
|
|
54
|
-
function sendShare(data) {
|
|
55
|
-
return new Promise((resolve, reject) => {
|
|
56
|
-
chrome.runtime.sendMessage({ type: 'SHARE', data }, (res) => {
|
|
57
|
-
if (chrome.runtime.lastError) return reject(new Error(chrome.runtime.lastError.message));
|
|
58
|
-
if (res && res.success) return resolve(res);
|
|
59
|
-
reject(new Error(res?.error || 'Unknown error'));
|
|
60
|
-
});
|
|
61
|
-
});
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// ---- Show status ----
|
|
65
|
-
function showStatus(message, isError = false) {
|
|
66
|
-
statusMsg.textContent = message;
|
|
67
|
-
statusMsg.className = `status-msg ${isError ? 'error' : 'success'}`;
|
|
68
|
-
statusMsg.classList.remove('hidden');
|
|
69
|
-
setTimeout(() => statusMsg.classList.add('hidden'), 3000);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
// ---- Remember ----
|
|
73
|
-
btnRemember.addEventListener('click', async () => {
|
|
74
|
-
if (btnRemember.disabled) return;
|
|
75
|
-
const originalText = btnRemember.innerHTML;
|
|
76
|
-
btnRemember.innerHTML = '⏳ Remembering...';
|
|
77
|
-
btnRemember.disabled = true;
|
|
78
|
-
btnRemember.classList.add('loading');
|
|
79
|
-
try {
|
|
80
|
-
const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
|
|
81
|
-
const note = noteInput.value.trim();
|
|
82
|
-
const targetUrl = tab?.url || urlInput.value || '';
|
|
83
|
-
|
|
84
|
-
// If we don't have a URL, we must have a note/excerpt
|
|
85
|
-
if (!targetUrl && !note) {
|
|
86
|
-
throw new Error('Please enter a note or browse to a webpage to remember.');
|
|
87
|
-
}
|
|
88
|
-
if (targetUrl && (targetUrl.startsWith('chrome://') || targetUrl.startsWith('about:') || targetUrl.startsWith('chrome-extension://'))) {
|
|
89
|
-
if (!note) {
|
|
90
|
-
throw new Error('Cannot capture system pages. Please enter a note to save.');
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
const payload = {
|
|
95
|
-
url: targetUrl && !targetUrl.startsWith('chrome://') && !targetUrl.startsWith('about:') && !targetUrl.startsWith('chrome-extension://') ? targetUrl : undefined,
|
|
96
|
-
title: tab?.title || '',
|
|
97
|
-
action: 'remember',
|
|
98
|
-
source: 'chrome-extension-popup'
|
|
99
|
-
};
|
|
100
|
-
if (note) payload.excerpt = note;
|
|
101
|
-
|
|
102
|
-
await sendShare(payload);
|
|
103
|
-
showStatus('Page remembered!');
|
|
104
|
-
noteInput.value = '';
|
|
105
|
-
} catch (err) {
|
|
106
|
-
showStatus('Error: ' + err.message, true);
|
|
107
|
-
} finally {
|
|
108
|
-
btnRemember.innerHTML = originalText;
|
|
109
|
-
btnRemember.disabled = false;
|
|
110
|
-
btnRemember.classList.remove('loading');
|
|
111
|
-
}
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
// ---- Research ----
|
|
115
|
-
btnResearch.addEventListener('click', async () => {
|
|
116
|
-
if (btnResearch.disabled) return;
|
|
117
|
-
const originalText = btnResearch.innerHTML;
|
|
118
|
-
btnResearch.innerHTML = '⏳ Researching...';
|
|
119
|
-
btnResearch.disabled = true;
|
|
120
|
-
btnResearch.classList.add('loading');
|
|
121
|
-
try {
|
|
122
|
-
const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
|
|
123
|
-
const note = noteInput.value.trim();
|
|
124
|
-
const targetUrl = tab?.url || urlInput.value || '';
|
|
125
|
-
|
|
126
|
-
if (!targetUrl && !note) {
|
|
127
|
-
throw new Error('Please enter research notes or browse to a webpage to queue research.');
|
|
128
|
-
}
|
|
129
|
-
if (targetUrl && (targetUrl.startsWith('chrome://') || targetUrl.startsWith('about:') || targetUrl.startsWith('chrome-extension://'))) {
|
|
130
|
-
if (!note) {
|
|
131
|
-
throw new Error('Cannot research system pages. Please enter research notes.');
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
const payload = {
|
|
136
|
-
url: targetUrl && !targetUrl.startsWith('chrome://') && !targetUrl.startsWith('about:') && !targetUrl.startsWith('chrome-extension://') ? targetUrl : undefined,
|
|
137
|
-
title: tab?.title || '',
|
|
138
|
-
action: 'research',
|
|
139
|
-
source: 'chrome-extension-popup'
|
|
140
|
-
};
|
|
141
|
-
if (note) payload.excerpt = note;
|
|
142
|
-
|
|
143
|
-
await sendShare(payload);
|
|
144
|
-
showStatus('Research queued!');
|
|
145
|
-
noteInput.value = '';
|
|
146
|
-
} catch (err) {
|
|
147
|
-
showStatus('Error: ' + err.message, true);
|
|
148
|
-
} finally {
|
|
149
|
-
btnResearch.innerHTML = originalText;
|
|
150
|
-
btnResearch.disabled = false;
|
|
151
|
-
btnResearch.classList.remove('loading');
|
|
152
|
-
}
|
|
153
|
-
});
|
|
154
|
-
|
|
155
|
-
// ---- Tracking toggle ----
|
|
156
|
-
async function loadTrackingPref() {
|
|
157
|
-
const { passiveTracking } = await chrome.storage.sync.get('passiveTracking');
|
|
158
|
-
trackingToggle.checked = passiveTracking || false;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
trackingToggle.addEventListener('change', () => {
|
|
162
|
-
chrome.storage.sync.set({ passiveTracking: trackingToggle.checked });
|
|
163
|
-
});
|
|
164
|
-
|
|
165
|
-
// ---- Init ----
|
|
166
|
-
fillCurrentTab();
|
|
167
|
-
checkConnection();
|
|
168
|
-
loadTrackingPref();
|
|
169
|
-
|
|
170
|
-
// Populate version label
|
|
171
|
-
const versionLabel = document.getElementById('version-label');
|
|
172
|
-
if (versionLabel) {
|
|
173
|
-
versionLabel.textContent = 'v' + chrome.runtime.getManifest().version;
|
|
174
|
-
}
|
|
175
|
-
})();
|