443 lines
16 KiB
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; }
|
|
}
|