diff --git a/cortex/routers/settings.py b/cortex/routers/settings.py index 746904d..8b32656 100644 --- a/cortex/routers/settings.py +++ b/cortex/routers/settings.py @@ -127,8 +127,8 @@ def _settings_page(username: str, personas: list[str], back_persona: str = "", s - - + + ''' for p in personas ) diff --git a/cortex/routers/tools_settings.py b/cortex/routers/tools_settings.py index 77e65e0..7e55343 100644 --- a/cortex/routers/tools_settings.py +++ b/cortex/routers/tools_settings.py @@ -53,13 +53,7 @@ def _build_tool_table(policy: dict) -> str: for category, tools in TOOL_CATEGORIES.items(): # Category header spanning all columns escaped_cat = _html.escape(category) - rows.append( - f'' - f'{escaped_cat}' - ) + rows.append(f'{escaped_cat}') for tool in tools: risk = TOOL_RISK.get(tool, "medium") risk_cls = f"risk-{risk}" diff --git a/cortex/static/help.html b/cortex/static/help.html index e9f4394..68b7143 100644 --- a/cortex/static/help.html +++ b/cortex/static/help.html @@ -8,56 +8,10 @@ + +
- -

Help & Reference

diff --git a/cortex/static/notifications.html b/cortex/static/notifications.html index 532f8df..9445100 100644 --- a/cortex/static/notifications.html +++ b/cortex/static/notifications.html @@ -7,218 +7,65 @@ + -
- - - + +
+

Notifications

+

How Inara reaches out proactively — reminders, cron jobs, and memory digests.

@@ -254,7 +101,7 @@

Nextcloud Talk

-

+

Configure to send and receive messages via your Nextcloud Talk bot. Sending requires the bot URL, secret, and notification room. Reading history (nc_talk_history tool) additionally @@ -266,7 +113,7 @@

Set these up in your Nextcloud Talk room → Bot settings. - See the setup guide for step-by-step instructions. + See the setup guide for step-by-step instructions.

@@ -322,7 +169,7 @@

Home Assistant

-

+

Receive events from HA automations and let Inara call the HA REST API (read states, control devices). Webhook ID is the shared secret used in your HA rest_command URL. @@ -374,7 +221,7 @@

Google Chat

-

+

Outbound webhook for proactive messages to a Google Chat space. Incoming messages are handled separately via the Google Chat Add-on.

@@ -383,8 +230,7 @@ Outbound webhook

- Create a webhook in your Google Chat space → Manage webhooks. - Paste the full URL here. + Create a webhook in your Google Chat space → Manage webhooks. Paste the full URL here.

@@ -397,19 +243,19 @@
- +

Test

-

+

Fire a notification via your configured channel or run the reminder check immediately — no need to wait for the daily 09:00 scheduler job.

-
- - +
+ +
diff --git a/cortex/static/pg.css b/cortex/static/pg.css new file mode 100644 index 0000000..b00dfc2 --- /dev/null +++ b/cortex/static/pg.css @@ -0,0 +1,188 @@ +/* ─── Cortex settings pages — shared stylesheet ───────────────────────────── */ + +/* ── Variables ── */ +:root { + --pg-bg: #0f1117; --pg-surface: #1a1d27; --pg-border: #2d3148; + --pg-text: #e2e8f0; --pg-muted: #94a3b8; + --pg-dim: #64748b; --pg-dimmer: #475569; + --pg-bright: #cbd5e1; --pg-nav-hover: rgba(255,255,255,0.05); + --pg-accent: #a78bfa; /* heading/highlight purple */ + --pg-action: #7c3aed; /* button/focus purple */ +} +[data-theme="light"] { + --pg-bg: #f4f2fa; --pg-surface: #ffffff; --pg-border: #d0c8e8; + --pg-text: #1a1228; --pg-muted: #5a5478; + --pg-dim: #7a7290; --pg-dimmer: #9e98b0; + --pg-bright: #1a1228; --pg-nav-hover: rgba(0,0,0,0.05); + --pg-accent: #7c3aed; + --pg-action: #6d28d9; +} + +/* ── Reset ── */ +*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; } + +/* ── Base ── */ +body { + min-height: 100vh; + background: var(--pg-bg); + font-family: 'Inter', system-ui, -apple-system, sans-serif; + font-weight: 450; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + color: var(--pg-text); +} + +/* ── Top nav ── */ +.page-nav { + display: flex; align-items: center; gap: 0.25rem; + padding: 0.5rem 1rem; background: var(--pg-surface); + border-bottom: 1px solid var(--pg-border); flex-wrap: wrap; +} +.nav-link { + display: inline-flex; align-items: center; + padding: 0.3rem 0.6rem; border-radius: 6px; + font-size: 0.8rem; font-weight: 500; color: var(--pg-dim); + text-decoration: none; transition: color 0.15s, background 0.15s; + white-space: nowrap; +} +.nav-link:hover { color: var(--pg-bright); background: var(--pg-nav-hover); } +.nav-link.active { color: var(--pg-accent); } +.nav-spacer { flex: 1; min-width: 0.5rem; } +.nav-link.nav-logout { color: var(--pg-dimmer); } +.nav-link.nav-logout:hover { color: var(--pg-muted); background: none; } + +/* ── Page container ── */ +.page-wrap { + max-width: 860px; margin: 0 auto; + padding: 2rem 1.5rem 4rem; width: 100%; +} + +/* ── Page heading ── */ +.page-title { + font-size: 1.4rem; font-weight: 700; color: var(--pg-accent); +} +.page-subtitle { + font-size: 0.8rem; color: var(--pg-muted); + margin-top: 0.2rem; margin-bottom: 1.75rem; line-height: 1.5; +} + +/* ── Sections (settings-style, bottom-bordered h2) ── */ +.section { margin-bottom: 2rem; } +.section > h2 { + font-size: 0.9rem; font-weight: 600; color: var(--pg-muted); + margin-bottom: 1rem; padding-bottom: 0.4rem; + border-bottom: 1px solid var(--pg-border); +} + +/* ── Form elements ── */ +.field { margin-bottom: 1rem; } + +label { + display: block; font-size: 0.8rem; font-weight: 500; + color: var(--pg-muted); margin-bottom: 0.4rem; +} + +input, select, textarea { + width: 100%; padding: 0.65rem 0.85rem; + background: var(--pg-bg); border: 1px solid var(--pg-border); + border-radius: 6px; color: var(--pg-text); font-size: 0.95rem; + font-family: inherit; outline: none; transition: border-color 0.15s; +} +input:focus, select:focus, textarea:focus { border-color: var(--pg-action); } +input[readonly] { color: var(--pg-muted); cursor: default; } +input[type="password"] { font-family: monospace; letter-spacing: 0.05em; } + +textarea { + font-family: 'SF Mono', 'Fira Mono', 'Menlo', monospace; + font-size: 0.88rem; line-height: 1.55; resize: vertical; +} + +/* ── Buttons ── */ + +/* Full-width primary form submit */ +.btn-submit { + width: 100%; padding: 0.7rem; margin-top: 0.25rem; + background: var(--pg-action); border: none; border-radius: 6px; + color: #fff; font-size: 1rem; font-weight: 600; + cursor: pointer; transition: background 0.15s; +} +.btn-submit:hover { opacity: 0.88; } + +/* Compact inline primary (e.g. rename save, inline forms) */ +.btn-save { + padding: 0.4rem 0.9rem; background: var(--pg-action); border: none; + border-radius: 6px; color: #fff; font-size: 0.9rem; + font-weight: 600; cursor: pointer; transition: opacity 0.15s; +} +.btn-save:hover { opacity: 0.88; } + +/* Outline secondary (e.g. clear cache, cancel, test actions) */ +.btn-secondary { + padding: 0.5rem 1rem; background: none; + border: 1px solid var(--pg-border); border-radius: 6px; + color: var(--pg-muted); font-size: 0.88rem; font-weight: 500; + cursor: pointer; transition: border-color 0.15s, color 0.15s; +} +.btn-secondary:hover { border-color: var(--pg-muted); color: var(--pg-text); } +.btn-secondary:disabled { opacity: 0.5; cursor: default; } + +/* Inline cancel */ +.btn-cancel { + padding: 0.4rem 0.75rem; background: none; + border: 1px solid var(--pg-border); border-radius: 6px; + color: var(--pg-muted); font-size: 0.9rem; + cursor: pointer; transition: border-color 0.15s, color 0.15s; +} +.btn-cancel:hover { border-color: var(--pg-muted); color: var(--pg-text); } + +/* Button-styled link (purple, used for "Settings →" style CTAs) */ +.action-link { + display: inline-block; padding: 0.5rem 1rem; + background: var(--pg-action); border-radius: 6px; + color: #fff; font-size: 0.88rem; font-weight: 600; + text-decoration: none; transition: opacity 0.15s; +} +.action-link:hover { opacity: 0.88; } + +/* Inline button row */ +.btn-row { display: flex; gap: 0.5rem; margin-top: 0.5rem; } + +/* ── Text utilities ── */ + +/* Small muted helper text below inputs */ +.hint { font-size: 0.78rem; color: var(--pg-dim); margin-top: 0.35rem; line-height: 1.5; } + +/* Section-level description paragraph */ +.section-note { font-size: 0.8rem; color: var(--pg-muted); margin-bottom: 0.85rem; line-height: 1.55; } + +/* Inline code */ +code { + font-size: 0.82rem; font-family: 'SF Mono', 'Fira Mono', 'Menlo', monospace; + background: var(--pg-bg); border: 1px solid var(--pg-border); + padding: 0.1rem 0.35rem; border-radius: 4px; color: var(--pg-accent); +} + +/* ── Feedback messages ── */ +.success { color: #4ade80; font-size: 0.85rem; text-align: center; margin-bottom: 1rem; } +.error { color: #f87171; font-size: 0.85rem; text-align: center; margin-bottom: 1rem; } + +/* ── Usage table (JS-rendered in settings) ── */ +.usage-table { border-collapse: collapse; width: 100%; min-width: 360px; } +.usage-table th { + padding: 0.35rem 0.5rem; font-size: 0.75rem; color: var(--pg-muted); + font-weight: 600; text-align: right; border-bottom: 1px solid var(--pg-border); +} +.usage-table th:first-child { padding-left: 0; text-align: left; } +.usage-table td { + padding: 0.4rem 0.5rem; font-size: 0.82rem; color: var(--pg-muted); text-align: right; +} +.usage-table td:first-child { padding-left: 0; color: var(--pg-text); text-align: left; white-space: nowrap; } +.usage-table td:last-child { color: var(--pg-text); font-weight: 600; } + +/* ── Tool category header row (tools_settings.py generated) ── */ +.tool-cat-row td { + padding: 0.75rem 0.9rem 0.3rem; + font-size: 0.72rem; font-weight: 700; letter-spacing: 0.07em; + text-transform: uppercase; color: var(--pg-dimmer); + border-bottom: 1px solid var(--pg-border); +} diff --git a/cortex/static/settings.html b/cortex/static/settings.html index 88de91e..6a451d8 100644 --- a/cortex/static/settings.html +++ b/cortex/static/settings.html @@ -7,273 +7,93 @@ + -
- - - + +
+

Account Settings

+

Manage your account and personas.

@@ -289,26 +109,21 @@ {{ user_role }}
- - @@ -322,18 +137,15 @@ placeholder="No Google account linked" style="{{ google_email == '' and 'color:var(--pg-dimmer)' or '' }}">
-

- To link or change your Google account, contact Scott. -

+

To link or change your Google account, contact Scott.

Email Allowlist

-

- One regex pattern per line. The email_send - tool will only send to addresses that match at least one pattern. - Leave blank to block all outbound email. +

+ One regex pattern per line. The email_send tool will only send to addresses + that match at least one pattern. Leave blank to block all outbound email.

@@ -342,17 +154,16 @@ placeholder=".*@example\.com alice@example\.com" spellcheck="false">{{ email_allowlist }}
- +

HTTP POST Allowlist

-

- One URL prefix per line. The http_post - tool will only POST to URLs that start with a listed prefix. - Leave blank to block all outbound POST requests. +

+ One URL prefix per line. The http_post tool will only POST to URLs that + start with a listed prefix. Leave blank to block all outbound POST requests.

@@ -361,103 +172,73 @@ placeholder="https://ha.dgrzone.com/api/webhook/ https://n8n.dgrzone.com/webhook/" spellcheck="false">{{ http_allowlist }}
- +

Notifications

-

+

Configure how Inara reaches out proactively — reminders, cron jobs, and memory digests.

- - Notification settings → - + Notification settings →
- +

Tool Permissions

-

+

Configure tool access, risk policy, and confirmation gate overrides on the Tools page.

- - Tool Settings → - + Tool settings →
-

Usage

-

+

Token consumption tracked for API-backed models (Gemini API, local OpenAI-compatible). Claude CLI calls are not metered.

-
-

Loading…

+
+

Loading…

+

Browser Cache

-

+

Clears UI preferences stored in this browser: active mode, session ID, memory toggles, theme, font size, and context tier. Does not sign you out.

- - + + Cleared.
- +

Model Registry

- - - +

Change Password

@@ -476,27 +257,22 @@
- +
-

Sessions

-

+

Auto-name any sessions that still show a random ID, using their first message as the name. Only unnamed sessions are affected — existing names are left alone.

- - + +
+

Personas

    @@ -528,16 +304,6 @@ document.getElementById('show-rename-user').style.display = ''; }); - // Gemini key — "remove" link clears the input and submits the form - const geminiRemove = document.getElementById('gemini-remove-link'); - if (geminiRemove) { - geminiRemove.addEventListener('click', e => { - e.preventDefault(); - document.getElementById('gemini_api_key').value = ''; - document.querySelector('form[action="/settings/gemini-key"]').submit(); - }); - } - // Clear localStorage (keeps JWT cookie — no sign-out) document.getElementById('clear-ls-btn').addEventListener('click', () => { localStorage.clear(); @@ -548,8 +314,7 @@ (async () => { try { const d = await fetch('/backend').then(r => r.json()); - const roles = d.available_roles || []; - if (roles.length === 0) { + if ((d.available_roles || []).length === 0) { document.getElementById('openrouter-quickstart').style.display = 'block'; } } catch (_) {} @@ -563,7 +328,7 @@ if (!resp.ok) throw new Error(resp.statusText); const rows_data = await resp.json(); if (!rows_data.length) { - wrap.innerHTML = '

    No usage recorded yet.

    '; + wrap.innerHTML = '

    No usage recorded yet.

    '; return; } const fmt = n => n >= 1000 ? (n / 1000).toFixed(1) + 'k' : String(n); @@ -572,27 +337,22 @@ ? `${d.label}` : `${d.key}`; return ` - ${labelCell} - ${d.calls} - ${fmt(d.prompt_tokens)} - ${fmt(d.completion_tokens)} - ${fmt(d.total_tokens)} + ${labelCell} + ${d.calls} + ${fmt(d.prompt_tokens)} + ${fmt(d.completion_tokens)} + ${fmt(d.total_tokens)} `; }).join(''); - wrap.innerHTML = ` - - - - - - - - - + wrap.innerHTML = `
    ModelCallsPromptOutputTotal
    + + + + ${rows}
    ModelCallsPromptOutputTotal
    `; } catch (e) { - wrap.innerHTML = `

    Could not load usage data.

    `; + wrap.innerHTML = '

    Could not load usage data.

    '; } })(); diff --git a/cortex/static/tools_settings.html b/cortex/static/tools_settings.html index 6652a0a..6a71077 100644 --- a/cortex/static/tools_settings.html +++ b/cortex/static/tools_settings.html @@ -4,51 +4,15 @@ Tool Settings — Cortex + + + + + @@ -146,8 +112,8 @@
    -

    Tool Settings

    -

    +

    Tool Settings

    +

    Control which orchestrator tools are available. The risk level sets an automatic threshold; whitelist and blacklist let you fine-tune individual tools beyond that.

    @@ -157,15 +123,14 @@
    - +

    Risk Policy

    -
    Max risk level @@ -175,53 +140,42 @@ Medium tools write to local data or send notifications to you (cron jobs, scratch, task management).
    High tools affect external systems or the host (shell exec, email, device control, service restart).

    -

    The Auto column below shows each tool's status at your current max risk level. Use the override column to force-include or force-exclude individual tools.

    - +
    Auto-included by risk level Auto-excluded by risk level
    - + {{ tool_table_html }} - +

    Confirmation Gate

    -

    +

    Some tools require explicit confirmation before executing. Override the defaults here.
    - Tools requiring confirmation by default: {{ confirm_required_tools }} + Tools requiring confirmation by default: {{ confirm_required_tools }}

    -
    -
    - +
    +
    + -

    One tool name per line. These tools skip the confirmation prompt.

    + autocomplete="off" spellcheck="false">{{ tool_allow }} +

    One tool name per line. These tools skip the confirmation prompt.

    -
    - +
    + -

    These tools are always blocked regardless of risk policy.

    + autocomplete="off" spellcheck="false">{{ tool_deny }} +

    These tools are always blocked regardless of risk policy.