@sidx1scr-apps/prefrontal 1.1.79

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/index.html ADDED
@@ -0,0 +1,576 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+
4
+ <head>
5
+ <meta charset="UTF-8" />
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
7
+ <title>Prefrontal — Powered by AI</title>
8
+ <meta name="description"
9
+ content="A local AI chatbot powered by AI running entirely on your device with complete privacy. Private, offline, no ads." />
10
+ <!-- iOS / PWA -->
11
+ <meta name="apple-mobile-web-app-capable" content="yes" />
12
+ <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
13
+ <meta name="mobile-web-app-capable" content="yes" />
14
+ <meta name="theme-color" content="#212121" />
15
+ <!-- Prevent iOS double-tap-zoom on buttons -->
16
+ <meta name="format-detection" content="telephone=no" />
17
+ <link rel="stylesheet" href="vendor/highlight-dark.min.css" id="hlTheme" />
18
+ <link rel="stylesheet" href="style.css" />
19
+ </head>
20
+
21
+ <body>
22
+
23
+ <!-- Ambient Background for Glassmorphism -->
24
+ <div class="ambient-bg">
25
+ <div class="orb orb-1"></div>
26
+ <div class="orb orb-2"></div>
27
+ <div class="orb orb-3"></div>
28
+ </div>
29
+
30
+ <!-- ── SIDEBAR ── -->
31
+ <aside class="sidebar" id="sidebar">
32
+ <div class="sidebar-header">
33
+ <div class="logo">
34
+ <div class="logo-icon">
35
+ <svg viewBox="0 0 28 28" fill="none" xmlns="http://www.w3.org/2000/svg">
36
+ <rect width="28" height="28" rx="7" fill="#10a37f" />
37
+ <path d="M20 10h-7a3 3 0 000 6h4a3 3 0 010 6H9" stroke="#fff" stroke-width="2" stroke-linecap="round" />
38
+ </svg>
39
+ </div>
40
+ <span class="logo-text">Prefrontal</span>
41
+ </div>
42
+ <button class="btn-icon" id="newChatBtn" title="New Chat">
43
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
44
+ stroke-linejoin="round">
45
+ <path d="M12 5v14M5 12h14" />
46
+ </svg>
47
+ </button>
48
+ </div>
49
+
50
+ <div class="sidebar-search">
51
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
52
+ stroke-linejoin="round" class="search-icon">
53
+ <circle cx="11" cy="11" r="8" />
54
+ <path d="m21 21-4.35-4.35" />
55
+ </svg>
56
+ <input type="text" id="searchChats" placeholder="Search conversations…" autocomplete="off" />
57
+ </div>
58
+
59
+ <div class="sidebar-section-label">Recent</div>
60
+ <div class="chat-list" id="chatList"></div>
61
+
62
+ <div class="sidebar-footer-actions">
63
+ <button class="sidebar-footer-btn" id="exportAllBtn" title="Export All Chats">
64
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
65
+ stroke-linejoin="round">
66
+ <path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4" />
67
+ <polyline points="7 10 12 15 17 10" />
68
+ <line x1="12" y1="15" x2="12" y2="3" />
69
+ </svg>
70
+ Export All
71
+ </button>
72
+ <button class="sidebar-footer-btn danger" id="clearAllBtn" title="Clear All">
73
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
74
+ stroke-linejoin="round">
75
+ <polyline points="3 6 5 6 21 6" />
76
+ <path d="M19 6l-1 14a2 2 0 01-2 2H8a2 2 0 01-2-2L5 6" />
77
+ <path d="M10 11v6M14 11v6" />
78
+ <path d="M9 6V4a1 1 0 011-1h4a1 1 0 011 1v2" />
79
+ </svg>
80
+ Clear All
81
+ </button>
82
+ </div>
83
+
84
+ <!-- Profile Card -->
85
+ <div class="profile-card" id="profileCard">
86
+ <div class="profile-avatar" id="sidebarAvatar">🧠</div>
87
+ <div class="profile-info">
88
+ <div class="profile-name" id="sidebarName">Loading…</div>
89
+ <div class="profile-id" id="sidebarId">device id</div>
90
+ </div>
91
+ <button class="profile-edit-btn" id="openProfileBtn" title="Manage Profile">
92
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
93
+ stroke-linejoin="round">
94
+ <circle cx="12" cy="12" r="1" />
95
+ <circle cx="19" cy="12" r="1" />
96
+ <circle cx="5" cy="12" r="1" />
97
+ </svg>
98
+ </button>
99
+ </div>
100
+ </aside>
101
+
102
+ <!-- ── MAIN AREA ── -->
103
+ <div class="main-wrapper">
104
+
105
+ <!-- TOP NAV -->
106
+ <header class="topbar">
107
+ <button class="btn-icon" id="sidebarToggle" title="Toggle Sidebar">
108
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
109
+ stroke-linejoin="round">
110
+ <line x1="3" y1="6" x2="21" y2="6" />
111
+ <line x1="3" y1="12" x2="21" y2="12" />
112
+ <line x1="3" y1="18" x2="21" y2="18" />
113
+ </svg>
114
+ </button>
115
+
116
+ <div class="topbar-title" id="topbarTitle">New Conversation</div>
117
+
118
+ <div class="topbar-actions">
119
+ <div class="model-badge" id="modelBadge">
120
+ <span class="model-dot"></span>
121
+ <span id="modelNameDisplay">gemma4:e2b</span>
122
+ </div>
123
+ <button class="btn-icon" id="settingsBtn" title="Settings">
124
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
125
+ stroke-linejoin="round">
126
+ <circle cx="12" cy="12" r="3" />
127
+ <path
128
+ d="M19.4 15a1.65 1.65 0 00.33 1.82l.06.06a2 2 0 010 2.83 2 2 0 01-2.83 0l-.06-.06a1.65 1.65 0 00-1.82-.33 1.65 1.65 0 00-1 1.51V21a2 2 0 01-4 0v-.09A1.65 1.65 0 009 19.4a1.65 1.65 0 00-1.82.33l-.06.06a2 2 0 01-2.83 0 2 2 0 010-2.83l.06-.06A1.65 1.65 0 008.5 15a1.65 1.65 0 001-1.51V13a2 2 0 014 0v.09a1.65 1.65 0 001 1.51 1.65 1.65 0 001.82-.33l.06-.06a2 2 0 012.83 0 2 2 0 010 2.83l-.06.06a1.65 1.65 0 00-.33 1.82" />
129
+ </svg>
130
+ </button>
131
+ <button class="btn-icon" id="exportChatBtn" title="Export Chat">
132
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
133
+ stroke-linejoin="round">
134
+ <path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4" />
135
+ <polyline points="7 10 12 15 17 10" />
136
+ <line x1="12" y1="15" x2="12" y2="3" />
137
+ </svg>
138
+ </button>
139
+ </div>
140
+ </header>
141
+
142
+ <!-- CHAT AREA -->
143
+ <main class="chat-area" id="chatArea">
144
+ <!-- Welcome screen shown when no messages -->
145
+ <div class="welcome-screen" id="welcomeScreen">
146
+ <div class="welcome-logo">
147
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
148
+ stroke-linejoin="round">
149
+ <path d="M21 15a2 2 0 01-2 2H7l-4 4V5a2 2 0 012-2h14a2 2 0 012 2z" />
150
+ </svg>
151
+ </div>
152
+ <h1 class="welcome-title">How can I help you today?</h1>
153
+ <p class="welcome-subtitle">Powered by <strong>AI</strong> · Runs locally · No ads · 100% private</p>
154
+ <div class="welcome-personality-bar" id="welcomePersonalityBar">
155
+ <button class="personality-pill active" data-preset="balanced">⚖️ Balanced</button>
156
+ <button class="personality-pill" data-preset="creative">🎨 Creative</button>
157
+ <button class="personality-pill" data-preset="precise">🎯 Precise</button>
158
+ <button class="personality-pill" data-preset="developer">💻 Developer</button>
159
+ </div>
160
+ <div class="welcome-chips">
161
+ <button class="chip" data-prompt="Explain quantum computing using a simple analogy">⚛️ Explain quantum computing simply</button>
162
+ <button class="chip" data-prompt="Write a Python function to sort a list of dictionaries by a key">🐍 Sort a list of dicts in Python</button>
163
+ <button class="chip" data-prompt="What are the best practices for REST API design?">🌐 REST API best practices</button>
164
+ <button class="chip" data-prompt="Write a short creative story about a robot discovering music">🤖 Story: robot discovers music</button>
165
+ <button class="chip" data-prompt="What are 5 productivity habits backed by science?">🧠 5 science-backed productivity habits</button>
166
+ <button class="chip" data-prompt="Debug this code: for i in rang(10): print(i)">🐛 Debug this Python code</button>
167
+ </div>
168
+ </div>
169
+
170
+ <!-- Messages container -->
171
+ <div class="messages-wrapper" id="messagesWrapper"></div>
172
+ </main>
173
+
174
+ <!-- STATUS BAR -->
175
+ <div class="status-bar" id="statusBar">
176
+ <div class="status-indicator" id="statusIndicator">
177
+ <span class="status-dot" id="statusDot"></span>
178
+ <span id="statusText">Checking server…</span>
179
+ </div>
180
+ <div class="token-counter" id="tokenCounter"></div>
181
+ </div>
182
+
183
+ <!-- INPUT AREA -->
184
+ <footer class="input-area">
185
+ <div class="input-wrapper">
186
+ <div class="input-tools">
187
+ <button class="tool-btn" id="attachBtn" title="Attach Image (Coming Soon)" disabled>
188
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
189
+ stroke-linejoin="round">
190
+ <path
191
+ d="M21.44 11.05l-9.19 9.19a6 6 0 01-8.49-8.49l9.19-9.19a4 4 0 015.66 5.66l-9.2 9.19a2 2 0 01-2.83-2.83l8.49-8.48" />
192
+ </svg>
193
+ </button>
194
+ </div>
195
+ <div class="textarea-container">
196
+ <textarea id="userInput" placeholder="Message AI…" rows="1" maxlength="32000" autocomplete="off"
197
+ spellcheck="true"></textarea>
198
+ <div class="char-count" id="charCount"></div>
199
+ </div>
200
+ <button class="send-btn" id="sendBtn" title="Send (Enter)">
201
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
202
+ stroke-linejoin="round">
203
+ <line x1="22" y1="2" x2="11" y2="13" />
204
+ <polygon points="22 2 15 22 11 13 2 9 22 2" />
205
+ </svg>
206
+ </button>
207
+ </div>
208
+ <div class="input-footer-text">ai but local what the big companies couldnt do · 100% private · No data leaves your machine</div>
209
+ </footer>
210
+ </div>
211
+
212
+ <!-- ── SETTINGS MODAL ── -->
213
+ <div class="modal-overlay" id="settingsOverlay">
214
+ <div class="modal" id="settingsModal">
215
+ <div class="modal-header">
216
+ <h2>Settings</h2>
217
+ <button class="btn-icon" id="closeSettings">
218
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
219
+ stroke-linejoin="round">
220
+ <line x1="18" y1="6" x2="6" y2="18" />
221
+ <line x1="6" y1="6" x2="18" y2="18" />
222
+ </svg>
223
+ </button>
224
+ </div>
225
+ <div class="modal-body">
226
+
227
+ <div class="setting-group">
228
+ <label class="setting-label">AI Runtime</label>
229
+ <div class="shortcut-options" id="runtimeOptions" style="margin-bottom: 12px;">
230
+ <button class="shortcut-btn active" data-runtime="ollama">Ollama</button>
231
+ <button class="shortcut-btn" data-runtime="openai">Llama.cpp / OpenAI</button>
232
+ <button class="shortcut-btn" data-runtime="openrouter">OpenRouter</button>
233
+ </div>
234
+ </div>
235
+
236
+ <div class="setting-group">
237
+ <label class="setting-label">Server URL
238
+ <span class="server-type-badge" id="serverTypeBadge">local</span>
239
+ </label>
240
+ <input type="text" class="setting-input" id="serverUrl" value="http://localhost:11434"
241
+ placeholder="http://localhost:11434" />
242
+ <div class="server-quick-btns" id="serverQuickBtns">
243
+ <button class="server-quick-btn" data-url="http://localhost:11434" data-runtime="ollama">🏠 Local Ollama</button>
244
+ <button class="server-quick-btn" data-url="http://localhost:8080/v1" data-runtime="openai">🏠 Local Llama.cpp</button>
245
+ <button class="server-quick-btn" data-url="https://openrouter.ai/api/v1" data-runtime="openrouter">🌐 OpenRouter</button>
246
+ <button class="server-quick-btn" data-url="http://192.168.1.X:11434" data-type="lan">📡 LAN Ollama</button>
247
+ </div>
248
+ <p class="setting-hint" id="serverUrlHint">
249
+ Enter any server URL — local, LAN (e.g. <code>http://192.168.1.50:11434</code>), or a remote server.
250
+ For OpenRouter, use your API key. For external servers, ensure CORS is enabled: <code>OLLAMA_ORIGINS=*</code>.
251
+ </p>
252
+ </div>
253
+
254
+ <div class="setting-group">
255
+ <label class="setting-label">API Key <span style="font-size:10.5px;font-weight:normal;color:var(--text-muted)">(Optional)</span></label>
256
+ <input type="password" class="setting-input" id="apiKey" autocomplete="off" placeholder="sk-... (required for OpenRouter)" />
257
+ <p class="setting-hint">Required for OpenRouter and commercial OpenAI-compatible providers (Groq, Anthropic, etc). Get a free OpenRouter key at <a href="https://openrouter.com/keys" target="_blank" style="color:var(--accent-color);text-decoration:underline">openrouter.com/keys</a>. Stored locally.</p>
258
+ </div>
259
+
260
+ <div class="setting-group" id="webSearchGroup" style="display:none">
261
+ <div class="toggle-row">
262
+ <div>
263
+ <label class="setting-label" style="margin-bottom:2px">Web Search</label>
264
+ <p class="setting-hint" style="margin-top:0">Lets the model search DuckDuckGo before answering when it decides it needs to. Only available on OpenRouter — local/offline runtimes can't reach the internet. Adds a short instruction to the system prompt and runs searches from your browser; no extra API cost beyond the normal per-token price.</p>
265
+ </div>
266
+ <label class="toggle">
267
+ <input type="checkbox" id="webSearchToggle" />
268
+ <span class="toggle-slider"></span>
269
+ </label>
270
+ </div>
271
+ </div>
272
+
273
+ <div class="setting-group">
274
+ <label class="setting-label">Model</label>
275
+ <div class="model-select-row">
276
+ <input type="text" class="setting-input" id="modelInput" value="gemma4:e2b" placeholder="e.g. gemma4:e2b" />
277
+ <button class="btn-secondary" id="fetchModelsBtn">Refresh</button>
278
+ </div>
279
+ <div class="model-list" id="modelList"></div>
280
+ <p class="setting-hint">Type a model name or select from available models.</p>
281
+ </div>
282
+
283
+ <div class="setting-group">
284
+ <label class="setting-label">Personality Preset</label>
285
+ <div class="personality-preset-grid" id="personalityPresets">
286
+ <button class="personality-preset-btn" data-preset="balanced" title="Helpful, clear, and to the point">
287
+ <span class="preset-icon">⚖️</span>
288
+ <span class="preset-name">Balanced</span>
289
+ <span class="preset-desc">Default helpful assistant</span>
290
+ </button>
291
+ <button class="personality-preset-btn" data-preset="creative" title="Imaginative, expressive, and explorative">
292
+ <span class="preset-icon">🎨</span>
293
+ <span class="preset-name">Creative</span>
294
+ <span class="preset-desc">Imaginative &amp; expressive</span>
295
+ </button>
296
+ <button class="personality-preset-btn" data-preset="precise" title="Concise, factual, no filler">
297
+ <span class="preset-icon">🎯</span>
298
+ <span class="preset-name">Precise</span>
299
+ <span class="preset-desc">Concise &amp; factual</span>
300
+ </button>
301
+ <button class="personality-preset-btn" data-preset="developer" title="Code-first, technical, no hand-holding">
302
+ <span class="preset-icon">💻</span>
303
+ <span class="preset-name">Developer</span>
304
+ <span class="preset-desc">Code-first, technical</span>
305
+ </button>
306
+ </div>
307
+ <p class="setting-hint">Quickly switch the AI's personality. Selecting a preset updates the System Prompt and Temperature below.</p>
308
+ </div>
309
+
310
+ <div class="setting-group">
311
+ <label class="setting-label">System Prompt</label>
312
+ <textarea class="setting-input setting-textarea" id="systemPrompt"
313
+ placeholder="You are a helpful AI assistant…"
314
+ rows="4">You are Prefrontal, a helpful, honest, and harmless AI assistant. You are running entirely locally on the user's device with complete privacy. Be concise, clear, and friendly.</textarea>
315
+ <p class="setting-hint">Defines the assistant's personality and behaviour. Editing this manually will deselect any preset.</p>
316
+ </div>
317
+
318
+ <div class="setting-group">
319
+ <label class="setting-label">Temperature
320
+ <span class="temp-badge" id="tempBadge">Balanced</span>
321
+ — <span id="tempDisplay">0.7</span>
322
+ </label>
323
+ <input type="range" class="setting-slider" id="tempSlider" min="0" max="2" step="0.05" value="0.7" />
324
+ <div class="temp-labels">
325
+ <span>🧊 0 · Deterministic</span>
326
+ <span>⚖️ 0.7 · Balanced</span>
327
+ <span>🔥 2 · Wild</span>
328
+ </div>
329
+ <p class="setting-hint">Controls randomness. Lower = focused &amp; deterministic. Higher = creative &amp; unpredictable. <strong>This is sent live with every message.</strong></p>
330
+ </div>
331
+
332
+ <div class="setting-group">
333
+ <label class="setting-label">Context Window (num_ctx) — <span id="ctxDisplay">8192</span></label>
334
+ <input type="range" class="setting-slider" id="ctxSlider" min="512" max="131072" step="512" value="8192" />
335
+ <p class="setting-hint">Max tokens kept in context. AI models support various sizes (up to 128K for Gemma 4).
336
+ </p>
337
+ </div>
338
+
339
+ <div class="setting-group">
340
+ <label class="setting-label">Theme</label>
341
+ <div class="theme-options" id="themeOptions">
342
+ <button class="theme-btn active" data-theme="dark">🌑 Dark</button>
343
+ <button class="theme-btn" data-theme="midnight">🌌 Midnight</button>
344
+ <button class="theme-btn" data-theme="emerald">🌿 Emerald</button>
345
+ <button class="theme-btn" data-theme="light">☀️ Light</button>
346
+ </div>
347
+ </div>
348
+
349
+ <div class="setting-group">
350
+ <div class="toggle-row">
351
+ <div>
352
+ <label class="setting-label" style="margin-bottom:2px">Stream Responses</label>
353
+ <p class="setting-hint" style="margin-top:0">Show words as they generate in real-time.</p>
354
+ </div>
355
+ <label class="toggle">
356
+ <input type="checkbox" id="streamToggle" checked />
357
+ <span class="toggle-slider"></span>
358
+ </label>
359
+ </div>
360
+ </div>
361
+
362
+ <div class="setting-group">
363
+ <div class="toggle-row">
364
+ <div>
365
+ <label class="setting-label" style="margin-bottom:2px">Auto-scroll</label>
366
+ <p class="setting-hint" style="margin-top:0">Scroll to bottom as response streams.</p>
367
+ </div>
368
+ <label class="toggle">
369
+ <input type="checkbox" id="autoScrollToggle" checked />
370
+ <span class="toggle-slider"></span>
371
+ </label>
372
+ </div>
373
+ </div>
374
+
375
+ <div class="setting-group">
376
+ <div class="toggle-row">
377
+ <div>
378
+ <label class="setting-label" style="margin-bottom:2px">Sound Effects</label>
379
+ <p class="setting-hint" style="margin-top:0">Subtle audio feedback on send/receive.</p>
380
+ </div>
381
+ <label class="toggle">
382
+ <input type="checkbox" id="soundToggle" />
383
+ <span class="toggle-slider"></span>
384
+ </label>
385
+ </div>
386
+ </div>
387
+
388
+ <div class="setting-group">
389
+ <label class="setting-label">Keyboard Shortcut</label>
390
+ <div class="shortcut-options" id="shortcutOptions">
391
+ <button class="shortcut-btn active" data-mode="enter">Enter to Send</button>
392
+ <button class="shortcut-btn" data-mode="shift">Shift+Enter to Send</button>
393
+ </div>
394
+ </div>
395
+
396
+ </div>
397
+ <div class="modal-footer">
398
+ <button class="btn-secondary" id="resetSettingsBtn">Reset Defaults</button>
399
+ <button class="btn-primary" id="saveSettingsBtn">Save Settings</button>
400
+ </div>
401
+ </div>
402
+ </div>
403
+
404
+ <!-- ── CONFIRM DIALOG ── -->
405
+ <div class="modal-overlay" id="confirmOverlay">
406
+ <div class="modal confirm-modal" id="confirmModal">
407
+ <div class="modal-header">
408
+ <h2 id="confirmTitle">Confirm</h2>
409
+ </div>
410
+ <div class="modal-body">
411
+ <p id="confirmMessage" style="color:var(--text-secondary);line-height:1.6"></p>
412
+ </div>
413
+ <div class="modal-footer">
414
+ <button class="btn-secondary" id="confirmCancel">Cancel</button>
415
+ <button class="btn-danger" id="confirmOk">Delete</button>
416
+ </div>
417
+ </div>
418
+ </div>
419
+
420
+ <!-- TOAST -->
421
+ <div class="toast-container" id="toastContainer"></div>
422
+
423
+ <!-- ── FIRST-RUN SETUP MODAL ── -->
424
+ <div class="modal-overlay" id="setupOverlay">
425
+ <div class="modal setup-modal" id="setupModal">
426
+ <div class="setup-header">
427
+ <div class="setup-logo">
428
+ <svg viewBox="0 0 28 28" fill="none" xmlns="http://www.w3.org/2000/svg">
429
+ <rect width="28" height="28" rx="7" fill="#10a37f" />
430
+ <path d="M20 10h-7a3 3 0 000 6h4a3 3 0 010 6H9" stroke="#fff" stroke-width="2" stroke-linecap="round" />
431
+ </svg>
432
+ </div>
433
+ <h2>Welcome to Prefrontal</h2>
434
+ <p>Set up your local device profile. No account, no internet — just your device.</p>
435
+ </div>
436
+ <div class="setup-body">
437
+ <!-- Avatar picker -->
438
+ <div class="setup-field">
439
+ <label class="setup-label">Choose your avatar</label>
440
+ <div class="avatar-grid" id="avatarGrid">
441
+ <button class="avatar-btn selected" data-emoji="🧠">🧠</button>
442
+ <button class="avatar-btn" data-emoji="🚀">🚀</button>
443
+ <button class="avatar-btn" data-emoji="⚡">⚡</button>
444
+ <button class="avatar-btn" data-emoji="🔥">🔥</button>
445
+ <button class="avatar-btn" data-emoji="🎯">🎯</button>
446
+ <button class="avatar-btn" data-emoji="🌙">🌙</button>
447
+ <button class="avatar-btn" data-emoji="💎">💎</button>
448
+ <button class="avatar-btn" data-emoji="🦊">🦊</button>
449
+ <button class="avatar-btn" data-emoji="🐉">🐉</button>
450
+ <button class="avatar-btn" data-emoji="🎮">🎮</button>
451
+ <button class="avatar-btn" data-emoji="🤖">🤖</button>
452
+ <button class="avatar-btn" data-emoji="👾">👾</button>
453
+ </div>
454
+ </div>
455
+ <!-- Display name -->
456
+ <div class="setup-field">
457
+ <label class="setup-label">Display name</label>
458
+ <input type="text" class="setup-input" id="setupName" placeholder="e.g. sidx1" maxlength="32"
459
+ autocomplete="off" />
460
+ <p class="setup-hint">Only stored locally on this device.</p>
461
+ </div>
462
+ <!-- Device ID preview -->
463
+ <div class="setup-field">
464
+ <label class="setup-label">Your Device ID <span class="setup-badge">auto-generated</span></label>
465
+ <div class="device-id-box" id="deviceIdPreview">Generating…</div>
466
+ <p class="setup-hint">A unique ID for this device. You can export it as a file.</p>
467
+ </div>
468
+ </div>
469
+ <div class="setup-footer">
470
+ <button class="btn-primary" id="completeSetupBtn" style="width:100%;justify-content:center">Create My Profile
471
+ →</button>
472
+ </div>
473
+ </div>
474
+ </div>
475
+
476
+ <!-- ── PROFILE MODAL ── -->
477
+ <div class="modal-overlay" id="profileOverlay">
478
+ <div class="modal profile-modal" id="profileModal">
479
+ <div class="modal-header">
480
+ <h2>Your Profile</h2>
481
+ <button class="btn-icon" id="closeProfileBtn">
482
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
483
+ stroke-linejoin="round">
484
+ <line x1="18" y1="6" x2="6" y2="18" />
485
+ <line x1="6" y1="6" x2="18" y2="18" />
486
+ </svg>
487
+ </button>
488
+ </div>
489
+ <div class="modal-body">
490
+ <!-- Preview -->
491
+ <div class="profile-preview">
492
+ <div class="profile-preview-avatar" id="profilePreviewAvatar">🧠</div>
493
+ <div>
494
+ <div class="profile-preview-name" id="profilePreviewName">—</div>
495
+ <div class="profile-preview-meta" id="profilePreviewMeta">—</div>
496
+ </div>
497
+ </div>
498
+
499
+ <div class="setting-group">
500
+ <label class="setting-label">Avatar</label>
501
+ <div class="avatar-grid" id="profileAvatarGrid">
502
+ <button class="avatar-btn selected" data-emoji="🧠">🧠</button>
503
+ <button class="avatar-btn" data-emoji="🚀">🚀</button>
504
+ <button class="avatar-btn" data-emoji="⚡">⚡</button>
505
+ <button class="avatar-btn" data-emoji="🔥">🔥</button>
506
+ <button class="avatar-btn" data-emoji="🎯">🎯</button>
507
+ <button class="avatar-btn" data-emoji="🌙">🌙</button>
508
+ <button class="avatar-btn" data-emoji="💎">💎</button>
509
+ <button class="avatar-btn" data-emoji="🦊">🦊</button>
510
+ <button class="avatar-btn" data-emoji="🐉">🐉</button>
511
+ <button class="avatar-btn" data-emoji="🎮">🎮</button>
512
+ <button class="avatar-btn" data-emoji="🤖">🤖</button>
513
+ <button class="avatar-btn" data-emoji="👾">👾</button>
514
+ </div>
515
+ </div>
516
+
517
+ <div class="setting-group">
518
+ <label class="setting-label">Display Name</label>
519
+ <input type="text" class="setting-input" id="profileNameInput" maxlength="32" autocomplete="off" />
520
+ </div>
521
+
522
+ <div class="setting-group">
523
+ <label class="setting-label">Device ID</label>
524
+ <div class="device-id-box" id="profileDeviceId" style="cursor:pointer" title="Click to copy"
525
+ onclick="copyDeviceId()"></div>
526
+ <p class="setting-hint">Click to copy · Unique to this device</p>
527
+ </div>
528
+
529
+ <div class="setting-group">
530
+ <label class="setting-label">Member Since</label>
531
+ <div class="device-id-box" id="profileCreatedAt"></div>
532
+ </div>
533
+
534
+ <div class="setting-group">
535
+ <label class="setting-label">Profile File</label>
536
+ <div style="display:flex;gap:8px;flex-wrap:wrap">
537
+ <button class="btn-secondary" id="exportProfileBtn" style="flex:1">
538
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
539
+ stroke-linejoin="round" style="width:14px;height:14px;vertical-align:middle;margin-right:5px">
540
+ <path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4" />
541
+ <polyline points="7 10 12 15 17 10" />
542
+ <line x1="12" y1="15" x2="12" y2="3" />
543
+ </svg>
544
+ Export Profile
545
+ </button>
546
+ <label class="btn-secondary"
547
+ style="flex:1;cursor:pointer;display:flex;align-items:center;justify-content:center;gap:5px">
548
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
549
+ stroke-linejoin="round" style="width:14px;height:14px">
550
+ <polyline points="16 16 12 12 8 16" />
551
+ <line x1="12" y1="12" x2="12" y2="21" />
552
+ <path d="M20.39 18.39A5 5 0 0018 9h-1.26A8 8 0 103 16.3" />
553
+ </svg>
554
+ Import Profile
555
+ <input type="file" id="importProfileInput" accept=".json" style="display:none" />
556
+ </label>
557
+ </div>
558
+ <p class="setting-hint">Export saves a <code>prefrontal_profile.json</code> file. Import loads a profile from
559
+ another device.</p>
560
+ </div>
561
+
562
+ </div>
563
+ <div class="modal-footer">
564
+ <button class="btn-secondary" id="cancelProfileBtn">Cancel</button>
565
+ <button class="btn-primary" id="saveProfileBtn">Save Changes</button>
566
+ </div>
567
+ </div>
568
+ </div>
569
+
570
+ <!-- Local vendor libs (no internet required) -->
571
+ <script src="vendor/marked.min.js"></script>
572
+ <script src="vendor/highlight.min.js"></script>
573
+ <script src="app.js"></script>
574
+ </body>
575
+
576
+ </html>
package/package.json ADDED
@@ -0,0 +1,31 @@
1
+ {
2
+ "name": "@sidx1scr-apps/prefrontal",
3
+ "version": "1.1.79",
4
+ "description": "Prefrontal — Local AI Chatbot · 100% Private · OpenRouter + Ollama + Llama.cpp",
5
+ "main": "app.js",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/sidx1-scratch/prefrontal"
9
+ },
10
+ "scripts": {
11
+ "start": "npx -y serve ."
12
+ },
13
+ "files": [
14
+ "app.js",
15
+ "src",
16
+ "index.html",
17
+ "vendor"
18
+ ],
19
+ "keywords": [
20
+ "ai",
21
+ "local",
22
+ "offline",
23
+ "private",
24
+ "ollama",
25
+ "openrouter",
26
+ "llama",
27
+ "chatbot"
28
+ ],
29
+ "author": "sidx1-scratch",
30
+ "license": "GPL-3.0"
31
+ }
@@ -0,0 +1,10 @@
1
+ pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}/*!
2
+ Theme: GitHub Dark
3
+ Description: Dark theme as seen on github.com
4
+ Author: github.com
5
+ Maintainer: @Hirse
6
+ Updated: 2021-05-15
7
+
8
+ Outdated base version: https://github.com/primer/github-syntax-dark
9
+ Current colors taken from GitHub's CSS
10
+ */.hljs{color:#c9d1d9;background:#0d1117}.hljs-doctag,.hljs-keyword,.hljs-meta .hljs-keyword,.hljs-template-tag,.hljs-template-variable,.hljs-type,.hljs-variable.language_{color:#ff7b72}.hljs-title,.hljs-title.class_,.hljs-title.class_.inherited__,.hljs-title.function_{color:#d2a8ff}.hljs-attr,.hljs-attribute,.hljs-literal,.hljs-meta,.hljs-number,.hljs-operator,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-id,.hljs-variable{color:#79c0ff}.hljs-meta .hljs-string,.hljs-regexp,.hljs-string{color:#a5d6ff}.hljs-built_in,.hljs-symbol{color:#ffa657}.hljs-code,.hljs-comment,.hljs-formula{color:#8b949e}.hljs-name,.hljs-quote,.hljs-selector-pseudo,.hljs-selector-tag{color:#7ee787}.hljs-subst{color:#c9d1d9}.hljs-section{color:#1f6feb;font-weight:700}.hljs-bullet{color:#f2cc60}.hljs-emphasis{color:#c9d1d9;font-style:italic}.hljs-strong{color:#c9d1d9;font-weight:700}.hljs-addition{color:#aff5b4;background-color:#033a16}.hljs-deletion{color:#ffdcd7;background-color:#67060c}
@@ -0,0 +1,10 @@
1
+ pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}/*!
2
+ Theme: GitHub
3
+ Description: Light theme as seen on github.com
4
+ Author: github.com
5
+ Maintainer: @Hirse
6
+ Updated: 2021-05-15
7
+
8
+ Outdated base version: https://github.com/primer/github-syntax-light
9
+ Current colors taken from GitHub's CSS
10
+ */.hljs{color:#24292e;background:#fff}.hljs-doctag,.hljs-keyword,.hljs-meta .hljs-keyword,.hljs-template-tag,.hljs-template-variable,.hljs-type,.hljs-variable.language_{color:#d73a49}.hljs-title,.hljs-title.class_,.hljs-title.class_.inherited__,.hljs-title.function_{color:#6f42c1}.hljs-attr,.hljs-attribute,.hljs-literal,.hljs-meta,.hljs-number,.hljs-operator,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-id,.hljs-variable{color:#005cc5}.hljs-meta .hljs-string,.hljs-regexp,.hljs-string{color:#032f62}.hljs-built_in,.hljs-symbol{color:#e36209}.hljs-code,.hljs-comment,.hljs-formula{color:#6a737d}.hljs-name,.hljs-quote,.hljs-selector-pseudo,.hljs-selector-tag{color:#22863a}.hljs-subst{color:#24292e}.hljs-section{color:#005cc5;font-weight:700}.hljs-bullet{color:#735c0f}.hljs-emphasis{color:#24292e;font-style:italic}.hljs-strong{color:#24292e;font-weight:700}.hljs-addition{color:#22863a;background-color:#f0fff4}.hljs-deletion{color:#b31d28;background-color:#ffeef0}