mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-06-16 01:35:36 -04:00
8ce945d338
* feat: Add plan mode to the chat agent
Adds a plan mode: the agent investigates read-only, proposes a checklist, and
waits for approval before changing anything. On approval it runs with full
tools and checks items off as it goes. Enforcement reuses the existing
disabled_tools gate.
Includes a slash command: `/plan [on|off]` (and `/toggle plan`) to flip the
plan toggle from the chat input.
- src/tool_security.py, src/mcp_manager.py: read-only allowlist (tools + MCP).
- src/agent_loop.py, routes/chat_routes.py: union the disabled set, prepend the
plan directive, force agent mode.
- static/: plan toggle pill, Approve & Run, dockable plan window, task-list
checkboxes, and the /plan slash command.
- tests/test_plan_mode.py.
* Plan mode: persistent re-referenceable plan + agent write-back
Three improvements so a long plan survives a weak model and stays in reach:
1. Re-reference the plan (out-of-context fix). On the execution turn the frontend
sends the approved checklist back (`approved_plan`); the backend pins it as a
top-of-context `## ACTIVE PLAN` system note (kept by the context trimmer), so
the agent can always re-read the plan instead of losing the thread on a long
run. New `build_active_plan_note()` (unit-tested).
2. Re-open / dock the plan anytime. The plan checklist is stored per-session
(localStorage). When a plan exists, the plan-mode button opens a small menu
("Show plan" / "Plan mode: On/Off") that re-opens the side-dockable plan
window — so it can stay docked while the agent works. The window live-refreshes
as the plan changes.
3. Agent write-back: new `update_plan` tool. The agent calls it to tick steps
`- [x]` after finishing them, or to revise steps when the user asks. Marker
tool (no I/O) → `plan_update` SSE event → the stored plan + docked window
update live. The ACTIVE PLAN note instructs the agent to use it.
Backend: src/agent_loop.py (param + pin + note builder + emit + prompt blurb),
src/tool_execution.py (update_plan handler), routes/chat_routes.py (parse
`approved_plan`, relay `plan_update`), registration in tool_schemas / agent_tools
/ tool_index (always-available, not admin-gated).
Frontend: static/js/chat.js (plan store, send `approved_plan`, handle
`plan_update`, capture restated checklists), static/app.js (plan-button menu),
static/js/planWindow.js (`isPlanWindowOpen`), static/js/storage.js (PLAN key).
Tests: tests/test_plan_mode.py (plan-note), tests/test_update_plan_tool.py.
* Plan mode: drop bash/python, rely on read-only discovery tools
Shell can mutate (write files, hit the network) and can't be constrained to
read-only at the tool layer, so plan mode no longer relies on a prompt to keep
it well-behaved — bash/python are removed from the read-only allowlist and added
to the fail-closed block set. Discovery is covered by the dedicated read-only
tools (read_file, grep, glob, ls) instead.
Rewrites the plan-mode directive to state shell is disabled and lists the
available read-only tools positively. Addresses review feedback on #638.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* Comment: note _MCP_READONLY_VERBS are prefixes not whole words
Clarifies that entries like "summar" are intentional stems matched via
startswith (covers summarise/summarize/summary), not typos. Addresses review
feedback on #638.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* Plan mode: clarify why gating inverts the allowlist into a denylist
Rename _PLAN_MODE_FALLBACK_BLOCK -> _PLAN_MODE_KNOWN_MUTATORS and rewrite the
comments. The tool gate is a denylist (disabled_tools); plan mode's policy is an
allowlist, so it returns the inverse (all known tool names minus the allowlist).
The static mutator set is a backstop for the schema-derived name list, which
misses XML-only tools and can fail to import. Addresses review feedback on #638.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* Plan mode: stop hardcoding the read-only tool list in the directive
The model is already shown its available (read-only) tools by _assemble_prompt,
which removes every disabled tool. Enumerating them again in the directive only
duplicated that list and would drift as tools change. Point at the tools listed
below instead. Addresses review feedback on #638.
2308 lines
194 KiB
HTML
2308 lines
194 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8" />
|
|
<title>Odysseus Chat</title>
|
|
<link rel="icon" type="image/svg+xml" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Cpath d='M16 4L16 22L6 22Z' fill='%23e06c75'/%3E%3Cpath d='M16 8L16 22L24 22Z' fill='%23e06c75' opacity='0.6'/%3E%3Cpath d='M4 24Q10 20 16 24Q22 28 28 24' stroke='%23e06c75' stroke-width='2.5' fill='none' stroke-linecap='round'/%3E%3C/svg%3E">
|
|
<link rel="manifest" href="/static/manifest.json">
|
|
<meta name="theme-color" content="#282c34">
|
|
<meta name="apple-mobile-web-app-capable" content="yes">
|
|
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
|
<!-- Disable iOS / Android auto-linkifying of numbers (phone/date/address)
|
|
in email bodies — was wrapping random digits in <a href="tel:..."> with
|
|
browser-default styling that didn't match the Odysseus theme. -->
|
|
<meta name="format-detection" content="telephone=no, date=no, address=no, email=no">
|
|
<link rel="apple-touch-icon" href="/static/icon-192.png">
|
|
<script nonce="{{CSP_NONCE}}">
|
|
window._odysseusLoadTime = Date.now();
|
|
(function(){
|
|
try {
|
|
var t = JSON.parse(localStorage.getItem('odysseus-theme'));
|
|
if (t && t.colors) {
|
|
var s = document.documentElement.style;
|
|
var c = t.colors;
|
|
s.setProperty('--bg', c.bg);
|
|
s.setProperty('--fg', c.fg);
|
|
s.setProperty('--panel', c.panel);
|
|
s.setProperty('--border', c.border);
|
|
if (c.red) s.setProperty('--red', c.red);
|
|
// Set --brand-color now (= custom brand or the theme red) so the
|
|
// loading-screen ASCII wave shows its final colour from the first
|
|
// paint. Otherwise it starts on --red and visibly switches once the
|
|
// app boots and applyColors() fills --brand-color.
|
|
var _bc = (c.advanced && c.advanced.brandColor) || c.red;
|
|
if (_bc) s.setProperty('--brand-color', _bc);
|
|
// Match the mobile browser toolbar / status bar to the saved theme bg
|
|
// from the very first paint. Otherwise the hardcoded dark meta sits
|
|
// there during load and the bar "switches" colour once the app boots.
|
|
var mtc = document.querySelector('meta[name="theme-color"]');
|
|
if (mtc && c.bg) mtc.setAttribute('content', c.bg);
|
|
// Update favicon to match accent color
|
|
var ac = c.red || '#e06c75';
|
|
var fav = document.querySelector("link[rel='icon']");
|
|
if (fav) fav.href = 'data:image/svg+xml,' + encodeURIComponent("<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'><path d='M16 4L16 22L6 22Z' fill='" + ac + "'/><path d='M16 8L16 22L24 22Z' fill='" + ac + "' opacity='0.6'/><path d='M4 24Q10 20 16 24Q22 28 28 24' stroke='" + ac + "' stroke-width='2.5' fill='none' stroke-linecap='round'/></svg>");
|
|
// Derive syntax highlighting colors from theme
|
|
function h2hsl(hex) {
|
|
hex = hex.replace('#','');
|
|
var r=parseInt(hex.substring(0,2),16)/255, g=parseInt(hex.substring(2,4),16)/255, b=parseInt(hex.substring(4,6),16)/255;
|
|
var mx=Math.max(r,g,b), mn=Math.min(r,g,b), h, sv, l=(mx+mn)/2;
|
|
if(mx===mn){h=sv=0}else{var d=mx-mn;sv=l>0.5?d/(2-mx-mn):d/(mx+mn);if(mx===r)h=((g-b)/d+(g<b?6:0))/6;else if(mx===g)h=((b-r)/d+2)/6;else h=((r-g)/d+4)/6}
|
|
return[h*360,sv*100,l*100];
|
|
}
|
|
function hsl2h(h,sv,l) {
|
|
h=((h%360)+360)%360;sv=Math.max(0,Math.min(100,sv))/100;l=Math.max(0,Math.min(100,l))/100;
|
|
var a=sv*Math.min(l,1-l);function f(n){var k=(n+h/30)%12;return l-a*Math.max(-1,Math.min(k-3,9-k,1))}
|
|
function th(v){return Math.round(v*255).toString(16).padStart(2,'0')}
|
|
return'#'+th(f(0))+th(f(8))+th(f(4));
|
|
}
|
|
var fH=h2hsl(c.fg),bH=h2hsl(c.bg),rH=h2hsl(c.red||'#e06c75');
|
|
var dk=bH[2]<50;
|
|
s.setProperty('--hl-bg', hsl2h(bH[0],bH[1],dk?Math.max(bH[2]-4,0):Math.min(bH[2]+4,100)));
|
|
s.setProperty('--hl-fg', c.fg);
|
|
s.setProperty('--hl-keyword', hsl2h((rH[0]+280)%360,Math.min(rH[1]+10,80),dk?70:45));
|
|
s.setProperty('--hl-string', hsl2h(40,Math.min(fH[1]+20,70),dk?72:42));
|
|
s.setProperty('--hl-comment', hsl2h(fH[0],Math.max(fH[1]-20,5),(fH[2]*0.5+bH[2]*0.5)));
|
|
s.setProperty('--hl-function', hsl2h(210,Math.min(fH[1]+20,75),dk?70:45));
|
|
s.setProperty('--hl-number', hsl2h(20,Math.min(fH[1]+15,65),dk?68:48));
|
|
s.setProperty('--hl-builtin', hsl2h(180,Math.min(fH[1]+15,60),dk?65:40));
|
|
s.setProperty('--hl-variable', hsl2h((fH[0]+30)%360,Math.min(fH[1]+5,60),fH[2]));
|
|
s.setProperty('--hl-params', hsl2h(fH[0],Math.max(fH[1]-5,10),dk?Math.min(fH[2]+8,85):Math.max(fH[2]-8,25)));
|
|
// Apply advanced overrides if present
|
|
if (c.advanced) {
|
|
var a = c.advanced;
|
|
var advMap = {userBubbleBg:'--user-bubble-bg',aiBubbleBg:'--ai-bubble-bg',bubbleBorder:'--bubble-border',sidebarBg:'--sidebar-bg',sectionAccent:'--section-accent',brandColor:'--brand-color',inputBg:'--input-bg',inputBorder:'--input-border',sendBtnBg:'--send-btn-bg',sendBtnHover:'--send-btn-hover',codeBg:'--code-bg',codeFg:'--code-fg',toggleBg:'--toggle-bg',toggleActive:'--toggle-active',accentPrimary:'--accent-primary',accentError:'--accent-error'};
|
|
for (var k in advMap) { if (a[k]) s.setProperty(advMap[k], a[k]); }
|
|
}
|
|
}
|
|
// Apply font early
|
|
if (t && t.font) {
|
|
var fm = {mono:"'Fira Code', monospace",sans:"system-ui, -apple-system, 'Segoe UI', sans-serif",serif:"Georgia, 'Times New Roman', serif"};
|
|
if (fm[t.font]) { s.setProperty('--font-family', fm[t.font]); }
|
|
else { s.setProperty('--font-family', "'" + t.font.replace(/'/g,'') + "', sans-serif"); }
|
|
}
|
|
// Apply density class on html
|
|
if (t && t.density && t.density !== 'comfortable') {
|
|
document.documentElement.classList.add('density-' + t.density);
|
|
}
|
|
// Apply background pattern on body once available
|
|
if (t && t.bgPattern && t.bgPattern !== 'none') {
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
document.body.classList.add('bg-pattern-' + t.bgPattern);
|
|
}, {once:true});
|
|
}
|
|
} catch(e){}
|
|
})();
|
|
</script>
|
|
<!-- Per-route favicon. Bookmarking /calendar shows a calendar icon,
|
|
/notes a notes icon, etc. Each shape is rendered in the current
|
|
theme accent color. Falls back to the boat logo on the root path. -->
|
|
<script nonce="{{CSP_NONCE}}">
|
|
(function(){
|
|
try {
|
|
var path = (window.location.pathname || '').toLowerCase();
|
|
var theme = (function(){ try { return JSON.parse(localStorage.getItem('odysseus-theme')); } catch(_) { return null; } })();
|
|
var ac = (theme && (theme.red || theme.colors && theme.colors.red)) || '#e06c75';
|
|
// Shapes are line-stroke SVGs (no emoji glyphs). Each entry returns
|
|
// the inner SVG markup rendered against currentColor=`ac`.
|
|
var SHAPES = {
|
|
'/calendar':
|
|
"<rect x='4' y='6' width='24' height='22' rx='2' fill='none' stroke='" + ac + "' stroke-width='2.5'/>" +
|
|
"<line x1='4' y1='12' x2='28' y2='12' stroke='" + ac + "' stroke-width='2.5'/>" +
|
|
"<line x1='10' y1='3' x2='10' y2='9' stroke='" + ac + "' stroke-width='2.5' stroke-linecap='round'/>" +
|
|
"<line x1='22' y1='3' x2='22' y2='9' stroke='" + ac + "' stroke-width='2.5' stroke-linecap='round'/>",
|
|
'/notes':
|
|
"<rect x='6' y='4' width='20' height='24' rx='2' fill='none' stroke='" + ac + "' stroke-width='2.5'/>" +
|
|
"<line x1='10' y1='10' x2='22' y2='10' stroke='" + ac + "' stroke-width='2'/>" +
|
|
"<line x1='10' y1='15' x2='22' y2='15' stroke='" + ac + "' stroke-width='2'/>" +
|
|
"<line x1='10' y1='20' x2='18' y2='20' stroke='" + ac + "' stroke-width='2'/>",
|
|
'/cookbook':
|
|
"<path d='M5 8 L5 26 A2 2 0 0 0 7 28 L25 28 A2 2 0 0 0 27 26 L27 8' fill='none' stroke='" + ac + "' stroke-width='2.5' stroke-linejoin='round'/>" +
|
|
"<path d='M9 4 L23 4 L23 8 L9 8 Z' fill='none' stroke='" + ac + "' stroke-width='2.5' stroke-linejoin='round'/>" +
|
|
"<line x1='11' y1='14' x2='21' y2='14' stroke='" + ac + "' stroke-width='2'/>" +
|
|
"<line x1='11' y1='19' x2='17' y2='19' stroke='" + ac + "' stroke-width='2'/>",
|
|
'/email':
|
|
"<rect x='4' y='7' width='24' height='18' rx='2' fill='none' stroke='" + ac + "' stroke-width='2.5'/>" +
|
|
"<path d='M5 9 L16 17 L27 9' fill='none' stroke='" + ac + "' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'/>",
|
|
'/memory':
|
|
"<path d='M16 5 C10 5 6 9 6 14 C6 19 10 21 11 22 L11 26 L21 26 L21 22 C22 21 26 19 26 14 C26 9 22 5 16 5 Z' fill='none' stroke='" + ac + "' stroke-width='2.5' stroke-linejoin='round'/>" +
|
|
"<line x1='12' y1='28' x2='20' y2='28' stroke='" + ac + "' stroke-width='2'/>",
|
|
'/gallery':
|
|
"<rect x='4' y='4' width='24' height='24' rx='2' fill='none' stroke='" + ac + "' stroke-width='2.5'/>" +
|
|
"<circle cx='12' cy='12' r='2.5' fill='" + ac + "'/>" +
|
|
"<path d='M4 22 L11 16 L18 21 L23 17 L28 22' fill='none' stroke='" + ac + "' stroke-width='2.5' stroke-linejoin='round'/>",
|
|
'/tasks':
|
|
"<rect x='4' y='4' width='24' height='24' rx='3' fill='none' stroke='" + ac + "' stroke-width='2.5'/>" +
|
|
"<path d='M9 16 L14 21 L23 11' fill='none' stroke='" + ac + "' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'/>",
|
|
'/library':
|
|
"<rect x='5' y='5' width='5' height='22' rx='1' fill='none' stroke='" + ac + "' stroke-width='2.5'/>" +
|
|
"<rect x='13' y='5' width='5' height='22' rx='1' fill='none' stroke='" + ac + "' stroke-width='2.5'/>" +
|
|
"<rect x='21' y='8' width='6' height='19' rx='1' fill='none' stroke='" + ac + "' stroke-width='2.5' transform='rotate(8 24 17)'/>",
|
|
};
|
|
var inner = SHAPES[path];
|
|
if (!inner) return; // Root path keeps the default boat icon.
|
|
var svg = "<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'>" + inner + "</svg>";
|
|
var href = 'data:image/svg+xml,' + encodeURIComponent(svg);
|
|
var fav = document.querySelector("link[rel='icon']");
|
|
if (!fav) { fav = document.createElement('link'); fav.rel = 'icon'; fav.type = 'image/svg+xml'; document.head.appendChild(fav); }
|
|
fav.href = href;
|
|
var apple = document.querySelector("link[rel='apple-touch-icon']");
|
|
if (!apple) { apple = document.createElement('link'); apple.rel = 'apple-touch-icon'; document.head.appendChild(apple); }
|
|
apple.href = href;
|
|
// Also customize the page title so the bookmark inherits a useful name.
|
|
var titles = {
|
|
'/calendar': 'Calendar — Odysseus',
|
|
'/notes': 'Notes — Odysseus',
|
|
'/cookbook': 'Cookbook — Odysseus',
|
|
'/email': 'Email — Odysseus',
|
|
'/memory': 'Memory — Odysseus',
|
|
'/gallery': 'Gallery — Odysseus',
|
|
'/tasks': 'Tasks — Odysseus',
|
|
'/library': 'Library — Odysseus',
|
|
};
|
|
if (titles[path]) document.title = titles[path];
|
|
// Per-route Android home-screen icon. We swap the <link rel="manifest">
|
|
// to a per-page Blob URL with this route's SVG icon — that way "Add to
|
|
// Home Screen" picks up the route-specific glyph instead of the shared
|
|
// boat logo. Falls back silently on browsers without Blob URL support.
|
|
try {
|
|
if (inner && typeof Blob !== 'undefined') {
|
|
var pwa = {
|
|
name: (titles[path] || 'Odysseus'),
|
|
short_name: (titles[path] || 'Odysseus').split('—')[0].trim(),
|
|
start_url: path,
|
|
scope: '/',
|
|
display: 'standalone',
|
|
background_color: '#0e0e10',
|
|
theme_color: ac,
|
|
icons: [
|
|
{ src: href, sizes: '192x192', type: 'image/svg+xml', purpose: 'any maskable' },
|
|
{ src: href, sizes: '512x512', type: 'image/svg+xml', purpose: 'any maskable' },
|
|
],
|
|
};
|
|
var blob = new Blob([JSON.stringify(pwa)], { type: 'application/manifest+json' });
|
|
var url = URL.createObjectURL(blob);
|
|
var ml = document.querySelector("link[rel='manifest']");
|
|
if (!ml) { ml = document.createElement('link'); ml.rel = 'manifest'; document.head.appendChild(ml); }
|
|
ml.href = url;
|
|
}
|
|
} catch(_) {}
|
|
} catch(e){}
|
|
})();
|
|
</script>
|
|
<script defer src="/static/lib/highlight.min.js"></script>
|
|
<!-- Inter font — self-hosted, no Google dependencies -->
|
|
<style>
|
|
@font-face { font-family: 'Inter'; font-weight: 400; font-style: normal; font-display: swap; src: url('/static/fonts/Inter-Regular.woff2') format('woff2'); }
|
|
@font-face { font-family: 'Inter'; font-weight: 500; font-style: normal; font-display: swap; src: url('/static/fonts/Inter-Medium.woff2') format('woff2'); }
|
|
@font-face { font-family: 'Inter'; font-weight: 600; font-style: normal; font-display: swap; src: url('/static/fonts/Inter-SemiBold.woff2') format('woff2'); }
|
|
</style>
|
|
<!-- KaTeX CSS is loaded with media="print" so it doesn't block render,
|
|
then flipped to "all" via JS after load. Mermaid init runs once the
|
|
library finishes loading. Both hooks are wired via addEventListener
|
|
below (inline onload= attrs are blocked by CSP script-src-attr). -->
|
|
<link id="katex-css" rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.22/dist/katex.min.css" media="print">
|
|
<script async src="https://cdn.jsdelivr.net/npm/katex@0.16.22/dist/katex.min.js"></script>
|
|
<script id="mermaid-script" async src="https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.min.js"></script>
|
|
<script nonce="{{CSP_NONCE}}">
|
|
(function(){
|
|
var k = document.getElementById('katex-css');
|
|
if (k) k.addEventListener('load', function(){ k.media = 'all'; }, { once: true });
|
|
var m = document.getElementById('mermaid-script');
|
|
if (m) m.addEventListener('load', function(){
|
|
if (window.odysseusInitMermaid) window.odysseusInitMermaid();
|
|
}, { once: true });
|
|
})();
|
|
</script>
|
|
<link rel="stylesheet" href="/static/style.css">
|
|
<link rel="modulepreload" href="/static/app.js">
|
|
<link rel="modulepreload" href="/static/js/chat.js">
|
|
<link rel="modulepreload" href="/static/js/ui.js">
|
|
<link rel="modulepreload" href="/static/js/sessions.js">
|
|
<link rel="modulepreload" href="/static/js/markdown.js">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, interactive-widget=resizes-content, viewport-fit=cover" />
|
|
</head>
|
|
<body>
|
|
<!-- Loading overlay — hides all flashing until app is ready -->
|
|
<div id="app-loader" style="position:fixed;inset:0;z-index:99999;background:var(--bg,#282c34);display:flex;align-items:center;justify-content:center;flex-direction:column;gap:8px;transition:opacity .3s">
|
|
<div id="loader-wave" style="color:var(--brand-color,var(--red,#e06c75));font-family:monospace;font-size:11px;opacity:.5">▁▂▃</div>
|
|
</div>
|
|
<script nonce="{{CSP_NONCE}}">
|
|
(function(){
|
|
var el=document.getElementById('loader-wave');
|
|
if(!el)return;
|
|
var frames=['▁▂▃','▂▃▄','▃▄▅','▄▅▆','▅▆▅','▆▅▄','▅▄▃','▄▃▂','▃▂▁'];
|
|
var i=0;
|
|
var iv=setInterval(function(){
|
|
if(!document.getElementById('app-loader')){clearInterval(iv);return}
|
|
el.textContent=frames[i%frames.length];
|
|
i++;
|
|
},150);
|
|
setTimeout(function(){var l=document.getElementById('app-loader');if(l){l.style.opacity='0';setTimeout(function(){l.remove()},300)}},5000);
|
|
})();
|
|
</script>
|
|
<!-- Memory Management Modal -->
|
|
<div id="memory-modal" class="modal hidden">
|
|
<div class="modal-content memory-modal-content" role="dialog" aria-label="Brain" style="background:var(--bg)">
|
|
<div class="modal-header">
|
|
<h4><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:-2px;margin-right:6px"><path d="M12 5a3 3 0 1 0-5.997.125 4 4 0 0 0-2.526 5.77 4 4 0 0 0 .556 6.588A4 4 0 1 0 12 18Z"/><path d="M12 5a3 3 0 1 1 5.997.125 4 4 0 0 1 2.526 5.77 4 4 0 0 1-.556 6.588A4 4 0 1 1 12 18Z"/><path d="M15 13a4.5 4.5 0 0 1-3-4 4.5 4.5 0 0 1-3 4"/></svg>Brain</h4>
|
|
<button class="close-btn" id="close-memory-modal" aria-label="Close memory modal">✖</button>
|
|
</div>
|
|
<div class="modal-body memory-modal-body">
|
|
<div class="memory-tabs">
|
|
<button class="memory-tab active" data-memory-tab="browse"><svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:-2px;margin-right:5px"><path d="M12 2a7 7 0 0 1 7 7c0 2.4-1.2 4.5-3 5.7V17a2 2 0 0 1-2 2h-4a2 2 0 0 1-2-2v-2.3C6.2 13.5 5 11.4 5 9a7 7 0 0 1 7-7z"/><line x1="10" y1="22" x2="14" y2="22"/></svg>Memories <span id="memory-count" class="memory-count" style="font-size:0.8em;opacity:0.6;font-weight:normal;margin-left:4px">0</span></button>
|
|
<button class="memory-tab" data-memory-tab="skills"><svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:-2px;margin-right:5px"><polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"/></svg>Skills <span id="skills-count" class="memory-count" style="font-size:0.8em;opacity:0.6;font-weight:normal;margin-left:4px">0</span></button>
|
|
<button class="memory-tab" data-memory-tab="add"><svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:-2px;margin-right:5px"><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="16"/><line x1="8" y1="12" x2="16" y2="12"/></svg>Add</button>
|
|
<button class="memory-tab" data-memory-tab="settings"><svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:-2px;margin-right:5px"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 2.83-2.83l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>Settings</button>
|
|
</div>
|
|
<!-- ── Browse tab ── -->
|
|
<div class="memory-tab-panel" data-memory-panel="browse">
|
|
<div class="admin-card" style="display:flex;flex-direction:column;overflow:hidden;flex:1;min-height:0;">
|
|
<div style="display:flex;align-items:center;gap:8px;margin-bottom:2px;">
|
|
<h2 style="display:flex;align-items:center;gap:6px;margin:0;padding:0;line-height:1;">Memories <span id="memory-count-h2" class="memory-count" style="font-size:0.6em;opacity:0.6;font-weight:normal"></span></h2>
|
|
<span style="flex:1"></span>
|
|
<label class="admin-switch" title="Include memories in chat context"><input type="checkbox" id="memory-enabled-header-toggle" checked /><span class="admin-slider"></span></label>
|
|
</div>
|
|
<p class="memory-desc doclib-desc" style="margin-top:6px;">Long-term facts the AI remembers across chats — recall, edit, or curate.</p>
|
|
<div class="memory-toolbar">
|
|
<div class="memory-toolbar-row">
|
|
<select id="memory-sort" class="memory-sort-select" aria-label="Sort memories">
|
|
<option value="newest">Newest</option>
|
|
<option value="oldest">Oldest</option>
|
|
<option value="alpha">A-Z</option>
|
|
<option value="uses">Most used</option>
|
|
</select>
|
|
<button id="memory-select-btn" class="memory-toolbar-btn" title="Select multiple memories">Select</button>
|
|
<button id="memory-tidy-btn" class="memory-toolbar-btn" title="AI tidy: deduplicate and clean up memories"><svg width="11" height="11" viewBox="0 0 24 24" fill="currentColor" style="vertical-align:-1px;margin-right:2px;"><path d="M12 0L14.59 8.41L23 12L14.59 15.59L12 24L9.41 15.59L1 12L9.41 8.41Z"/></svg> Tidy</button>
|
|
</div>
|
|
<input type="text" id="memory-search" placeholder="Search memories…" class="memory-search-input" aria-label="Search memories" />
|
|
<div id="memory-category-filters" class="memory-category-filters">
|
|
<button class="memory-cat-chip active" data-cat="all">all</button>
|
|
</div>
|
|
</div>
|
|
<div id="memory-bulk-bar" class="memory-bulk-bar hidden">
|
|
<label class="memory-bulk-check-all" style="position:relative;top:0px;"><input type="checkbox" id="memory-select-all" /> All</label>
|
|
<span id="memory-selected-count">0 Selected</span>
|
|
<button id="memory-bulk-delete" class="memory-toolbar-btn danger" disabled><svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:-1px;margin-right:3px;"><polyline points="3 6 5 6 21 6"/><path d="M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6"/><path d="M10 11v6"/><path d="M14 11v6"/></svg>Delete</button>
|
|
<button id="memory-bulk-cancel" class="memory-toolbar-btn" title="Cancel (Esc)" style="margin-left:4px;padding:3px 6px;"><svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg></button>
|
|
</div>
|
|
<div id="memory-list" class="memory-list"></div>
|
|
<div id="memory-suggestions-body" class="memory-suggestions hidden"></div>
|
|
</div>
|
|
</div>
|
|
<!-- ── Add tab ── -->
|
|
<div class="memory-tab-panel hidden" data-memory-panel="add">
|
|
<div class="admin-card">
|
|
<div style="display:flex;align-items:center;gap:8px;margin-bottom:2px;">
|
|
<h2 style="margin:0;padding:0;line-height:1;"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:-2px;margin-right:6px"><path d="M12 2a7 7 0 0 1 7 7c0 2.4-1.2 4.5-3 5.7V17a2 2 0 0 1-2 2h-4a2 2 0 0 1-2-2v-2.3C6.2 13.5 5 11.4 5 9a7 7 0 0 1 7-7z"/><line x1="10" y1="22" x2="14" y2="22"/></svg>Add Memory</h2>
|
|
<span style="flex:1"></span>
|
|
<button id="memory-import-btn" class="theme-io-btn" title="Import memories from a file" style="height:26px;font-size:12px;"><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:-2px;margin-right:4px;"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>Import</button>
|
|
<button id="memory-export-btn" class="theme-io-btn" title="Export all memories as JSON" style="height:26px;font-size:12px;"><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:-2px;margin-right:4px;"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="17 8 12 3 7 8"/><line x1="12" y1="3" x2="12" y2="15"/></svg>Export</button>
|
|
<input type="file" id="memory-import-file" accept=".txt,.md,.pdf,.csv,.log,.json,.py,.js,.html" hidden />
|
|
</div>
|
|
<p class="memory-desc doclib-desc" style="margin:4px 0 6px;">
|
|
Import a <code>.txt</code>, <code>.md</code>, <code>.pdf</code>, <code>.csv</code>, <code>.log</code>, <code>.json</code>, <code>.py</code>, <code>.js</code>, or <code>.html</code> file — the AI reads it and suggests candidate memories you can approve.
|
|
</p>
|
|
<div class="memory-add-row" style="margin-top:8px;">
|
|
<div class="skill-ph-wrap" style="flex:1;min-width:0;">
|
|
<input type="text" id="new-memory-input" placeholder=" " class="memory-add-input skill-hint-input" aria-label="New memory text" />
|
|
<span class="skill-rich-ph"><span class="k">Add a memory</span> — e.g. 'I prefer concise replies' <svg class="k" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:-2px;margin-left:4px;" aria-hidden="true"><polyline points="9 10 4 15 9 20"/><path d="M20 4v7a4 4 0 0 1-4 4H4"/></svg></span>
|
|
</div>
|
|
<select id="new-memory-category" class="memory-edit-cat-select" aria-label="Memory category"></select>
|
|
</div>
|
|
</div>
|
|
<div class="admin-card">
|
|
<div style="display:flex;align-items:baseline;gap:8px;margin-bottom:2px;">
|
|
<h2 style="margin:0;padding:0;line-height:1;"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:-2px;margin-right:6px"><polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"/></svg>Add Skill</h2>
|
|
</div>
|
|
<p class="memory-desc doclib-desc" style="margin-top:6px;">Create a skill by hand — title, what it solves, and an approach.</p>
|
|
<div class="skill-ph-wrap" style="margin-top:4px;margin-bottom:6px;">
|
|
<input type="text" id="new-skill-title" placeholder=" " class="memory-add-input skill-hint-input" aria-label="Skill title" />
|
|
<span class="skill-rich-ph"><span class="k">Title</span> — short name, e.g. “build-vllm-wheel”</span>
|
|
</div>
|
|
<div class="skill-ph-wrap" style="margin-bottom:6px;">
|
|
<input type="text" id="new-skill-problem" placeholder=" " class="memory-add-input skill-hint-input" aria-label="When to use this skill" />
|
|
<span class="skill-rich-ph"><span class="k">When to use</span> — what problem does this skill solve?</span>
|
|
</div>
|
|
<div class="skill-ph-wrap" style="margin-bottom:6px;">
|
|
<textarea id="new-skill-solution" placeholder=" " class="memory-add-input skill-hint-input" rows="2" style="resize:vertical;" aria-label="How — the approach or steps"></textarea>
|
|
<span class="skill-rich-ph skill-rich-ph-top"><span class="k">How</span> — the approach, steps, commands, or rules to follow</span>
|
|
</div>
|
|
<div class="skill-ph-wrap" style="margin-bottom:8px;">
|
|
<input type="text" id="new-skill-tags" placeholder=" " class="memory-add-input skill-hint-input" aria-label="Tags" />
|
|
<span class="skill-rich-ph"><span class="k">Tags</span> — comma-separated, e.g. python, build, vllm</span>
|
|
</div>
|
|
<div style="display:flex;justify-content:flex-end;">
|
|
<button id="add-skill-btn" class="memory-toolbar-btn">Add Skill</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!-- ── Skills tab ── -->
|
|
<div class="memory-tab-panel hidden" data-memory-panel="skills">
|
|
<div class="admin-card" style="display:flex;flex-direction:column;overflow:hidden;flex:1;min-height:0;">
|
|
<div style="display:flex;align-items:center;gap:8px;margin-bottom:2px;">
|
|
<h2 style="margin:0;padding:0;line-height:1;">Skills <span id="skills-count-h2" class="memory-count" style="font-size:0.6em;opacity:0.6;font-weight:normal"></span></h2>
|
|
<span style="flex:1"></span>
|
|
<label class="admin-switch" title="Inject relevant skills into chat context"><input type="checkbox" id="skills-enabled-header-toggle" checked /><span class="admin-slider"></span></label>
|
|
</div>
|
|
<p class="memory-desc doclib-desc" style="margin-top:6px;">Reusable procedures the AI can call via /skill — sort by confidence to surface the proven ones.</p>
|
|
<div class="memory-toolbar">
|
|
<div class="memory-toolbar-row">
|
|
<select id="skills-sort" class="memory-sort-select">
|
|
<optgroup label="Sort">
|
|
<option value="sort:confidence">Confidence</option>
|
|
<option value="sort:uses">Most used</option>
|
|
<option value="sort:alpha">A-Z</option>
|
|
<option value="sort:recent">Recent</option>
|
|
</optgroup>
|
|
<optgroup label="Filter">
|
|
<option value="filter:all">All skills</option>
|
|
<option value="filter:drafts">Drafts only</option>
|
|
<option value="filter:published">Published only</option>
|
|
<option value="filter:conf95">Confidence ≤ 95%</option>
|
|
<option value="filter:conf90">Confidence ≤ 90%</option>
|
|
<option value="filter:conf85">Confidence ≤ 85%</option>
|
|
<option value="filter:conf80">Confidence ≤ 80%</option>
|
|
<option value="filter:conf75">Confidence ≤ 75%</option>
|
|
<option value="filter:conf70">Confidence ≤ 70%</option>
|
|
</optgroup>
|
|
</select>
|
|
<button id="skills-select-btn" class="memory-toolbar-btn" title="Select multiple skills">Select</button>
|
|
<button id="skills-audit-btn" class="memory-toolbar-btn" title="Test every skill, auto-fix the weak ones, flag what still fails"><svg width="11" height="11" viewBox="0 0 24 24" fill="currentColor" style="vertical-align:-1px;margin-right:3px;"><path d="M12 0L14.59 8.41L23 12L14.59 15.59L12 24L9.41 15.59L1 12L9.41 8.41Z"/></svg>Audit all</button>
|
|
</div>
|
|
<input type="text" id="skills-search" placeholder="Search skills…" class="memory-search-input" aria-label="Search skills" />
|
|
</div>
|
|
<div id="skills-audit-panel" class="skills-audit-panel hidden"></div>
|
|
<div id="skills-bulk-bar" class="memory-bulk-bar hidden">
|
|
<label class="memory-bulk-check-all" style="position:relative;top:0px;"><input type="checkbox" id="skills-select-all" /> All</label>
|
|
<span id="skills-selected-count">0 Selected</span>
|
|
<button id="skills-bulk-publish" class="memory-toolbar-btn" disabled title="Publish selected drafts" style="margin-left:auto;"><svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:-1px;margin-right:3px;"><polyline points="20 6 9 17 4 12"/></svg>Approve</button>
|
|
<button id="skills-bulk-audit" class="memory-toolbar-btn" disabled title="Audit selected draft skills"><svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:-1px;margin-right:3px;"><polygon points="5 3 19 12 5 21 5 3"/></svg>Audit</button>
|
|
<button id="skills-bulk-delete-nonpassing" class="memory-toolbar-btn danger" disabled title="Delete selected duplicates, generic/irrelevant skills, failed audits, and skills below threshold"><svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:-1px;margin-right:3px;"><path d="M3 6h18"/><path d="M8 6V4h8v2"/><path d="M19 6l-1 14H6L5 6"/><path d="M10 11l4 4"/><path d="M14 11l-4 4"/></svg>Delete non passing</button>
|
|
<button id="skills-bulk-delete" class="memory-toolbar-btn danger" disabled><svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:-1px;margin-right:3px;"><polyline points="3 6 5 6 21 6"/><path d="M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6"/><path d="M10 11v6"/><path d="M14 11v6"/></svg>Delete</button>
|
|
<button id="skills-bulk-cancel" class="memory-toolbar-btn" title="Cancel (Esc)" style="padding:3px 6px;"><svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg></button>
|
|
</div>
|
|
<div id="skills-list" class="memory-list"></div>
|
|
</div>
|
|
</div>
|
|
<!-- ── Settings tab ── -->
|
|
<div class="memory-tab-panel hidden" data-memory-panel="settings">
|
|
<div class="admin-card">
|
|
<div style="display:flex;align-items:center;justify-content:space-between;gap:12px">
|
|
<h2 style="margin:0">Auto-extract memories</h2>
|
|
<label class="admin-switch" style="flex-shrink:0"><input type="checkbox" id="auto-memory-toggle" checked /><span class="admin-slider"></span></label>
|
|
</div>
|
|
<span class="admin-toggle-sub" style="display:block;margin-top:6px">Automatically extract memories from conversations.</span>
|
|
</div>
|
|
<div class="admin-card">
|
|
<div style="display:flex;align-items:center;justify-content:space-between;gap:12px">
|
|
<h2 style="margin:0">Auto-extract skills</h2>
|
|
<label class="admin-switch" style="flex-shrink:0"><input type="checkbox" id="auto-skills-toggle" /><span class="admin-slider"></span></label>
|
|
</div>
|
|
<span class="admin-toggle-sub" style="display:block;margin-top:6px;opacity:0.6">Automatically draft reusable skills from your workflows. Audit all can publish passing skills using the threshold below.</span>
|
|
<span class="admin-toggle-sub" style="display:block;margin-top:6px;opacity:0.6">The library can grow; cleanup retires weak/duplicate skills only after review.</span>
|
|
</div>
|
|
<div class="admin-card">
|
|
<div style="display:flex;align-items:center;justify-content:space-between;gap:12px">
|
|
<h2 style="margin:0">Inject Skills</h2>
|
|
</div>
|
|
<span class="admin-toggle-sub" style="display:block;margin-top:6px;opacity:0.6">Controls how many relevant published or approved skills are added to each agent request.</span>
|
|
<div style="display:flex;align-items:center;justify-content:space-between;gap:12px;margin-top:8px">
|
|
<span class="admin-toggle-sub" style="margin:0">Max skills per request</span>
|
|
<input type="number" id="skill-max-input" min="0" max="12" step="1" value="3" aria-label="Max skills to inject" style="flex-shrink:0;width:72px;background:var(--input-bg,var(--panel));color:var(--fg);border:1px solid var(--border);border-radius:6px;padding:4px 6px;font-size:12px;text-align:right;font-variant-numeric:tabular-nums" />
|
|
</div>
|
|
<span class="admin-toggle-sub" style="display:block;margin-top:6px;opacity:0.5">Set to 0 to disable skill injection.</span>
|
|
</div>
|
|
<div class="admin-card">
|
|
<div style="display:flex;align-items:center;justify-content:space-between;gap:12px">
|
|
<h2 style="margin:0">Auto-approve skills</h2>
|
|
<label class="admin-switch" style="flex-shrink:0"><input type="checkbox" id="auto-approve-skills-toggle" checked /><span class="admin-slider"></span></label>
|
|
</div>
|
|
<span class="admin-toggle-sub" style="display:block;margin-top:6px;opacity:0.6">Audit all publishes passing, necessary skills at or above this confidence. Off = keep audit results as drafts unless manually approved.</span>
|
|
<div style="display:flex;align-items:center;justify-content:space-between;gap:12px;margin-top:6px">
|
|
<span class="admin-toggle-sub" style="margin:0">Minimum confidence</span>
|
|
<span style="display:flex;align-items:center;gap:8px;flex-shrink:0">
|
|
<span id="skill-confidence-label" class="admin-toggle-sub" style="margin:0;min-width:42px;text-align:right;font-variant-numeric:tabular-nums">≥ 85%</span>
|
|
<input type="range" id="skill-confidence-slider" class="preset-range" min="50" max="100" step="5" value="85" style="width:120px;margin:0" />
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Theme Popup (floating panel) -->
|
|
<div id="theme-modal" class="modal hidden">
|
|
<div id="theme-popup" class="modal-content admin-modal-content" role="dialog" aria-label="Theme" style="background:var(--bg)">
|
|
<div class="modal-header theme-popup-header" id="theme-popup-header">
|
|
<h4><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:-2px;margin-right:6px"><circle cx="12" cy="12" r="10"/><path d="M12 2a7 7 0 0 0 0 20 4 4 0 0 1 0-8 4 4 0 0 0 0-8"/><circle cx="8" cy="9" r="1.5" fill="currentColor"/><circle cx="15" cy="14" r="1.5" fill="currentColor"/><circle cx="9" cy="15" r="1.5" fill="currentColor"/></svg>Theme</h4>
|
|
<button type="button" class="theme-opacity-wrap theme-opacity-toggle hidden" id="theme-opacity-wrap" title="Fade this window to preview the page behind it" aria-pressed="false">
|
|
<svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg>
|
|
<span class="theme-opacity-label">Peek</span>
|
|
</button>
|
|
<button class="close-btn" id="close-theme-popup" aria-label="Close theme">✖</button>
|
|
</div>
|
|
<!-- Theme tabs -->
|
|
<div class="admin-tabs" id="theme-tabs">
|
|
<button class="admin-tab active" data-tab="theme-tab-browse"><svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:-2px;margin-right:5px"><circle cx="12" cy="12" r="10"/><path d="M12 2a7 7 0 0 0 0 20 4 4 0 0 1 0-8 4 4 0 0 0 0-8"/><circle cx="8" cy="9" r="1.4" fill="currentColor"/><circle cx="15" cy="14" r="1.4" fill="currentColor"/><circle cx="9" cy="15" r="1.4" fill="currentColor"/></svg>Themes</button>
|
|
<button class="admin-tab" data-tab="theme-tab-customize"><svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:-2px;margin-right:5px"><path d="M9.06 11.9l-3.5 3.5a2.85 2.85 0 1 0 4.03 4.03l8.49-8.49a4.5 4.5 0 1 0-6.36-6.36L3.18 12.62"/><path d="M14 7l3 3"/></svg>Customize</button>
|
|
</div>
|
|
|
|
<!-- Tab: Browse themes -->
|
|
<div id="theme-tab-browse" class="theme-tab-panel">
|
|
<div class="admin-card">
|
|
<h2>Default Themes</h2>
|
|
<div class="theme-grid" id="themeGrid"></div>
|
|
</div>
|
|
<div class="admin-card" id="themeUserCard" style="display:none">
|
|
<h2>Your Themes</h2>
|
|
<div class="theme-grid" id="themeUserGrid"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Tab: Customize -->
|
|
<div id="theme-tab-customize" class="theme-tab-panel" style="display:none">
|
|
<div class="admin-card">
|
|
<h2>Colors</h2>
|
|
<div class="theme-custom" id="themeCustom">
|
|
<div class="color-row"><label>Background</label><input type="color" id="clr-bg"><button class="color-reset-btn" data-reset="bg" title="Reset this color" aria-label="Reset color">↺</button></div>
|
|
<div class="color-row"><label>Text</label><input type="color" id="clr-fg"><button class="color-reset-btn" data-reset="fg" title="Reset this color" aria-label="Reset color">↺</button></div>
|
|
<div class="color-row"><label>Panel</label><input type="color" id="clr-panel"><button class="color-reset-btn" data-reset="panel" title="Reset this color" aria-label="Reset color">↺</button></div>
|
|
<div class="color-row"><label>Sidebar</label><input type="color" id="adv-sidebarBg"><button class="color-reset-btn" data-reset-adv="sidebarBg" title="Reset this color" aria-label="Reset color">↺</button></div>
|
|
<div class="color-row"><label>Border</label><input type="color" id="clr-border"><button class="color-reset-btn" data-reset="border" title="Reset this color" aria-label="Reset color">↺</button></div>
|
|
<div class="color-row"><label>Accent</label><input type="color" id="clr-red"><button class="color-reset-btn" data-reset="red" title="Reset this color" aria-label="Reset color">↺</button></div>
|
|
</div>
|
|
</div>
|
|
<div class="theme-adv-toggle" id="theme-adv-toggle">
|
|
<span class="theme-adv-arrow">▶</span> More Colors
|
|
</div>
|
|
<div class="theme-adv-section hidden" id="themeAdvanced">
|
|
<div class="theme-adv-group">
|
|
<div class="theme-adv-group-label">Chat Bubbles</div>
|
|
<div class="theme-custom">
|
|
<div class="color-row"><label>User Chat Bubble</label><input type="color" id="adv-userBubbleBg"><button class="color-reset-btn" data-reset-adv="userBubbleBg" title="Reset this color" aria-label="Reset color">↺</button></div>
|
|
<div class="color-row"><label>AI Chat Bubble</label><input type="color" id="adv-aiBubbleBg"><button class="color-reset-btn" data-reset-adv="aiBubbleBg" title="Reset this color" aria-label="Reset color">↺</button></div>
|
|
<div class="color-row"><label>Border Chat Bubble</label><input type="color" id="adv-bubbleBorder"><button class="color-reset-btn" data-reset-adv="bubbleBorder" title="Reset this color" aria-label="Reset color">↺</button></div>
|
|
</div>
|
|
</div>
|
|
<div class="theme-adv-group">
|
|
<div class="theme-adv-group-label">Sidebar</div>
|
|
<div class="theme-custom">
|
|
<div class="color-row"><label>Odysseus Logo</label><input type="color" id="adv-brandColor"><button class="color-reset-btn" data-reset-adv="brandColor" title="Reset this color" aria-label="Reset color">↺</button></div>
|
|
<div class="color-row"><label title="Hamburger menu"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" style="vertical-align:-2px;"><line x1="3" y1="6" x2="21" y2="6"/><line x1="3" y1="12" x2="21" y2="12"/><line x1="3" y1="18" x2="21" y2="18"/></svg></label><input type="color" id="adv-hamburgerColor"><button class="color-reset-btn" data-reset-adv="hamburgerColor" title="Reset this color" aria-label="Reset color">↺</button></div>
|
|
</div>
|
|
</div>
|
|
<div class="theme-adv-group">
|
|
<div class="theme-adv-group-label">Chat Input / Prompt Area</div>
|
|
<div class="theme-custom">
|
|
<div class="color-row"><label>Input Bg</label><input type="color" id="adv-inputBg"><button class="color-reset-btn" data-reset-adv="inputBg" title="Reset this color" aria-label="Reset color">↺</button></div>
|
|
<div class="color-row"><label>Input Border</label><input type="color" id="adv-inputBorder"><button class="color-reset-btn" data-reset-adv="inputBorder" title="Reset this color" aria-label="Reset color">↺</button></div>
|
|
<div class="color-row"><label>Send Btn</label><input type="color" id="adv-sendBtnBg"><button class="color-reset-btn" data-reset-adv="sendBtnBg" title="Reset this color" aria-label="Reset color">↺</button></div>
|
|
<div class="color-row"><label>Send Hover</label><input type="color" id="adv-sendBtnHover"><button class="color-reset-btn" data-reset-adv="sendBtnHover" title="Reset this color" aria-label="Reset color">↺</button></div>
|
|
</div>
|
|
</div>
|
|
<div class="theme-adv-group">
|
|
<div class="theme-adv-group-label">Code Blocks</div>
|
|
<div class="theme-custom">
|
|
<div class="color-row"><label>Code Bg</label><input type="color" id="adv-codeBg"><button class="color-reset-btn" data-reset-adv="codeBg" title="Reset this color" aria-label="Reset color">↺</button></div>
|
|
<div class="color-row"><label>Code Text</label><input type="color" id="adv-codeFg"><button class="color-reset-btn" data-reset-adv="codeFg" title="Reset this color" aria-label="Reset color">↺</button></div>
|
|
</div>
|
|
</div>
|
|
<div class="theme-adv-group">
|
|
<div class="theme-adv-group-label">Controls</div>
|
|
<div class="theme-custom">
|
|
<div class="color-row"><label>Toggle On</label><input type="color" id="adv-toggleActive"><button class="color-reset-btn" data-reset-adv="toggleActive" title="Reset this color" aria-label="Reset color">↺</button></div>
|
|
</div>
|
|
</div>
|
|
<div class="theme-adv-group">
|
|
<div class="theme-adv-group-label">Custom Fonts</div>
|
|
<div class="theme-fd-label" style="opacity:0.4;">Drop <code>.woff2</code>, <code>.ttf</code>, or <code>.otf</code> files into <code>static/fonts/custom/</code> and reload — they'll appear in the Font dropdown above.</div>
|
|
</div>
|
|
<button class="theme-adv-clear-btn" id="theme-adv-clear">Clear Advanced Overrides</button>
|
|
</div>
|
|
<!-- Color Harmony Generator (its own card) -->
|
|
<div class="admin-card" id="theme-harmony-card">
|
|
<h2><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:-2px;margin-right:6px"><circle cx="13.5" cy="6.5" r="2.5"/><circle cx="19" cy="13" r="2.5"/><circle cx="6" cy="12" r="2.5"/><circle cx="10" cy="20" r="2.5"/></svg>Color Harmony</h2>
|
|
<div class="theme-harmony-row">
|
|
<div class="theme-fd-group">
|
|
<label class="theme-fd-label">Accent Color</label>
|
|
<div class="color-row" style="justify-content:flex-start;gap:8px;">
|
|
<input type="color" id="harmony-accent" value="#e06c75">
|
|
<span id="harmony-accent-hex" style="font-size:11px;opacity:0.65;font-family:ui-monospace,monospace;letter-spacing:0.02em;">#e06c75</span>
|
|
</div>
|
|
</div>
|
|
<div class="theme-fd-group">
|
|
<label class="theme-fd-label">Harmony</label>
|
|
<select id="harmony-type" class="theme-fd-select">
|
|
<option value="complementary">Complementary</option>
|
|
<option value="analogous">Analogous</option>
|
|
<option value="triadic">Triadic</option>
|
|
<option value="monochromatic">Monochromatic</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
<div class="theme-harmony-row" style="margin-top:6px;">
|
|
<div class="theme-fd-group">
|
|
<label class="theme-fd-label">Mode</label>
|
|
<select id="harmony-mode" class="theme-fd-select">
|
|
<option value="dark">Dark</option>
|
|
<option value="light">Light</option>
|
|
</select>
|
|
</div>
|
|
<div class="theme-fd-group" style="justify-content:flex-end;">
|
|
<button id="harmony-generate-btn" class="harmony-generate-btn">Generate</button>
|
|
</div>
|
|
</div>
|
|
<div id="harmony-preview" class="harmony-preview"></div>
|
|
</div>
|
|
<div class="admin-card">
|
|
<h2>Font & Layout</h2>
|
|
<div class="theme-fd-row">
|
|
<div class="theme-fd-group">
|
|
<label class="theme-fd-label">Font</label>
|
|
<select id="theme-font-select" class="theme-fd-select" aria-label="Font">
|
|
<option value="mono">Monospace</option>
|
|
<option value="sans">Sans-serif</option>
|
|
<option value="serif">Serif</option>
|
|
</select>
|
|
</div>
|
|
<div class="theme-fd-group">
|
|
<label class="theme-fd-label">Density</label>
|
|
<select id="theme-density-select" class="theme-fd-select" aria-label="Density">
|
|
<option value="compact">Compact</option>
|
|
<option value="comfortable">Comfortable</option>
|
|
<option value="spacious">Spacious</option>
|
|
</select>
|
|
</div>
|
|
<div class="theme-fd-group" id="theme-frosted-group">
|
|
<label class="theme-fd-label" for="theme-frosted-toggle">Frosted</label>
|
|
<label class="admin-switch" style="margin-top:4px;">
|
|
<input type="checkbox" id="theme-frosted-toggle">
|
|
<span class="admin-slider"></span>
|
|
</label>
|
|
</div>
|
|
</div>
|
|
<div class="theme-fd-row">
|
|
<div class="theme-fd-group" style="flex:1 1 0;">
|
|
<label class="theme-fd-label">Background / Effect</label>
|
|
<select id="theme-bg-pattern-select" class="theme-fd-select">
|
|
<option value="none">Solid</option>
|
|
<option value="dots">Dots</option>
|
|
<option value="synapse">Synapse</option>
|
|
<option value="rain">Rain</option>
|
|
<option value="constellations">Constellations</option>
|
|
<option value="perlin-flow">Perlin Flow</option>
|
|
<option value="petals">Petals</option>
|
|
<option value="sparkles">Sparkles</option>
|
|
<option value="embers">Embers</option>
|
|
</select>
|
|
</div>
|
|
<div class="theme-fd-group" style="flex:0 0 auto;align-items:center;">
|
|
<label class="theme-fd-label"> </label>
|
|
<div class="color-row" style="margin:0;padding:0;">
|
|
<input type="color" id="theme-bg-effect-color" value="#9cdef2" title="Effect color">
|
|
<button class="color-reset-btn" data-reset-effect title="Reset to text color">↺</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="theme-fd-row">
|
|
<div class="theme-fd-group" id="theme-bg-intensity-group" style="flex:1 1 0;">
|
|
<label class="theme-fd-label">Intensity</label>
|
|
<input type="range" id="theme-bg-intensity" class="theme-fd-range" min="0" max="100" step="5" value="100">
|
|
</div>
|
|
<div class="theme-fd-group" id="theme-bg-size-group" style="flex:1 1 0;">
|
|
<label class="theme-fd-label">Size</label>
|
|
<input type="range" id="theme-bg-size" class="theme-fd-range" min="30" max="250" step="10" value="100">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="admin-card" style="margin-top:8px;">
|
|
<h2>Save / Share</h2>
|
|
<div class="theme-save-row" id="theme-save-row">
|
|
<input type="text" id="theme-save-name" placeholder="Theme name..." maxlength="32">
|
|
<button id="theme-save-go">Save</button>
|
|
</div>
|
|
<div class="theme-save-error" id="theme-save-error"></div>
|
|
<div class="theme-io-row" style="margin-top:6px;">
|
|
<button id="theme-import-btn" class="theme-io-btn" title="Import a theme from JSON">⤒ Import</button>
|
|
<button id="theme-export-btn" class="theme-io-btn" title="Export current colors as JSON">⤓ Export</button>
|
|
</div>
|
|
<textarea id="theme-import-area" class="theme-import-area hidden" placeholder="Paste theme JSON here..." rows="3"></textarea>
|
|
<div class="theme-import-actions hidden" id="theme-import-actions">
|
|
<button id="theme-import-go" class="theme-io-btn">Apply</button>
|
|
<button id="theme-import-cancel" class="theme-io-btn">Cancel</button>
|
|
</div>
|
|
</div>
|
|
<button id="theme-reset-btn">Reset to Default</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="mobile-backdrop"></div>
|
|
<button id="mobile-menu-btn" aria-label="Toggle sidebar">☰</button>
|
|
|
|
<button class="hamburger-btn" id="hamburger-btn" title="Show sidebar"><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round"><line x1="3" y1="6" x2="21" y2="6"/><line x1="3" y1="12" x2="21" y2="12"/><line x1="3" y1="18" x2="21" y2="18"/></svg></button>
|
|
<!-- new session button removed — use + in send button instead -->
|
|
<div class="icon-rail" id="icon-rail">
|
|
<div class="rail-resize-handle" id="rail-resize-handle"></div>
|
|
<!-- Static: core actions -->
|
|
<button class="icon-rail-btn" id="rail-search-btn" title="Search conversations (Ctrl+K)"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round"><circle cx="10" cy="10" r="7"/><path d="M21 21l-4.35-4.35"/></svg></button>
|
|
<button class="icon-rail-btn rail-new-chat" id="rail-new-session" title="New chat"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg></button>
|
|
<button class="icon-rail-btn" id="rail-delete-session" title="Delete session">✕</button>
|
|
<div class="rail-separator"></div>
|
|
<!-- Dynamic contextual indicators (shown only while active) -->
|
|
<button class="icon-rail-btn rail-dynamic" id="rail-chats" title="Chat ready" style="display:none"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg></button>
|
|
<button class="icon-rail-btn rail-dynamic" id="rail-documents" title="Documents" style="display:none"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="8" y1="13" x2="16" y2="13"/><line x1="8" y1="17" x2="13" y2="17"/></svg></button>
|
|
<!-- Tool launchers — always visible, alphabetical -->
|
|
<button class="icon-rail-btn" id="rail-calendar" title="Calendar"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="4" width="18" height="18" rx="2"/><line x1="16" y1="2" x2="16" y2="6"/><line x1="8" y1="2" x2="8" y2="6"/><line x1="3" y1="10" x2="21" y2="10"/></svg></button>
|
|
<button class="icon-rail-btn" id="rail-compare" title="Compare"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="18" cy="18" r="3"/><circle cx="6" cy="6" r="3"/><path d="M13 6h3a2 2 0 0 1 2 2v7"/><path d="M11 18H8a2 2 0 0 1-2-2V9"/></svg></button>
|
|
<button class="icon-rail-btn" id="rail-cookbook" title="Cookbook"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round" style="opacity:0.7"><path d="M12 7v14"/><path d="M3 18a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h5a4 4 0 0 1 4 4 4 4 0 0 1 4-4h5a1 1 0 0 1 1 1v13a1 1 0 0 1-1 1h-6a3 3 0 0 0-3 3 3 3 0 0 0-3-3z"/></svg></button>
|
|
<button class="icon-rail-btn" id="rail-research" title="Deep Research"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="11" cy="11" r="8"/><path d="M21 21l-4.35-4.35"/><line x1="11" y1="8" x2="11" y2="14"/><line x1="8" y1="11" x2="14" y2="11"/></svg></button>
|
|
<button class="icon-rail-btn" id="rail-email" title="Email"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="4" width="20" height="16" rx="2"/><path d="m22 7-8.97 5.7a1.94 1.94 0 0 1-2.06 0L2 7"/></svg></button>
|
|
<button class="icon-rail-btn" id="rail-gallery" title="Gallery"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2"/><circle cx="8.5" cy="8.5" r="1.5"/><path d="M21 15l-5-5L5 21"/></svg></button>
|
|
<button class="icon-rail-btn" id="rail-archive" title="Library"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"/><path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"/><path d="M9 7h6M9 11h4"/></svg></button>
|
|
<button class="icon-rail-btn" id="rail-memory" title="Brain"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 5a3 3 0 1 0-5.997.125 4 4 0 0 0-2.526 5.77 4 4 0 0 0 .556 6.588A4 4 0 1 0 12 18Z"/><path d="M12 5a3 3 0 1 1 5.997.125 4 4 0 0 1 2.526 5.77 4 4 0 0 1-.556 6.588A4 4 0 1 1 12 18Z"/><path d="M15 13a4.5 4.5 0 0 1-3-4 4.5 4.5 0 0 1-3 4"/></svg></button>
|
|
<button class="icon-rail-btn" id="rail-notes" title="Notes"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 3h10l4 4v14H5z"/><path d="M15 3v5h5"/><path d="M8 17.5 15.5 10l2.5 2.5L10.5 20H8z"/></svg></button>
|
|
<button class="icon-rail-btn" id="rail-tasks" title="Tasks"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="4" width="18" height="18" rx="2"/><line x1="16" y1="2" x2="16" y2="6"/><line x1="8" y1="2" x2="8" y2="6"/><line x1="3" y1="10" x2="21" y2="10"/><path d="M9 16l2 2 4-4"/></svg></button>
|
|
<button class="icon-rail-btn" id="rail-theme" title="Theme"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="M12 2a10 10 0 0 0 0 20 5 5 0 0 0 5-5 3 3 0 0 0-3-3h-2a3 3 0 0 1-3-3 5 5 0 0 1 5-5"/></svg></button>
|
|
<div style="flex:1"></div>
|
|
<!-- Static: bottom -->
|
|
<button class="icon-rail-btn" id="rail-settings" title="Settings"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg></button>
|
|
</div>
|
|
|
|
<nav class="sidebar" id="sidebar" role="navigation" aria-label="Sidebar">
|
|
<div class="sidebar-resize-handle" id="sidebar-resize-handle"></div>
|
|
<div class="sidebar-header">
|
|
<button class="sidebar-hamburger" id="sidebar-toggle-btn" title="Toggle sidebar"><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round"><line x1="3" y1="6" x2="21" y2="6"/><line x1="3" y1="12" x2="21" y2="12"/><line x1="3" y1="18" x2="21" y2="18"/></svg></button>
|
|
<div class="sidebar-brand" id="sidebar-brand-btn" style="cursor:pointer;" title="New chat">
|
|
<span class="sidebar-brand-title">Odysseus</span>
|
|
</div>
|
|
</div>
|
|
<div class="sidebar-inner">
|
|
<div class="list-item" id="sidebar-new-chat-btn" title="New chat">
|
|
<svg class="sidebar-action-icon" width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" style="position:relative;left:-2px;"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>
|
|
<span class="grow" style="position:relative;left:-3px;">New Chat</span>
|
|
</div>
|
|
<div class="list-item" id="sidebar-search-btn" title="Search conversations (Ctrl+K)">
|
|
<svg class="sidebar-action-icon" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round"><circle cx="10" cy="10" r="7"/><path d="M21 21l-4.35-4.35"/></svg>
|
|
<span class="grow" style="position:relative;left:-1px;">Search</span>
|
|
</div>
|
|
<!-- Assistant sidebar entry removed — its log + settings live inside
|
|
the Tasks modal (Activity + Settings tabs). The backend
|
|
assistant session is still maintained for log_to_assistant()
|
|
writes from scheduled tasks. -->
|
|
|
|
<div class="section" id="sessions-section">
|
|
<div class="section-header-flex">
|
|
<span class="section-title" id="chats-section-title"><svg class="section-icon" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg><span id="chats-section-label" class="section-title-label">Chats</span><span id="chats-notif-dot" class="sidebar-notif-dot" style="display:none"></span></span>
|
|
<div style="position:relative; display:inline-block; display:flex; gap:4px; align-items:center;">
|
|
<button type="button" class="section-header-btn chats-manage-btn" id="chats-library-btn" title="Manage Chats (Library)">
|
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
<path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"/>
|
|
<path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"/>
|
|
<path d="M9 7h6M9 11h4"/>
|
|
</svg>
|
|
</button>
|
|
<button type="button" class="section-header-btn" id="session-sort-btn" title="Sort sessions">
|
|
<svg class="sort-icon" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
<line x1="4" y1="6" x2="20" y2="6"/>
|
|
<line x1="4" y1="12" x2="14" y2="12"/>
|
|
<line x1="4" y1="18" x2="9" y2="18"/>
|
|
</svg>
|
|
<svg class="sort-spinner" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" style="display:none; animation: spin 0.7s linear infinite;">
|
|
<path d="M12 2a10 10 0 0 1 10 10" />
|
|
</svg>
|
|
</button>
|
|
<div id="session-sort-dropdown" class="dropdown sort-dropdown" style="display:none;">
|
|
<div class="dropdown-item sort-option sort-dropdown-item" data-sort="active">Last Active</div>
|
|
<div class="dropdown-item sort-option sort-dropdown-item" data-sort="newest">Newest First</div>
|
|
<div class="dropdown-item sort-option sort-dropdown-item" data-sort="group">By Folder</div>
|
|
<div class="dropdown-item sort-dropdown-item sort-dropdown-sep" id="auto-sort-sessions-row" style="display:flex;align-items:center;padding:0;">
|
|
<span id="auto-sort-sessions-btn" style="flex:1;padding:5px 10px;cursor:pointer;display:inline-flex;align-items:center;gap:4px;">
|
|
<span class="auto-sort-icon"><svg width="11" height="11" viewBox="0 0 24 24" fill="currentColor" style="vertical-align:-1px;margin-right:2px;"><path d="M12 0L14.59 8.41L23 12L14.59 15.59L12 24L9.41 15.59L1 12L9.41 8.41Z"/></svg> Tidy</span>
|
|
<span class="auto-sort-spinner" style="display:none;">Sorting...</span>
|
|
</span>
|
|
<button type="button" id="auto-sort-sessions-more" title="Tidy options" aria-label="Tidy options" style="background:none;border:none;border-left:1px solid var(--border);color:inherit;cursor:pointer;padding:5px 8px;font-size:9px;opacity:0.7;"><svg width="8" height="8" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 12 15 18 9"/></svg></button>
|
|
</div>
|
|
<div class="dropdown-item sort-dropdown-item" id="auto-sort-sessions-noai-btn" style="display:none;padding-left:24px;">
|
|
Tidy <span class="auto-sort-noai-spinner" style="display:none;font-size:9px;opacity:0.6;margin-left:4px;">Cleaning...</span>
|
|
</div>
|
|
<div class="dropdown-item rearrange-toggle sort-dropdown-item sort-dropdown-sep" id="session-rearrange-toggle">
|
|
↑↓ Rearrange <span class="rearrange-check" style="float:right; opacity:0;">•</span>
|
|
</div>
|
|
<div class="dropdown-item sort-dropdown-item" id="session-select-from-dropdown">
|
|
● Select
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div id="session-bulk-bar" class="session-bulk-bar hidden">
|
|
<span id="session-select-all-dot" style="cursor:pointer;font-size:14px;opacity:0.4;user-select:none;">○</span><input type="checkbox" id="session-select-all" style="display:none;"><span style="font-size:10px;cursor:pointer;opacity:0.5;" id="session-select-all-label">All</span>
|
|
<div style="margin-left:auto;display:flex;gap:2px;align-items:center;">
|
|
<button class="session-bulk-btn" id="session-bulk-archive" title="Archive selected"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="3" width="20" height="5" rx="1"/><path d="M4 8v11a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8"/><path d="M10 12h4"/></svg></button>
|
|
<button class="session-bulk-btn session-bulk-btn-danger" id="session-bulk-delete" title="Delete selected"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 6h18"/><path d="M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6"/></svg></button>
|
|
<button class="session-bulk-btn" id="session-bulk-cancel" title="Cancel"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg></button>
|
|
</div>
|
|
</div>
|
|
<div id="session-list" role="listbox"></div>
|
|
</div>
|
|
<!-- Hidden dropdown for session actions -->
|
|
<div id="session-actions-dropdown" class="dropdown hidden">
|
|
<div class="dropdown-item" id="rename-session-option">
|
|
<h4>Rename</h4>
|
|
<p>Change the session name</p>
|
|
</div>
|
|
<div class="dropdown-item" id="delete-session-option">
|
|
<h4>Delete</h4>
|
|
<p>Remove this session permanently</p>
|
|
</div>
|
|
<div class="dropdown-item" id="memory-session-option">
|
|
<h4>Memory</h4>
|
|
<p>Extract memories from this session</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="section" id="email-section">
|
|
<div class="section-header-flex">
|
|
<span class="section-title" id="email-section-title" style="cursor:pointer;" title="Open email inbox"><svg class="section-icon" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="4" width="20" height="16" rx="2"/><path d="m22 7-8.97 5.7a1.94 1.94 0 0 1-2.06 0L2 7"/></svg><span>Email</span></span>
|
|
<button type="button" class="section-header-btn list-item-plus-btn" id="email-compose-btn" title="Compose email">
|
|
<svg class="list-item-plus-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>
|
|
<span class="list-item-plus-label">new</span>
|
|
</button>
|
|
<!-- Unread dot is a direct header child placed AFTER the compose
|
|
button (it used to live inside the title, before the button).
|
|
With the title's flex:1 eating the free space, this lands the
|
|
dot at the far-right corner with the + just to its left —
|
|
i.e. the dot and + swap places when the dot appears. -->
|
|
<span id="email-unread-dot" class="sidebar-notif-dot" style="display:none"></span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="section" id="models-section">
|
|
<div class="section-header-flex">
|
|
<span class="section-title"> Models</span>
|
|
<div style="position:relative; display:inline-block;">
|
|
<button type="button" class="section-header-btn" id="model-sort-btn" title="Sort models">
|
|
<svg class="sort-icon" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
<line x1="4" y1="6" x2="20" y2="6"/>
|
|
<line x1="4" y1="12" x2="14" y2="12"/>
|
|
<line x1="4" y1="18" x2="9" y2="18"/>
|
|
</svg>
|
|
</button>
|
|
<div id="model-sort-dropdown" class="dropdown sort-dropdown" style="display:none;">
|
|
<div class="dropdown-item sort-option sort-dropdown-item" data-sort="alpha">A-Z</div>
|
|
<div class="dropdown-item sort-option sort-dropdown-item" data-sort="last-used">Last used</div>
|
|
<div class="dropdown-item sort-option sort-dropdown-item" data-sort="most-used">Most used</div>
|
|
<div class="dropdown-item rearrange-toggle sort-dropdown-item sort-dropdown-sep" id="model-rearrange-toggle">
|
|
↑↓ Rearrange <span class="rearrange-check" style="float:right; opacity:0;">•</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div id="models">
|
|
<div class="models-row">
|
|
<select id="model-select" aria-label="Select model" style="flex: 1; padding: 6px 8px; border-radius: 4px; border: 1px solid var(--border); background: var(--bg); color: var(--fg);"></select>
|
|
<button type="button" id="btn-model-chat" class="model-chat-btn" aria-label="Add model chat" style="transition: all 0.2s ease;"><span class="model-chat-btn-label">+ Chat</span></button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="section" id="tools-section">
|
|
<div class="section-header-flex">
|
|
<span class="section-title"><svg class="section-icon" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"/></svg>Tools</span>
|
|
</div>
|
|
<div class="list-item" id="tool-memory-btn">
|
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
|
stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
|
|
style="flex-shrink:0;opacity:0.5;">
|
|
<path d="M12 5a3 3 0 1 0-5.997.125 4 4 0 0 0-2.526 5.77 4 4 0 0 0 .556 6.588A4 4 0 1 0 12 18Z"/>
|
|
<path d="M12 5a3 3 0 1 1 5.997.125 4 4 0 0 1 2.526 5.77 4 4 0 0 1-.556 6.588A4 4 0 1 1 12 18Z"/>
|
|
<path d="M15 13a4.5 4.5 0 0 1-3-4 4.5 4.5 0 0 1-3 4"/>
|
|
</svg>
|
|
<span class="grow">Brain</span>
|
|
</div>
|
|
<div class="list-item" id="tool-calendar-btn">
|
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
|
stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
|
|
style="flex-shrink:0;opacity:0.5;">
|
|
<rect x="3" y="4" width="18" height="18" rx="2"/>
|
|
<line x1="16" y1="2" x2="16" y2="6"/>
|
|
<line x1="8" y1="2" x2="8" y2="6"/>
|
|
<line x1="3" y1="10" x2="21" y2="10"/>
|
|
</svg>
|
|
<span class="grow">Calendar</span>
|
|
</div>
|
|
<div class="list-item" id="tool-compare-btn">
|
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="flex-shrink:0;opacity:0.5;"><circle cx="18" cy="18" r="3"/><circle cx="6" cy="6" r="3"/><path d="M13 6h3a2 2 0 0 1 2 2v7"/><path d="M11 18H8a2 2 0 0 1-2-2V9"/></svg>
|
|
<span class="grow">Compare</span>
|
|
</div>
|
|
<div class="list-item" id="tool-cookbook-btn">
|
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
|
stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"
|
|
style="flex-shrink:0;opacity:0.5;">
|
|
<path d="M12 7v14"/>
|
|
<path d="M3 18a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h5a4 4 0 0 1 4 4 4 4 0 0 1 4-4h5a1 1 0 0 1 1 1v13a1 1 0 0 1-1 1h-6a3 3 0 0 0-3 3 3 3 0 0 0-3-3z"/>
|
|
</svg>
|
|
<span class="grow">Cookbook</span>
|
|
<span id="cookbook-bg-status" style="display:none;font-size:9px;opacity:0.5;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;margin-right:12px;flex-shrink:1;min-width:0;position:relative;top:-1px;"></span>
|
|
<span class="cookbook-notif-dot" id="cookbook-notif-dot" style="display:none;margin-left:6px;margin-right:4px;position:relative;top:-1px;left:0px;"></span>
|
|
</div>
|
|
<div class="list-item" id="tool-research-btn">
|
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="flex-shrink:0;opacity:0.5;"><circle cx="11" cy="11" r="8"/><path d="M21 21l-4.35-4.35"/><line x1="11" y1="8" x2="11" y2="14"/><line x1="8" y1="11" x2="14" y2="11"/></svg>
|
|
<span class="grow">Deep Research</span>
|
|
</div>
|
|
<div class="list-item" id="tool-gallery-btn">
|
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
|
stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
|
|
style="flex-shrink:0;opacity:0.5;">
|
|
<rect x="3" y="3" width="18" height="18" rx="2"/>
|
|
<circle cx="8.5" cy="8.5" r="1.5"/>
|
|
<path d="M21 15l-5-5L5 21"/>
|
|
</svg>
|
|
<span class="grow">Gallery</span>
|
|
</div>
|
|
<div class="list-item" id="tool-library-btn">
|
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="flex-shrink:0;opacity:0.5;">
|
|
<path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"/><path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"/><path d="M9 7h6M9 11h4"/>
|
|
</svg>
|
|
<span class="grow">Library</span>
|
|
<button type="button" class="list-item-plus-btn" id="library-new-doc-btn" title="New document">
|
|
<svg class="list-item-plus-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" style="width:11px;height:11px;"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>
|
|
<span class="list-item-plus-label">new</span>
|
|
</button>
|
|
</div>
|
|
<div class="list-item" id="tool-notes-btn">
|
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
|
stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
|
|
style="flex-shrink:0;opacity:0.5;">
|
|
<path d="M5 3h10l4 4v14H5z"/>
|
|
<path d="M15 3v5h5"/>
|
|
<path d="M8 17.5 15.5 10l2.5 2.5L10.5 20H8z"/>
|
|
</svg>
|
|
<span class="grow">Notes</span>
|
|
</div>
|
|
<div class="list-item" id="tool-tasks-btn">
|
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
|
stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
|
|
style="flex-shrink:0;opacity:0.5;">
|
|
<rect x="3" y="4" width="18" height="18" rx="2"/>
|
|
<line x1="16" y1="2" x2="16" y2="6"/>
|
|
<line x1="8" y1="2" x2="8" y2="6"/>
|
|
<line x1="3" y1="10" x2="21" y2="10"/>
|
|
<path d="M9 16l2 2 4-4"/>
|
|
</svg>
|
|
<span class="grow">Tasks</span>
|
|
<span id="assistant-notif-dot" class="sidebar-notif-dot" style="display:none"></span>
|
|
</div>
|
|
|
|
<div class="list-item" id="tool-theme-btn">
|
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
|
stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
|
|
style="flex-shrink:0;opacity:0.5;">
|
|
<circle cx="12" cy="12" r="10"/>
|
|
<path d="M12 2a7 7 0 0 0 0 20 4 4 0 0 1 0-8 4 4 0 0 0 0-8"/>
|
|
<circle cx="8" cy="9" r="1.5" fill="currentColor"/>
|
|
<circle cx="15" cy="14" r="1.5" fill="currentColor"/>
|
|
<circle cx="9" cy="15" r="1.5" fill="currentColor"/>
|
|
</svg>
|
|
<span class="grow">Theme</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="sidebar-user-bar" id="sidebar-user-bar">
|
|
<div class="user-bar-left" id="user-bar-profile">
|
|
<div class="user-bar-avatar" id="user-bar-avatar"></div>
|
|
<span class="user-bar-name" id="user-bar-name">User</span>
|
|
</div>
|
|
<div class="user-bar-actions">
|
|
<button type="button" class="user-bar-btn" id="user-bar-settings" title="Settings">
|
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</nav>
|
|
|
|
<main class="chat-container welcome-active" id="chat-container" aria-label="Chat area" aria-busy="false">
|
|
<!-- Persistent page heading for assistive tech. Visually hidden so it
|
|
never affects layout, but always present inside the main landmark
|
|
(the sidebar that shows the visible brand is hidden off-canvas on
|
|
mobile) so the page always exposes a single level-1 heading. -->
|
|
<h1 class="a11y-visually-hidden">Odysseus</h1>
|
|
<div class="chat-top-bar">
|
|
<button type="button" class="incognito-indicator" id="incognito-indicator" title="Nobody mode active — click to deactivate" style="display:none;"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><line x1="8" y1="16" x2="16" y2="8"/><line x1="8" y1="8" x2="16" y2="16"/></svg></button>
|
|
<div class="chat-meta-overlay"><span id="current-meta">Odysseus Chat</span><span id="current-meta-count" class="chat-meta-count" aria-hidden="true"></span><span id="session-cost-display" class="session-cost-display" style="display:none;"></span><span class="export-dropdown-wrap" id="export-dropdown-wrap"><button type="button" class="export-dl-btn" id="export-dl-btn" title="More"><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 12 15 18 9"/></svg></button><div class="export-dropdown-menu" id="export-dropdown-menu"><div class="export-dropdown-item" id="export-rename-btn"><span class="dropdown-icon"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M17 3a2.83 2.83 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5Z"/></svg></span><span>Rename</span></div><div class="export-dropdown-item" id="export-copy-btn"><span class="dropdown-icon"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg></span><span>Copy Chat</span></div><div class="export-dropdown-item" id="export-pdf-btn"><span class="dropdown-icon"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><path d="M9 15v-2h2a1.5 1.5 0 0 1 0 3H9z"/></svg></span><span>PDF</span></div><div class="export-dropdown-item" id="export-doc-btn"><span class="dropdown-icon"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="16" y1="13" x2="8" y2="13"/><line x1="16" y1="17" x2="8" y2="17"/><polyline points="10 9 9 9 8 9"/></svg></span><span>Save to Documents</span></div></div></span></div> </div>
|
|
<div id="welcome-screen">
|
|
<div class="welcome-name"><svg class="welcome-boat" viewBox="0 0 32 32"><path d="M16 4L16 22L6 22Z" fill="currentColor"/><path d="M16 8L16 22L24 22Z" fill="currentColor" opacity="0.6"/><path d="M4 24Q10 20 16 24Q22 28 28 24" stroke="currentColor" stroke-width="2.5" fill="none" stroke-linecap="round"/></svg>Odysseus</div>
|
|
<div class="welcome-sub" id="welcome-sub">Welcome, <span class="setup-trigger-link" style="color:var(--accent,var(--red));font-weight:600;cursor:pointer;text-decoration:underline;" title="Click to launch setup">type /setup</span> to get started.</div>
|
|
<div class="welcome-tip" id="welcome-tip"></div>
|
|
<button type="button" class="incognito-btn" id="incognito-btn" title="Enable Nobody mode — no memory, no history saved">
|
|
<svg class="eye-open" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
<path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/>
|
|
</svg>
|
|
<svg class="eye-blinded" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="display:none">
|
|
<path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><line x1="8" y1="16" x2="16" y2="8"/><line x1="8" y1="8" x2="16" y2="16"/>
|
|
</svg>
|
|
<span class="incognito-label">Nobody</span>
|
|
</button>
|
|
<div id="welcome-setup" style="display:none"></div>
|
|
</div>
|
|
<script nonce="{{CSP_NONCE}}">
|
|
(function(){
|
|
var mobile = window.matchMedia('(max-width: 768px)').matches;
|
|
var desktop = [
|
|
'Tip: Press Ctrl+K to search across all your conversations.',
|
|
'Tip: Press Ctrl+B to quickly toggle the sidebar.',
|
|
'Tip: Shift-click the sidebar toggle to swap it to the other side.',
|
|
'Tip: Drag and drop files onto the chat to attach them.',
|
|
'Tip: Right-click a session for rename, delete, and memory options.',
|
|
];
|
|
var phone = [
|
|
'Tip: Long-press a session for rename, delete, and memory options.',
|
|
'Tip: Tap the eye icon for Nobody mode — no history saved.',
|
|
'Tip: Switch to Agent mode for web search and code execution.',
|
|
'Tip: Use Compare mode to test different models side by side.',
|
|
'Tip: Attach images or files using the + button next to the input.',
|
|
];
|
|
var tips = mobile ? phone : desktop;
|
|
var el = document.getElementById('welcome-tip');
|
|
if (el) {
|
|
el.textContent = 'Type /setup, then choose Local models or API.';
|
|
}
|
|
fetch('/api/version').then(function(r){return r.json()}).then(function(d){
|
|
if (d.version) window._appVersion = d.version;
|
|
}).catch(function(){});
|
|
})();
|
|
</script>
|
|
<div id="chat-history" class="chat-history" role="log" aria-live="polite"></div>
|
|
|
|
<!-- Attachments strip -->
|
|
<div id="attach-strip" class="attach-strip"></div>
|
|
|
|
<!-- Hidden elements for form logic -->
|
|
<input type="checkbox" id="research-toggle" style="display:none;">
|
|
<input type="checkbox" id="rag-toggle" style="display:none;">
|
|
<input type="checkbox" id="incognito-toggle" style="display:none;">
|
|
<input type="file" id="file-input" class="hidden" multiple />
|
|
|
|
<!-- Unified chat input bar -->
|
|
<div class="chat-input-bar">
|
|
<div class="chat-input-top">
|
|
<div id="message-ghost" class="ghost-text-overlay" aria-hidden="true"></div>
|
|
<textarea id="message" placeholder="Message Odysseus..." required autocomplete="off" aria-label="Message input" rows="1" autofocus></textarea>
|
|
<!-- Model picker (inside chatbox, top-right) -->
|
|
<div class="model-picker-wrap" id="model-picker-wrap">
|
|
<button type="button" class="model-picker-btn" id="model-picker-btn" title="Switch model"><span id="model-picker-label">Select model</span> <svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 15 12 9 18 15"/></svg></button>
|
|
<div class="model-picker-menu hidden" id="model-picker-menu">
|
|
<div class="model-picker-search-row">
|
|
<input type="text" id="model-picker-search" placeholder="Search models..." autocomplete="off" aria-label="Search models">
|
|
<button type="button" class="model-picker-action-btn primary" id="model-picker-add-models-btn" title="Add model endpoints" aria-label="Add model endpoints">
|
|
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"><path d="M12 5v14"/><path d="M5 12h14"/></svg>
|
|
</button>
|
|
</div>
|
|
<div class="model-picker-list" id="model-picker-list"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div id="pinned-tools-bar"></div>
|
|
<div class="chat-input-bottom" style="visibility:hidden">
|
|
<div class="chat-input-left">
|
|
<!-- Overflow menu (+) — always first/left -->
|
|
<div class="overflow-wrapper">
|
|
<button type="button" class="input-icon-btn overflow-plus-btn" id="overflow-plus-btn" title="More tools" aria-label="More tools" aria-haspopup="true">
|
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
|
<polyline points="6 15 12 9 18 15"/>
|
|
</svg>
|
|
<span class="plus-active-dot"></span>
|
|
</button>
|
|
<div id="overflow-menu" class="overflow-menu hidden">
|
|
<button type="button" class="overflow-menu-item" id="overflow-attach-btn">
|
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21.44 11.05l-9.19 9.19a6 6 0 0 1-8.49-8.49l9.19-9.19a4 4 0 0 1 5.66 5.66l-9.2 9.19a2 2 0 0 1-2.83-2.83l8.49-8.48"/></svg>
|
|
<span>Attach files</span>
|
|
</button>
|
|
<button type="button" class="overflow-menu-item" id="overflow-doc-btn">
|
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="16" y1="13" x2="8" y2="13"/><line x1="16" y1="17" x2="8" y2="17"/><polyline points="10 9 9 9 8 9"/>
|
|
</svg>
|
|
<span>Documents</span>
|
|
</button>
|
|
<button type="button" class="overflow-menu-item" id="overflow-rag-btn">
|
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
<ellipse cx="12" cy="5" rx="9" ry="3"/><path d="M21 12c0 1.66-4 3-9 3s-9-1.34-9-3"/><path d="M3 5v14c0 1.66 4 3 9 3s9-1.34 9-3V5"/>
|
|
</svg>
|
|
<span>RAG</span>
|
|
<span class="overflow-active-dot"></span>
|
|
</button>
|
|
<button type="button" class="overflow-menu-item" id="overflow-workspace-btn">
|
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
<path d="M3 7a2 2 0 0 1 2-2h4l2 2h8a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/>
|
|
</svg>
|
|
<span>Workspace</span>
|
|
<span class="overflow-active-dot"></span>
|
|
</button>
|
|
<!-- Inline "deep research mode" toggle removed (superseded by the
|
|
Deep Research sidebar / trigger_research). The hidden
|
|
#research-toggle checkbox is kept inert so existing JS refs
|
|
don't break; without this entry point it can't be enabled. -->
|
|
<!-- Group Chat moved to Characters modal Group tab -->
|
|
<!-- TTS Mode hidden — read-aloud feature is off in this build. -->
|
|
<button type="button" class="overflow-menu-item" id="overflow-tts-btn" hidden style="display:none">
|
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"/><path d="M19.07 4.93a10 10 0 0 1 0 14.14"/><path d="M15.54 8.46a5 5 0 0 1 0 7.07"/></svg>
|
|
<span>TTS Mode</span>
|
|
<span class="overflow-active-dot"></span>
|
|
</button>
|
|
<button type="button" class="overflow-menu-item" id="overflow-preset-btn">
|
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
<path d="m18 2 4 4"/><path d="m17 7 3-3"/><path d="M19 9 8.7 19.3c-1 1-2.5 1-3.4 0l-.6-.6c-1-1-1-2.5 0-3.4L15 5"/><path d="m9 11 4 4"/><path d="m5 19-3 3"/><path d="m14 4 6 6"/>
|
|
</svg>
|
|
<span>Prompt</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<!-- Web search (magnifying glass) -->
|
|
<button type="button" class="input-icon-btn" title="Web search" id="web-toggle-btn" data-mode-tool="true" aria-label="Web search" aria-pressed="false">
|
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
<circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/>
|
|
</svg>
|
|
</button>
|
|
<!-- Shell commands (terminal) -->
|
|
<button type="button" class="input-icon-btn" title="Shell Access" id="bash-toggle-btn" data-mode-tool="true" aria-label="Shell access" aria-pressed="false">
|
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
<polyline points="4 17 10 11 4 5"/><line x1="12" y1="19" x2="20" y2="19"/>
|
|
</svg>
|
|
</button>
|
|
<!-- Workspace indicator (hidden until a folder is set) -->
|
|
<button type="button" class="input-icon-btn tool-indicator" title="Workspace — click to clear" id="workspace-indicator-btn" aria-label="Clear workspace" style="display:none;">
|
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 7a2 2 0 0 1 2-2h4l2 2h8a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/></svg>
|
|
<span style="font-size:11px;margin-left:2px;max-width:120px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;" id="workspace-indicator-name"></span>
|
|
<svg class="tool-indicator-x" width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round"><line x1="6" y1="6" x2="18" y2="18"/><line x1="18" y1="6" x2="6" y2="18"/></svg>
|
|
</button>
|
|
<!-- Plan mode (investigate read-only, propose a plan to approve) -->
|
|
<button type="button" class="input-icon-btn" title="Plan mode — investigate read-only, then propose a plan to approve" id="plan-toggle-btn" data-mode-tool="true">
|
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
<path d="M9 11l3 3L22 4"/><path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11"/>
|
|
</svg>
|
|
</button>
|
|
<!-- RAG toolbar indicator (hidden until active) -->
|
|
<button type="button" class="input-icon-btn tool-indicator" title="RAG active — click to deactivate" id="rag-indicator-btn" style="display:none;">
|
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
<ellipse cx="12" cy="5" rx="9" ry="3"/><path d="M21 12c0 1.66-4 3-9 3s-9-1.34-9-3"/><path d="M3 5v14c0 1.66 4 3 9 3s9-1.34 9-3V5"/>
|
|
</svg>
|
|
<span style="font-size:11px;margin-left:2px;">RAG</span>
|
|
<svg class="tool-indicator-x" width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round"><line x1="6" y1="6" x2="18" y2="18"/><line x1="18" y1="6" x2="6" y2="18"/></svg>
|
|
</button>
|
|
<!-- 6. Deep Research (hidden until active) -->
|
|
<button type="button" class="input-icon-btn tool-indicator" title="Deep Research active — click to deactivate" id="research-toggle-btn" style="display:none;">
|
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="11" cy="11" r="8"/><path d="M21 21l-4.35-4.35"/><line x1="11" y1="8" x2="11" y2="14"/><line x1="8" y1="11" x2="14" y2="11"/></svg>
|
|
<span style="font-size:11px;margin-left:2px;">Research</span>
|
|
<svg class="tool-indicator-x" width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round"><line x1="6" y1="6" x2="18" y2="18"/><line x1="18" y1="6" x2="6" y2="18"/></svg>
|
|
</button>
|
|
<!-- 7. Group Chat (hidden until active) -->
|
|
<button type="button" class="input-icon-btn tool-indicator" title="Group Chat active — click to deactivate" id="group-toggle-btn" style="display:none;">
|
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M23 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/></svg>
|
|
<span style="font-size:11px;margin-left:2px;">Group</span>
|
|
<svg class="tool-indicator-x" width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round"><line x1="6" y1="6" x2="18" y2="18"/><line x1="18" y1="6" x2="6" y2="18"/></svg>
|
|
</button>
|
|
<input type="checkbox" id="group-toggle" style="display:none;">
|
|
<!-- Character indicator (hidden until active) -->
|
|
<button type="button" class="input-icon-btn tool-indicator" title="Persona active — click to deactivate" id="character-indicator-btn" style="display:none;">
|
|
<svg id="char-indicator-icon" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></svg>
|
|
<span id="character-indicator-name" style="font-size:11px;margin-left:2px;max-width:80px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;"></span>
|
|
<svg class="tool-indicator-x" width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round"><line x1="6" y1="6" x2="18" y2="18"/><line x1="18" y1="6" x2="6" y2="18"/></svg>
|
|
</button>
|
|
<!-- Compare toolbar indicator (hidden until active) -->
|
|
<button type="button" class="input-icon-btn tool-indicator" title="Compare active — click to deactivate" id="compare-indicator-btn" style="display:none;">
|
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="18" cy="18" r="3"/><circle cx="6" cy="6" r="3"/><path d="M13 6h3a2 2 0 0 1 2 2v7"/><path d="M11 18H8a2 2 0 0 1-2-2V9"/></svg>
|
|
<span style="font-size:11px;margin-left:2px;">Compare</span>
|
|
<svg class="tool-indicator-x" width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round"><line x1="6" y1="6" x2="18" y2="18"/><line x1="18" y1="6" x2="6" y2="18"/></svg>
|
|
</button>
|
|
</div>
|
|
<div class="chat-input-right">
|
|
<!-- Agent / Chat mode toggle -->
|
|
<div class="mode-toggle">
|
|
<button type="button" class="mode-toggle-btn active" id="mode-agent-btn" aria-pressed="true">Agent</button>
|
|
<button type="button" class="mode-toggle-btn" id="mode-chat-btn" aria-pressed="false">Chat</button>
|
|
</div>
|
|
<button type="submit" form="chat-form" class="send-btn newchat-mode" data-mode="newchat" aria-label="New chat">
|
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg><span class="send-btn-label">+ New</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<!-- Hidden checkboxes for state -->
|
|
<input type="checkbox" id="web-toggle" style="display:none;">
|
|
<input type="checkbox" id="bash-toggle" style="display:none;">
|
|
<input type="checkbox" id="plan-toggle" style="display:none;">
|
|
</div>
|
|
<form id="chat-form" autocomplete="off" action="javascript:void(0);" style="display:none;"></form>
|
|
|
|
<!-- Character (custom preset) modal -->
|
|
<div id="custom-preset-modal" class="modal hidden">
|
|
<div class="modal-content preset-modal-content" role="dialog" aria-label="Prompt" style="background:var(--bg)">
|
|
<div class="modal-header">
|
|
<h4><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:-2px;margin-right:6px"><path d="m18 2 4 4"/><path d="m17 7 3-3"/><path d="M19 9 8.7 19.3c-1 1-2.5 1-3.4 0l-.6-.6c-1-1-1-2.5 0-3.4L15 5"/><path d="m9 11 4 4"/><path d="m5 19-3 3"/><path d="m14 4 6 6"/></svg>Prompt</h4>
|
|
<button class="close-btn" id="close-custom-preset" aria-label="Close prompt">✖</button>
|
|
</div>
|
|
<div class="modal-body preset-modal-body">
|
|
<div id="char-fields-wrap">
|
|
<div class="preset-tabs">
|
|
<button class="preset-tab active" data-chartab="inject"><svg class="preset-tab-icon" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m18 2 4 4"/><path d="m17 7 3-3"/><path d="M19 9 8.7 19.3c-1 1-2.5 1-3.4 0l-.6-.6c-1-1-1-2.5 0-3.4L15 5"/><path d="m9 11 4 4"/><path d="m5 19-3 3"/><path d="m14 4 6 6"/></svg><span>Inject</span></button>
|
|
<button class="preset-tab" data-chartab="character"><svg class="preset-tab-icon" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></svg><span>Persona</span></button>
|
|
<button class="preset-tab" data-chartab="group"><svg class="preset-tab-icon" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M22 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/></svg><span>Group</span></button>
|
|
</div>
|
|
<!-- Inject tab (also holds model tuning: temperature + max tokens) -->
|
|
<div class="preset-chartab" data-chartab-panel="inject">
|
|
<label for="inject-prefix">Prefix</label>
|
|
<textarea id="inject-prefix" rows="2" placeholder="Added before your message" style="margin-bottom:8px"></textarea>
|
|
<label for="inject-suffix">Suffix</label>
|
|
<textarea id="inject-suffix" rows="2" placeholder="Added after your message" style="margin-bottom:12px"></textarea>
|
|
<div class="preset-slider-row">
|
|
<label>Temperature <span class="preset-hint-icon" title="Controls randomness. Lower values give focused, deterministic answers (good for code). Higher values give more creative, varied responses.">?</span></label>
|
|
<span class="preset-slider-value" id="temp-value">1.0</span>
|
|
</div>
|
|
<input type="range" class="preset-range" id="custom-temperature" min="0" max="2" step="0.1" value="1.0">
|
|
<div class="preset-temp-hints">
|
|
<span>Precise / Code</span>
|
|
<span>Balanced</span>
|
|
<span>Creative</span>
|
|
</div>
|
|
<div class="preset-slider-row">
|
|
<label>Max Tokens <span class="preset-hint-icon" title="Maximum length of the AI response. 'No limit' lets the model decide when to stop.">?</span></label>
|
|
<span class="preset-slider-value" id="tokens-value">No limit</span>
|
|
</div>
|
|
<input type="range" class="preset-range" id="custom-max-tokens" min="256" max="8448" step="256" value="8448">
|
|
</div>
|
|
<!-- Prompt (character/persona) tab -->
|
|
<div class="preset-chartab" data-chartab-panel="character" style="display:none">
|
|
<label>Persona</label>
|
|
<div class="char-name-combo">
|
|
<select id="char-template-select" class="char-template-select">
|
|
<option value="">Select persona...</option>
|
|
</select>
|
|
<button type="button" id="char-new-btn" class="char-action-btn" title="Create a new persona">+ New</button>
|
|
</div>
|
|
<div id="char-name-row">
|
|
<label for="custom-character-name">Name</label>
|
|
<div class="char-name-combo">
|
|
<input type="text" id="custom-character-name" maxlength="50" placeholder="Give your persona a name..." autocomplete="off" style="flex:1">
|
|
<button type="button" id="char-delete-template-btn" class="char-action-btn" title="Delete this persona and its memories" style="display:none;margin-top:-6px !important"><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:-2px;margin-right:4px"><polyline points="3 6 5 6 21 6"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/><line x1="10" y1="11" x2="10" y2="17"/><line x1="14" y1="11" x2="14" y2="17"/></svg>Delete</button>
|
|
<button type="button" id="reset-character-btn" class="char-action-btn" title="Reset to default" style="margin-top:-6px !important">↺ Reset</button>
|
|
</div>
|
|
</div>
|
|
<label for="custom-system-prompt">System prompt</label>
|
|
<div class="char-prompt-wrap">
|
|
<textarea id="custom-system-prompt" rows="4" placeholder="Write rough notes and click Expand, or leave empty"></textarea>
|
|
<button type="button" id="char-expand-btn" class="char-expand-btn" title="AI expand — turn your notes into a full system prompt">
|
|
<svg width="11" height="11" viewBox="0 0 24 24" fill="currentColor" style="vertical-align:-1px;margin-right:2px;"><path d="M12 0L14.59 8.41L23 12L14.59 15.59L12 24L9.41 15.59L1 12L9.41 8.41Z"/></svg>
|
|
Expand
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<!-- Group tab -->
|
|
<div class="preset-chartab" data-chartab-panel="group" style="display:none">
|
|
<div style="display:flex;gap:4px;margin-bottom:8px;">
|
|
<button type="button" class="compare-parallel-toggle" id="group-mode-btn" style="flex:1;height:auto;width:auto;">
|
|
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><line x1="8" y1="6" x2="20" y2="6"/><line x1="8" y1="12" x2="20" y2="12"/><line x1="8" y1="18" x2="20" y2="18"/><circle cx="4" cy="6" r="1.5" fill="currentColor"/><circle cx="4" cy="12" r="1.5" fill="currentColor"/><circle cx="4" cy="18" r="1.5" fill="currentColor"/></svg>
|
|
<span class="compare-toggle-label">Sequential</span>
|
|
</button>
|
|
</div>
|
|
<div id="group-participants" style="display:flex;flex-direction:column;gap:4px;margin-bottom:0;max-height:220px;overflow-y:auto;"></div>
|
|
<button type="button" id="group-add-btn" class="preset-save-btn" style="width:100%;background:none;border:1px dashed var(--border);color:var(--fg);opacity:0.6;font-size:11px;padding:4px;margin-top:0;">+ Add participant</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<div style="flex:1"></div>
|
|
<button type="button" id="cancel-custom-preset" style="margin-right:8px;display:none;">Cancel</button>
|
|
<button type="button" id="save-custom-preset">Start</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
</main>
|
|
<button id="scroll-bottom-btn" class="scroll-nav-btn" title="Scroll to bottom">▼</button>
|
|
<script nonce="{{CSP_NONCE}}">
|
|
(function(){
|
|
const container = document.getElementById('chat-history');
|
|
const chatBar = document.querySelector('.chat-input-bar');
|
|
const bottomBtn = document.getElementById('scroll-bottom-btn');
|
|
if (!container || !chatBar || !bottomBtn) return;
|
|
|
|
function reposition() {
|
|
const barRect = chatBar.getBoundingClientRect();
|
|
bottomBtn.style.bottom = (window.innerHeight - barRect.top + 16) + 'px';
|
|
bottomBtn.style.right = (window.innerWidth - barRect.right + 8) + 'px';
|
|
}
|
|
|
|
function update() {
|
|
reposition();
|
|
const scrollable = container.scrollHeight > container.clientHeight + 10;
|
|
const atBottom = container.scrollHeight - container.scrollTop - container.clientHeight < 300;
|
|
if (scrollable && !atBottom) {
|
|
bottomBtn.classList.remove('slide-out');
|
|
bottomBtn.classList.add('show');
|
|
} else if (bottomBtn.classList.contains('show')) {
|
|
bottomBtn.classList.add('slide-out');
|
|
bottomBtn.classList.remove('show');
|
|
}
|
|
}
|
|
|
|
let _scrollRaf = null;
|
|
let _scrollTimeout = null;
|
|
let _scrollingToBottom = false;
|
|
|
|
function cancelAutoScroll() {
|
|
if (_scrollRaf) { cancelAnimationFrame(_scrollRaf); _scrollRaf = null; }
|
|
if (_scrollTimeout) { clearTimeout(_scrollTimeout); _scrollTimeout = null; }
|
|
_scrollingToBottom = false;
|
|
}
|
|
|
|
container.addEventListener('scroll', update, {passive:true});
|
|
container.addEventListener('wheel', () => { if (_scrollingToBottom) cancelAutoScroll(); }, {passive:true});
|
|
container.addEventListener('touchstart', () => { if (_scrollingToBottom) cancelAutoScroll(); }, {passive:true});
|
|
|
|
bottomBtn.addEventListener('click', () => {
|
|
cancelAutoScroll();
|
|
const target = container.scrollHeight - container.clientHeight;
|
|
_scrollingToBottom = true;
|
|
function step() {
|
|
if (!_scrollingToBottom) return;
|
|
const diff = target - container.scrollTop;
|
|
if (diff <= 8) { container.scrollTop = target; _scrollingToBottom = false; return; }
|
|
container.scrollTop += diff * 0.2;
|
|
_scrollRaf = requestAnimationFrame(step);
|
|
}
|
|
step();
|
|
_scrollTimeout = setTimeout(() => { if (_scrollingToBottom) { container.scrollTop = target; _scrollingToBottom = false; } }, 1500);
|
|
});
|
|
new ResizeObserver(reposition).observe(chatBar);
|
|
new MutationObserver(update).observe(container, {childList:true, subtree:true});
|
|
window.addEventListener('resize', reposition);
|
|
update();
|
|
})();
|
|
</script>
|
|
|
|
<!-- Rename Session Modal -->
|
|
<div id="rename-session-modal" class="modal hidden">
|
|
<div class="modal-content" role="dialog" aria-label="Rename session" style="width: 400px;">
|
|
<div class="modal-header">
|
|
<h4>Rename Session</h4>
|
|
<button class="close-btn" id="close-rename-session" aria-label="Close rename session modal">✖</button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<div style="margin-bottom: 12px;">
|
|
<label for="session-name-input" style="display: block; margin-bottom: 6px; font-weight: 500;">Session Name</label>
|
|
<input
|
|
type="text"
|
|
id="session-name-input"
|
|
placeholder="Enter session name"
|
|
style="width: 100%; padding: 8px; border-radius: 4px;"
|
|
/>
|
|
</div>
|
|
<div style="display: flex; justify-content: flex-end; gap: 8px;">
|
|
<button id="cancel-rename-session" style="padding: 6px 12px;">Cancel</button>
|
|
<button id="save-session-name" style="padding: 6px 12px; background: var(--fg); color: var(--bg);">Save</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<!-- Cookbook Modal -->
|
|
<div id="cookbook-modal" class="modal hidden">
|
|
<div class="modal-content" role="dialog" aria-label="Cookbook" style="width: min(780px, 92vw); height: 94vh; max-height: 94vh; background: var(--bg);">
|
|
<div class="modal-header">
|
|
<h4 style="margin:0;margin-right:auto"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:-2px;margin-right:6px"><path d="M12 7v14"/><path d="M3 18a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h5a4 4 0 0 1 4 4 4 4 0 0 1 4-4h5a1 1 0 0 1 1 1v13a1 1 0 0 1-1 1h-6a3 3 0 0 0-3 3 3 3 0 0 0-3-3z"/></svg>Cookbook</h4>
|
|
<button class="close-btn" id="close-cookbook-modal" aria-label="Close cookbook">✖</button>
|
|
</div>
|
|
<div class="modal-body cookbook-body"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Settings Modal (all users) -->
|
|
<div id="settings-modal" class="modal hidden">
|
|
<div class="modal-content settings-modal-content" role="dialog" aria-label="Settings">
|
|
<div class="modal-header">
|
|
<h4><span style="vertical-align:-1px;margin-right:6px;font-size:15px">⚙</span>Settings</h4>
|
|
<button type="button" class="theme-opacity-wrap theme-opacity-toggle hidden" id="settings-opacity-wrap" title="Fade this window to preview the page behind it" aria-pressed="false">
|
|
<svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"></path><circle cx="12" cy="12" r="3"></circle></svg>
|
|
<span class="theme-opacity-label">Peek</span>
|
|
</button>
|
|
<button class="close-btn" aria-label="Close settings">✖</button>
|
|
</div>
|
|
<div class="admin-toggle-sub" style="padding:0 12px 8px;opacity:0.6;font-size:11px;">Toggle on/off visibility of tools and modules across the interface.</div>
|
|
<div class="settings-layout">
|
|
<div class="settings-sidebar">
|
|
<!-- Section 1: AI plumbing (Add Models → AI Defaults → Search) -->
|
|
<button class="settings-nav-item active" data-settings-tab="services">
|
|
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="2" width="20" height="8" rx="2"/><rect x="2" y="14" width="20" height="8" rx="2"/><circle cx="6" cy="6" r="1"/><circle cx="6" cy="18" r="1"/></svg>
|
|
<span>Add Models</span>
|
|
</button>
|
|
<button class="settings-nav-item" data-settings-tab="ai">
|
|
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 2a4 4 0 0 0-4 4v2H6a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2h-2V6a4 4 0 0 0-4-4z"/></svg>
|
|
<span>AI Defaults</span>
|
|
</button>
|
|
<button class="settings-nav-item" data-settings-tab="search">
|
|
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
|
|
<span>Search</span>
|
|
</button>
|
|
<div class="settings-sidebar-divider"></div>
|
|
<!-- Section 2: Comms (Integrations → Email → Reminders).
|
|
Integrations leads here because it's where the underlying
|
|
accounts the other two tabs depend on actually live. -->
|
|
<button class="settings-nav-item" data-settings-tab="integrations">
|
|
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg>
|
|
<span>Integrations</span>
|
|
</button>
|
|
<button class="settings-nav-item" data-settings-tab="email">
|
|
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="4" width="20" height="16" rx="2"/><path d="m22 7-8.97 5.7a1.94 1.94 0 0 1-2.06 0L2 7"/></svg>
|
|
<span>Email</span>
|
|
</button>
|
|
<button class="settings-nav-item" data-settings-tab="reminders">
|
|
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9"/><path d="M13.73 21a2 2 0 0 1-3.46 0"/></svg>
|
|
<span>Reminders</span>
|
|
</button>
|
|
<div class="settings-sidebar-divider"></div>
|
|
<!-- Section 3: UX (Appearance → Shortcuts) -->
|
|
<button class="settings-nav-item" data-settings-tab="appearance">
|
|
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><path d="M12 2a7 7 0 0 0 0 20 4 4 0 0 1 0-8 4 4 0 0 0 0-8"/></svg>
|
|
<span>Appearance</span>
|
|
</button>
|
|
<button class="settings-nav-item" data-settings-tab="shortcuts">
|
|
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="4" width="20" height="16" rx="2"/><path d="M6 8h.01M10 8h.01M14 8h.01M18 8h.01M8 12h.01M12 12h.01M16 12h.01M7 16h10"/></svg>
|
|
<span>Shortcuts</span>
|
|
</button>
|
|
<div class="settings-sidebar-divider"></div>
|
|
<!-- Section 4: Account (sits at the bottom of the user section) -->
|
|
<button class="settings-nav-item" data-settings-tab="account">
|
|
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></svg>
|
|
<span>Account</span>
|
|
</button>
|
|
<div class="settings-sidebar-divider admin-only"></div>
|
|
<div class="settings-sidebar-label admin-only">Admin</div>
|
|
<button class="settings-nav-item admin-only" data-settings-tab="tools">
|
|
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"/></svg>
|
|
<span>Agent Tools</span>
|
|
</button>
|
|
<button class="settings-nav-item admin-only" data-settings-tab="users">
|
|
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M23 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/></svg>
|
|
<span>Users</span>
|
|
</button>
|
|
<button class="settings-nav-item admin-only" data-settings-tab="system">
|
|
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>
|
|
<span>System</span>
|
|
</button>
|
|
</div>
|
|
<div class="settings-panels">
|
|
|
|
<!-- ═══ AI TAB ═══ -->
|
|
|
|
<div data-settings-panel="ai" class="hidden">
|
|
<div class="admin-card">
|
|
<h2><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:-2px;margin-right:5px;opacity:0.6"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg>Default Chat Model</h2>
|
|
<div class="admin-toggle-sub" style="margin-bottom:8px">The model used when creating a new chat session.</div>
|
|
<div class="settings-col">
|
|
<div class="settings-row">
|
|
<label class="settings-label">Endpoint</label>
|
|
<select id="set-defaultEpSelect" class="settings-select"></select>
|
|
</div>
|
|
<div class="settings-row">
|
|
<label class="settings-label">Model</label>
|
|
<select id="set-defaultModelSelect" class="settings-select"></select>
|
|
</div>
|
|
<div id="set-defaultFallbacks" class="settings-fallbacks"></div>
|
|
<button type="button" class="settings-fallback-add" id="set-defaultAddFallback" title="Add a model to try if the one above fails">+ Add fallback</button>
|
|
<div id="set-defaultChatMsg" style="font-size:11px;color:color-mix(in srgb, var(--fg) 45%, transparent);"></div>
|
|
</div>
|
|
</div>
|
|
<div class="admin-card">
|
|
<h2 style="display:flex;align-items:center;gap:6px;"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-right:1px;opacity:0.6;flex-shrink:0"><path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"/></svg>Utility Model <span style="font-size:0.72em;opacity:0.55;font-weight:normal;">(Recommended: Local Endpoint)</span></h2>
|
|
<div class="admin-toggle-sub" style="margin-bottom:8px">Runs background tasks (compaction, cleanup, auto-naming, retrieving memories from files) on a small/local model instead of your chat model. Leave blank to use the chat model.</div>
|
|
<div class="settings-col">
|
|
<div class="settings-row">
|
|
<label class="settings-label">Endpoint</label>
|
|
<select id="set-utilityEpSelect" class="settings-select"><option value="">—</option></select>
|
|
</div>
|
|
<div class="settings-row">
|
|
<label class="settings-label">Model</label>
|
|
<select id="set-utilityModelSelect" class="settings-select"><option value="">—</option></select>
|
|
</div>
|
|
<div id="set-utilityFallbacks" class="settings-fallbacks"></div>
|
|
<button type="button" class="settings-fallback-add" id="set-utilityAddFallback" title="Add a model to try if the utility model fails">+ Add fallback</button>
|
|
<div id="set-utilityChatMsg" style="font-size:11px;color:color-mix(in srgb, var(--fg) 45%, transparent);"></div>
|
|
</div>
|
|
</div>
|
|
<div class="admin-card">
|
|
<h2 style="display:flex;align-items:center;gap:6px;"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-right:1px;opacity:0.6;flex-shrink:0"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg>Vision<span style="flex:1"></span><label class="admin-switch" title="Analyze images with a vision-capable model"><input type="checkbox" id="set-visionEnabledToggle" checked><span class="admin-slider"></span></label></h2>
|
|
<div class="admin-toggle-sub" style="margin-bottom:8px">Analyze images with a vision-capable model.</div>
|
|
<div style="display:flex;flex-direction:column;gap:0.5rem;">
|
|
<div style="display:flex;align-items:center;gap:0.75rem;">
|
|
<label class="settings-label">Model</label>
|
|
<select id="set-vlModelSelect" class="settings-select"><option value="">Auto-detect</option></select>
|
|
</div>
|
|
<div id="set-visionFallbacks" class="settings-fallbacks"></div>
|
|
<button type="button" class="settings-fallback-add" id="set-visionAddFallback" title="Add a vision model to try if the one above fails">+ Add fallback</button>
|
|
<div id="set-visionSettingsMsg" style="font-size:11px;color:color-mix(in srgb, var(--fg) 45%, transparent);"></div>
|
|
</div>
|
|
</div>
|
|
<div class="admin-card">
|
|
<h2><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:-2px;margin-right:5px;opacity:0.6"><circle cx="11" cy="11" r="8"/><path d="M21 21l-4.35-4.35"/><line x1="11" y1="8" x2="11" y2="14"/><line x1="8" y1="11" x2="14" y2="11"/></svg>Research Model</h2>
|
|
<div class="admin-toggle-sub" style="margin-bottom:8px">Model used for Deep Research. Falls back to the default chat model if not set.</div>
|
|
<div class="settings-col">
|
|
<div class="settings-row">
|
|
<label class="settings-label">Endpoint</label>
|
|
<select id="set-researchEndpoint" class="settings-select">
|
|
<option value="">Same as chat</option>
|
|
</select>
|
|
</div>
|
|
<div class="settings-row">
|
|
<label class="settings-label">Model</label>
|
|
<select id="set-researchModel" class="settings-select">
|
|
<option value="">Same as chat</option>
|
|
</select>
|
|
</div>
|
|
<div class="settings-row">
|
|
<label class="settings-label">Search</label>
|
|
<select id="set-researchSearch" class="settings-select">
|
|
<option value="">Same as web search</option>
|
|
<option value="searxng">SearXNG</option>
|
|
<option value="duckduckgo">DuckDuckGo</option>
|
|
<option value="tavily">Tavily</option>
|
|
<option value="brave">Brave</option>
|
|
<option value="google">Google</option>
|
|
<option value="serper">Serper</option>
|
|
</select>
|
|
</div>
|
|
<div class="settings-row">
|
|
<label class="settings-label">Max Tokens</label>
|
|
<input id="set-researchMaxTokens" type="text" inputmode="numeric" placeholder="8192 (default)" class="settings-select" style="width:120px;">
|
|
</div>
|
|
<div class="settings-row">
|
|
<label class="settings-label">Extract Timeout</label>
|
|
<input id="set-researchExtractTimeout" type="text" inputmode="numeric" placeholder="90 sec" class="settings-select" style="width:120px;">
|
|
</div>
|
|
<div class="settings-row">
|
|
<label class="settings-label">Extract Parallel</label>
|
|
<input id="set-researchExtractConcurrency" type="text" inputmode="numeric" placeholder="3" class="settings-select" style="width:120px;">
|
|
</div>
|
|
<div class="settings-row">
|
|
<label class="settings-label">Max Time</label>
|
|
<input id="set-researchRunTimeout" type="text" inputmode="numeric" placeholder="1800 sec (0 = no limit)" class="settings-select" style="width:120px;">
|
|
</div>
|
|
<div id="set-researchMsg" style="font-size:11px;color:color-mix(in srgb, var(--fg) 45%, transparent);"></div>
|
|
</div>
|
|
</div>
|
|
<div class="admin-card">
|
|
<h2><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:-2px;margin-right:5px;opacity:0.6"><path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"/></svg>Agent</h2>
|
|
<div class="admin-toggle-sub" style="margin-bottom:8px">Controls for the agent tool loop.</div>
|
|
<div class="settings-col">
|
|
<div class="settings-row">
|
|
<label class="settings-label">Tool call limit</label>
|
|
<input id="set-agentMaxTools" type="text" inputmode="numeric" placeholder="0 = unlimited" class="settings-select" style="width:120px;">
|
|
</div>
|
|
<div class="settings-row">
|
|
<label class="settings-label">Max steps per message</label>
|
|
<input id="set-agentMaxRounds" type="text" inputmode="numeric" placeholder="20" class="settings-select" style="width:120px;">
|
|
</div>
|
|
<div id="set-agentMsg" style="font-size:11px;color:color-mix(in srgb, var(--fg) 45%, transparent);"></div>
|
|
</div>
|
|
</div>
|
|
<!-- Image Generation removed — only inpaint remains in this build,
|
|
and inpaint is configured via the gallery editor not this card.
|
|
Keeping the DOM (hidden) so JS wiring against the inputs
|
|
doesn't throw. -->
|
|
<div class="admin-card" hidden style="display:none">
|
|
<h2 style="display:flex;align-items:center;gap:6px;"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-right:1px;opacity:0.6;flex-shrink:0"><rect x="3" y="3" width="18" height="18" rx="2"/><circle cx="8.5" cy="8.5" r="1.5"/><path d="M21 15l-5-5L5 21"/></svg>Image Generation<span style="flex:1"></span><label class="admin-switch"><input type="checkbox" id="set-imgEnabledToggle" checked><span class="admin-slider"></span></label></h2>
|
|
<div class="admin-toggle-sub" style="margin-bottom:8px">Configure which model to use for image generation.</div>
|
|
<div style="display:flex;flex-direction:column;gap:0.5rem;">
|
|
<div style="display:flex;align-items:center;gap:0.75rem;">
|
|
<label class="settings-label">Model</label>
|
|
<select id="set-imgModelSelect" class="settings-select"><option value="">Auto-detect</option></select>
|
|
</div>
|
|
<div style="display:flex;align-items:center;gap:0.75rem;">
|
|
<label class="settings-label">Quality</label>
|
|
<select id="set-imgQualitySelect" class="settings-select">
|
|
<option value="low">Low (fastest, cheapest)</option>
|
|
<option value="medium">Medium (default)</option>
|
|
<option value="high">High (best quality)</option>
|
|
</select>
|
|
</div>
|
|
<div id="set-imgSettingsMsg" style="font-size:11px;color:color-mix(in srgb, var(--fg) 45%, transparent);"></div>
|
|
</div>
|
|
</div>
|
|
<!-- TTS panel hidden — user opted out of read-aloud entirely. Kept
|
|
in DOM so the JS that wires set-ttsProviderSelect etc. doesn't
|
|
throw on missing nodes; not visible to anyone. -->
|
|
<div class="admin-card" hidden style="display:none">
|
|
<h2 style="display:flex;align-items:center;gap:6px;"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:-2px;margin-right:5px;opacity:0.6"><polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"/><path d="M19.07 4.93a10 10 0 0 1 0 14.14"/><path d="M15.54 8.46a5 5 0 0 1 0 7.07"/></svg>Text to Speech<span style="flex:1"></span><label class="admin-switch"><input type="checkbox" id="set-ttsEnabledToggle" checked><span class="admin-slider"></span></label></h2>
|
|
<div class="admin-toggle-sub" style="margin-bottom:8px">Configure TTS provider for assistant message read-aloud.</div>
|
|
<div style="display:flex;flex-direction:column;gap:0.5rem;">
|
|
<div style="display:flex;align-items:center;gap:0.75rem;">
|
|
<label class="settings-label">Provider</label>
|
|
<select id="set-ttsProviderSelect" class="settings-select">
|
|
<option value="disabled">Disabled</option>
|
|
<option value="browser">Browser (built-in)</option>
|
|
<option value="local">Local (Kokoro-82M)</option>
|
|
</select>
|
|
</div>
|
|
<div id="set-ttsModelRow" style="display:flex;align-items:center;gap:0.75rem;">
|
|
<label class="settings-label">Model</label>
|
|
<select id="set-ttsModelSelect" class="settings-select">
|
|
<option value="tts-1">tts-1 (fast)</option>
|
|
<option value="tts-1-hd">tts-1-hd (quality)</option>
|
|
<option value="gpt-4o-mini-tts">gpt-4o-mini-tts (steerable)</option>
|
|
</select>
|
|
<input id="set-ttsModelInput" type="text" placeholder="model name" style="flex:1;padding:5px;display:none;">
|
|
</div>
|
|
<div id="set-ttsVoiceRow" style="display:flex;align-items:center;gap:0.75rem;">
|
|
<label class="settings-label">Voice</label>
|
|
<select id="set-ttsVoiceSelect" class="settings-select">
|
|
<option value="alloy">Alloy</option>
|
|
<option value="ash">Ash</option>
|
|
<option value="coral">Coral</option>
|
|
<option value="echo">Echo</option>
|
|
<option value="fable">Fable</option>
|
|
<option value="nova">Nova</option>
|
|
<option value="onyx">Onyx</option>
|
|
<option value="sage">Sage</option>
|
|
<option value="shimmer">Shimmer</option>
|
|
</select>
|
|
<input id="set-ttsVoiceInput" type="text" placeholder="af_heart" style="flex:1;padding:5px;display:none;">
|
|
</div>
|
|
<div id="set-ttsSpeedRow" style="display:flex;align-items:center;gap:0.75rem;">
|
|
<label class="settings-label">Speed</label>
|
|
<select id="set-ttsSpeedSelect" class="settings-select">
|
|
<option value="0.5">0.5x</option>
|
|
<option value="0.75">0.75x</option>
|
|
<option value="1" selected>1x (normal)</option>
|
|
<option value="1.25">1.25x</option>
|
|
<option value="1.5">1.5x</option>
|
|
<option value="2">2x</option>
|
|
</select>
|
|
</div>
|
|
<button type="button" id="set-ttsPreviewBtn" class="admin-btn-sm" style="margin-top:4px;padding:5px 16px;font-size:12px;">Preview</button>
|
|
<div id="set-ttsSettingsMsg" style="font-size:11px;color:color-mix(in srgb, var(--fg) 45%, transparent);"></div>
|
|
</div>
|
|
</div>
|
|
<div class="admin-card">
|
|
<h2 style="display:flex;align-items:center;gap:6px;"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:-2px;margin-right:5px;opacity:0.6"><path d="M22 10v6M2 10l10-5 10 5-10 5z"/><path d="M6 12v5c3 3 9 3 12 0v-5"/></svg>Teacher Model <span style="font-size:0.72em;opacity:0.55;font-weight:normal;">(Experimental)</span><span style="flex:1"></span><label class="admin-switch"><input type="checkbox" id="set-teacherEnabledToggle"><span class="admin-slider"></span></label></h2>
|
|
<div class="admin-toggle-sub" style="margin-bottom:8px">When a self-hosted student fails an agent-mode task, escalate to a SOTA teacher that writes a SKILL.md procedure so the student can do it next time. Off by default.</div>
|
|
<div class="settings-col">
|
|
<div class="settings-row">
|
|
<label class="settings-label">Endpoint</label>
|
|
<select id="set-teacherEpSelect" class="settings-select"><option value="">—</option></select>
|
|
</div>
|
|
<div class="settings-row">
|
|
<label class="settings-label">Model</label>
|
|
<select id="set-teacherModelSelect" class="settings-select"><option value="">—</option></select>
|
|
</div>
|
|
<div id="set-teacherChatMsg" style="font-size:11px;color:color-mix(in srgb, var(--fg) 45%, transparent);"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ═══ SEARCH TAB ═══ -->
|
|
<div data-settings-panel="search" class="hidden">
|
|
<div class="admin-card">
|
|
<h2><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:-2px;margin-right:5px;opacity:0.6"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>Web Search</h2>
|
|
<div class="admin-toggle-sub" style="margin-bottom:8px">Search API used for web search and deep research.</div>
|
|
<div class="settings-col">
|
|
<div class="settings-row">
|
|
<label class="settings-label">Provider</label>
|
|
<!-- Custom picker (with logos). Hidden native <select> mirrors
|
|
its value so the existing JS that reads
|
|
set-searchProvider keeps working unchanged. -->
|
|
<div class="adm-provider-picker search-provider-picker" id="search-provider-picker" style="flex:1;position:relative;top:5px;">
|
|
<button type="button" class="adm-provider-btn" id="search-provider-btn">
|
|
<span class="adm-provider-current"><span class="adm-provider-logo"></span><span class="adm-provider-name">SearXNG</span></span>
|
|
<svg class="adm-provider-caret" width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 12 15 18 9"/></svg>
|
|
</button>
|
|
<div class="adm-provider-menu hidden" id="search-provider-menu"></div>
|
|
</div>
|
|
<select id="set-searchProvider" class="settings-select" style="display:none;">
|
|
<option value="searxng" data-search-logo="searxng">SearXNG (self-hosted)</option>
|
|
<option value="duckduckgo" data-search-logo="duckduckgo">DuckDuckGo (free, no key)</option>
|
|
<option value="brave" data-search-logo="brave">Brave Search</option>
|
|
<option value="google_pse" data-search-logo="google_pse">Google PSE</option>
|
|
<option value="tavily" data-search-logo="tavily">Tavily</option>
|
|
<option value="serper" data-search-logo="serper">Serper.dev</option>
|
|
<option value="disabled" data-search-logo="disabled">Disabled</option>
|
|
</select>
|
|
<button type="button" class="admin-btn-sm" id="set-searchTestBtn" title="Run a test query against the configured provider" style="margin-left:6px;flex-shrink:0;position:relative;top:2px;">Test</button>
|
|
</div>
|
|
<div class="settings-row">
|
|
<label class="settings-label">Results</label>
|
|
<div style="display:flex;gap:8px;flex:1;">
|
|
<select id="set-searchResultCount" class="settings-select" style="flex:1;">
|
|
<option value="3">3</option>
|
|
<option value="5" selected>5</option>
|
|
<option value="10">10</option>
|
|
<option value="20">20</option>
|
|
<option value="custom">Custom</option>
|
|
</select>
|
|
<input id="set-searchResultCountCustom" type="number" class="settings-select" placeholder="Enter custom value" style="flex:1;display:none;min-width:120px;" min="1" max="100">
|
|
</div>
|
|
</div>
|
|
<div id="set-searchUrlRow" class="settings-row">
|
|
<label class="settings-label">URL</label>
|
|
<input id="set-searchUrl" type="text" placeholder="http://localhost:8080" class="settings-select">
|
|
</div>
|
|
<div id="set-searchKeyRow" class="settings-row" style="display:none;">
|
|
<label class="settings-label">API Key</label>
|
|
<input id="set-searchApiKey" type="password" placeholder="API key" class="settings-select">
|
|
</div>
|
|
<div id="set-searchCxRow" class="settings-row" style="display:none;">
|
|
<label class="settings-label">CX ID</label>
|
|
<input id="set-searchCx" type="text" placeholder="Google PSE engine ID" class="settings-select">
|
|
</div>
|
|
<div class="settings-row">
|
|
<label class="settings-label" title="Providers tried in order when the primary fails or hits a rate limit">Fallbacks</label>
|
|
<div class="search-fallback-chain" id="set-searchFallbackChain"></div>
|
|
</div>
|
|
<div id="set-searchHint" class="admin-toggle-sub"></div>
|
|
<div id="set-searchMsg" style="font-size:11px;"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ═══ APPEARANCE TAB ═══ -->
|
|
<div data-settings-panel="appearance" class="settings-appearance-panel hidden">
|
|
<div class="admin-card" style="padding-bottom:6px;">
|
|
<h2><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:-2px;margin-right:5px;opacity:0.6"><rect x="3" y="3" width="18" height="18" rx="2"/><line x1="9" y1="3" x2="9" y2="21"/></svg>Sidebar</h2>
|
|
<div class="vis-toggles">
|
|
<label class="vis-row">
|
|
<span class="vis-icon"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round"><circle cx="12" cy="12" r="10"/><path d="M8 12l2.5 2.5L16 9"/></svg></span>
|
|
<span class="vis-label">Odysseus <span class="vis-hint">Brand name</span></span>
|
|
<input type="checkbox" checked data-ui-key="sidebar-brand"><span class="vis-switch"></span>
|
|
</label>
|
|
<label class="vis-row">
|
|
<span class="vis-icon"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round"><circle cx="10" cy="10" r="7"/><path d="M21 21l-4.35-4.35"/></svg></span>
|
|
<span class="vis-label">Search</span>
|
|
<input type="checkbox" checked data-ui-key="sidebar-search"><span class="vis-switch"></span>
|
|
</label>
|
|
<label class="vis-row">
|
|
<span class="vis-icon"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg></span>
|
|
<span class="vis-label">New Chat</span>
|
|
<input type="checkbox" checked data-ui-key="sidebar-new-chat"><span class="vis-switch"></span>
|
|
</label>
|
|
<label class="vis-row">
|
|
<span class="vis-icon"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg></span>
|
|
<span class="vis-label">Chats <span class="vis-hint">Chat history list</span></span>
|
|
<input type="checkbox" checked data-ui-key="sessions-section"><span class="vis-switch"></span>
|
|
</label>
|
|
<label class="vis-row">
|
|
<span class="vis-icon"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="4" width="20" height="16" rx="2"/><path d="m22 7-8.97 5.7a1.94 1.94 0 0 1-2.06 0L2 7"/></svg></span>
|
|
<span class="vis-label">Email</span>
|
|
<input type="checkbox" checked data-ui-key="email-section"><span class="vis-switch"></span>
|
|
</label>
|
|
<label class="vis-row">
|
|
<span class="vis-icon"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M12 2L2 7l10 5 10-5-10-5z"/><path d="M2 17l10 5 10-5"/><path d="M2 12l10 5 10-5"/></svg></span>
|
|
<span class="vis-label">Models <span class="vis-hint">Model selector & quick-chat</span></span>
|
|
<input type="checkbox" checked data-ui-key="models-section"><span class="vis-switch"></span>
|
|
</label>
|
|
<label class="vis-row">
|
|
<span class="vis-icon"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"/></svg></span>
|
|
<span class="vis-label">Tools <span class="vis-hint">Whole section (header + all tools)</span></span>
|
|
<input type="checkbox" checked data-ui-key="tools-section"><span class="vis-switch"></span>
|
|
</label>
|
|
<label class="vis-row">
|
|
<span class="vis-icon"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 5a3 3 0 1 0-5.997.125 4 4 0 0 0-2.526 5.77 4 4 0 0 0 .556 6.588A4 4 0 1 0 12 18Z"/><path d="M12 5a3 3 0 1 1 5.997.125 4 4 0 0 1 2.526 5.77 4 4 0 0 1-.556 6.588A4 4 0 1 1 12 18Z"/><path d="M15 13a4.5 4.5 0 0 1-3-4 4.5 4.5 0 0 1-3 4"/></svg></span>
|
|
<span class="vis-label">Brain</span>
|
|
<input type="checkbox" checked data-ui-key="tool-memory"><span class="vis-switch"></span>
|
|
</label>
|
|
<label class="vis-row">
|
|
<span class="vis-icon"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="4" width="18" height="18" rx="2"/><line x1="16" y1="2" x2="16" y2="6"/><line x1="8" y1="2" x2="8" y2="6"/><line x1="3" y1="10" x2="21" y2="10"/></svg></span>
|
|
<span class="vis-label">Calendar</span>
|
|
<input type="checkbox" checked data-ui-key="tool-calendar"><span class="vis-switch"></span>
|
|
</label>
|
|
<label class="vis-row">
|
|
<span class="vis-icon"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="18" cy="18" r="3"/><circle cx="6" cy="6" r="3"/><path d="M13 6h3a2 2 0 0 1 2 2v7"/><path d="M11 18H8a2 2 0 0 1-2-2V9"/></svg></span>
|
|
<span class="vis-label">Compare</span>
|
|
<input type="checkbox" checked data-ui-key="tool-compare"><span class="vis-switch"></span>
|
|
</label>
|
|
<label class="vis-row">
|
|
<span class="vis-icon"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"/><path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"/><path d="M9 7h6M9 11h4"/></svg></span>
|
|
<span class="vis-label">Cookbook</span>
|
|
<input type="checkbox" checked data-ui-key="tool-cookbook"><span class="vis-switch"></span>
|
|
</label>
|
|
<label class="vis-row">
|
|
<span class="vis-icon"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="11" cy="11" r="8"/><path d="M21 21l-4.35-4.35"/><line x1="11" y1="8" x2="11" y2="14"/><line x1="8" y1="11" x2="14" y2="11"/></svg></span>
|
|
<span class="vis-label">Deep Research</span>
|
|
<input type="checkbox" checked data-ui-key="tool-research"><span class="vis-switch"></span>
|
|
</label>
|
|
<label class="vis-row">
|
|
<span class="vis-icon"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2"/><circle cx="8.5" cy="8.5" r="1.5"/><path d="M21 15l-5-5L5 21"/></svg></span>
|
|
<span class="vis-label">Gallery</span>
|
|
<input type="checkbox" checked data-ui-key="tool-gallery"><span class="vis-switch"></span>
|
|
</label>
|
|
<label class="vis-row">
|
|
<span class="vis-icon"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="8" y1="13" x2="16" y2="13"/><line x1="8" y1="17" x2="13" y2="17"/></svg></span>
|
|
<span class="vis-label">Library</span>
|
|
<input type="checkbox" checked data-ui-key="tool-library"><span class="vis-switch"></span>
|
|
</label>
|
|
<label class="vis-row">
|
|
<span class="vis-icon"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2"/><path d="M9 7h6M9 11h6M9 15h4"/></svg></span>
|
|
<span class="vis-label">Notes</span>
|
|
<input type="checkbox" checked data-ui-key="tool-notes"><span class="vis-switch"></span>
|
|
</label>
|
|
<label class="vis-row">
|
|
<span class="vis-icon"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="4" width="18" height="18" rx="2"/><line x1="16" y1="2" x2="16" y2="6"/><line x1="8" y1="2" x2="8" y2="6"/><line x1="3" y1="10" x2="21" y2="10"/><path d="M9 16l2 2 4-4"/></svg></span>
|
|
<span class="vis-label">Tasks</span>
|
|
<input type="checkbox" checked data-ui-key="tool-tasks"><span class="vis-switch"></span>
|
|
</label>
|
|
<label class="vis-row">
|
|
<span class="vis-icon"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="M12 2a10 10 0 0 0 0 20 5 5 0 0 0 5-5 3 3 0 0 0-3-3h-2a3 3 0 0 1-3-3 5 5 0 0 1 5-5"/></svg></span>
|
|
<span class="vis-label">Theme</span>
|
|
<input type="checkbox" checked data-ui-key="tool-theme"><span class="vis-switch"></span>
|
|
</label>
|
|
<label class="vis-row">
|
|
<span class="vis-icon"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></svg></span>
|
|
<span class="vis-label">User <span class="vis-hint">Avatar & name</span></span>
|
|
<input type="checkbox" checked data-ui-key="user-bar"><span class="vis-switch"></span>
|
|
</label>
|
|
<label class="vis-row">
|
|
<span class="vis-icon"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09a1.65 1.65 0 0 0-1-1.51 1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.6 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.6a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg></span>
|
|
<span class="vis-label">Settings Button <span class="vis-hint">Cog next to user — re-open with <code>/settings</code></span></span>
|
|
<input type="checkbox" checked data-ui-key="sidebar-settings-btn"><span class="vis-switch"></span>
|
|
</label>
|
|
</div>
|
|
</div>
|
|
<div class="admin-card" style="padding-bottom:6px;">
|
|
<h2><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:-2px;margin-right:5px;opacity:0.6"><path d="M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z"/></svg>Chat Area</h2>
|
|
<div class="vis-toggles">
|
|
<label class="vis-row">
|
|
<span class="vis-icon"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M4 6h16"/><path d="M4 10h8"/></svg></span>
|
|
<span class="vis-label">Session Header <span class="vis-hint">Model name & export above chat</span></span>
|
|
<input type="checkbox" checked data-ui-key="chat-meta"><span class="vis-switch"></span>
|
|
</label>
|
|
<label class="vis-row">
|
|
<span class="vis-icon"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M12 3v2m0 14v2m-7-9H3m18 0h-2m-1.5-6.5L16 7m-8-1.5L6.5 7m11 11l-1.5-1.5M8 18l-1.5 1.5"/><circle cx="12" cy="12" r="4"/></svg></span>
|
|
<span class="vis-label">Welcome Message <span class="vis-hint">Logo & tips on empty chat</span></span>
|
|
<input type="checkbox" checked data-ui-key="welcome-text"><span class="vis-switch"></span>
|
|
</label>
|
|
<label class="vis-row">
|
|
<span class="vis-icon"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg></span>
|
|
<span class="vis-label">Incognito Mode <span class="vis-hint">No memory, no history saved</span></span>
|
|
<input type="checkbox" checked data-ui-key="incognito-btn"><span class="vis-switch"></span>
|
|
</label>
|
|
<label class="vis-row">
|
|
<span class="vis-icon" style="font-size:13px;line-height:14px;">Aa</span>
|
|
<span class="vis-label">Text-only Emojis <span class="vis-hint">Strip emojis from AI replies</span></span>
|
|
<input type="checkbox" data-ui-key="text-emojis"><span class="vis-switch"></span>
|
|
</label>
|
|
<label class="vis-row">
|
|
<span class="vis-icon"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M12 2a7 7 0 0 0-7 7c0 3 2 5.5 4.5 7.5.7.6 1.2 1.3 1.5 2h2c.3-.7.8-1.4 1.5-2C17 14.5 19 12 19 9a7 7 0 0 0-7-7z"/><line x1="10" y1="22" x2="14" y2="22"/></svg></span>
|
|
<span class="vis-label">Thinking Process <span class="vis-hint">Show <think> collapsible bars</span></span>
|
|
<input type="checkbox" checked data-ui-key="show-thinking"><span class="vis-switch"></span>
|
|
</label>
|
|
<label class="vis-row">
|
|
<span class="vis-icon"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M2 12s4-7 10-7 10 7 10 7-4 7-10 7S2 12 2 12z"/><circle cx="12" cy="12" r="3"/><path d="M4 4l16 16"/></svg></span>
|
|
<span class="vis-label">Sensitive Blur <span class="vis-hint">Blur emails, tokens, and secrets in AI output</span></span>
|
|
<input type="checkbox" data-privacy-key="sensitive-blur"><span class="vis-switch"></span>
|
|
</label>
|
|
</div>
|
|
</div>
|
|
<div class="admin-card" style="padding-bottom:6px;">
|
|
<h2><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:-2px;margin-right:5px;opacity:0.6"><line x1="17" y1="10" x2="3" y2="10"/><line x1="21" y1="6" x2="3" y2="6"/><line x1="21" y1="14" x2="3" y2="14"/><line x1="17" y1="18" x2="3" y2="18"/></svg>Chat Bar</h2>
|
|
<div class="vis-toggles">
|
|
<label class="vis-row">
|
|
<span class="vis-icon"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg></span>
|
|
<span class="vis-label">Web Search</span>
|
|
<input type="checkbox" checked data-ui-key="web-toggle-btn"><span class="vis-switch"></span>
|
|
</label>
|
|
<label class="vis-row">
|
|
<span class="vis-icon"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="16" y1="13" x2="8" y2="13"/><line x1="16" y1="17" x2="8" y2="17"/></svg></span>
|
|
<span class="vis-label">Document Editor</span>
|
|
<input type="checkbox" checked data-ui-key="doc-toggle-btn"><span class="vis-switch"></span>
|
|
</label>
|
|
<label class="vis-row">
|
|
<span class="vis-icon"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="4 17 10 11 4 5"/><line x1="12" y1="19" x2="20" y2="19"/></svg></span>
|
|
<span class="vis-label">Shell</span>
|
|
<input type="checkbox" checked data-ui-key="bash-toggle-btn"><span class="vis-switch"></span>
|
|
</label>
|
|
<label class="vis-row">
|
|
<span class="vis-icon"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="18 15 12 9 6 15"/></svg></span>
|
|
<span class="vis-label">More Tools <span class="vis-hint">Overflow menu</span></span>
|
|
<input type="checkbox" checked data-ui-key="overflow-plus-btn"><span class="vis-switch"></span>
|
|
</label>
|
|
<label class="vis-row">
|
|
<span class="vis-icon vis-icon-text">A|C</span>
|
|
<span class="vis-label">Agent / Chat <span class="vis-hint">Mode switcher</span></span>
|
|
<input type="checkbox" checked data-ui-key="mode-toggle"><span class="vis-switch"></span>
|
|
</label>
|
|
<label class="vis-row">
|
|
<span class="vis-icon"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21.44 11.05l-9.19 9.19a6 6 0 0 1-8.49-8.49l9.19-9.19a4 4 0 0 1 5.66 5.66l-9.2 9.19a2 2 0 0 1-2.83-2.83l8.49-8.48"/></svg></span>
|
|
<span class="vis-label">Attach Files</span>
|
|
<input type="checkbox" checked data-ui-key="attach-btn"><span class="vis-switch"></span>
|
|
</label>
|
|
<label class="vis-row">
|
|
<span class="vis-icon"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><path d="M21 21l-4.35-4.35"/><line x1="11" y1="8" x2="11" y2="14"/><line x1="8" y1="11" x2="14" y2="11"/></svg></span>
|
|
<span class="vis-label">Deep Research</span>
|
|
<input type="checkbox" checked data-ui-key="research-btn"><span class="vis-switch"></span>
|
|
</label>
|
|
<label class="vis-row">
|
|
<span class="vis-icon"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></svg></span>
|
|
<span class="vis-label">Personas <span class="vis-hint">Persona picker & system prompt</span></span>
|
|
<input type="checkbox" checked data-ui-key="preset-mini-btn"><span class="vis-switch"></span>
|
|
</label>
|
|
</div>
|
|
</div>
|
|
<div style="text-align:right;padding:0 4px;">
|
|
<button type="button" class="admin-btn-sm" id="set-uiVisResetBtn" style="opacity:0.5;">Reset All</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ═══ THEME TAB ═══ -->
|
|
|
|
<!-- ═══ MEMORY TAB ═══ -->
|
|
<!-- ═══ SHORTCUTS TAB ═══ -->
|
|
<div data-settings-panel="shortcuts" class="hidden">
|
|
<div class="admin-card" style="display:flex;align-items:center;justify-content:space-between;padding:10px 16px;">
|
|
<div>
|
|
<h2 style="margin:0;font-size:13px;"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:-2px;margin-right:5px;opacity:0.6"><rect x="2" y="4" width="20" height="16" rx="2"/><path d="M6 8h.01M10 8h.01M14 8h.01M18 8h.01M8 12h.01M12 12h.01M16 12h.01M7 16h10"/></svg>Keyboard Shortcuts</h2>
|
|
<p style="font-size:10px;opacity:0.4;margin:2px 0 0;">Click a shortcut to rebind. Press Escape to cancel.</p>
|
|
</div>
|
|
<button type="button" class="shortcut-action-btn is-reset" id="shortcuts-reset-btn" title="Reset Shortcuts" style="width:28px;height:28px;font-size:15px;">↩</button>
|
|
</div>
|
|
<div class="admin-card">
|
|
<div id="shortcuts-list"></div>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<!-- ═══ ACCOUNT TAB ═══ -->
|
|
<div data-settings-panel="account" class="hidden">
|
|
<div class="admin-card">
|
|
<h2><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:-2px;margin-right:5px;opacity:0.6"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></svg>Account</h2>
|
|
<div style="display:flex;align-items:center;gap:10px;margin:4px 0 12px;">
|
|
<div class="user-bar-avatar" id="settings-account-avatar" style="width:32px;height:32px;font-size:14px;"></div>
|
|
<div style="flex:1;">
|
|
<div id="settings-account-username" style="font-size:13px;font-weight:600;"></div>
|
|
<div id="settings-account-role" style="font-size:11px;opacity:0.5;"></div>
|
|
</div>
|
|
<button id="settings-logout-btn" style="background:color-mix(in srgb, var(--color-error) 10%, transparent);border:1px solid var(--color-error);border-radius:6px;padding:6px 12px;color:var(--color-error);font-family:inherit;font-size:12px;font-weight:500;cursor:pointer;transition:opacity 0.15s,background 0.15s;display:inline-flex;align-items:center;gap:6px;">
|
|
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/><polyline points="16 17 21 12 16 7"/><line x1="21" y1="12" x2="9" y2="12"/></svg>
|
|
Logout
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div class="admin-card">
|
|
<h2><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:-2px;margin-right:5px;opacity:0.6"><rect x="3" y="11" width="18" height="11" rx="2" ry="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></svg>Change Password</h2>
|
|
<div class="settings-col">
|
|
<input id="settings-pw-current" type="password" placeholder="Current password" autocomplete="current-password" style="padding:6px 8px;background:var(--bg);border:1px solid var(--border);border-radius:4px;color:var(--fg);font-family:inherit;font-size:12px;">
|
|
<input id="settings-pw-new" type="password" placeholder="New password (min 8)" autocomplete="new-password" style="padding:6px 8px;background:var(--bg);border:1px solid var(--border);border-radius:4px;color:var(--fg);font-family:inherit;font-size:12px;">
|
|
<input id="settings-pw-confirm" type="password" placeholder="Confirm new password" autocomplete="new-password" style="padding:6px 8px;background:var(--bg);border:1px solid var(--border);border-radius:4px;color:var(--fg);font-family:inherit;font-size:12px;">
|
|
<div class="settings-row" style="margin-top:2px;justify-content:flex-end;">
|
|
<span id="settings-pw-msg" style="font-size:11px;margin-right:auto;"></span>
|
|
<button class="admin-btn-add" id="settings-pw-save">Update Password</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="admin-card" id="settings-2fa-card">
|
|
<h2><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:-2px;margin-right:5px;opacity:0.6"><rect x="3" y="11" width="18" height="11" rx="2" ry="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/><circle cx="12" cy="16" r="1"/></svg>Two-Factor Authentication</h2>
|
|
<div id="settings-2fa-content">
|
|
<!-- Populated by JS -->
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ═══ EMAIL TAB ═══ -->
|
|
<div data-settings-panel="email" class="hidden">
|
|
<div class="admin-card">
|
|
<h2><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:-2px;margin-right:5px;opacity:0.6"><rect x="2" y="4" width="20" height="16" rx="2"/><path d="m22 7-8.97 5.7a1.94 1.94 0 0 1-2.06 0L2 7"/></svg>Email Accounts</h2>
|
|
<div class="settings-row" style="align-items:center;">
|
|
<div class="admin-toggle-sub" style="margin:0;flex:1;">Add, edit, delete, and test accounts in Integrations.</div>
|
|
<button class="admin-btn-add" id="set-email-open-integrations" style="display:inline-flex;align-items:center;gap:6px;"><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" style="opacity:0.7"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg>Manage in Integrations</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="admin-card">
|
|
<h2><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:-2px;margin-right:5px;opacity:0.6"><rect x="3" y="4" width="18" height="18" rx="2"/><line x1="16" y1="2" x2="16" y2="6"/><line x1="8" y1="2" x2="8" y2="6"/><line x1="3" y1="10" x2="21" y2="10"/><path d="M9 16l2 2 4-4"/></svg>Email Tasks</h2>
|
|
<div class="settings-row" style="align-items:center;">
|
|
<div class="admin-toggle-sub" style="margin:0;flex:1;">Manage email background tasks in Tasks.</div>
|
|
<button class="admin-btn-add" id="set-email-open-tasks" style="display:inline-flex;align-items:center;gap:6px;"><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" style="opacity:0.7"><rect x="3" y="4" width="18" height="18" rx="2"/><line x1="16" y1="2" x2="16" y2="6"/><line x1="8" y1="2" x2="8" y2="6"/><line x1="3" y1="10" x2="21" y2="10"/><path d="M9 16l2 2 4-4"/></svg>Open Tasks</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="admin-card">
|
|
<h2><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:-2px;margin-right:5px;opacity:0.6"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/></svg>Writing Style</h2>
|
|
<div class="admin-toggle-sub" style="margin-bottom:8px">AI-extracted from your sent emails. Used when AI drafts replies.</div>
|
|
<div class="settings-col">
|
|
<textarea id="set-email-style" rows="4" class="settings-select" style="font-family:inherit;resize:vertical" placeholder="e.g. I write emails in this style. I don't use exclamation marks. I sign emails with: ..."></textarea>
|
|
<div class="settings-row" style="margin-top:4px">
|
|
<span id="set-email-style-msg" style="font-size:11px;"></span>
|
|
<button class="admin-btn-add" id="set-email-style-extract" style="margin-left:auto;">Extract from Sent (15 emails)</button>
|
|
<button class="admin-btn-add" id="set-email-style-save">Save</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ═══ REMINDERS TAB ═══ -->
|
|
<div data-settings-panel="reminders" class="hidden">
|
|
<div class="admin-card">
|
|
<h2><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:-2px;margin-right:5px;opacity:0.6"><path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9"/><path d="M13.73 21a2 2 0 0 1-3.46 0"/></svg>How you're reminded</h2>
|
|
<div class="admin-toggle-sub" style="margin-bottom:8px">Controls how fired note reminders are delivered.</div>
|
|
<div class="settings-col">
|
|
<div class="settings-row">
|
|
<label class="settings-label">Channel</label>
|
|
<select id="set-reminder-channel" class="settings-select">
|
|
<option value="browser">Browser notification (default)</option>
|
|
<option value="email" id="set-reminder-channel-email-opt">Email</option>
|
|
<option value="ntfy" id="set-reminder-channel-ntfy-opt">ntfy</option>
|
|
</select>
|
|
</div>
|
|
<div id="set-reminder-email-from-row" class="settings-row" style="display:none">
|
|
<label class="settings-label">Send from</label>
|
|
<select id="set-reminder-email-account" class="settings-select"></select>
|
|
</div>
|
|
<div id="set-reminder-email-to-row" class="settings-row" style="display:none">
|
|
<label class="settings-label">Send to</label>
|
|
<input id="set-reminder-email-to" class="settings-select" type="email" placeholder="you@example.com" />
|
|
</div>
|
|
<div id="set-reminder-ntfy-topic-row" class="settings-row" style="display:none">
|
|
<label class="settings-label">ntfy topic</label>
|
|
<input id="set-reminder-ntfy-topic" class="settings-select" type="text" placeholder="reminders" />
|
|
</div>
|
|
<div id="set-reminder-channel-hint" style="font-size:11px;opacity:0.6;"></div>
|
|
<div style="font-size:11px;opacity:0.6;margin-top:4px;">Configure email account, ntfy server, etc. in <a href="#" id="set-reminders-open-integrations" style="color:var(--accent, var(--red));text-decoration:none;font-weight:600;">Integrations</a>.</div>
|
|
</div>
|
|
</div>
|
|
<div class="admin-card">
|
|
<h2 style="display:flex;align-items:center;gap:6px;"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-right:1px;opacity:0.6;flex-shrink:0"><path d="M12 0L14.59 8.41L23 12L14.59 15.59L12 24L9.41 15.59L1 12L9.41 8.41Z"/></svg>AI Synthesis<span style="flex:1"></span><label class="admin-switch" title="Use the utility model to write reminder messages"><input type="checkbox" id="set-reminder-llm-toggle"><span class="admin-slider"></span></label></h2>
|
|
<div class="admin-toggle-sub" style="margin-bottom:8px">When on, the utility model writes a short, warm one-line reminder for browser, email, AND ntfy reminders instead of just the raw note content.</div>
|
|
</div>
|
|
<div class="admin-card">
|
|
<h2><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:-2px;margin-right:5px;opacity:0.6"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg>Public App URL</h2>
|
|
<div class="admin-toggle-sub" style="margin-bottom:8px">Used to build clickable links back to Odysseus inside outgoing reminder / urgent-email emails (e.g. <code>https://chat.yourdomain.com</code>). Leave blank to omit links.</div>
|
|
<div class="settings-col">
|
|
<div class="settings-row">
|
|
<label class="settings-label">URL</label>
|
|
<input id="set-app-public-url" class="settings-select" type="url" placeholder="https://chat.example.com" style="flex:1;" />
|
|
</div>
|
|
<div id="set-app-public-url-msg" style="font-size:11px;color:color-mix(in srgb, var(--fg) 55%, transparent);"></div>
|
|
</div>
|
|
</div>
|
|
<div class="admin-card">
|
|
<h2><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:-2px;margin-right:5px;opacity:0.6"><path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/><polyline points="22 4 12 14.01 9 11.01"/></svg>Test</h2>
|
|
<div class="admin-toggle-sub" style="margin-bottom:8px">Fire a test reminder using your current settings to verify everything works.</div>
|
|
<div class="settings-row">
|
|
<span id="set-reminder-test-msg" style="font-size:11px;"></span>
|
|
<button class="admin-btn-add" id="set-reminder-test-btn" style="margin-left:auto;">Send Test Reminder</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ═══ ADMIN: USERS TAB ═══ -->
|
|
<div data-settings-panel="users" class="hidden">
|
|
<div class="admin-card">
|
|
<h2><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:-2px;margin-right:5px;opacity:0.6"><path d="M16 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="8.5" cy="7" r="4"/><line x1="20" y1="8" x2="20" y2="14"/><line x1="23" y1="11" x2="17" y2="11"/></svg>Registration</h2>
|
|
<div class="admin-toggle-row">
|
|
<div>
|
|
<div class="admin-toggle-label">Open signup</div>
|
|
<div class="admin-toggle-sub">Allow anyone to create an account from the login page</div>
|
|
</div>
|
|
<label class="admin-switch"><input type="checkbox" id="adm-signupToggle"><span class="admin-slider"></span></label>
|
|
</div>
|
|
</div>
|
|
<div class="admin-card">
|
|
<h2><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:-2px;margin-right:5px;opacity:0.6"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M23 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/></svg>Users</h2>
|
|
<div id="adm-userList"><div class="admin-empty">Loading...</div></div>
|
|
</div>
|
|
<div class="admin-card">
|
|
<h2><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:-2px;margin-right:5px;opacity:0.6"><path d="M16 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="8.5" cy="7" r="4"/><line x1="20" y1="8" x2="20" y2="14"/><line x1="23" y1="11" x2="17" y2="11"/></svg>Add User</h2>
|
|
<div class="admin-add-form">
|
|
<input id="adm-newUsername" type="text" placeholder="Username (email)">
|
|
<input id="adm-newPassword" type="password" placeholder="Password (min 8)">
|
|
<div class="admin-switch-inline" title="Grant full admin access"><label class="admin-switch"><input type="checkbox" id="adm-newIsAdmin"><span class="admin-slider"></span></label> Admin</div>
|
|
</div>
|
|
<div class="settings-row" style="margin-top:6px;">
|
|
<button class="admin-btn-add" id="adm-addBtn">Add User</button>
|
|
<span id="adm-addMsg" style="font-size:11px"></span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ═══ SERVICES TAB ═══ -->
|
|
<div data-settings-panel="services">
|
|
<div class="admin-card">
|
|
<h2><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:-2px;margin-right:5px;opacity:0.6"><rect x="2" y="2" width="20" height="8" rx="2"/><rect x="2" y="14" width="20" height="8" rx="2"/><circle cx="6" cy="6" r="1"/><circle cx="6" cy="18" r="1"/></svg>Add Models <span style="opacity:0.45;font-weight:normal;font-size:0.82em">(Endpoints)</span></h2>
|
|
<div class="admin-toggle-sub" style="margin-bottom:10px">Connect local models first, or add a cloud API.</div>
|
|
|
|
<!-- Local subsection -->
|
|
<div class="adm-add-section collapsible collapsed" id="adm-add-local">
|
|
<div class="adm-ep-section-head adm-section-toggle" role="button" tabindex="0" aria-expanded="false">
|
|
<svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:-1px;margin-right:4px;"><rect x="2" y="3" width="20" height="14" rx="2"/><path d="M8 21h8"/><path d="M12 17v4"/></svg>
|
|
<span>Local</span>
|
|
<svg class="adm-section-caret" width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 12 15 18 9"/></svg>
|
|
</div>
|
|
<div class="admin-model-form">
|
|
<div class="admin-model-form-row">
|
|
<input id="adm-epLocalUrl" type="text" placeholder="Paste endpoint URL, e.g. http://localhost:11434/v1" style="flex:1">
|
|
<select id="adm-epLocalType" style="padding:5px;width:72px;flex-shrink:0;">
|
|
<option value="llm">LLM</option>
|
|
<option value="image">Image</option>
|
|
</select>
|
|
</div>
|
|
<div class="admin-model-form-row">
|
|
<input id="adm-epLocalApiKey" type="password" placeholder="API key (optional — for protected local endpoints)" autocomplete="off" style="flex:1">
|
|
</div>
|
|
<div class="admin-model-form-row">
|
|
<span style="flex:1"></span>
|
|
<button class="admin-btn-sm" id="adm-epLocalTestBtn" style="width:55px;text-align:center;">Test</button>
|
|
<button class="admin-btn-add" id="adm-epLocalAddBtn" style="width:55px;text-align:center;">Add</button>
|
|
</div>
|
|
<div class="adm-quickstart-section collapsed" id="adm-add-local-quickstart">
|
|
<div class="adm-quickstart-toggle" role="button" tabindex="0" aria-expanded="false">
|
|
<span>Quickstart</span>
|
|
<svg class="adm-section-caret" width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 12 15 18 9"/></svg>
|
|
</div>
|
|
<div class="adm-quickstart-body">
|
|
<button class="admin-btn-sm" id="adm-epDiscoverBtn" title="Scan your network for running model servers">
|
|
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" style="vertical-align:-1px;margin-right:4px;"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>Scan for Servers
|
|
</button>
|
|
<button class="admin-btn-sm" id="adm-epOllamaBtn" title="Fill the default Ollama endpoint">Ollama</button>
|
|
</div>
|
|
</div>
|
|
<div id="adm-epLocalMsg" class="adm-ep-inline-msg"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- API subsection -->
|
|
<div class="adm-add-section collapsible collapsed" id="adm-add-api" style="margin-top:14px">
|
|
<div class="adm-ep-section-head adm-section-toggle" role="button" tabindex="0" aria-expanded="false">
|
|
<svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:-1px;margin-right:4px;"><circle cx="12" cy="12" r="10"/><line x1="2" y1="12" x2="22" y2="12"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/></svg>
|
|
<span>API</span>
|
|
<svg class="adm-section-caret" width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 12 15 18 9"/></svg>
|
|
</div>
|
|
<div class="admin-model-form">
|
|
<!-- Custom picker (with logos). Hidden native <select> mirrors
|
|
its value so the existing JS that reads adm-epProvider
|
|
keeps working unchanged. -->
|
|
<div class="adm-provider-picker adm-provider-combo" id="adm-provider-picker">
|
|
<input id="adm-epUrl" type="text" placeholder="Base URL or pick provider" autocomplete="off">
|
|
<button type="button" class="adm-provider-btn" id="adm-provider-btn" title="Pick provider">
|
|
<span class="adm-provider-current"><span class="adm-provider-logo"></span><span class="adm-provider-name">Provider</span></span>
|
|
<svg class="adm-provider-caret" width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 12 15 18 9"/></svg>
|
|
</button>
|
|
<div class="adm-provider-menu hidden" id="adm-provider-menu"></div>
|
|
</div>
|
|
<select id="adm-epProvider" style="display:none">
|
|
<option value="">Custom URL</option>
|
|
<option value="https://api.anthropic.com" data-logo="anthropic">Anthropic</option>
|
|
<option value="https://api.deepseek.com/v1" data-logo="deepseek" selected>DeepSeek</option>
|
|
<option value="https://api.openai.com/v1" data-logo="openai">OpenAI</option>
|
|
<option value="https://openrouter.ai/api/v1" data-logo="openrouter">OpenRouter</option>
|
|
<option value="https://ollama.com/api" data-logo="ollama">Ollama Cloud</option>
|
|
<option value="https://api.groq.com/openai/v1" data-logo="groq">Groq</option>
|
|
<option value="https://api.mistral.ai/v1" data-logo="mistral">Mistral</option>
|
|
<option value="https://api.together.xyz/v1" data-logo="together">Together AI</option>
|
|
<option value="https://api.fireworks.ai/inference/v1" data-logo="fireworks">Fireworks AI</option>
|
|
<option value="https://generativelanguage.googleapis.com/v1beta/openai" data-logo="gemini">Google Gemini</option>
|
|
<option value="https://api.x.ai/v1" data-logo="grok">xAI Grok</option>
|
|
<option value="https://api.z.ai/api/paas/v4" data-logo="zhipu">Z.AI (Zhipu)</option>
|
|
<option value="https://api.z.ai/api/coding/paas/v4" data-logo="zhipu">Z.AI Coding Plan</option>
|
|
</select>
|
|
<div class="admin-model-form-row">
|
|
<input id="adm-epApiKey" type="password" placeholder="API key">
|
|
<select id="adm-epKind" style="padding:5px;width:82px;">
|
|
<option value="proxy">Proxy</option>
|
|
<option value="api">API</option>
|
|
</select>
|
|
<select id="adm-epType" style="padding:5px;width:80px;">
|
|
<option value="llm">LLM</option>
|
|
<option value="image">Image</option>
|
|
</select>
|
|
<button class="admin-btn-sm" id="adm-epApiTestBtn" style="width:55px;text-align:center;">Test</button>
|
|
<button class="admin-btn-sm hidden" id="adm-epApiCancelTestBtn" style="width:62px;text-align:center;">Cancel</button>
|
|
<button class="admin-btn-add" id="adm-epAddBtn" style="width:55px;text-align:center;">Add</button>
|
|
</div>
|
|
<div id="adm-epApiMsg" class="adm-ep-inline-msg"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="admin-card">
|
|
<h2><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:-2px;margin-right:5px;opacity:0.6"><rect x="2" y="3" width="20" height="14" rx="2"/><line x1="8" y1="21" x2="16" y2="21"/><line x1="12" y1="17" x2="12" y2="21"/></svg>Added Models <span style="opacity:0.45;font-weight:normal;font-size:0.82em">(Endpoints)</span></h2>
|
|
<div class="admin-toggle-sub" style="margin-bottom:10px">Manage the endpoints you've added.</div>
|
|
<div class="adm-ep-section">
|
|
<div class="adm-ep-section-head">
|
|
<svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:-1px;margin-right:4px;"><rect x="2" y="3" width="20" height="14" rx="2"/><path d="M8 21h8"/><path d="M12 17v4"/></svg>
|
|
<span>Local</span>
|
|
</div>
|
|
<div id="adm-epList-local"><div class="admin-empty">Loading...</div></div>
|
|
</div>
|
|
<div class="adm-ep-section" style="margin-top:14px">
|
|
<div class="adm-ep-section-head">
|
|
<svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:-1px;margin-right:4px;"><circle cx="12" cy="12" r="10"/><line x1="2" y1="12" x2="22" y2="12"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/></svg>
|
|
<span>API</span>
|
|
</div>
|
|
<div id="adm-epList-api"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ═══ TOOLS TAB ═══ -->
|
|
<!-- ═══ INTEGRATIONS TAB ═══ -->
|
|
<div data-settings-panel="integrations" class="hidden">
|
|
<div class="admin-card">
|
|
<h2><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:-2px;margin-right:5px;opacity:0.6"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg>Integrations</h2>
|
|
<div class="admin-toggle-sub" style="margin-bottom:8px">All external service connections in one place.</div>
|
|
<div id="unified-integrations-list"></div>
|
|
<div id="unified-intg-form" style="display:none"></div>
|
|
<div style="text-align:center;padding:8px 0;">
|
|
<button type="button" class="admin-btn-sm" id="unified-intg-add-btn" style="display:inline-flex;align-items:center;gap:6px;">+ Add Integration<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="opacity:0.7;"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ═══ TOOLS TAB ═══ -->
|
|
<div data-settings-panel="tools" class="hidden">
|
|
<div class="admin-card" style="margin-bottom:12px;">
|
|
<h2><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:-2px;margin-right:5px;opacity:0.6"><path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"/></svg>Built-in Tools</h2>
|
|
<div class="admin-toggle-sub" style="margin-bottom:8px">Enable or disable tools available to the AI agent.</div>
|
|
<div id="adm-builtin-tools-list" class="admin-user-list"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ═══ SYSTEM TAB ═══ -->
|
|
<div data-settings-panel="system" class="hidden">
|
|
|
|
<div class="admin-card">
|
|
<h2><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:-2px;margin-right:5px;opacity:0.6"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="17 8 12 3 7 8"/><line x1="12" y1="3" x2="12" y2="15"/></svg>Data Backup</h2>
|
|
<div class="admin-toggle-sub" style="margin-bottom:8px">Export or import your user data (memories, presets, settings, skills, preferences) as a JSON file.</div>
|
|
<div style="display:flex;gap:8px;flex-wrap:wrap;">
|
|
<button class="admin-btn-add" id="adm-exportDataBtn">Export Data</button>
|
|
<button class="admin-btn-add" id="adm-importDataBtn">Import Data</button>
|
|
<input type="file" id="adm-importFile" accept=".json" style="display:none;">
|
|
</div>
|
|
<div id="adm-backupMsg" style="margin-top:6px;"></div>
|
|
</div>
|
|
<div class="admin-card admin-danger-card">
|
|
<h2 style="color:#e55;">Danger Zone</h2>
|
|
<div class="admin-toggle-sub" style="margin-bottom:8px">Irreversible. Each wipe targets one category — pick exactly what you want gone.</div>
|
|
|
|
<div style="display:flex;justify-content:space-between;align-items:center;">
|
|
<div>
|
|
<div class="admin-toggle-label">Wipe all chats</div>
|
|
<div class="admin-toggle-sub">Every session, message, and chat history. Documents/notes/etc. stay.</div>
|
|
</div>
|
|
<button class="admin-btn-delete" data-wipe-kind="chats" style="white-space:nowrap;">Wipe</button>
|
|
</div>
|
|
|
|
<div style="display:flex;justify-content:space-between;align-items:center;margin-top:8px;">
|
|
<div>
|
|
<div class="admin-toggle-label">Wipe all memory</div>
|
|
<div class="admin-toggle-sub">Clears `memory.json`, the Memory table, and the vector store. Skills not affected.</div>
|
|
</div>
|
|
<button class="admin-btn-delete" data-wipe-kind="memory" style="white-space:nowrap;">Wipe</button>
|
|
</div>
|
|
|
|
<div style="display:flex;justify-content:space-between;align-items:center;margin-top:8px;">
|
|
<div>
|
|
<div class="admin-toggle-label">Wipe all skills</div>
|
|
<div class="admin-toggle-sub">Drops `data/skills/` (all SKILL.md files). Memory not affected.</div>
|
|
</div>
|
|
<button class="admin-btn-delete" data-wipe-kind="skills" style="white-space:nowrap;">Wipe</button>
|
|
</div>
|
|
|
|
<div style="display:flex;justify-content:space-between;align-items:center;margin-top:8px;">
|
|
<div>
|
|
<div class="admin-toggle-label">Wipe all notes</div>
|
|
<div class="admin-toggle-sub">Every note, todo, and checklist.</div>
|
|
</div>
|
|
<button class="admin-btn-delete" data-wipe-kind="notes" style="white-space:nowrap;">Wipe</button>
|
|
</div>
|
|
|
|
<div style="display:flex;justify-content:space-between;align-items:center;margin-top:8px;">
|
|
<div>
|
|
<div class="admin-toggle-label">Wipe all tasks</div>
|
|
<div class="admin-toggle-sub">Every scheduled task and its run history (Tasks tool).</div>
|
|
</div>
|
|
<button class="admin-btn-delete" data-wipe-kind="tasks" style="white-space:nowrap;">Wipe</button>
|
|
</div>
|
|
|
|
<div style="display:flex;justify-content:space-between;align-items:center;margin-top:8px;">
|
|
<div>
|
|
<div class="admin-toggle-label">Wipe all documents</div>
|
|
<div class="admin-toggle-sub">Every document and version. Drafts, exports, library — all gone.</div>
|
|
</div>
|
|
<button class="admin-btn-delete" data-wipe-kind="documents" style="white-space:nowrap;">Wipe</button>
|
|
</div>
|
|
|
|
<div style="display:flex;justify-content:space-between;align-items:center;margin-top:8px;">
|
|
<div>
|
|
<div class="admin-toggle-label">Wipe all gallery</div>
|
|
<div class="admin-toggle-sub">Every image record and the upload directory on disk.</div>
|
|
</div>
|
|
<button class="admin-btn-delete" data-wipe-kind="gallery" style="white-space:nowrap;">Wipe</button>
|
|
</div>
|
|
|
|
<div style="display:flex;justify-content:space-between;align-items:center;margin-top:8px;">
|
|
<div>
|
|
<div class="admin-toggle-label">Wipe all calendar</div>
|
|
<div class="admin-toggle-sub">Every event and every calendar (incl. CalDAV-synced ones; resync to restore).</div>
|
|
</div>
|
|
<button class="admin-btn-delete" data-wipe-kind="calendar" style="white-space:nowrap;">Wipe</button>
|
|
</div>
|
|
|
|
<div id="adm-wipeMsg" style="margin-top:8px;"></div>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Search overlay (Ctrl+K command palette) -->
|
|
<div class="search-overlay hidden" id="search-overlay">
|
|
<div class="search-popup">
|
|
<input type="text" id="search-input" placeholder="Search conversations..." autocomplete="off" />
|
|
<div class="search-results" id="search-results"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Toast container (aria-live for screen readers) -->
|
|
<div id="toast" class="toast" role="status" aria-live="polite"></div>
|
|
|
|
|
|
|
|
<!-- Load modules in this order -->
|
|
<script type="module" src="/static/js/storage.js"></script>
|
|
<script type="module" src="/static/js/ui.js"></script>
|
|
<script type="module" src="/static/js/markdown.js"></script>
|
|
<script type="module" src="/static/js/dragSort.js"></script>
|
|
<script type="module" src="/static/js/sessions.js"></script>
|
|
<script type="module" src="/static/js/memory.js"></script>
|
|
<script type="module" src="/static/js/skills.js"></script>
|
|
<script type="module" src="/static/js/tourHints.js"></script>
|
|
<script type="module" src="/static/js/tourAutoplay.js"></script>
|
|
<script type="module" src="/static/js/fileHandler.js"></script>
|
|
<script type="module" src="/static/js/voiceRecorder.js"></script>
|
|
<script type="module" src="/static/js/models.js"></script> <!-- This must come BEFORE app.js -->
|
|
<script type="module" src="/static/js/rag.js"></script>
|
|
<script type="module" src="/static/js/presets.js"></script>
|
|
<script type="module" src="/static/js/search.js"></script>
|
|
<script type="module" src="/static/js/spinner.js"></script>
|
|
<script type="module" src="/static/js/tts-ai.js"></script>
|
|
<script type="module" src="/static/js/document.js"></script>
|
|
<script type="module" src="/static/js/gallery.js"></script>
|
|
<script type="module" src="/static/js/chatRenderer.js"></script>
|
|
<script type="module" src="/static/js/codeRunner.js"></script>
|
|
<script type="module" src="/static/js/chatStream.js"></script>
|
|
<script type="module" src="/static/js/chat.js?v=20260604s"></script>
|
|
<script type="module" src="/static/js/cookbook.js"></script>
|
|
<script src="/static/js/cookbookSchedule.js"></script>
|
|
<script type="module" src="/static/js/search-chat.js"></script>
|
|
<script type="module" src="/static/js/compare/index.js"></script>
|
|
<script type="module" src="/static/js/theme.js"></script>
|
|
<script type="module" src="/static/js/censor.js"></script>
|
|
<script type="module" src="/static/js/settings.js"></script>
|
|
<script type="module" src="/static/js/admin.js"></script>
|
|
<script type="module" src="/static/js/assistant.js"></script>
|
|
<script type="module" src="/static/app.js"></script> <!-- app.js must be LAST -->
|
|
<script type="module" src="/static/js/init.js"></script>
|
|
<script type="module" src="/static/js/a11y.js"></script>
|
|
<script nonce="{{CSP_NONCE}}">if('serviceWorker' in navigator){navigator.serviceWorker.register('/static/sw.js').catch(()=>{});}</script>
|
|
</body>
|
|
</html>
|