Files
sovereign_browser/www/agents/chat.css

443 lines
16 KiB
CSS

/* Shared sovereign:// page CSS (theme variables, base elements, buttons).
* Mirrors sovereign_page_css() in src/nostr_bridge.c so the chat page
* stays consistent with settings/profile/bookmarks pages. */
:root {
--font: monospace;
--primary: #000000;
--secondary: #ffffff;
--accent: #ff0000;
--muted: #dddddd;
--border: var(--muted);
--radius: 5px;
--bg: var(--secondary);
--fg: var(--primary);
--img-gray: 100%;
--img-gray-hover: 20%;
color-scheme: light;
}
html.dark {
--primary: #ffffff;
--secondary: #000000;
--accent: #ff0000;
--muted: #777777;
--border: var(--muted);
color-scheme: dark;
}
* { font-family: var(--font); margin: 0; padding: 0; box-sizing: border-box; }
html { transition: background-color 0.2s ease, color 0.2s ease; }
body { color: var(--fg); background: var(--bg); line-height: 1.5; }
a { color: var(--accent); text-decoration: none; transition: 0.2s; }
a:hover { text-decoration: underline; }
input, select, textarea {
background: var(--bg); color: var(--fg);
border: 1px solid var(--border); border-radius: var(--radius);
padding: 6px 10px; font-family: var(--font); font-size: 13px;
min-width: 200px;
}
input:focus, select:focus, textarea:focus {
border-color: var(--accent); outline: none;
}
.btn, .save-btn {
background: var(--bg); color: var(--primary);
border: 1px solid var(--primary); border-radius: var(--radius);
padding: 6px 16px; cursor: pointer; font-family: var(--font);
font-size: 13px; font-weight: bold; margin-left: 8px;
transition: border-color 0.2s, background 0.2s, color 0.2s;
}
.btn:hover, .save-btn:hover { border-color: var(--accent); }
.btn:active, .save-btn:active { background: var(--accent); color: var(--secondary); }
.btn:disabled { opacity: 0.5; cursor: not-allowed; }
.btn-del { border-color: var(--accent); color: var(--accent); }
.btn-del:hover { background: var(--accent); color: var(--secondary); }
/* ── Chat page layout (tabbed) ───────────────────────────────── */
#chat-root {
display: flex; flex-direction: column;
height: 100vh; padding: 8px;
}
/* Tab bar — horizontal row of tab buttons. */
#tab-bar {
display: flex; flex-direction: row; gap: 0;
border-bottom: 1px solid var(--border);
flex-shrink: 0; margin-bottom: 8px;
}
.tab {
background: transparent; color: var(--fg);
border: none; border-bottom: 2px solid transparent;
padding: 8px 14px; cursor: pointer;
font-family: var(--font); font-size: 13px; font-weight: bold;
margin-left: 0; transition: border-color 0.15s, color 0.15s;
}
.tab:hover { color: var(--accent); }
.tab.active {
border-bottom-color: var(--accent);
color: var(--accent);
}
/* Tab content — only the active tab is visible. */
.tab-content {
display: none; flex: 1; min-height: 0;
flex-direction: column;
}
.tab-content.active { display: flex; }
/* Toolbar inside a tab (refresh + action buttons). */
.tab-toolbar {
display: flex; flex-direction: row; gap: 6px;
flex-shrink: 0; margin-bottom: 8px;
}
.tab-toolbar .btn { margin-left: 0; }
#new-chat-btn { margin-left: 0; }
#conv-list {
flex: 1; overflow-y: auto; display: flex;
flex-direction: column; gap: 4px;
}
/* Conversation list items — match ai.html conversation styling:
* title + timestamp on the left, delete button appears on hover. */
.conv-item {
display: flex; align-items: center; gap: 6px;
padding: 8px 10px; border-radius: var(--radius); cursor: pointer;
border: 1px solid transparent;
transition: background 0.15s, border-color 0.15s;
}
.conv-item:hover {
background: rgba(128,128,128,0.08);
}
.conv-active {
border-color: var(--accent);
background: rgba(255,0,0,0.06);
}
.conv-item-content {
flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px;
}
.conv-title {
overflow: hidden; text-overflow: ellipsis;
white-space: nowrap; font-size: 0.9em; color: var(--fg);
}
.conv-timestamp {
font-size: 0.72em; color: var(--muted);
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.conv-del {
flex-shrink: 0; padding: 2px 8px; font-size: 0.75em;
opacity: 0; transition: opacity 0.15s;
}
.conv-item:hover .conv-del { opacity: 1; }
.conv-empty { color: var(--muted); font-size: 0.85em; padding: 8px; }
#create-skill-btn { margin-left: 0; }
/* Rename button — same hover-reveal behavior as the delete button. */
.conv-rename {
flex-shrink: 0; padding: 2px 8px; font-size: 0.75em;
opacity: 0; transition: opacity 0.15s; margin-left: 0;
border-color: var(--border); color: var(--fg);
}
.conv-rename:hover { border-color: var(--accent); color: var(--accent); }
.conv-item:hover .conv-rename { opacity: 1; }
/* "No chat selected" placeholder shown in the chat pane when no
* conversation is active. */
.no-chat-placeholder {
color: var(--muted); font-style: italic; font-size: 0.9em;
padding: 16px; text-align: center;
}
#skill-list {
flex: 1; overflow-y: auto; display: flex;
flex-direction: column; gap: 4px;
}
/* Skills list items — match ai.html skills styling:
* checkbox + skill name + tool badges, with delete on hover for
* user-authored skills. */
.skill-item {
display: flex; align-items: center; gap: 6px;
padding: 6px 8px; border-radius: var(--radius); flex-wrap: wrap;
border: 1px solid transparent;
transition: background 0.15s, border-color 0.15s;
}
.skill-item:hover {
background: rgba(128,128,128,0.06);
border-color: var(--border);
}
.skill-checkbox { flex-shrink: 0; margin: 0; min-width: auto; }
.skill-name { font-size: 0.88em; flex-shrink: 0; color: var(--fg); }
.skill-badge {
font-size: 0.7em; padding: 1px 6px; border-radius: 3px;
white-space: nowrap;
}
.skill-badge-tools {
background: rgba(0,180,0,0.15); color: #2a2;
border: 1px solid rgba(0,180,0,0.4);
}
html.dark .skill-badge-tools {
background: rgba(0,180,0,0.2); color: #6c6;
border-color: rgba(0,180,0,0.5);
}
.skill-tool-enabled { border-left: 3px solid rgba(0,180,0,0.5); }
.skill-item .conv-del {
opacity: 0; padding: 1px 6px; font-size: 0.75em;
}
.skill-item:hover .conv-del { opacity: 1; }
/* ── Skill editor (inline, matching ai.html) ──────────────────── */
.skill-badge-unsaved {
background: rgba(255,165,0,0.15); color: #a60;
border: 1px solid rgba(255,165,0,0.4);
}
html.dark .skill-badge-unsaved {
background: rgba(255,165,0,0.2); color: #fc6;
border-color: rgba(255,165,0,0.5);
}
.skillStackedItem {
border: 1px solid var(--border); border-radius: var(--radius);
padding: 6px 8px; margin-bottom: 4px;
background: rgba(128,128,128,0.04);
}
.skillStackedItem > summary {
display: flex; align-items: center; gap: 6px;
cursor: pointer; flex-wrap: wrap; list-style: none;
user-select: none;
}
.skillStackedItem > summary::-webkit-details-marker { display: none; }
.skillStackedItem > summary .skill-checkbox {
flex-shrink: 0; margin: 0; min-width: auto;
}
.skillStackedItem > summary .skill-name {
font-size: 0.88em; flex-shrink: 0; color: var(--fg);
}
.aiSkillControlsRow {
display: flex; flex-direction: column; gap: 6px;
margin-top: 8px; padding-top: 6px;
border-top: 1px solid var(--border);
}
.aiSkillInlineField {
display: flex; flex-direction: column; gap: 2px;
}
.aiSkillInlineField label {
font-size: 0.75em; color: var(--muted); font-weight: bold;
}
.aiInput {
background: var(--bg); color: var(--fg);
border: 1px solid var(--border); border-radius: var(--radius);
padding: 4px 6px; font-family: var(--font); font-size: 0.85em;
min-width: 0; width: 100%; box-sizing: border-box;
}
.aiInput:focus { border-color: var(--accent); outline: none; }
.taSkillTemplateEditor {
background: var(--bg); color: var(--fg);
border: 1px solid var(--border); border-radius: var(--radius);
padding: 4px 6px; font-family: var(--font); font-size: 0.85em;
width: 100%; box-sizing: border-box; min-height: 80px;
resize: vertical;
}
.taSkillTemplateEditor:focus { border-color: var(--accent); outline: none; }
.aiSkillActionsRow {
display: flex; gap: 6px; margin-top: 6px;
}
.aiSkillActionsRow .btn { margin-left: 0; }
.aiSkillActionsRow .btn:disabled { opacity: 0.5; cursor: not-allowed; }
#create-skill-form {
display: flex; flex-direction: column; gap: 4px;
margin-top: 4px; padding: 8px;
border: 1px solid var(--border); border-radius: var(--radius);
background: rgba(128,128,128,0.05);
}
.skill-input {
width: 100%; box-sizing: border-box;
font-family: var(--font); font-size: 0.85em; padding: 4px 6px;
border: 1px solid var(--border); border-radius: var(--radius);
background: var(--bg); color: var(--fg);
}
#skill-content { resize: vertical; min-height: 60px; }
/* The chat tab content fills the available height: messages scroll,
* input pinned to the bottom. */
#tab-chat { min-width: 0; }
#messages {
flex: 1; overflow-y: auto; border: 1px solid #444;
padding: 8px; margin-bottom: 8px; background: rgba(0,0,0,0.05);
}
/* ── Message bubbles (ported from ai.html messaging-ui.css) ────── */
.msg-bubble-row {
display: flex; width: 100%;
}
.msg-bubble-row.outgoing { justify-content: flex-end; }
.msg-bubble-row.incoming,
.msg-bubble-row.system { justify-content: flex-start; }
.msg-bubble {
max-width: 75%;
border: 1px solid var(--primary);
border-radius: 10px;
padding: 8px 10px;
white-space: normal;
word-wrap: break-word;
overflow-wrap: break-word;
font-size: 90%;
color: var(--fg);
background: var(--bg);
margin-bottom: 10px;
}
.msg-bubble.outgoing { border-color: var(--primary); }
.msg-bubble.incoming { border-color: var(--muted); }
.msg-bubble.system {
border-style: dashed;
border-color: var(--muted);
color: var(--muted);
font-size: 82%;
}
.msg-bubble-header {
display: flex; align-items: center;
justify-content: space-between; gap: 8px; margin-bottom: 6px;
}
.msg-bubble-header-main {
min-width: 0; flex: 1; display: flex;
align-items: center; gap: 8px;
}
.msg-bubble-sender-meta {
min-width: 0; display: flex; flex-direction: column; gap: 1px;
}
.msg-bubble-sender-name {
font-size: 78%; color: var(--fg); font-weight: bold;
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.msg-bubble-menu-host { flex-shrink: 0; }
.msg-bubble-content { line-height: 1.35; }
.msg-bubble-content > *:first-child { margin-top: 0; }
.msg-bubble-content > *:last-child { margin-bottom: 0; }
.msg-bubble-content p { margin: 0 0 0.3em 0; }
.msg-bubble-content h1, .msg-bubble-content h2, .msg-bubble-content h3,
.msg-bubble-content h4, .msg-bubble-content h5, .msg-bubble-content h6 {
margin: 0.4em 0 0.2em 0; font-size: 1em;
}
.msg-bubble-content code {
background: rgba(128,128,128,0.18);
border-radius: 4px; padding: 1px 4px; font-size: 85%;
}
.msg-bubble-content pre {
background: rgba(0,0,0,0.15);
border-radius: 6px; padding: 6px 8px; margin: 4px 0;
overflow-x: auto;
}
.msg-bubble-content pre code {
background: transparent; padding: 0;
border-radius: 0; font-size: 90%;
}
.msg-bubble-content blockquote {
margin: 4px 0; padding: 2px 8px;
border-left: 3px solid var(--muted);
color: var(--muted);
}
.msg-bubble-content a {
color: var(--accent); text-decoration: underline; word-break: break-all;
}
.msg-bubble-content img {
display: block; max-width: 100%;
border-radius: 6px; margin: 4px 0;
}
.msg-bubble-content ul, .msg-bubble-content ol {
margin: 4px 0; padding-left: 20px;
}
.msg-bubble-content table {
width: 100%; border-collapse: collapse;
margin: 6px 0; font-size: 90%;
}
.msg-bubble-content th, .msg-bubble-content td {
border: 1px solid var(--border);
padding: 4px 8px; text-align: left; vertical-align: top;
}
.msg-bubble-content thead th { background: rgba(128,128,128,0.12); }
.msg-bubble-content tbody tr:nth-child(even) { background: rgba(128,128,128,0.06); }
.msg-bubble-content del, .msg-bubble-content s { opacity: 0.75; }
.msg-bubble-content strong { font-weight: bold; }
.msg-tcid { font-size: 0.8em; color: var(--muted); margin-top: 4px; }
/* ── Dot-menu ────────────────────────────────────────────────────
* The dot-menu styles now come from the shared sovereign://css/dot-menu.css
* (copied from the client). Only the host sizing is page-specific. */
.msg-bubble-menu-host .dot-menu-wrap { flex-shrink: 0; }
/* ── Tool calls, status message, input area ───────────────────── */
.tool-calls { margin-top: 4px; }
.tool-calls pre {
font-size: 0.85em; white-space: pre-wrap; word-wrap: break-word;
}
/* Status message — a temporary system bubble shown in the chat window
* (replaces the old #status-bar). It is italic and muted to distinguish
* it from real agent responses. */
#status-msg {
margin-bottom: 8px;
}
#status-msg-bubble {
font-style: italic; color: var(--muted);
max-width: 90%; border-style: dashed;
}
/* The input area is a flex row: the composer (textarea + send button)
* on the left taking all available space, and the Stop button on the
* right (only visible while the agent is running). */
#input-area {
display: flex; align-items: flex-end; gap: 8px;
border: 1px solid var(--border); border-radius: var(--radius);
padding: 8px; background: var(--bg);
}
#composer-host {
flex: 1; min-width: 0;
}
#stop-btn {
flex-shrink: 0; min-width: 80px; margin-left: 0;
}
/* The shared post-composer library wraps the host element: it inserts a
* .post-composer-wrapper around #composer-host and appends the send
* button as a SIBLING of the host (inside the wrapper). So the send
* button is NOT a descendant of #composer-host — it lives in
* #input-area > .post-composer-wrapper > [.post-composer-editor-shell >
* #composer-host, .post-composer-send-btn]. Target it via #input-area.
* Make the wrapper fill the row and ensure the send button has a
* visible border matching the page .btn style (post-composer.css uses
* --primary-color which is undefined here, so we override it). */
#input-area .post-composer-wrapper {
width: 100%; min-width: 0; max-width: none; margin-bottom: 0;
border: none; padding: 0; background: transparent;
display: flex; flex-direction: column; gap: 0;
}
#input-area .post-composer-send-btn {
margin-left: 0; min-width: 80px;
border: 1px solid var(--primary);
color: var(--primary);
}
#input-area .post-composer-send-btn:hover,
#input-area .post-composer-send-btn:focus {
border-color: var(--accent);
}
#input-area .post-composer-send-btn:disabled {
opacity: 0.5; cursor: not-allowed;
}
/* ── Responsive: narrow sidebar viewport (< 400px) ──────────────
* When the chat page is loaded in the agent sidebar (~350px wide),
* switch to a compact layout: smaller fonts, shorter tab labels,
* full-width messages, tighter padding. */
@media (max-width: 400px) {
#chat-root { padding: 4px; }
.tab {
padding: 6px 8px; font-size: 12px;
}
/* Shorten tab labels to single characters in narrow view. */
.tab[data-tab="chat"] { font-size: 0; }
.tab[data-tab="chat"]::before { content: "💬"; font-size: 14px; }
.tab[data-tab="conversations"] { font-size: 0; }
.tab[data-tab="conversations"]::before { content: "☰"; font-size: 14px; }
.tab[data-tab="skills"] { font-size: 0; }
.tab[data-tab="skills"]::before { content: "⚙"; font-size: 14px; }
#messages {
padding: 4px; font-size: 12px;
}
.msg-bubble {
max-width: 92%; font-size: 85%; padding: 6px 8px;
}
.msg-bubble.system { font-size: 78%; }
#input-area { padding: 4px; gap: 4px; }
.tab-toolbar { gap: 4px; }
.tab-toolbar .btn { padding: 4px 10px; font-size: 12px; }
.conv-item { padding: 6px 8px; }
.conv-title { font-size: 0.85em; }
.skill-name { font-size: 0.82em; }
.skill-input { font-size: 12px; }
}