The settings were 18 rows on one level: the broker URL, the pace in milliseconds and the model all shouted equally loud, although one is set once and the other is changed daily. - Model picker moved into the header. It is the switch you touch every day and it sat at the end of the settings list. Source depends on driveMode: the verified gateway models in direct mode, the broker's list otherwise; hidden when there is nothing to choose. - Settings grouped into five collapsible sections (Verbindung & Antrieb, Verhalten, Anzeige & Sicherheit, Modell & Zugang, Memory). Only "Modell & Zugang" starts open — that is what first-time setup needs. - Existing rows were wrapped in place, not moved, so every element id and every binding stays intact. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1227 lines
41 KiB
HTML
1227 lines
41 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="de">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>Nexus Browser Pilot</title>
|
||
<style>
|
||
/* Keine externen Ressourcen, keine Webfonts — MV3-CSP verbietet sie. */
|
||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||
|
||
:root {
|
||
--bg: #101318;
|
||
--surface: #171b22;
|
||
--surface2: #1e242e;
|
||
--border: #272e3a;
|
||
--border-lt: #333c4a;
|
||
--text: #dfe4ec;
|
||
--text-dim: #8a94a6;
|
||
--text-mute: #626c7d;
|
||
--accent: #4ea3c8;
|
||
/* riskMode 'off' — ruhiges Gruen: der Agent laeuft durch */
|
||
--auto: #3fb27f;
|
||
--auto-bg: rgba(63, 178, 127, 0.13);
|
||
/* riskMode 'confirm' — Bernstein: es wird angehalten und gefragt */
|
||
--ask: #e0a338;
|
||
--ask-bg: rgba(224, 163, 56, 0.13);
|
||
--error: #e05c5c;
|
||
--error-bg: rgba(224, 92, 92, 0.12);
|
||
--success: #3fb27f;
|
||
--radius: 8px;
|
||
}
|
||
|
||
html, body { height: 100%; }
|
||
body {
|
||
font-family: system-ui, 'Segoe UI', -apple-system, BlinkMacSystemFont, sans-serif;
|
||
background: var(--bg);
|
||
color: var(--text);
|
||
display: flex;
|
||
flex-direction: column;
|
||
font-size: 13px;
|
||
line-height: 1.4;
|
||
overflow: hidden;
|
||
}
|
||
|
||
/* ── Kopfzeile ───────────────────────────────────────────────────────── */
|
||
.header {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 7px;
|
||
padding: 8px 10px;
|
||
background: var(--surface);
|
||
border-bottom: 1px solid var(--border);
|
||
flex: 0 0 auto;
|
||
}
|
||
.status-dot {
|
||
width: 8px; height: 8px; border-radius: 50%;
|
||
background: var(--error);
|
||
flex: 0 0 auto;
|
||
transition: background 0.25s;
|
||
}
|
||
.status-dot.connected { background: var(--success); }
|
||
/* driveMode 'direct' braucht keinen Broker — kein Rot fuer einen Zustand, der in Ordnung ist. */
|
||
.status-dot.direct { background: var(--accent); }
|
||
.header h1 {
|
||
font-size: 13px;
|
||
font-weight: 600;
|
||
letter-spacing: 0.2px;
|
||
white-space: nowrap;
|
||
}
|
||
.header .spacer { flex: 1; }
|
||
.badge {
|
||
font-size: 10px;
|
||
font-weight: 600;
|
||
padding: 2px 6px;
|
||
border-radius: 4px;
|
||
white-space: nowrap;
|
||
border: 1px solid transparent;
|
||
}
|
||
/* Moduskennung auch dann sichtbar, wenn der Schalter weggescrollt ist */
|
||
#modeBadge[data-mode="off"] { color: var(--auto); background: var(--auto-bg); border-color: var(--auto); }
|
||
#modeBadge[data-mode="confirm"] { color: var(--ask); background: var(--ask-bg); border-color: var(--ask); }
|
||
.round-counter {
|
||
font-size: 11px;
|
||
font-weight: 600;
|
||
color: var(--accent);
|
||
font-variant-numeric: tabular-nums;
|
||
white-space: nowrap;
|
||
}
|
||
.mcp-status {
|
||
font-size: 10px;
|
||
color: var(--text-dim);
|
||
background: var(--surface2);
|
||
padding: 2px 5px;
|
||
border-radius: 4px;
|
||
white-space: nowrap;
|
||
}
|
||
.mcp-status:empty { display: none; }
|
||
|
||
/* ── Token-Usage-Bar ─────────────────────────────────────────────────── */
|
||
.usage-bar-wrap {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 5px;
|
||
font-size: 10px;
|
||
color: var(--text-mute);
|
||
white-space: nowrap;
|
||
}
|
||
.usage-bar-wrap:empty { display: none; }
|
||
.usage-bar {
|
||
width: 52px;
|
||
height: 4px;
|
||
background: var(--surface2);
|
||
border-radius: 2px;
|
||
overflow: hidden;
|
||
flex: 0 0 52px;
|
||
}
|
||
.usage-bar-fill {
|
||
height: 100%;
|
||
border-radius: 2px;
|
||
background: var(--accent);
|
||
transition: width 0.4s ease, background 0.4s;
|
||
width: 0%;
|
||
}
|
||
.usage-bar-fill.warn { background: var(--ask); }
|
||
.usage-bar-fill.crit { background: var(--error); }
|
||
|
||
.target-tab {
|
||
font-size: 10px;
|
||
color: var(--text-dim);
|
||
background: var(--surface2);
|
||
padding: 2px 6px;
|
||
border-radius: 4px;
|
||
white-space: nowrap;
|
||
max-width: 120px;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
}
|
||
.target-tab:empty { display: none; }
|
||
.status-text { font-size: 10px; color: var(--text-mute); white-space: nowrap; }
|
||
.icon-btn {
|
||
background: transparent;
|
||
border: 1px solid var(--border-lt);
|
||
border-radius: 5px;
|
||
color: var(--text-dim);
|
||
cursor: pointer;
|
||
font-size: 12px;
|
||
line-height: 1;
|
||
padding: 4px 6px;
|
||
flex: 0 0 auto;
|
||
}
|
||
.icon-btn:hover { color: var(--text); border-color: var(--accent); }
|
||
|
||
/* ── Risikomodus-Schalter ────────────────────────────────────────────── */
|
||
.mode-bar {
|
||
display: flex;
|
||
gap: 6px;
|
||
padding: 8px 10px;
|
||
background: var(--surface);
|
||
border-bottom: 1px solid var(--border);
|
||
flex: 0 0 auto;
|
||
}
|
||
.mode-btn {
|
||
flex: 1 1 0;
|
||
min-width: 0;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: flex-start;
|
||
gap: 1px;
|
||
text-align: left;
|
||
padding: 6px 8px;
|
||
border-radius: 7px;
|
||
border: 1px solid var(--border-lt);
|
||
background: var(--bg);
|
||
color: var(--text-mute);
|
||
cursor: pointer;
|
||
font-family: inherit;
|
||
transition: background 0.15s, border-color 0.15s, color 0.15s;
|
||
}
|
||
.mode-btn:hover { border-color: var(--text-dim); }
|
||
.mode-btn .t {
|
||
font-size: 12px;
|
||
font-weight: 650;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 5px;
|
||
white-space: nowrap;
|
||
}
|
||
/* Ausgefuellter Punkt = aktiv, hohler Ring = inaktiv. Zweites Signal neben Farbe. */
|
||
.mode-btn .t::before {
|
||
content: '';
|
||
width: 8px; height: 8px;
|
||
border-radius: 50%;
|
||
border: 2px solid currentColor;
|
||
flex: 0 0 auto;
|
||
}
|
||
.mode-btn[aria-pressed="true"] .t::before { background: currentColor; }
|
||
.mode-btn .s {
|
||
font-size: 10px;
|
||
line-height: 1.25;
|
||
color: var(--text-mute);
|
||
}
|
||
#modeOff[aria-pressed="true"] {
|
||
border-color: var(--auto);
|
||
background: var(--auto-bg);
|
||
color: var(--auto);
|
||
}
|
||
#modeConfirm[aria-pressed="true"] {
|
||
border-color: var(--ask);
|
||
background: var(--ask-bg);
|
||
color: var(--ask);
|
||
}
|
||
.mode-btn[aria-pressed="true"] .s { color: var(--text-dim); }
|
||
|
||
/* ── Einstellungen ───────────────────────────────────────────────────── */
|
||
.settings {
|
||
background: var(--surface);
|
||
border-bottom: 1px solid var(--border);
|
||
flex: 0 0 auto;
|
||
max-height: 45vh;
|
||
overflow-y: auto;
|
||
}
|
||
.settings > summary {
|
||
padding: 6px 10px;
|
||
font-size: 11px;
|
||
font-weight: 600;
|
||
color: var(--text-dim);
|
||
cursor: pointer;
|
||
user-select: none;
|
||
outline: none;
|
||
list-style: none;
|
||
}
|
||
.settings > summary::-webkit-details-marker { display: none; }
|
||
.settings > summary::before { content: '▸ '; color: var(--text-mute); }
|
||
.settings[open] > summary::before { content: '▾ '; }
|
||
.settings > summary:hover { color: var(--text); }
|
||
.settings-body { padding: 2px 10px 10px; }
|
||
|
||
/* Klappbare Abschnitte: 18 Zeilen auf einer Ebene sind keine Bedienung,
|
||
sondern eine Liste. Was man taeglich anfasst, steht offen; der Rest
|
||
liegt zugeklappt darunter. */
|
||
.sgroup { border-top: 1px solid var(--border); margin-top: 6px; }
|
||
.sgroup > summary {
|
||
padding: 6px 0 5px;
|
||
font-size: 10px;
|
||
font-weight: 700;
|
||
letter-spacing: 0.5px;
|
||
text-transform: uppercase;
|
||
color: var(--text-mute);
|
||
cursor: pointer;
|
||
user-select: none;
|
||
list-style: none;
|
||
outline: none;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 5px;
|
||
}
|
||
.sgroup > summary::-webkit-details-marker { display: none; }
|
||
.sgroup > summary::before {
|
||
content: '';
|
||
width: 0; height: 0;
|
||
border-left: 4px solid currentColor;
|
||
border-top: 3px solid transparent;
|
||
border-bottom: 3px solid transparent;
|
||
transition: transform 0.15s;
|
||
flex: 0 0 auto;
|
||
}
|
||
.sgroup[open] > summary::before { transform: rotate(90deg); }
|
||
.sgroup > summary:hover { color: var(--text); }
|
||
.sgroup-body { padding: 1px 0 7px; }
|
||
|
||
/* Modellwahl in der Kopfzeile — der am haeufigsten benutzte Schalter
|
||
gehoert nicht ans Ende einer Einstellungsliste. */
|
||
#headerModel {
|
||
font-size: 10px;
|
||
max-width: 116px;
|
||
padding: 2px 4px;
|
||
background: var(--surface2);
|
||
color: var(--text-dim);
|
||
border: 1px solid var(--border-lt);
|
||
border-radius: 4px;
|
||
font-family: inherit;
|
||
cursor: pointer;
|
||
}
|
||
#headerModel:hover { color: var(--text); border-color: var(--accent); }
|
||
.row {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
padding: 3px 0;
|
||
}
|
||
.row > label {
|
||
flex: 0 0 88px;
|
||
font-size: 11px;
|
||
color: var(--text-dim);
|
||
white-space: nowrap;
|
||
}
|
||
.row input[type="text"],
|
||
.row input[type="password"],
|
||
.row input[type="number"],
|
||
.row select {
|
||
flex: 1 1 auto;
|
||
min-width: 0;
|
||
background: var(--bg);
|
||
border: 1px solid var(--border-lt);
|
||
border-radius: 5px;
|
||
color: var(--text);
|
||
padding: 4px 7px;
|
||
font-size: 11px;
|
||
font-family: inherit;
|
||
outline: none;
|
||
}
|
||
.row input[type="text"], .row input[type="password"] { font-family: ui-monospace, 'Cascadia Code', 'Consolas', monospace; }
|
||
.row input:focus, .row select:focus, .settings textarea:focus { border-color: var(--accent); }
|
||
.row.check { gap: 6px; }
|
||
.row.check > label { flex: 1 1 auto; order: 2; }
|
||
.row input[type="checkbox"] {
|
||
order: 1;
|
||
width: 14px; height: 14px;
|
||
accent-color: var(--accent);
|
||
cursor: pointer;
|
||
flex: 0 0 auto;
|
||
}
|
||
.settings .field-block { padding: 4px 0; }
|
||
.settings .field-block > label {
|
||
display: block;
|
||
font-size: 11px;
|
||
color: var(--text-dim);
|
||
margin-bottom: 3px;
|
||
}
|
||
.settings textarea {
|
||
width: 100%;
|
||
background: var(--bg);
|
||
border: 1px solid var(--border-lt);
|
||
border-radius: 5px;
|
||
color: var(--text);
|
||
padding: 6px 7px;
|
||
font-size: 11px;
|
||
font-family: inherit;
|
||
resize: vertical;
|
||
min-height: 70px;
|
||
outline: none;
|
||
}
|
||
.group-title {
|
||
font-size: 10px;
|
||
font-weight: 700;
|
||
letter-spacing: 0.6px;
|
||
text-transform: uppercase;
|
||
color: var(--text-mute);
|
||
margin: 8px 0 2px;
|
||
padding-top: 7px;
|
||
border-top: 1px solid var(--border);
|
||
}
|
||
.hint { font-size: 10px; color: var(--text-mute); padding: 2px 0 0; }
|
||
|
||
/* Nexus-Modellauswahl */
|
||
.inline { display: flex; gap: 6px; flex: 1 1 auto; }
|
||
.mini {
|
||
background: var(--bg);
|
||
border: 1px solid var(--border-lt);
|
||
border-radius: 5px;
|
||
color: var(--text-dim);
|
||
padding: 3px 8px;
|
||
font-size: 10px;
|
||
font-family: inherit;
|
||
cursor: pointer;
|
||
}
|
||
.mini:hover { color: var(--text); border-color: var(--text-mute); }
|
||
.mini:disabled { opacity: .5; cursor: default; }
|
||
/* Zustand des gewaehlten Modells auf einen Blick — eine Drosselung ist der
|
||
haeufigste Grund, warum "nichts passiert". */
|
||
#nexusModel.throttled { border-color: var(--ask); color: var(--ask); }
|
||
#nexusModel.unavailable { border-color: var(--error); color: var(--error); }
|
||
#nexusModelHint.throttled { color: var(--ask); }
|
||
#nexusModelHint.err { color: var(--error); }
|
||
#nexusModelHint.ok { color: var(--auto); }
|
||
[hidden] { display: none !important; }
|
||
|
||
/* ── Diagnose-Panel ──────────────────────────────────────────────────── */
|
||
.diag-panel {
|
||
display: none;
|
||
flex-direction: column;
|
||
gap: 6px;
|
||
padding: 8px 10px;
|
||
background: var(--surface);
|
||
border-bottom: 1px solid var(--border);
|
||
flex: 0 0 auto;
|
||
max-height: 40vh;
|
||
overflow-y: auto;
|
||
}
|
||
.diag-panel.open { display: flex; }
|
||
.diag-row {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
font-size: 11px;
|
||
}
|
||
.diag-label {
|
||
flex: 0 0 110px;
|
||
color: var(--text-dim);
|
||
white-space: nowrap;
|
||
}
|
||
.diag-status {
|
||
flex: 1 1 auto;
|
||
font-family: ui-monospace, 'Cascadia Code', 'Consolas', monospace;
|
||
font-size: 10px;
|
||
color: var(--text-mute);
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
}
|
||
.diag-status.ok { color: var(--auto); }
|
||
.diag-status.err { color: var(--error); }
|
||
.diag-status.warn { color: var(--ask); }
|
||
.diag-btn {
|
||
flex: 0 0 auto;
|
||
background: var(--bg);
|
||
border: 1px solid var(--border-lt);
|
||
border-radius: 4px;
|
||
color: var(--text-dim);
|
||
cursor: pointer;
|
||
font-size: 10px;
|
||
padding: 2px 7px;
|
||
font-family: inherit;
|
||
}
|
||
.diag-btn:hover { border-color: var(--accent); color: var(--text); }
|
||
.diag-section {
|
||
font-size: 10px;
|
||
font-weight: 700;
|
||
letter-spacing: 0.5px;
|
||
text-transform: uppercase;
|
||
color: var(--text-mute);
|
||
padding-top: 4px;
|
||
border-top: 1px solid var(--border);
|
||
margin-top: 2px;
|
||
}
|
||
.diag-section:first-child { border-top: none; padding-top: 0; margin-top: 0; }
|
||
.conv-bar {
|
||
display: flex;
|
||
gap: 5px;
|
||
padding: 5px 10px;
|
||
background: var(--surface);
|
||
border-bottom: 1px solid var(--border);
|
||
flex: 0 0 auto;
|
||
align-items: center;
|
||
}
|
||
.conv-bar select {
|
||
flex: 1 1 auto;
|
||
min-width: 0;
|
||
background: var(--bg);
|
||
border: 1px solid var(--border-lt);
|
||
border-radius: 5px;
|
||
color: var(--text);
|
||
padding: 3px 6px;
|
||
font-size: 11px;
|
||
font-family: inherit;
|
||
outline: none;
|
||
}
|
||
.conv-bar select:focus { border-color: var(--accent); }
|
||
.conv-btn {
|
||
background: var(--bg);
|
||
border: 1px solid var(--border-lt);
|
||
border-radius: 5px;
|
||
color: var(--text-dim);
|
||
cursor: pointer;
|
||
font-size: 13px;
|
||
line-height: 1;
|
||
padding: 4px 7px;
|
||
flex: 0 0 auto;
|
||
}
|
||
.conv-btn:hover { color: var(--text); border-color: var(--accent); }
|
||
|
||
/* ── Chat ────────────────────────────────────────────────────────────── */
|
||
.chat-container {
|
||
flex: 1 1 auto;
|
||
overflow-y: auto;
|
||
overflow-x: hidden;
|
||
padding: 10px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 7px;
|
||
}
|
||
.message {
|
||
padding: 7px 10px;
|
||
border-radius: var(--radius);
|
||
max-width: 96%;
|
||
overflow-wrap: anywhere;
|
||
white-space: pre-wrap;
|
||
font-size: 13px;
|
||
}
|
||
.message.user {
|
||
background: var(--surface2);
|
||
align-self: flex-end;
|
||
border-bottom-right-radius: 2px;
|
||
}
|
||
.message.assistant {
|
||
background: var(--surface);
|
||
align-self: flex-start;
|
||
border-bottom-left-radius: 2px;
|
||
position: relative;
|
||
}
|
||
.message.assistant:hover .copy-btn,
|
||
.message.assistant:focus-within .copy-btn {
|
||
opacity: 1;
|
||
}
|
||
.copy-btn {
|
||
position: absolute;
|
||
top: 5px;
|
||
right: 6px;
|
||
opacity: 0;
|
||
transition: opacity 0.15s;
|
||
background: var(--surface2);
|
||
border: 1px solid var(--border-lt);
|
||
border-radius: 5px;
|
||
color: var(--text-dim);
|
||
font-size: 11px;
|
||
padding: 2px 6px;
|
||
cursor: pointer;
|
||
line-height: 1.4;
|
||
user-select: none;
|
||
}
|
||
.copy-btn:hover { background: var(--accent); color: #fff; border-color: var(--accent); }
|
||
.copy-btn.copied { background: var(--auto); color: #0d1014; border-color: var(--auto); opacity: 1; }
|
||
.message.tool {
|
||
background: var(--surface);
|
||
border-left: 3px solid var(--accent);
|
||
font-size: 11px;
|
||
color: var(--text-dim);
|
||
padding: 5px 8px;
|
||
max-width: 100%;
|
||
white-space: normal;
|
||
}
|
||
.message.error {
|
||
background: var(--error-bg);
|
||
border-left: 3px solid var(--error);
|
||
color: #f0a0a0;
|
||
max-width: 100%;
|
||
}
|
||
.message.system {
|
||
background: transparent;
|
||
color: var(--text-mute);
|
||
font-size: 11px;
|
||
text-align: center;
|
||
align-self: center;
|
||
padding: 2px 0;
|
||
max-width: 100%;
|
||
overflow-wrap: anywhere;
|
||
}
|
||
.message.system.warn { color: var(--ask); }
|
||
|
||
/* ── Markdown in Nachrichten ────────────────────────────────────────── */
|
||
.message.assistant.md { white-space: normal; }
|
||
.message.assistant.md p { margin: 0 0 0.5em; }
|
||
.message.assistant.md p:last-child { margin-bottom: 0; }
|
||
.message.assistant.md code {
|
||
background: var(--surface2);
|
||
padding: 1px 4px;
|
||
border-radius: 3px;
|
||
font-family: ui-monospace, 'Cascadia Code', 'Consolas', monospace;
|
||
font-size: 0.9em;
|
||
}
|
||
.message.assistant.md pre {
|
||
background: var(--bg);
|
||
border: 1px solid var(--border);
|
||
border-radius: 5px;
|
||
padding: 8px;
|
||
overflow-x: auto;
|
||
margin: 0.5em 0;
|
||
font-size: 11px;
|
||
}
|
||
.message.assistant.md pre code {
|
||
background: none;
|
||
padding: 0;
|
||
font-size: inherit;
|
||
}
|
||
.message.assistant.md ul, .message.assistant.md ol {
|
||
margin: 0.4em 0;
|
||
padding-left: 1.4em;
|
||
}
|
||
.message.assistant.md li { margin: 0.15em 0; }
|
||
.message.assistant.md h1, .message.assistant.md h2,
|
||
.message.assistant.md h3, .message.assistant.md h4 {
|
||
margin: 0.6em 0 0.3em;
|
||
font-size: 1em;
|
||
font-weight: 700;
|
||
}
|
||
.message.assistant.md h1 { font-size: 1.15em; }
|
||
.message.assistant.md h2 { font-size: 1.05em; }
|
||
.message.assistant.md blockquote {
|
||
border-left: 3px solid var(--border-lt);
|
||
padding-left: 8px;
|
||
color: var(--text-dim);
|
||
margin: 0.4em 0;
|
||
}
|
||
.message.assistant.md table {
|
||
border-collapse: collapse;
|
||
margin: 0.4em 0;
|
||
font-size: 11px;
|
||
}
|
||
.message.assistant.md th, .message.assistant.md td {
|
||
border: 1px solid var(--border);
|
||
padding: 3px 6px;
|
||
}
|
||
.message.assistant.md th { background: var(--surface2); font-weight: 600; }
|
||
.message.assistant.md a { color: var(--accent); text-decoration: underline; }
|
||
|
||
.streaming-cursor::after {
|
||
content: '▊';
|
||
animation: blink 0.9s steps(1) infinite;
|
||
color: var(--accent);
|
||
}
|
||
@keyframes blink { 50% { opacity: 0; } }
|
||
|
||
/* Werkzeugereignisse eingeklappt — sonst friert der Renderer bei vielen Events ein. */
|
||
details.tool-collapsed > summary {
|
||
font-size: 11px;
|
||
cursor: pointer;
|
||
outline: none;
|
||
user-select: none;
|
||
list-style: none;
|
||
font-family: ui-monospace, 'Cascadia Code', 'Consolas', monospace;
|
||
}
|
||
details.tool-collapsed > summary::-webkit-details-marker { display: none; }
|
||
details.tool-collapsed > pre {
|
||
margin: 5px 0 0;
|
||
font-size: 10.5px;
|
||
font-family: ui-monospace, 'Cascadia Code', 'Consolas', monospace;
|
||
white-space: pre-wrap;
|
||
overflow-wrap: anywhere;
|
||
max-height: 190px;
|
||
overflow: auto;
|
||
color: var(--text-mute);
|
||
}
|
||
|
||
/* ── Spotlight-Viewer (was die KI gerade sieht) ─────────────────────── */
|
||
#spotlight {
|
||
position: fixed;
|
||
bottom: 80px;
|
||
right: 12px;
|
||
width: 220px;
|
||
background: rgba(16, 19, 24, 0.92);
|
||
border: 1px solid var(--border-lt);
|
||
border-radius: 10px;
|
||
box-shadow: 0 8px 32px rgba(0,0,0,0.55), 0 0 0 1px rgba(78,163,200,0.12);
|
||
backdrop-filter: blur(8px);
|
||
-webkit-backdrop-filter: blur(8px);
|
||
overflow: hidden;
|
||
z-index: 40;
|
||
opacity: 0;
|
||
transform: translateY(8px) scale(0.97);
|
||
transition: opacity 0.25s ease, transform 0.25s ease;
|
||
pointer-events: none;
|
||
}
|
||
#spotlight.visible {
|
||
opacity: 1;
|
||
transform: translateY(0) scale(1);
|
||
pointer-events: auto;
|
||
}
|
||
#spotlight.hidden-by-user { display: none; }
|
||
.spotlight-header {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 6px;
|
||
padding: 6px 8px 4px;
|
||
font-size: 10px;
|
||
color: var(--text-mute);
|
||
border-bottom: 1px solid var(--border);
|
||
user-select: none;
|
||
}
|
||
.spotlight-io {
|
||
display: flex;
|
||
gap: 3px;
|
||
align-items: center;
|
||
}
|
||
.spotlight-io span {
|
||
width: 5px; height: 5px;
|
||
border-radius: 50%;
|
||
background: var(--accent);
|
||
opacity: 0.3;
|
||
animation: io-pulse 1.4s ease-in-out infinite;
|
||
}
|
||
.spotlight-io span:nth-child(2) { animation-delay: 0.2s; }
|
||
.spotlight-io span:nth-child(3) { animation-delay: 0.4s; }
|
||
@keyframes io-pulse {
|
||
0%, 100% { opacity: 0.2; transform: scale(0.8); }
|
||
50% { opacity: 1; transform: scale(1.2); }
|
||
}
|
||
.spotlight-label {
|
||
flex: 1;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
}
|
||
.spotlight-close {
|
||
background: none;
|
||
border: none;
|
||
color: var(--text-mute);
|
||
cursor: pointer;
|
||
font-size: 11px;
|
||
padding: 0 2px;
|
||
line-height: 1;
|
||
opacity: 0.6;
|
||
}
|
||
.spotlight-close:hover { opacity: 1; color: var(--text); }
|
||
.spotlight-img-wrap {
|
||
position: relative;
|
||
background: #000;
|
||
cursor: zoom-in;
|
||
}
|
||
.spotlight-img-wrap img {
|
||
display: block;
|
||
width: 100%;
|
||
height: auto;
|
||
max-height: 140px;
|
||
object-fit: cover;
|
||
object-position: top;
|
||
opacity: 0;
|
||
transition: opacity 0.3s;
|
||
}
|
||
.spotlight-img-wrap img.loaded { opacity: 1; }
|
||
.spotlight-action {
|
||
font-size: 10px;
|
||
color: var(--text-mute);
|
||
padding: 4px 8px;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
border-top: 1px solid var(--border);
|
||
min-height: 22px;
|
||
}
|
||
|
||
/* ── Screenshot-Miniatur ─────────────────────────────────────────────── */
|
||
.shot {
|
||
align-self: stretch;
|
||
background: var(--surface);
|
||
border: 1px solid var(--border);
|
||
border-radius: var(--radius);
|
||
padding: 5px;
|
||
max-width: 100%;
|
||
}
|
||
.shot .cap {
|
||
font-size: 10px;
|
||
color: var(--text-mute);
|
||
padding: 0 2px 4px;
|
||
display: flex;
|
||
justify-content: space-between;
|
||
gap: 6px;
|
||
}
|
||
.shot img {
|
||
display: block;
|
||
max-width: 100%;
|
||
height: auto;
|
||
border-radius: 4px;
|
||
cursor: zoom-in;
|
||
background: #000;
|
||
}
|
||
/* Aeltere Vorschauen geben ihre Base64-Daten frei, sonst waechst das Panel unbegrenzt. */
|
||
.shot.dropped img { display: none; }
|
||
|
||
/* Lightbox: im schmalen Panel ist die Miniatur zu klein zum Lesen. */
|
||
.lightbox {
|
||
position: fixed;
|
||
inset: 0;
|
||
background: rgba(0, 0, 0, 0.88);
|
||
z-index: 50;
|
||
overflow: auto;
|
||
cursor: zoom-out;
|
||
padding: 8px;
|
||
}
|
||
.lightbox img {
|
||
display: block;
|
||
max-width: none;
|
||
border-radius: 4px;
|
||
}
|
||
.lightbox .lb-hint {
|
||
position: sticky;
|
||
top: 0;
|
||
font-size: 10px;
|
||
color: #b9c2d0;
|
||
padding: 4px 2px;
|
||
}
|
||
|
||
/* ── Risiko-/Freigabe-Karte ──────────────────────────────────────────── */
|
||
.risk-card {
|
||
align-self: stretch;
|
||
max-width: 100%;
|
||
background: var(--ask-bg);
|
||
border: 1px solid var(--ask);
|
||
border-radius: var(--radius);
|
||
padding: 10px;
|
||
}
|
||
.risk-card .rc-title {
|
||
font-size: 12px;
|
||
font-weight: 700;
|
||
color: var(--ask);
|
||
margin-bottom: 6px;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 6px;
|
||
}
|
||
.risk-card .rc-reason {
|
||
font-size: 12px;
|
||
color: var(--text);
|
||
margin-bottom: 7px;
|
||
overflow-wrap: anywhere;
|
||
}
|
||
.risk-card dl {
|
||
display: grid;
|
||
grid-template-columns: 52px 1fr;
|
||
gap: 2px 8px;
|
||
font-size: 11px;
|
||
margin-bottom: 7px;
|
||
}
|
||
.risk-card dt { color: var(--text-mute); }
|
||
.risk-card dd {
|
||
color: var(--text-dim);
|
||
overflow-wrap: anywhere;
|
||
font-family: ui-monospace, 'Cascadia Code', 'Consolas', monospace;
|
||
}
|
||
.chips { display: flex; flex-wrap: wrap; gap: 4px; margin-bottom: 8px; }
|
||
.chip {
|
||
font-size: 10px;
|
||
font-weight: 600;
|
||
color: var(--ask);
|
||
border: 1px solid var(--ask);
|
||
border-radius: 10px;
|
||
padding: 1px 7px;
|
||
white-space: nowrap;
|
||
}
|
||
.rc-actions { display: flex; gap: 7px; }
|
||
.rc-actions button {
|
||
flex: 1 1 0;
|
||
border: none;
|
||
border-radius: 6px;
|
||
padding: 7px 10px;
|
||
font-size: 12px;
|
||
font-weight: 650;
|
||
font-family: inherit;
|
||
cursor: pointer;
|
||
color: #0d1014;
|
||
}
|
||
.btn-go { background: var(--auto); }
|
||
.btn-go:hover { filter: brightness(1.1); }
|
||
.btn-cancel { background: var(--surface2); color: var(--text); border: 1px solid var(--border-lt); }
|
||
.btn-cancel:hover { border-color: var(--error); color: #f0a0a0; }
|
||
.rc-timer {
|
||
font-size: 10px;
|
||
color: var(--text-mute);
|
||
margin-top: 7px;
|
||
font-variant-numeric: tabular-nums;
|
||
}
|
||
.rc-timer b { color: var(--ask); font-weight: 700; font-variant-numeric: tabular-nums; }
|
||
.rc-timer.urgent b { color: var(--error); }
|
||
/* Entschiedene Karte tritt zurueck — offen ist nur, was noch eine Antwort braucht. */
|
||
.risk-card.resolved { background: var(--surface); border-color: var(--border); }
|
||
.risk-card.resolved .rc-title { color: var(--text-dim); }
|
||
.risk-card.resolved .chip { color: var(--text-mute); border-color: var(--border-lt); }
|
||
.rc-resolved { font-size: 11px; font-weight: 600; }
|
||
.rc-resolved.ok { color: var(--auto); }
|
||
.rc-resolved.no { color: var(--error); }
|
||
.rc-resolved.stale { color: var(--text-mute); }
|
||
|
||
/* ── Eingabe ─────────────────────────────────────────────────────────── */
|
||
.input-area {
|
||
flex: 0 0 auto;
|
||
padding: 8px 10px;
|
||
border-top: 1px solid var(--border);
|
||
background: var(--surface);
|
||
display: flex;
|
||
gap: 7px;
|
||
align-items: flex-end;
|
||
}
|
||
/* Bildanhänge: Streifen ÜBER der Eingabezeile, damit das Textfeld nicht springt. */
|
||
.attach-strip {
|
||
flex: 0 0 auto;
|
||
display: flex;
|
||
gap: 6px;
|
||
overflow-x: auto;
|
||
padding: 7px 10px 0;
|
||
background: var(--surface);
|
||
border-top: 1px solid var(--border);
|
||
}
|
||
.attach-chip { position: relative; flex: 0 0 auto; }
|
||
.attach-chip img {
|
||
height: 46px; width: 46px; object-fit: cover;
|
||
border-radius: var(--radius); border: 1px solid var(--border-lt); display: block;
|
||
}
|
||
.attach-chip button {
|
||
position: absolute; top: -5px; right: -5px;
|
||
width: 17px; height: 17px; padding: 0; line-height: 15px; font-size: 11px;
|
||
border-radius: 50%; border: 1px solid var(--border);
|
||
background: var(--surface); color: var(--text); cursor: pointer;
|
||
}
|
||
.attach-chip .attach-size {
|
||
position: absolute; bottom: 0; left: 0; right: 0;
|
||
font-size: 8px; text-align: center; color: var(--text-mute);
|
||
background: rgba(0,0,0,.55); border-radius: 0 0 var(--radius) var(--radius);
|
||
}
|
||
.attach-btn { background: var(--surface); border: 1px solid var(--border-lt); padding: 7px 9px; }
|
||
.input-area.drop-active { outline: 2px dashed var(--accent); outline-offset: -3px; }
|
||
.input-area textarea {
|
||
flex: 1 1 auto;
|
||
min-width: 0;
|
||
background: var(--bg);
|
||
border: 1px solid var(--border-lt);
|
||
border-radius: var(--radius);
|
||
color: var(--text);
|
||
padding: 8px 10px;
|
||
font-size: 13px;
|
||
font-family: inherit;
|
||
resize: none;
|
||
min-height: 36px;
|
||
max-height: 120px;
|
||
outline: none;
|
||
}
|
||
.input-area textarea:focus { border-color: var(--accent); }
|
||
.input-area button {
|
||
flex: 0 0 auto;
|
||
border: none;
|
||
border-radius: var(--radius);
|
||
padding: 9px 13px;
|
||
font-size: 12px;
|
||
font-weight: 650;
|
||
font-family: inherit;
|
||
cursor: pointer;
|
||
white-space: nowrap;
|
||
}
|
||
#sendBtn { background: var(--accent); color: #0d1014; }
|
||
#sendBtn:hover { filter: brightness(1.1); }
|
||
#sendBtn:disabled { opacity: 0.4; cursor: not-allowed; filter: none; }
|
||
#stopBtn { background: var(--error); color: #fff; }
|
||
#stopBtn:hover { filter: brightness(1.1); }
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="header">
|
||
<div class="status-dot" id="statusDot"></div>
|
||
<h1>Nexus Pilot</h1>
|
||
<span class="badge" id="modeBadge" data-mode="off">Autonom</span>
|
||
<span class="spacer"></span>
|
||
<span class="target-tab" id="targetTab" title="Aktiver Agent-Tab"></span>
|
||
<span class="round-counter" id="roundCounter"></span>
|
||
<div class="usage-bar-wrap" id="usageBarWrap" title="">
|
||
<div class="usage-bar"><div class="usage-bar-fill" id="usageBarFill"></div></div>
|
||
<span id="usageBarLabel"></span>
|
||
</div>
|
||
<span class="mcp-status" id="mcpStatus"></span>
|
||
<select id="headerModel" title="Aktives Modell"></select>
|
||
<span class="status-text" id="statusText">getrennt</span>
|
||
<button class="icon-btn" id="settingsToggle" type="button" title="Einstellungen ein-/ausklappen" aria-label="Einstellungen">⚙</button>
|
||
<button class="icon-btn" id="diagToggle" type="button" title="Diagnose / Tests" aria-label="Diagnose">🔬</button>
|
||
</div>
|
||
|
||
<div class="diag-panel" id="diagPanel">
|
||
<div class="diag-section">Version</div>
|
||
<div class="diag-row">
|
||
<span class="diag-label">Panel / Worker</span>
|
||
<span class="diag-status" id="diagVersion">—</span>
|
||
<button class="diag-btn" id="diagReloadBtn">Neu laden</button>
|
||
</div>
|
||
<div class="diag-section">Broker / Agent</div>
|
||
<div class="diag-row">
|
||
<span class="diag-label">Broker-WS</span>
|
||
<span class="diag-status" id="diagBroker">—</span>
|
||
<button class="diag-btn" id="diagBrokerBtn">Test</button>
|
||
</div>
|
||
<div class="diag-row">
|
||
<span class="diag-label">Agent-Health</span>
|
||
<span class="diag-status" id="diagAgent">—</span>
|
||
<button class="diag-btn" id="diagAgentBtn">Test</button>
|
||
</div>
|
||
<div class="diag-row">
|
||
<span class="diag-label">Verlauf (Turns)</span>
|
||
<span class="diag-status" id="diagConvTurns">—</span>
|
||
<button class="diag-btn" id="diagResetConvBtn">Reset</button>
|
||
</div>
|
||
|
||
<div class="diag-section">MCP-Bridge</div>
|
||
<div class="diag-row">
|
||
<span class="diag-label">MCP-Bridge</span>
|
||
<span class="diag-status" id="diagMcp">—</span>
|
||
<button class="diag-btn" id="diagMcpBtn">Test</button>
|
||
</div>
|
||
<div class="diag-row">
|
||
<span class="diag-label">MCP-Tools</span>
|
||
<span class="diag-status" id="diagMcpTools">—</span>
|
||
</div>
|
||
<div class="diag-row" id="diagMcpStartRow" hidden>
|
||
<span class="diag-label" style="color:var(--ask)">▸ Starten</span>
|
||
<span class="diag-status" style="font-size:10px;color:var(--text-mute);flex:1">mcp-bridge.bat ausführen</span>
|
||
<button class="diag-btn" id="diagMcpCopyBtn" title="Startbefehl kopieren">⎘</button>
|
||
</div>
|
||
|
||
<div class="diag-section">Memory</div>
|
||
<div class="diag-row">
|
||
<span class="diag-label">memory.cnull.net</span>
|
||
<span class="diag-status" id="diagMemory">—</span>
|
||
<button class="diag-btn" id="diagMemoryBtn">Test</button>
|
||
</div>
|
||
|
||
<div class="diag-section">Extension</div>
|
||
<div class="diag-row">
|
||
<span class="diag-label">Service Worker</span>
|
||
<span class="diag-status" id="diagSw">—</span>
|
||
<button class="diag-btn" id="diagSwBtn">Test</button>
|
||
</div>
|
||
<div class="diag-row">
|
||
<span class="diag-label">Aktiver Tab</span>
|
||
<span class="diag-status" id="diagTab">—</span>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="mode-bar" role="group" aria-label="Risikomodus">
|
||
<button class="mode-btn" id="modeOff" type="button" aria-pressed="true">
|
||
<span class="t">Autonom</span>
|
||
<span class="s">handelt ohne Rückfrage</span>
|
||
</button>
|
||
<button class="mode-btn" id="modeConfirm" type="button" aria-pressed="false">
|
||
<span class="t">Nachfragen</span>
|
||
<span class="s">fragt vor Absenden, Kauf, Löschen</span>
|
||
</button>
|
||
</div>
|
||
|
||
<div class="conv-bar" id="convBar">
|
||
<button class="conv-btn" id="newConvBtn" type="button" title="Neue Konversation">+</button>
|
||
<select id="convSelect" title="Konversation wählen"></select>
|
||
<button class="conv-btn" id="syncMemoryBtn" type="button" title="Zu Memory synchronisieren">☁</button>
|
||
</div>
|
||
|
||
<details class="settings" id="settingsPanel">
|
||
<summary>Einstellungen</summary>
|
||
<div class="settings-body">
|
||
|
||
<details class="sgroup"><summary>Verbindung & Antrieb</summary><div class="sgroup-body">
|
||
<div class="row">
|
||
<label for="brokerUrl">Broker</label>
|
||
<input type="text" id="brokerUrl" spellcheck="false" autocomplete="off" placeholder="ws://127.0.0.1:8765/ext/ws">
|
||
</div>
|
||
<div class="row">
|
||
<label for="driveMode">Antrieb</label>
|
||
<select id="driveMode">
|
||
<option value="broker">broker – Python-Broker steuert</option>
|
||
<option value="direct">direct – Loop im Worker</option>
|
||
</select>
|
||
</div>
|
||
|
||
<!-- Modelle des Compliance-Gateways. Nur im Broker-Modus sinnvoll: im
|
||
Modus 'direct' spricht der Worker direkt mit Anthropic/OpenAI. -->
|
||
<div id="nexusModelGroup">
|
||
<div class="row">
|
||
<label for="nexusModel">Nexus-Modell</label>
|
||
<select id="nexusModel">
|
||
<option value="">— Broker nicht erreichbar —</option>
|
||
</select>
|
||
</div>
|
||
<div class="row">
|
||
<label></label>
|
||
<div class="inline">
|
||
<button type="button" id="nexusModelRefresh" class="mini">Aktualisieren</button>
|
||
<button type="button" id="nexusModelProbe" class="mini" title="Prüft jede Modell-ID mit einem minimalen Aufruf gegen das Gateway">Verfügbarkeit prüfen</button>
|
||
</div>
|
||
</div>
|
||
<div class="row">
|
||
<label></label>
|
||
<div class="hint" id="nexusModelHint"></div>
|
||
</div>
|
||
</div>
|
||
</div></details>
|
||
|
||
<details class="sgroup"><summary>Verhalten</summary><div class="sgroup-body">
|
||
<div class="row">
|
||
<label for="inputMode">Eingabe</label>
|
||
<select id="inputMode">
|
||
<option value="trusted">trusted – echte CDP-Events</option>
|
||
<option value="synthetic">synthetic – nur DOM-Events</option>
|
||
<option value="auto">auto – trusted, sonst synthetic</option>
|
||
</select>
|
||
</div>
|
||
<div class="row">
|
||
<label for="maxRounds">Max. Runden</label>
|
||
<input type="number" id="maxRounds" min="1" max="500" step="10">
|
||
</div>
|
||
<div class="row">
|
||
<label for="paceMs">Tempo (ms)</label>
|
||
<input type="number" id="paceMs" min="0" max="5000" step="100" title="Pause zwischen Aktionen in Millisekunden. 0 = keine Pause.">
|
||
</div>
|
||
</div></details>
|
||
|
||
<details class="sgroup"><summary>Anzeige & Sicherheit</summary><div class="sgroup-body">
|
||
<div class="row">
|
||
<label for="pageOverlay">Anzeige</label>
|
||
<select id="pageOverlay" title="Sichtbare Rückmeldung auf der Seite, an der der Agent arbeitet.">
|
||
<option value="both">Glow + Spotlight</option>
|
||
<option value="spotlight">nur Spotlight (aktives Element)</option>
|
||
<option value="glow">nur Glow (aktiver Tab)</option>
|
||
<option value="off">aus</option>
|
||
</select>
|
||
</div>
|
||
<div class="row check">
|
||
<input type="checkbox" id="showCursor">
|
||
<label for="showCursor">Mauszeiger sichtbar einblenden</label>
|
||
</div>
|
||
<div class="row check">
|
||
<input type="checkbox" id="blockMailSend">
|
||
<label for="blockMailSend" title="Klicks auf Senden-Schaltflächen in Mail-Oberflächen werden abgelehnt. Der Agent legt nur Entwürfe an.">Senden in Mails blockieren (nur Entwürfe)</label>
|
||
</div>
|
||
|
||
</div></details>
|
||
|
||
<details class="sgroup" open id="directSection"><summary>Modell & Zugang</summary><div class="sgroup-body">
|
||
<div id="directGroup" hidden>
|
||
<div class="row">
|
||
<label for="provider">Anbieter</label>
|
||
<select id="provider">
|
||
<option value="nexus">nexus (Mercedes)</option>
|
||
<option value="anthropic">anthropic</option>
|
||
<option value="openai">openai</option>
|
||
<option value="requesty">requesty</option>
|
||
</select>
|
||
</div>
|
||
<div class="row">
|
||
<label for="baseUrl">Basis-URL</label>
|
||
<input type="text" id="baseUrl" spellcheck="false" autocomplete="off" placeholder="https://api.anthropic.com/v1">
|
||
</div>
|
||
<div class="row" id="apiKeyRow">
|
||
<label for="apiKey">API-Key</label>
|
||
<input type="password" id="apiKey" spellcheck="false" autocomplete="off" placeholder="sk-…">
|
||
</div>
|
||
<div class="row" id="nexusKeyRow" hidden>
|
||
<label for="nexusApiKey">Nexus-Key</label>
|
||
<input type="password" id="nexusApiKey" spellcheck="false" autocomplete="off" placeholder="Nexus-API-Key (developer.corpinter.net)">
|
||
</div>
|
||
<div class="row" id="modelRow">
|
||
<label for="model">Modell</label>
|
||
<input type="text" id="model" spellcheck="false" autocomplete="off" placeholder="claude-sonnet-5">
|
||
</div>
|
||
<div class="row" id="directNexusModelRow" hidden>
|
||
<label for="directNexusModel">Modell</label>
|
||
<select id="directNexusModel"></select>
|
||
</div>
|
||
<div class="hint" id="directNexusHint" hidden></div>
|
||
<div class="field-block">
|
||
<label for="systemPrompt">System-Prompt</label>
|
||
<textarea id="systemPrompt" spellcheck="false"></textarea>
|
||
</div>
|
||
</div>
|
||
|
||
</div></details>
|
||
|
||
<details class="sgroup"><summary>Memory (memory.cnull.net)</summary><div class="sgroup-body">
|
||
<div class="row check">
|
||
<input type="checkbox" id="memoryEnabled">
|
||
<label for="memoryEnabled">Memory-Server anbinden</label>
|
||
</div>
|
||
<div class="row">
|
||
<label for="memoryUrl">URL</label>
|
||
<input type="text" id="memoryUrl" spellcheck="false" autocomplete="off" placeholder="https://memory.cnull.net">
|
||
</div>
|
||
|
||
<!-- Angemeldet: Token-Zeile + Abmelden -->
|
||
<div id="memoryLoggedIn" hidden>
|
||
<div class="row">
|
||
<label>Token</label>
|
||
<span id="memoryTokenPreview" style="font-size:10px;color:var(--auto);flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-family:monospace"></span>
|
||
<button class="diag-btn" id="memoryLogoutBtn" type="button">Abmelden</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Nicht angemeldet: Login-Formular -->
|
||
<div id="memoryLoginForm">
|
||
<div class="row">
|
||
<label for="memoryUser">Benutzer</label>
|
||
<input type="text" id="memoryUser" spellcheck="false" autocomplete="username" placeholder="admin">
|
||
</div>
|
||
<div class="row">
|
||
<label for="memoryPass">Passwort</label>
|
||
<input type="password" id="memoryPass" spellcheck="false" autocomplete="current-password" placeholder="••••••">
|
||
</div>
|
||
<div class="row">
|
||
<label></label>
|
||
<button class="diag-btn" id="memoryLoginBtn" type="button" style="flex:1;padding:5px 8px;font-size:11px">Anmelden & Token holen</button>
|
||
</div>
|
||
<div id="memoryLoginStatus" class="hint" style="color:var(--error);padding-left:96px"></div>
|
||
</div>
|
||
|
||
<!-- Manuell: Token direkt eintragen -->
|
||
<div class="row">
|
||
<label for="memoryToken">Token (manuell)</label>
|
||
<input type="password" id="memoryToken" spellcheck="false" autocomplete="off" placeholder="mcp-… oder leer lassen">
|
||
</div>
|
||
|
||
<div class="hint">Änderungen werden sofort gespeichert.</div>
|
||
</div></details>
|
||
|
||
</div>
|
||
</details>
|
||
|
||
<!-- Spotlight: zeigt was die KI gerade sieht -->
|
||
<div id="spotlight">
|
||
<div class="spotlight-header">
|
||
<div class="spotlight-io"><span></span><span></span><span></span></div>
|
||
<span class="spotlight-label" id="spotlightLabel">KI sieht…</span>
|
||
<button class="spotlight-close" id="spotlightClose" title="Ausblenden">✕</button>
|
||
</div>
|
||
<div class="spotlight-img-wrap" id="spotlightImgWrap">
|
||
<img id="spotlightImg" alt="Was die KI gerade sieht">
|
||
</div>
|
||
<div class="spotlight-action" id="spotlightAction"></div>
|
||
</div>
|
||
|
||
<div class="chat-container" id="chatContainer"></div>
|
||
|
||
<div class="attach-strip" id="attachStrip" hidden></div>
|
||
|
||
<div class="input-area" id="inputArea">
|
||
<button id="attachBtn" type="button" class="attach-btn"
|
||
title="Bild anhängen — oder Strg+V einfügen / hierher ziehen">📎</button>
|
||
<textarea id="chatInput" placeholder="Auftrag eingeben…" rows="1"></textarea>
|
||
<button id="sendBtn" type="button">Senden</button>
|
||
<button id="stopBtn" type="button" hidden>Stop</button>
|
||
<input type="file" id="imgFile" accept="image/png,image/jpeg,image/gif,image/webp" multiple hidden>
|
||
</div>
|
||
|
||
<!-- Pfad: diese Seite liegt unter /src/sidepanel/, das Bundle unter /dist/ -> zwei Ebenen hoch. -->
|
||
<script src="../../dist/sidepanel.js" type="module"></script>
|
||
</body>
|
||
</html>
|