mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-10-14 19:02:22 +02:00
* feat(settings): opt-in reasoning effort for utility model calls Add utility_reasoning_effort (empty = provider default) and let utility calls inherit a chat session's effort only on that session's matching endpoint+model route. Explicit caller effort wins; otherwise the utility setting wins over session inheritance on utility-chain routes. Resolve the chain and setting per user and validate levels against model evidence. No-session work uses the setting only, with no chat-effort crossover. - src/utility_effort.py owns effort_for_call and candidate_effort_factory - remove global effort fill from llm_call/llm_call_async; utility call sites pass effort explicitly, so unrelated calls on the same route do not pick it up - wire auto-title, memory/persona/skill extraction, memory suggest/import/ audit, compaction, auto-sort, and per-owner background task candidates - wire no-session email writing-style, summary, and translation calls, plus skills evaluation, necessity/retrieval audits, and improvement - deliberately exclude email replies because thinking_mode='off' - add the optional candidate_request_factory to llm_call_async_with_fallback - add sync llm_call reasoning_effort and include it in the cache key - show the Utility Model effort selector only for advertised levels Fixes #6569 * fix(utility): exclude email replies and support inherited effort in settings Pass reasoning_effort='' to task_llm_call_async for scheduled reply drafting to honor the email reply exclusion. In AI settings, resolve the effective Default Chat endpoint and model when Utility is set to 'Same as chat' so supported reasoning effort levels can be selected. * feat(utility): support same-as-chat effort and session model fallback in compaction In AI settings, show 'Same as chat' as the default effort option when Utility Model is inherited, and support an explicit 'Provider default' (off) override. Pass the active session fallback in context compaction callers so compaction shares the live chat session's model and reasoning effort for maximum prompt/KV cache affinity. * fix(settings): clarify chat effort inheritance and keep option visible Label inherited effort as 'Same as chat (when supported)' and mention cache affinity and latency speedups in the help text. Ensure the effort control remains visible when the utility model is set to 'Same as chat' even if the default chat model lacks advertised reasoning levels, and populate candidate levels across configured endpoints so the user can configure effort or keep provider default. * feat(settings): per-fallback reasoning effort for utility model Each utility fallback row gets an Effort select, shown only when the selected model advertises reasoning levels. The choice is stored as reasoning_effort on the utility_model_fallbacks entry and sent only to that candidate; an empty value means provider default. * fix(utility): transmit reasoning effort in non-streaming calls --------- Co-authored-by: Alexandre Teixeira <111787685+alteixeira20@users.noreply.github.com>
1983 lines
53 KiB
CSS
1983 lines
53 KiB
CSS
/* 11-tasks - part of the former static/style.css.
|
|
*
|
|
* The stylesheet is split by area, but the split is mechanical: rules were
|
|
* moved verbatim and NOT regrouped. The numeric prefix is the load order, and
|
|
* that order is what preserves the cascade. A rule sits in the file its
|
|
* original position fell into, so an area's rules are not all in its file and
|
|
* a file is not only that area's rules.
|
|
*
|
|
* Do not reorder the <link> tags in static/index.html, and do not move a rule
|
|
* between files: with 1,151 redeclarations and 1,886 !important in this
|
|
* cascade, either can change which declaration wins.
|
|
*
|
|
* static/index.html and the PRECACHE list in static/sw.js must agree on this
|
|
* order. tests/test_static_stylesheet_manifest.py and
|
|
* tests/test_css_computed_style_snapshot.py fail if that drifts.
|
|
*/
|
|
/* Snapped/narrow Settings window: move the tab rail to the top. Viewport media
|
|
alone misses desktop right-half snapping, where the modal is narrow but the
|
|
browser window is not. */
|
|
@container settings-modal (max-width: 620px) {
|
|
.settings-layout {
|
|
flex-direction: column;
|
|
}
|
|
.settings-sidebar {
|
|
width: auto;
|
|
max-height: none;
|
|
flex-direction: row;
|
|
border-right: none;
|
|
border-bottom: 1px solid var(--border);
|
|
overflow-x: auto;
|
|
overflow-y: hidden;
|
|
padding: 6px;
|
|
scrollbar-width: none;
|
|
}
|
|
.settings-sidebar::-webkit-scrollbar {
|
|
display: none;
|
|
}
|
|
.settings-sidebar-divider,
|
|
.settings-sidebar-label {
|
|
display: none;
|
|
}
|
|
.settings-nav-item {
|
|
flex: 0 0 auto;
|
|
padding: 6px 10px;
|
|
font-size: 11px;
|
|
}
|
|
.settings-panels {
|
|
padding: 12px 14px;
|
|
}
|
|
}
|
|
|
|
/* ── Entrance Animations ── */
|
|
|
|
/* Welcome name — left-to-right wipe with a touch of horizontal stretch.
|
|
Fires on initial render via the .welcome-name rule; restarts on Nobody⇄
|
|
Odysseus toggle via JS reflow trick. */
|
|
.welcome-name {
|
|
transform-origin: left center;
|
|
animation: welcome-name-reveal 0.55s cubic-bezier(0.34, 1.32, 0.55, 1) both;
|
|
}
|
|
/* Hold the welcome-screen entrance animations until the app has finished its
|
|
initial load (fonts loaded + layout settled). Running them mid-load made the
|
|
splash jump/flicker as the page reflowed ("haywire"). Until body.welcome-ready
|
|
is set by JS, the splash sits in its final, static state (no flash, since the
|
|
non-animated state IS the resting state); adding the class then plays the
|
|
entrance once, cleanly. */
|
|
body:not(.welcome-ready) #welcome-screen,
|
|
body:not(.welcome-ready) .welcome-name {
|
|
animation: none !important;
|
|
}
|
|
/* Hold the splash invisible (the entrance's start state) until ready, so when
|
|
the animation is released it fades/wipes in smoothly instead of flashing
|
|
from fully-visible to the animation's hidden first frame. */
|
|
body:not(.welcome-ready) #welcome-screen {
|
|
opacity: 0;
|
|
}
|
|
@keyframes welcome-name-reveal {
|
|
from {
|
|
clip-path: inset(0 100% 0 0);
|
|
transform: scaleX(0.96);
|
|
opacity: 0.5;
|
|
}
|
|
to {
|
|
clip-path: inset(0 0 0 0);
|
|
transform: scaleX(1);
|
|
opacity: 1;
|
|
}
|
|
}
|
|
|
|
@keyframes welcome-enter {
|
|
from {
|
|
opacity: 0;
|
|
transform: translate(-50%, -45%);
|
|
}
|
|
to {
|
|
opacity: 1;
|
|
transform: translate(-50%, -50%);
|
|
}
|
|
}
|
|
|
|
@keyframes msg-enter {
|
|
from {
|
|
opacity: 0;
|
|
transform: translateY(10px);
|
|
}
|
|
to {
|
|
opacity: 1;
|
|
transform: translateY(0);
|
|
}
|
|
}
|
|
|
|
@keyframes picker-roll-up {
|
|
from {
|
|
opacity: 0;
|
|
transform: scaleY(0.4) translateY(8px);
|
|
}
|
|
to {
|
|
opacity: 1;
|
|
transform: scaleY(1) translateY(0);
|
|
}
|
|
}
|
|
@keyframes picker-roll-down {
|
|
from {
|
|
opacity: 1;
|
|
transform: scaleY(1) translateY(0);
|
|
}
|
|
to {
|
|
opacity: 0;
|
|
transform: scaleY(0.4) translateY(8px);
|
|
}
|
|
}
|
|
@keyframes model-picker-refresh-spin {
|
|
to { transform: rotate(360deg); }
|
|
}
|
|
|
|
@keyframes modal-enter {
|
|
from {
|
|
opacity: 0;
|
|
transform: scale(0.95) translateY(8px);
|
|
}
|
|
to {
|
|
opacity: 1;
|
|
transform: scale(1) translateY(0);
|
|
}
|
|
}
|
|
@keyframes modal-exit {
|
|
from {
|
|
opacity: 1;
|
|
transform: scale(1) translateY(0);
|
|
}
|
|
to {
|
|
opacity: 0;
|
|
transform: scale(0.97) translateY(6px);
|
|
}
|
|
}
|
|
.modal-content.modal-closing {
|
|
animation: modal-exit 0.18s ease-in both;
|
|
}
|
|
|
|
#cookbook-modal .modal-content.cookbook-modal-entering {
|
|
animation: cookbook-modal-enter 0.28s cubic-bezier(0.22, 1.35, 0.36, 1) both;
|
|
}
|
|
/* Mobile: real bottom-sheet slide-up — the existing 12px translate is
|
|
too subtle on a phone, the modal effectively just appeared. */
|
|
@media (max-width: 768px) {
|
|
#cookbook-modal .modal-content.cookbook-modal-entering {
|
|
animation: cookbook-modal-enter-mobile 0.32s cubic-bezier(0.22, 1, 0.36, 1) both;
|
|
}
|
|
}
|
|
#cookbook-modal .modal-content:not(.cookbook-modal-entering):not(.modal-closing) {
|
|
animation: none;
|
|
}
|
|
#cookbook-modal .modal-content.modal-closing {
|
|
animation: modal-exit 0.18s cubic-bezier(0.4, 0, 1, 1) both;
|
|
}
|
|
|
|
/* Per-token streaming fade — new words materialize */
|
|
.token-new {
|
|
animation: token-fade 0.4s ease-out both;
|
|
}
|
|
|
|
@keyframes token-fade {
|
|
from { opacity: 0; }
|
|
to { opacity: 1; }
|
|
}
|
|
|
|
/* Disable entrance animations when loading chat history (bulk render) */
|
|
.chat-history.no-animate .msg {
|
|
animation: none;
|
|
}
|
|
|
|
/* Prefer reduced motion */
|
|
@media (prefers-reduced-motion: reduce) {
|
|
.msg,
|
|
.modal-content,
|
|
#welcome-screen,
|
|
.toast,
|
|
.token-new {
|
|
animation: none !important;
|
|
transition: none !important;
|
|
}
|
|
}
|
|
|
|
@media (min-width: 769px) {
|
|
#tool-tasks-btn #assistant-notif-dot.sidebar-notif-dot,
|
|
#tool-tasks-btn.task-failure-pending::after,
|
|
#tool-tasks-btn.task-completion-pending::after {
|
|
transform: translateX(-2px);
|
|
}
|
|
}
|
|
.tasks-modal-content { max-width: 600px; width: min(600px, 92vw); background: var(--bg); font-size: 12px; }
|
|
.task-completed-preview-row {
|
|
cursor: default;
|
|
margin-bottom: 6px;
|
|
padding: 8px 10px 10px;
|
|
max-height: none;
|
|
overflow: visible;
|
|
}
|
|
.task-completed-preview-head {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 7px;
|
|
min-height: 20px;
|
|
font-size: 11px;
|
|
}
|
|
.task-completed-chat-preview {
|
|
padding: 0 0 4px;
|
|
}
|
|
.task-completed-chat-preview .doclib-chat-preview-messages {
|
|
max-height: 220px;
|
|
margin-top: 7px;
|
|
}
|
|
.task-completed-chat-preview .doclib-chat-bubble {
|
|
max-width: 92%;
|
|
}
|
|
.task-completed-chat-preview .doclib-chat-preview-actions {
|
|
top: 2px;
|
|
margin-top: 4px;
|
|
}
|
|
.task-completed-preview-row .task-log-time {
|
|
margin-left: 8px;
|
|
}
|
|
|
|
/* Tasks tabs reuse the .memory-tab look. The Brain window's tab bar is
|
|
full-bleed (its underline spans the whole modal width). The Tasks bar is a
|
|
direct child of .modal-content (padding:10px), so cancel that padding with
|
|
negative side margins to span edge-to-edge, then re-inset the tabs by 10px
|
|
so they line up with the rest of the modal content — matching the Brain bar. */
|
|
.tasks-modal-content .tasks-tabs {
|
|
margin: -2px -10px 8px;
|
|
padding: 0 10px;
|
|
}
|
|
|
|
/* Activity log — compact by default. Click the row to expand body+actions. */
|
|
.task-log-row {
|
|
--cat-hue: 220;
|
|
border: 1px solid var(--border);
|
|
border-radius: 8px;
|
|
padding: 4px 10px;
|
|
margin-bottom: 3px;
|
|
background: color-mix(in srgb, var(--fg) 2%, transparent);
|
|
position: relative;
|
|
cursor: pointer;
|
|
transition: padding .12s ease;
|
|
}
|
|
.task-log-row:hover {
|
|
background: color-mix(in srgb, var(--fg) 4%, transparent);
|
|
}
|
|
.task-log-row.task-log-row-error {
|
|
border-color: color-mix(in srgb, var(--red, #f87171) 35%, var(--border));
|
|
background: color-mix(in srgb, var(--red, #f87171) 4%, transparent);
|
|
}
|
|
.task-log-row.task-log-row-error:hover {
|
|
background: color-mix(in srgb, var(--red, #f87171) 7%, transparent);
|
|
}
|
|
.task-log-row.expanded { padding: 8px 10px 6px; }
|
|
.task-log-row-head {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
font-size: 11px;
|
|
margin-bottom: 0;
|
|
}
|
|
.task-log-row.expanded .task-log-row-head { margin-bottom: 4px; }
|
|
/* Collapsed: body + footer hidden. Expanded: visible. */
|
|
.task-log-row:not(.expanded):not(.is-skipped) .task-log-row-body,
|
|
.task-log-row:not(.expanded):not(.is-skipped) .task-log-row-actions,
|
|
.task-log-row:not(.expanded):not(.is-skipped) .task-log-prompt {
|
|
display: none;
|
|
}
|
|
.task-log-name {
|
|
font-weight: 600;
|
|
/* Pull saturation from the per-row hue but mix with the foreground so the
|
|
title still reads in dark mode. Lightness stays adaptive. */
|
|
color: hsl(var(--cat-hue) 60% 60%);
|
|
}
|
|
.task-log-failed-tag {
|
|
color: var(--red, #f87171);
|
|
font-size: 10px;
|
|
font-weight: 700;
|
|
line-height: 1;
|
|
white-space: nowrap;
|
|
margin-left: -2px;
|
|
}
|
|
.task-log-task-icon {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
flex: 0 0 auto;
|
|
margin-right: -3px;
|
|
}
|
|
.task-log-row.task-log-row-error .task-log-name,
|
|
.task-log-row.task-log-row-error .task-log-task-icon {
|
|
color: var(--red, #f87171);
|
|
}
|
|
.task-log-task-icon svg {
|
|
top: 0 !important;
|
|
opacity: 0.46 !important;
|
|
}
|
|
.task-log-row-head .task-ai-mark {
|
|
top: 0;
|
|
margin-left: -4px;
|
|
}
|
|
.task-log-repeat {
|
|
font-size: 10px;
|
|
font-weight: 500;
|
|
line-height: 1;
|
|
color: color-mix(in srgb, var(--fg) 62%, transparent);
|
|
border-radius: 999px;
|
|
padding: 1px 0;
|
|
white-space: nowrap;
|
|
}
|
|
@media (prefers-color-scheme: light) {
|
|
.task-log-name { color: hsl(var(--cat-hue) 65% 38%); }
|
|
}
|
|
/* Per-account prefix in fan-out results — e.g. "[Default] No recent emails"
|
|
becomes a compact accent chip + plain message. Makes multi-account activity
|
|
rows readable instead of a bracket soup. */
|
|
/* Running / queued status line in the Activity tab — whirlpool + label. */
|
|
.task-log-running {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 0;
|
|
font-size: 11px;
|
|
opacity: 0.75;
|
|
}
|
|
.task-log-running-label { font-style: normal; }
|
|
|
|
/* New right-side placement: "Running <whirlpool>" sits where the timestamp
|
|
normally would, on the head row's right edge. */
|
|
.task-log-running-inline {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 0;
|
|
font-size: 11px;
|
|
opacity: 0.75;
|
|
}
|
|
.task-log-force-run {
|
|
appearance: none;
|
|
box-sizing: border-box;
|
|
border: 0;
|
|
background: color-mix(in srgb, var(--fg) 7%, transparent);
|
|
box-shadow: none;
|
|
color: inherit;
|
|
opacity: .82;
|
|
margin-left: 7px;
|
|
padding: 1px 6px 1px 4px;
|
|
height: 18px;
|
|
min-height: 16px;
|
|
max-height: 18px;
|
|
border-radius: 999px;
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
gap: 3px;
|
|
font-family: inherit;
|
|
font-size: 10px;
|
|
line-height: 1;
|
|
cursor: pointer;
|
|
position: relative;
|
|
top: -3px;
|
|
}
|
|
.task-log-force-run svg,
|
|
.task-log-stop svg {
|
|
display: block;
|
|
flex-shrink: 0;
|
|
transform: translateY(1px);
|
|
}
|
|
.task-log-force-run:hover {
|
|
opacity: 1;
|
|
background: color-mix(in srgb, var(--green, #50fa7b) 16%, transparent);
|
|
color: var(--green, #50fa7b);
|
|
}
|
|
.task-log-stop {
|
|
border: 0;
|
|
background: transparent;
|
|
color: inherit;
|
|
opacity: .72;
|
|
padding: 0;
|
|
margin-left: 6px;
|
|
width: 12px;
|
|
height: 12px;
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
cursor: pointer;
|
|
position: relative;
|
|
top: -3px;
|
|
}
|
|
.task-log-stop:hover {
|
|
opacity: 1;
|
|
color: var(--red, #f87171);
|
|
}
|
|
|
|
/* Slim single-line row for skipped (noop) runs — body/actions stripped, font
|
|
shrunk, opacity dropped. Distinguishes "task ran but had nothing to do"
|
|
from a "real" entry without flooding the feed visually. */
|
|
.task-log-row.is-skipped {
|
|
padding: 4px 8px;
|
|
opacity: 0.45;
|
|
font-size: 11px;
|
|
background: transparent;
|
|
}
|
|
.task-log-row.is-skipped .task-log-row-head { padding: 0; }
|
|
.task-log-row.is-skipped .task-log-name { font-weight: 500; }
|
|
.task-log-row.is-skipped .task-log-skipped-reason {
|
|
flex: 1 1 auto;
|
|
margin-left: 6px;
|
|
font-style: italic;
|
|
opacity: 0.85;
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
min-width: 0;
|
|
}
|
|
.task-log-row.is-skipped:hover { opacity: 0.7; }
|
|
|
|
.task-log-account-tag {
|
|
display: inline-block;
|
|
font-size: 10px;
|
|
font-weight: 600;
|
|
letter-spacing: 0.02em;
|
|
padding: 1px 7px;
|
|
margin-right: 4px;
|
|
border-radius: 10px;
|
|
color: var(--accent, var(--red));
|
|
background: color-mix(in srgb, var(--accent, var(--red)) 12%, transparent);
|
|
border: 1px solid color-mix(in srgb, var(--accent, var(--red)) 35%, transparent);
|
|
vertical-align: 1px;
|
|
}
|
|
.task-log-status {
|
|
width: 7px;
|
|
height: 7px;
|
|
border-radius: 50%;
|
|
flex-shrink: 0;
|
|
background: color-mix(in srgb, var(--fg) 30%, transparent);
|
|
}
|
|
.task-log-status-ok { background: #4ade80; box-shadow: 0 0 6px #4ade80, 0 0 3px #4ade80; }
|
|
.task-log-status-error { background: var(--red, #f87171); box-shadow: 0 0 6px var(--red, #f87171), 0 0 3px var(--red, #f87171); }
|
|
.task-log-status-info { background: color-mix(in srgb, var(--fg) 25%, transparent); }
|
|
.task-log-status-queued { background: #fbbf24; box-shadow: 0 0 0 2px color-mix(in srgb, #fbbf24 30%, transparent); }
|
|
.task-log-status-running { background: #60a5fa; animation: task-log-pulse 1.4s ease-in-out infinite; }
|
|
.task-log-status-skipped { background: color-mix(in srgb, var(--fg) 20%, transparent); }
|
|
.task-log-status-aborted { background: color-mix(in srgb, var(--fg) 30%, transparent); border: 1px dashed color-mix(in srgb, var(--fg) 50%, transparent); }
|
|
.task-log-row-body {
|
|
font-size: 12px;
|
|
line-height: 1.5;
|
|
color: color-mix(in srgb, var(--fg) 85%, transparent);
|
|
overflow-wrap: anywhere;
|
|
word-break: break-word;
|
|
}
|
|
.task-log-row-body p { margin: 0 0 6px 0; }
|
|
.task-log-row-body p:last-child { margin-bottom: 0; }
|
|
.task-log-row-body pre {
|
|
margin: 4px 0;
|
|
padding: 6px 8px;
|
|
background: color-mix(in srgb, var(--fg) 5%, transparent);
|
|
border-radius: 4px;
|
|
white-space: pre-wrap;
|
|
word-break: break-word;
|
|
font-size: 11px;
|
|
max-height: 180px;
|
|
overflow: auto;
|
|
}
|
|
.task-log-row-body code {
|
|
font-size: 11px;
|
|
background: color-mix(in srgb, var(--fg) 6%, transparent);
|
|
padding: 1px 4px;
|
|
border-radius: 3px;
|
|
}
|
|
.task-log-row-body ul, .task-log-row-body ol {
|
|
margin: 4px 0;
|
|
padding-left: 20px;
|
|
}
|
|
/* Markdown headings inside a task result — without these, ## headings
|
|
render at browser-default huge sizes and blow out the cramped row. */
|
|
.task-log-row-body h1,
|
|
.task-log-row-body h2,
|
|
.task-log-row-body h3,
|
|
.task-log-row-body h4 {
|
|
margin: 10px 0 4px;
|
|
line-height: 1.3;
|
|
font-weight: 650;
|
|
color: var(--fg);
|
|
}
|
|
.task-log-row-body h1 { font-size: 14px; }
|
|
.task-log-row-body h2 { font-size: 13px; }
|
|
.task-log-row-body h3,
|
|
.task-log-row-body h4 { font-size: 12px; opacity: 0.9; }
|
|
.task-log-row-body h1:first-child,
|
|
.task-log-row-body h2:first-child,
|
|
.task-log-row-body h3:first-child { margin-top: 0; }
|
|
.task-log-row-body blockquote {
|
|
margin: 4px 0;
|
|
padding: 2px 0 2px 10px;
|
|
border-left: 2px solid color-mix(in srgb, var(--fg) 20%, transparent);
|
|
opacity: 0.85;
|
|
}
|
|
.task-log-row-body a { color: var(--accent, #60a5fa); text-decoration: none; }
|
|
.task-log-row-body a:hover { text-decoration: underline; }
|
|
.task-log-row-body strong { font-weight: 650; color: var(--fg); }
|
|
/* Email-summary tables — render compactly instead of default browser ugliness. */
|
|
.task-log-row-body table {
|
|
border-collapse: collapse;
|
|
width: 100%;
|
|
margin: 6px 0;
|
|
font-size: 11px;
|
|
}
|
|
.task-log-row-body th,
|
|
.task-log-row-body td {
|
|
border: 1px solid color-mix(in srgb, var(--fg) 10%, transparent);
|
|
padding: 3px 6px;
|
|
text-align: left;
|
|
vertical-align: top;
|
|
}
|
|
.task-log-row-body th {
|
|
background: color-mix(in srgb, var(--fg) 5%, transparent);
|
|
font-weight: 600;
|
|
}
|
|
.task-log-row-actions {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
gap: 8px;
|
|
margin-top: 4px;
|
|
}
|
|
.task-log-open-chat,
|
|
.task-log-open-report,
|
|
.task-log-copy,
|
|
.task-log-clear-cache,
|
|
.task-log-run-again,
|
|
.task-log-force-run-action {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 3px;
|
|
background: none;
|
|
border: 1px solid color-mix(in srgb, var(--fg) 14%, transparent);
|
|
border-radius: 5px;
|
|
color: color-mix(in srgb, var(--fg) 60%, transparent);
|
|
font-family: inherit;
|
|
font-size: 10px;
|
|
padding: 1px 6px;
|
|
cursor: pointer;
|
|
transition: background .15s, color .15s, border-color .15s;
|
|
line-height: 1.4;
|
|
}
|
|
.task-log-open-chat:hover,
|
|
.task-log-open-report:hover,
|
|
.task-log-copy:hover,
|
|
.task-log-clear-cache:hover,
|
|
.task-log-run-again:hover,
|
|
.task-log-force-run-action:hover {
|
|
color: var(--fg);
|
|
border-color: color-mix(in srgb, var(--fg) 30%, transparent);
|
|
background: color-mix(in srgb, var(--fg) 5%, transparent);
|
|
}
|
|
.task-log-row-actions > .task-log-open-chat,
|
|
.task-log-row-actions > .task-log-copy {
|
|
margin-left: auto;
|
|
}
|
|
.task-log-clear-cache svg {
|
|
position: relative;
|
|
top: 0;
|
|
}
|
|
/* Activity filter chips — toggle-out model: ON by default (solid),
|
|
click to toggle OFF (dimmed + strikethrough) to hide that group. */
|
|
.tasks-af-chip {
|
|
font-size: 11px;
|
|
padding: 3px 10px;
|
|
border: 1px solid color-mix(in srgb, var(--fg) 16%, transparent);
|
|
border-radius: 12px;
|
|
background: color-mix(in srgb, var(--fg) 6%, transparent);
|
|
color: var(--fg);
|
|
cursor: pointer;
|
|
font-family: inherit;
|
|
transition: opacity .12s, background .12s, border-color .12s;
|
|
}
|
|
.tasks-af-chip:hover { border-color: color-mix(in srgb, var(--fg) 32%, transparent); }
|
|
.tasks-af-chip.off {
|
|
opacity: 0.4;
|
|
text-decoration: line-through;
|
|
background: transparent;
|
|
}
|
|
.tasks-af-chip.active {
|
|
border-color: var(--accent, var(--red));
|
|
background: color-mix(in srgb, var(--accent, var(--red)) 18%, transparent);
|
|
opacity: 1;
|
|
}
|
|
.tasks-af-chip-error { border-color: color-mix(in srgb, var(--red, #f87171) 45%, transparent); }
|
|
.tasks-af-chip-error:not(.off) { color: var(--red, #f87171); }
|
|
|
|
/* "Open in Deep Research" is now a regular clickable chat link (a
|
|
`#research-<id>` markdown anchor in the assistant's message), rendered as
|
|
an a.chat-link. Nudge it 4px right so it sits slightly inset. */
|
|
a.chat-link[href^="#research-"] {
|
|
margin-left: 4px;
|
|
}
|
|
.task-log-row.is-long .task-log-row-body {
|
|
max-height: 8.5em;
|
|
overflow: hidden;
|
|
position: relative;
|
|
mask-image: linear-gradient(to bottom, #000 70%, transparent);
|
|
-webkit-mask-image: linear-gradient(to bottom, #000 70%, transparent);
|
|
}
|
|
.task-log-row.is-long.expanded .task-log-row-body {
|
|
max-height: none;
|
|
overflow: visible;
|
|
mask-image: none;
|
|
-webkit-mask-image: none;
|
|
}
|
|
.task-log-row-toggle {
|
|
margin-top: 6px;
|
|
background: none;
|
|
border: none;
|
|
font-family: inherit;
|
|
font-size: 11px;
|
|
cursor: pointer;
|
|
padding: 2px 0;
|
|
color: transparent; /* hide raw "Show more" text */
|
|
}
|
|
.task-log-row:not(.expanded) .task-log-row-toggle::before { content: 'Show more'; }
|
|
.task-log-row.expanded .task-log-row-toggle::before { content: 'Show less'; }
|
|
.task-log-prompt {
|
|
margin-top: 6px;
|
|
font-size: 11px;
|
|
}
|
|
.task-log-prompt summary {
|
|
cursor: pointer;
|
|
opacity: 0.5;
|
|
user-select: none;
|
|
}
|
|
.task-log-prompt summary:hover { opacity: 0.8; }
|
|
.task-log-prompt pre {
|
|
margin: 4px 0 0;
|
|
padding: 6px 8px;
|
|
background: color-mix(in srgb, var(--fg) 4%, transparent);
|
|
border-radius: 4px;
|
|
white-space: pre-wrap;
|
|
word-break: break-word;
|
|
font-size: 11px;
|
|
opacity: 0.75;
|
|
max-height: 200px;
|
|
overflow: auto;
|
|
}
|
|
|
|
/* Tasks is the shared theme reference for procedural cards. Keep the tint
|
|
together so Skills cannot drift when theme colors change. Hover and
|
|
selection remain owned by the shared card state rules above. */
|
|
.task-card,
|
|
.skill-card {
|
|
background: color-mix(in srgb, var(--fg) 2%, transparent);
|
|
}
|
|
.task-card {
|
|
border: 1px solid var(--border);
|
|
border-radius: 8px;
|
|
padding: 7px 8px;
|
|
margin-bottom: 0;
|
|
min-height: 46px;
|
|
box-sizing: border-box;
|
|
align-items: center;
|
|
}
|
|
/* When expanded, height grows to fit the detail panel. */
|
|
.task-card.expanded { align-items: flex-start; }
|
|
.task-card .memory-item-title {
|
|
position: relative;
|
|
top: -2px;
|
|
}
|
|
/* Always show the per-card ⋮ kebab on task cards (the default opacity:0 +
|
|
hover-reveal is unreliable on touch and easy to miss on desktop).
|
|
pointer-events / z-index belt-and-braces against any sibling overlay,
|
|
margin reset undoes the global `.modal-body button { margin-top:6px }`
|
|
that was punting the hit-target down. */
|
|
.task-card .memory-item-actions {
|
|
opacity: 0.55;
|
|
pointer-events: auto;
|
|
position: relative;
|
|
z-index: 5;
|
|
}
|
|
.task-card:hover .memory-item-actions,
|
|
.task-card .memory-item-actions:hover { opacity: 1; }
|
|
.task-card .memory-item-actions .memory-item-btn {
|
|
margin: 0 !important;
|
|
pointer-events: auto;
|
|
position: relative;
|
|
z-index: 5;
|
|
width: 28px;
|
|
min-width: 28px;
|
|
padding: 0;
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
}
|
|
/* Make the SVG inside the kebab transparent to pointer events so the click
|
|
always lands on the BUTTON itself (some browsers / nested SVG events lose
|
|
the click when hitting the inner glyph). */
|
|
.task-card .memory-item-actions .memory-item-btn svg { pointer-events: none; }
|
|
.task-ai-mark {
|
|
flex: 0 0 auto;
|
|
color: var(--accent, var(--red));
|
|
opacity: 0.78;
|
|
}
|
|
/* Per-card select checkbox rides up to the title line. The "All" checkbox is
|
|
#tasks-select-all (not .memory-select-cb), so it stays put. */
|
|
.task-card .memory-select-cb { position: relative; }
|
|
.task-card.task-card-deleting {
|
|
opacity: 0.68;
|
|
pointer-events: none;
|
|
position: relative;
|
|
}
|
|
.task-card-delete-busy {
|
|
position: absolute;
|
|
right: 11px;
|
|
top: 50%;
|
|
transform: translateY(-50%);
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 5px;
|
|
height: 22px;
|
|
padding: 0 8px;
|
|
border-radius: 999px;
|
|
color: var(--red);
|
|
background: color-mix(in srgb, var(--red) 12%, var(--panel, #111));
|
|
border: 1px solid color-mix(in srgb, var(--red) 24%, transparent);
|
|
font-size: 10px;
|
|
font-weight: 600;
|
|
line-height: 1;
|
|
z-index: 7;
|
|
}
|
|
.task-card-delete-busy-spin {
|
|
width: 10px;
|
|
height: 10px;
|
|
border-radius: 999px;
|
|
border: 1.5px solid color-mix(in srgb, currentColor 28%, transparent);
|
|
border-top-color: currentColor;
|
|
animation: spin 0.8s linear infinite;
|
|
}
|
|
#tasks-pause-all-btn { position: relative; top: -2px; }
|
|
#tasks-pause-all-btn:has(input:disabled) { opacity: 0.4; }
|
|
/* Bigger ⋮ dropdown on mobile (its buttons carry inline styles → !important). */
|
|
@media (max-width: 768px) {
|
|
.task-card .task-status-badge {
|
|
touch-action: manipulation;
|
|
-webkit-tap-highlight-color: transparent;
|
|
}
|
|
.task-card .task-state-badge svg,
|
|
.task-card .task-card-run-btn svg {
|
|
width: 10px;
|
|
height: 10px;
|
|
}
|
|
}
|
|
|
|
.task-card-header {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 4px;
|
|
flex-wrap: wrap;
|
|
}
|
|
|
|
.task-card-name {
|
|
font-size: 12px;
|
|
font-weight: 600;
|
|
}
|
|
|
|
.task-card-schedule {
|
|
font-size: 10.5px;
|
|
opacity: 0.5;
|
|
margin-left: auto;
|
|
}
|
|
|
|
.task-card-output {
|
|
font-size: 10px;
|
|
opacity: 0.45;
|
|
}
|
|
|
|
.task-card-prompt {
|
|
font-size: 11px;
|
|
opacity: 0.55;
|
|
margin: 4px 0;
|
|
line-height: 1.4;
|
|
}
|
|
|
|
.task-card-meta {
|
|
font-size: 10.5px;
|
|
opacity: 0.45;
|
|
margin-bottom: 6px;
|
|
}
|
|
|
|
.task-card-actions {
|
|
display: flex;
|
|
gap: 4px;
|
|
flex-wrap: wrap;
|
|
}
|
|
|
|
.task-btn {
|
|
font-family: inherit;
|
|
font-size: 11px;
|
|
padding: 4px 10px;
|
|
border: 1px solid var(--border);
|
|
border-radius: 6px;
|
|
background: none;
|
|
color: var(--fg);
|
|
cursor: pointer;
|
|
opacity: 0.7;
|
|
transition: opacity 0.15s, background 0.15s;
|
|
}
|
|
.task-btn:hover { opacity: 1; }
|
|
.task-btn:disabled { opacity: 0.3; cursor: not-allowed; }
|
|
|
|
.task-btn-primary {
|
|
background: var(--red);
|
|
color: #fff;
|
|
border-color: transparent;
|
|
opacity: 1;
|
|
}
|
|
.task-btn-primary:hover { opacity: 0.85; }
|
|
|
|
.task-btn-danger { color: var(--red); border-color: color-mix(in srgb, var(--red) 30%, transparent); }
|
|
.task-btn-danger:hover { opacity: 1; border-color: var(--red); }
|
|
|
|
.tasks-clock {
|
|
font-size: 10px;
|
|
opacity: 0.35;
|
|
text-align: center;
|
|
padding: 6px 0 8px;
|
|
}
|
|
|
|
.task-preset-grid {
|
|
display: grid; grid-template-columns: 1fr 1fr; gap: 8px; margin-bottom: 8px;
|
|
}
|
|
.task-preset-card {
|
|
display: flex; flex-direction: column; align-items: flex-start; gap: 2px;
|
|
padding: 12px; border: 1px solid var(--border); border-radius: 8px;
|
|
background: var(--bg); cursor: pointer; transition: all 0.15s; text-align: left;
|
|
color: var(--fg);
|
|
}
|
|
.task-preset-card:hover {
|
|
border-color: var(--accent, #cc6a3a); background: color-mix(in srgb, var(--accent, #cc6a3a) 8%, var(--bg));
|
|
}
|
|
.task-preset-icon { font-size: 20px; margin-bottom: 2px; }
|
|
.task-preset-label { font-size: 12px; font-weight: 600; }
|
|
.task-preset-desc { font-size: 10px; opacity: 0.5; line-height: 1.3; }
|
|
|
|
.task-form { display: flex; flex-direction: column; gap: 4px; }
|
|
.task-form-label {
|
|
font-size: 11px;
|
|
opacity: 0.55;
|
|
font-weight: 500;
|
|
display: block;
|
|
/* Consistent rhythm: 8px above each label (section gap), 4px below to the
|
|
field — same spacing for Name/Prompt/Trigger/Output/Model/Chain. */
|
|
margin: 8px 0 4px;
|
|
}
|
|
.task-form-input {
|
|
font-family: inherit;
|
|
font-size: 11px;
|
|
padding: 6px 8px;
|
|
background: var(--bg);
|
|
border: 1px solid var(--border);
|
|
border-radius: 6px;
|
|
color: var(--fg);
|
|
transition: border-color 0.15s;
|
|
box-sizing: border-box;
|
|
width: 100%;
|
|
max-width: 100%;
|
|
min-width: 0;
|
|
}
|
|
.task-form-input:focus {
|
|
outline: none;
|
|
border-color: var(--red);
|
|
}
|
|
.task-form-actions { display: flex; gap: 6px; justify-content: flex-end; margin-top: 8px; }
|
|
|
|
.task-form-notif-toggle {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
margin: 9px 0 3px;
|
|
cursor: pointer;
|
|
color: var(--fg);
|
|
}
|
|
.task-form-notif-toggle input {
|
|
position: absolute;
|
|
opacity: 0;
|
|
pointer-events: none;
|
|
}
|
|
.task-form-notif-switch {
|
|
width: 30px;
|
|
height: 16px;
|
|
border-radius: 999px;
|
|
border: 1px solid var(--border);
|
|
background: color-mix(in srgb, var(--fg) 8%, transparent);
|
|
position: relative;
|
|
flex: 0 0 auto;
|
|
transition: background 0.15s, border-color 0.15s;
|
|
}
|
|
.task-form-notif-toggle input:checked + .task-form-notif-switch {
|
|
border-color: color-mix(in srgb, var(--accent, var(--red)) 58%, transparent);
|
|
background: color-mix(in srgb, var(--accent, var(--red)) 20%, transparent);
|
|
}
|
|
.task-form-notif-copy {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 1px;
|
|
font-size: 11px;
|
|
font-weight: 500;
|
|
}
|
|
.task-form-notif-copy span:last-child {
|
|
font-size: 10px;
|
|
font-weight: 400;
|
|
opacity: 0.52;
|
|
}
|
|
|
|
.task-form-toggle {
|
|
display: flex; gap: 4px;
|
|
}
|
|
.task-toggle-btn {
|
|
flex: 1; padding: 4px 8px; font-size: 10px; font-family: inherit;
|
|
border: 1px solid var(--border); border-radius: 6px; background: transparent;
|
|
color: var(--fg); opacity: 0.4; cursor: pointer; transition: all 0.15s;
|
|
}
|
|
.task-toggle-btn:hover { opacity: 0.7; border-color: var(--fg); }
|
|
.task-toggle-btn.active {
|
|
opacity: 1; border-color: var(--red); color: var(--red);
|
|
background: color-mix(in srgb, var(--red) 10%, transparent);
|
|
}
|
|
|
|
.task-time-picker, .task-date-picker {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 4px;
|
|
}
|
|
.task-time-select, .task-date-select {
|
|
width: auto;
|
|
min-width: 48px;
|
|
font-size: 11px;
|
|
}
|
|
.task-time-sep { opacity: 0.4; font-size: 11px; }
|
|
|
|
.task-history-header {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
margin-bottom: 8px;
|
|
}
|
|
|
|
.task-runs-list { display: flex; flex-direction: column; gap: 6px; overflow-y: auto; }
|
|
.task-run-item {
|
|
border: 1px solid var(--border);
|
|
border-radius: 6px;
|
|
padding: 8px 10px;
|
|
}
|
|
.task-run-item-header {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 4px;
|
|
font-size: 11px;
|
|
}
|
|
.task-run-time { margin-left: auto; font-size: 10px; opacity: 0.45; }
|
|
.task-run-result {
|
|
font-size: 11px;
|
|
opacity: 0.6;
|
|
margin-top: 4px;
|
|
line-height: 1.4;
|
|
white-space: pre-wrap;
|
|
word-break: break-word;
|
|
}
|
|
|
|
/* ── Settings Form Utilities ── */
|
|
|
|
.settings-row {
|
|
display: flex;
|
|
align-items: center;
|
|
}
|
|
.settings-col {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 6px;
|
|
}
|
|
.settings-label {
|
|
font-size: 12px;
|
|
min-width: 85px;
|
|
}
|
|
#set-utilityEffortLabel { flex: 0 0 85px; line-height: 1.2; }
|
|
.settings-label-sub {
|
|
display: block;
|
|
font-size: 10px;
|
|
opacity: 0.6;
|
|
}
|
|
.settings-select {
|
|
flex: 1;
|
|
/* Allow the select to shrink below its longest option's width inside a flex
|
|
row — without this a long model name (e.g. the Vision picker) overflowed
|
|
the card off-screen on mobile. */
|
|
min-width: 0;
|
|
max-width: 100%;
|
|
padding: 5px 8px;
|
|
font-family: inherit;
|
|
font-size: 12px;
|
|
background: var(--bg);
|
|
border: 1px solid var(--border);
|
|
border-radius: 6px;
|
|
color: var(--fg);
|
|
transition: border-color 0.15s;
|
|
appearance: none;
|
|
-webkit-appearance: none;
|
|
-moz-appearance: textfield;
|
|
outline: none;
|
|
}
|
|
input.settings-select::-webkit-outer-spin-button,
|
|
input.settings-select::-webkit-inner-spin-button {
|
|
-webkit-appearance: none;
|
|
margin: 0;
|
|
}
|
|
input.settings-select::placeholder { color: color-mix(in srgb, var(--fg) 35%, transparent); }
|
|
.settings-select:focus {
|
|
outline: none;
|
|
border-color: var(--red);
|
|
}
|
|
|
|
/* Deep Research uses long labels and previously fixed-width fields. Keep
|
|
every control constrained by the card at intermediate desktop widths too. */
|
|
.admin-card:has(#set-researchSearch) .settings-row { min-width: 0; }
|
|
.admin-card:has(#set-researchSearch) .settings-row > .settings-label { flex: 0 0 112px; min-width: 0; }
|
|
#set-researchSearch,
|
|
#set-researchMaxTokens,
|
|
#set-researchRunTimeout,
|
|
.settings-row div:has(> #set-researchExtractTimeout),
|
|
.settings-row div:has(> #set-researchExtractConcurrency) {
|
|
width: auto !important;
|
|
max-width: 100% !important;
|
|
min-width: 0 !important;
|
|
flex: 1 1 0 !important;
|
|
}
|
|
|
|
@media (max-width: 768px) {
|
|
/* Deep Research settings contain desktop-sized inline widths. On mobile,
|
|
let each control claim the card width instead of escaping the admin block. */
|
|
.settings-row:has(#set-researchSearch),
|
|
.settings-row:has(#set-researchMaxTokens),
|
|
.settings-row:has(#set-researchExtractTimeout),
|
|
.settings-row:has(#set-researchExtractConcurrency),
|
|
.settings-row:has(#set-researchRunTimeout) {
|
|
flex-wrap: wrap;
|
|
align-items: stretch;
|
|
}
|
|
.settings-row:has(#set-researchSearch) .settings-label,
|
|
.settings-row:has(#set-researchMaxTokens) .settings-label,
|
|
.settings-row:has(#set-researchExtractTimeout) .settings-label,
|
|
.settings-row:has(#set-researchExtractConcurrency) .settings-label,
|
|
.settings-row:has(#set-researchRunTimeout) .settings-label {
|
|
flex: 1 1 auto;
|
|
}
|
|
#set-researchSearch,
|
|
#set-researchMaxTokens,
|
|
#set-researchRunTimeout,
|
|
.settings-row div:has(> #set-researchExtractTimeout),
|
|
.settings-row div:has(> #set-researchExtractConcurrency) {
|
|
width: 100% !important;
|
|
max-width: 100% !important;
|
|
flex: 1 1 100% !important;
|
|
margin-left: 0 !important;
|
|
}
|
|
}
|
|
|
|
/* Default-chat fallback chain editor. Each row mirrors the primary
|
|
endpoint/model selectors, indented under them to read as a chain. */
|
|
.settings-fallbacks {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 6px;
|
|
}
|
|
.settings-fallbacks:not(:empty) { margin-top: 2px; }
|
|
.settings-fallback-row {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 6px;
|
|
padding-left: 12px;
|
|
border-left: 2px solid color-mix(in srgb, var(--fg) 12%, transparent);
|
|
max-width: 100%;
|
|
box-sizing: border-box;
|
|
}
|
|
.settings-fallback-num {
|
|
font-size: 11px;
|
|
opacity: 0.4;
|
|
min-width: 14px;
|
|
text-align: right;
|
|
}
|
|
.settings-fallback-row .settings-select {
|
|
width: 0;
|
|
min-width: 0;
|
|
min-inline-size: 0;
|
|
box-sizing: border-box;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
}
|
|
.settings-fallback-row .settings-select:first-of-type {
|
|
flex: 0 1 128px;
|
|
width: 128px;
|
|
max-width: 34%;
|
|
}
|
|
.settings-fallback-row .settings-select:nth-of-type(2) {
|
|
flex: 1 1 0;
|
|
width: 0;
|
|
max-width: 100%;
|
|
}
|
|
.settings-fallback-row .settings-select.settings-fallback-effort {
|
|
flex: 0 1 130px;
|
|
width: 130px;
|
|
max-width: 32%;
|
|
}
|
|
details.hwfit-serve-advanced > .hwfit-serve-row,
|
|
details.hwfit-serve-advanced > .hwfit-serve-checks,
|
|
details.hwfit-serve-advanced > .hwfit-serve-cmd-wrap,
|
|
details.hwfit-serve-advanced > .hwfit-serve-extra {
|
|
margin-top: 0;
|
|
margin-bottom: 0;
|
|
}
|
|
/* Inside Advanced, tighten the per-row grid gap and the label →
|
|
select gap so rows of single-line dropdowns don't visually sit
|
|
on top of half a row of empty space. Outside Advanced (Core,
|
|
etc.) the original ~6px breathing room stays. */
|
|
details.hwfit-serve-advanced > .hwfit-serve-row {
|
|
gap: 4px;
|
|
row-gap: 4px;
|
|
}
|
|
details.hwfit-serve-advanced > .hwfit-serve-row.hwfit-backend-llamacpp,
|
|
details.hwfit-serve-advanced > .hwfit-serve-checks.hwfit-backend-llamacpp {
|
|
margin-top: -20px;
|
|
position: relative;
|
|
left: 4px;
|
|
}
|
|
details.hwfit-serve-advanced > .hwfit-serve-row.hwfit-backend-llamacpp {
|
|
column-gap: 8px;
|
|
row-gap: 2px;
|
|
}
|
|
details.hwfit-serve-advanced > .hwfit-llama-placement-row {
|
|
margin-top: -6px !important;
|
|
}
|
|
details.hwfit-serve-advanced > .hwfit-llama-memory-row {
|
|
margin-top: -20px !important;
|
|
}
|
|
.cookbook-llama-cpu-mode details.hwfit-serve-advanced > .hwfit-llama-memory-row {
|
|
margin-top: -8px !important;
|
|
}
|
|
details.hwfit-serve-advanced > .hwfit-llama-batch-row,
|
|
details.hwfit-serve-advanced > .hwfit-llama-monitor-row,
|
|
details.hwfit-serve-advanced > .hwfit-llama-checks-row {
|
|
margin-top: -18px !important;
|
|
}
|
|
details.hwfit-serve-advanced > .hwfit-diff-settings-row {
|
|
row-gap: 2px;
|
|
}
|
|
details.hwfit-serve-advanced > .hwfit-diff-checks-row,
|
|
details.hwfit-serve-advanced > .hwfit-diff-harmonize-row {
|
|
margin-top: -10px !important;
|
|
}
|
|
details.hwfit-serve-advanced > .hwfit-diff-checks-row {
|
|
row-gap: 2px;
|
|
}
|
|
details.hwfit-serve-advanced > .hwfit-serve-row.hwfit-vram-monitor {
|
|
gap: 4px !important;
|
|
grid-template-columns: max-content minmax(0, 1fr);
|
|
white-space: nowrap;
|
|
}
|
|
details.hwfit-serve-advanced .hwfit-sf[data-field="vllm_block_size"] {
|
|
width: calc(100% - 6px);
|
|
}
|
|
details.hwfit-serve-advanced .hwfit-sf[data-field="vllm_kv_cache_dtype"] {
|
|
width: 60px;
|
|
min-width: 60px;
|
|
}
|
|
details.hwfit-serve-advanced > .hwfit-serve-checks {
|
|
gap: 4px;
|
|
row-gap: 4px;
|
|
}
|
|
/* Pull the vLLM/SGLang checks row, Extra args, and the trailing
|
|
model-specific (Speculative) checks row up tight against the row
|
|
above — the previous 4px gap plus per-row baseline padding left a
|
|
~8px gap that read as too airy in the Advanced fold. */
|
|
details.hwfit-serve-advanced > .hwfit-serve-checks.hwfit-backend-vllm,
|
|
details.hwfit-serve-advanced > .hwfit-serve-checks.hwfit-backend-sglang,
|
|
details.hwfit-serve-advanced > .hwfit-serve-extra {
|
|
margin-top: -8px;
|
|
}
|
|
details.hwfit-serve-advanced > .hwfit-serve-extra {
|
|
margin-top: -18px;
|
|
}
|
|
details.hwfit-serve-advanced > .hwfit-serve-row:last-of-type,
|
|
details.hwfit-serve-advanced > .hwfit-serve-checks:last-of-type {
|
|
margin-bottom: 0;
|
|
}
|
|
|
|
.settings-fallback-remove {
|
|
flex-shrink: 0;
|
|
width: 32px;
|
|
height: 32px;
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
border: 1px solid var(--border);
|
|
border-radius: 6px;
|
|
background: transparent;
|
|
color: color-mix(in srgb, var(--fg) 55%, transparent);
|
|
cursor: pointer;
|
|
transition: border-color 0.12s, color 0.12s, background 0.12s;
|
|
}
|
|
.settings-fallback-remove:hover {
|
|
border-color: var(--red);
|
|
color: var(--red);
|
|
background: color-mix(in srgb, var(--red) 10%, transparent);
|
|
}
|
|
.settings-fallback-add {
|
|
align-self: flex-start;
|
|
margin-top: 2px;
|
|
font-size: 11px;
|
|
padding: 3px 9px;
|
|
border: 1px dashed var(--border);
|
|
border-radius: 6px;
|
|
background: transparent;
|
|
color: color-mix(in srgb, var(--fg) 65%, transparent);
|
|
cursor: pointer;
|
|
transition: border-color 0.12s, color 0.12s;
|
|
}
|
|
.settings-fallback-add:hover {
|
|
border-color: var(--red);
|
|
color: var(--red);
|
|
}
|
|
.settings-input {
|
|
flex: 1;
|
|
padding: 5px 8px;
|
|
background: var(--bg);
|
|
border: 1px solid var(--border);
|
|
border-radius: 6px;
|
|
color: var(--fg);
|
|
font-family: inherit;
|
|
font-size: 12px;
|
|
transition: border-color 0.15s;
|
|
}
|
|
.settings-input:focus {
|
|
outline: none;
|
|
border-color: var(--red);
|
|
}
|
|
/* ── Contacts manager (Settings → Integrations → CardDAV) ── */
|
|
.contacts-add-row {
|
|
display: flex;
|
|
gap: 6px;
|
|
margin-bottom: 8px;
|
|
align-items: center;
|
|
}
|
|
.contacts-list {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 2px;
|
|
max-height: 360px;
|
|
overflow-y: auto;
|
|
}
|
|
.contact-row {
|
|
padding: 6px 4px;
|
|
border-bottom: 1px solid color-mix(in srgb, var(--border) 35%, transparent);
|
|
}
|
|
.contact-row:last-child { border-bottom: none; }
|
|
.contact-row-edit { margin-top: 4px; }
|
|
|
|
/* ── Sort Dropdown ── */
|
|
|
|
.sort-dropdown {
|
|
position: absolute;
|
|
right: 0;
|
|
top: 100%;
|
|
z-index: 1000;
|
|
min-width: 120px !important;
|
|
width: max-content;
|
|
padding: 4px !important;
|
|
margin-top: 4px;
|
|
background: var(--panel) !important;
|
|
border: 1px solid var(--border) !important;
|
|
border-radius: 8px !important;
|
|
box-shadow: 0 4px 16px rgba(0,0,0,0.4) !important;
|
|
backdrop-filter: none !important;
|
|
}
|
|
.sort-dropdown-item {
|
|
cursor: pointer;
|
|
padding: 6px 8px 6px 6px !important;
|
|
font-size: 11px !important;
|
|
border-radius: 6px !important;
|
|
border-bottom: none !important;
|
|
white-space: nowrap;
|
|
transition: background 0.1s;
|
|
}
|
|
.sort-dropdown-item:hover {
|
|
background: color-mix(in srgb, var(--accent) 10%, transparent) !important;
|
|
}
|
|
.sort-dropdown-item.ai-sort-option {
|
|
color: var(--accent, var(--red));
|
|
background: color-mix(in srgb, var(--accent, var(--red)) 8%, transparent);
|
|
}
|
|
.sort-dropdown-item.ai-sort-option .auto-sort-icon {
|
|
color: var(--accent, var(--red));
|
|
display: inline-flex;
|
|
align-items: center;
|
|
}
|
|
#auto-sort-sessions-btn {
|
|
transform: translateX(-4px);
|
|
}
|
|
.sort-dropdown-item.ai-sort-option:hover {
|
|
background: color-mix(in srgb, var(--accent, var(--red)) 15%, transparent) !important;
|
|
}
|
|
.sort-dropdown-sep {
|
|
border-top: 1px solid var(--border);
|
|
margin-top: 2px;
|
|
padding-top: 6px;
|
|
}
|
|
|
|
/* Mobile: bigger taps for the chats sort/funnel menu and the select-mode
|
|
bulk-action bar. Also drop the "Rearrange" item — it's a touch-finicky
|
|
feature better left to desktop. */
|
|
@media (max-width: 768px) {
|
|
/* Nudge the funnel/sort button right on mobile so it doesn't hug the
|
|
chevron next to it. */
|
|
#session-sort-btn { transform: translateX(13px); }
|
|
#session-sort-dropdown.sort-dropdown {
|
|
min-width: 200px !important;
|
|
padding: 6px !important;
|
|
}
|
|
#session-sort-dropdown .sort-dropdown-item {
|
|
padding: 12px 14px !important;
|
|
font-size: 14px !important;
|
|
}
|
|
#session-rearrange-toggle { display: none !important; }
|
|
|
|
/* Select-mode bar — make Archive / Delete / Cancel buttons + the
|
|
Select-all toggle finger-sized. */
|
|
.session-bulk-bar {
|
|
padding: 8px 10px;
|
|
font-size: 14px;
|
|
gap: 10px;
|
|
}
|
|
.session-bulk-btn {
|
|
padding: 10px;
|
|
min-width: 44px;
|
|
min-height: 44px;
|
|
}
|
|
.session-bulk-btn svg { width: 20px; height: 20px; }
|
|
#session-select-all-dot { font-size: 22px !important; padding: 6px; }
|
|
#session-select-all-label { font-size: 14px !important; padding: 4px; }
|
|
}
|
|
|
|
|
|
/* ── Admin: Built-in tools ── */
|
|
.admin-tool-category {
|
|
border: 1px solid var(--border);
|
|
border-radius: 6px;
|
|
margin-bottom: 6px;
|
|
overflow: hidden;
|
|
transition: border-color 0.15s;
|
|
}
|
|
.admin-tool-category:hover {
|
|
border-color: color-mix(in srgb, var(--fg) 20%, var(--border));
|
|
}
|
|
.admin-tool-cat-header {
|
|
font-size: 12px;
|
|
font-weight: 500;
|
|
padding: 8px 10px;
|
|
transition: background 0.15s;
|
|
}
|
|
.admin-tool-cat-header:hover {
|
|
background: color-mix(in srgb, var(--fg) 4%, transparent);
|
|
}
|
|
.admin-tool-cat-body {
|
|
border-top: 1px solid var(--border);
|
|
padding: 4px 0;
|
|
}
|
|
.admin-tool-cat-body.hidden {
|
|
display: none;
|
|
}
|
|
.admin-tool-row {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
padding: 5px 10px;
|
|
padding-right: 28px; /* align toggles with header toggle (chevron 12px + gap 6px + padding) */
|
|
transition: background 0.08s;
|
|
}
|
|
.admin-tool-row:hover {
|
|
background: color-mix(in srgb, var(--fg) 4%, transparent);
|
|
}
|
|
.admin-tool-info {
|
|
flex: 1;
|
|
min-width: 0;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 1px;
|
|
}
|
|
.admin-tool-name {
|
|
font-size: 11px;
|
|
font-weight: 500;
|
|
}
|
|
.admin-tool-desc {
|
|
font-size: 10px;
|
|
opacity: 0.4;
|
|
}
|
|
.admin-tool-ctx {
|
|
font-size: 9px;
|
|
opacity: 0.3;
|
|
flex-shrink: 0;
|
|
min-width: 35px;
|
|
text-align: right;
|
|
font-family: 'Berkeley Mono', 'SF Mono', 'Fira Code', monospace;
|
|
}
|
|
|
|
/* Cookbook serve param hints */
|
|
.hwfit-hint {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
width: 12px;
|
|
height: 12px;
|
|
border-radius: 50%;
|
|
background: color-mix(in srgb, var(--fg) 8%, transparent);
|
|
color: var(--fg);
|
|
font-size: 8px;
|
|
font-weight: 600;
|
|
opacity: 0.35;
|
|
cursor: help;
|
|
margin-left: 2px;
|
|
vertical-align: middle;
|
|
transition: opacity 0.1s;
|
|
}
|
|
.hwfit-hint:hover {
|
|
opacity: 0.8;
|
|
background: color-mix(in srgb, var(--accent) 15%, transparent);
|
|
}
|
|
.hwfit-dl-dot {
|
|
color: var(--green);
|
|
font-size: 8px;
|
|
margin-left: 4px;
|
|
opacity: 0.7;
|
|
}
|
|
.hwfit-parser-tag {
|
|
font-size: 9px;
|
|
opacity: 0.4;
|
|
background: color-mix(in srgb, var(--fg) 8%, transparent);
|
|
padding: 1px 5px;
|
|
border-radius: 3px;
|
|
margin-left: 4px;
|
|
font-family: 'Berkeley Mono', 'SF Mono', 'Fira Code', monospace;
|
|
}
|
|
|
|
/* Cookbook output copy — show only on hover */
|
|
.cookbook-output-wrap .cookbook-output-copy {
|
|
opacity: 0;
|
|
transition: opacity 0.1s;
|
|
}
|
|
|
|
/* Cached model menu button */
|
|
.hwfit-cached-menu-btn {
|
|
background: none;
|
|
border: none;
|
|
color: var(--fg);
|
|
font-size: 16px;
|
|
cursor: pointer;
|
|
opacity: 0;
|
|
padding: 2px 4px;
|
|
border-radius: 4px;
|
|
transition: opacity 0.1s;
|
|
flex-shrink: 0;
|
|
position: relative;
|
|
top: -2px;
|
|
}
|
|
.memory-item:hover .hwfit-cached-menu-btn,
|
|
.hwfit-cached-item:hover .hwfit-cached-menu-btn { opacity: 0.4; }
|
|
.hwfit-cached-menu-btn:hover { opacity: 1 !important; }
|
|
.hwfit-cached-menu-btn svg { pointer-events: none; }
|
|
@media (max-width: 768px) {
|
|
#cookbook-modal .hwfit-cached-menu-btn {
|
|
opacity: 0.72 !important;
|
|
width: 32px;
|
|
height: 32px;
|
|
min-width: 32px;
|
|
padding: 0;
|
|
justify-content: center;
|
|
top: -4px;
|
|
}
|
|
#cookbook-modal .hwfit-cached-menu-btn:active {
|
|
opacity: 1 !important;
|
|
background: color-mix(in srgb, var(--fg) 9%, transparent);
|
|
}
|
|
}
|
|
|
|
/* Quick run button */
|
|
.cookbook-run-btn {
|
|
background: var(--accent, var(--red)) !important;
|
|
color: var(--panel) !important;
|
|
border-color: var(--accent, var(--red)) !important;
|
|
font-weight: 600;
|
|
}
|
|
.cookbook-run-btn:hover {
|
|
opacity: 0.9;
|
|
}
|
|
#hwfit-cache-scan {
|
|
position: relative;
|
|
top: 1px;
|
|
width: 26px;
|
|
height: 24px;
|
|
padding: 0;
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
flex-shrink: 0;
|
|
}
|
|
#hwfit-cache-scan.spinning svg {
|
|
animation: modelPickerRefreshSpin 0.75s linear infinite;
|
|
}
|
|
#serve-search {
|
|
height: 28px;
|
|
min-height: 28px;
|
|
font-family: inherit;
|
|
font-size: 11px;
|
|
background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%23e06c75' stroke-opacity='0.85' 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>");
|
|
background-repeat: no-repeat;
|
|
background-position: 9px center;
|
|
padding-left: 28px;
|
|
}
|
|
.cookbook-sort-select-wrap {
|
|
position: relative;
|
|
display: inline-flex;
|
|
flex-shrink: 0;
|
|
align-items: center;
|
|
}
|
|
.cookbook-sort-select-wrap > svg {
|
|
position: absolute;
|
|
left: 9px;
|
|
z-index: 1;
|
|
pointer-events: none;
|
|
opacity: 0.65;
|
|
transform: translateY(4px);
|
|
}
|
|
|
|
/* Cookbook model directory tags */
|
|
.cookbook-modeldir-tag {
|
|
font-size: 11px;
|
|
padding: 3px 8px;
|
|
border-radius: 4px;
|
|
background: color-mix(in srgb, var(--fg) 8%, transparent);
|
|
color: var(--fg);
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 4px;
|
|
font-family: 'Berkeley Mono', 'SF Mono', monospace;
|
|
}
|
|
.cookbook-modeldir-default {
|
|
opacity: 0.5;
|
|
}
|
|
.cookbook-server-bottom-actions {
|
|
display: flex;
|
|
justify-content: flex-end;
|
|
align-items: center;
|
|
gap: 4px;
|
|
margin-top: 5px;
|
|
}
|
|
.cookbook-server-bottom-actions > button {
|
|
height: 22px;
|
|
box-sizing: border-box;
|
|
display: inline-flex;
|
|
align-items: center;
|
|
}
|
|
/* Default-server radio-check in a Settings server title (same look as the
|
|
model-dir download target). Active = accent-tinted. */
|
|
.cookbook-srv-default {
|
|
cursor: pointer;
|
|
opacity: 0.35;
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 3px;
|
|
line-height: 1;
|
|
transition: opacity 0.12s, color 0.12s;
|
|
}
|
|
.cookbook-srv-default:hover { opacity: 0.8; }
|
|
.cookbook-srv-default.active { opacity: 1; color: var(--accent, var(--red)); }
|
|
.cookbook-srv-default-label { font-size: 10px; font-weight: 600; letter-spacing: 0.02em; }
|
|
.cookbook-srv-default-icon {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
line-height: 0;
|
|
}
|
|
.cookbook-srv-default-icon svg {
|
|
width: 13px;
|
|
height: 13px;
|
|
}
|
|
.cookbook-srv-default-icon,
|
|
.cookbook-srv-default-label {
|
|
position: relative;
|
|
top: 2px;
|
|
}
|
|
.email-settings-default,
|
|
.email-settings-default .cookbook-srv-default-icon,
|
|
.email-settings-default .cookbook-srv-default-label {
|
|
position: relative;
|
|
top: -2px;
|
|
}
|
|
/* Download-target toggle inside a model-dir tag */
|
|
.cookbook-modeldir-dl {
|
|
cursor: pointer;
|
|
opacity: 0.35;
|
|
display: inline-flex;
|
|
align-items: center;
|
|
line-height: 0;
|
|
transition: opacity 0.12s, color 0.12s;
|
|
}
|
|
.cookbook-modeldir-dl:hover { opacity: 0.8; }
|
|
.cookbook-modeldir-dl.active { opacity: 1; color: var(--accent, var(--red)); }
|
|
/* The tag currently flagged as the download target — clearly highlighted so
|
|
it's obvious where downloads land. */
|
|
.cookbook-modeldir-target {
|
|
opacity: 1;
|
|
color: var(--accent, var(--red));
|
|
font-weight: 600;
|
|
background: color-mix(in srgb, var(--accent, var(--red)) 16%, transparent);
|
|
box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--accent, var(--red)) 60%, transparent);
|
|
}
|
|
.cookbook-modeldir-rm {
|
|
cursor: pointer;
|
|
opacity: 0.4;
|
|
font-size: 10px;
|
|
}
|
|
.cookbook-modeldir-rm:hover {
|
|
opacity: 1;
|
|
}
|
|
.cookbook-modeldir-add {
|
|
background: none;
|
|
border: 1px solid var(--border);
|
|
border-radius: 4px;
|
|
color: var(--fg);
|
|
font-size: 10px;
|
|
font-weight: 500;
|
|
height: 19.5px;
|
|
box-sizing: border-box;
|
|
cursor: pointer;
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
gap: 2px;
|
|
position: relative;
|
|
top: -3px;
|
|
padding: 0 9px;
|
|
transition: border-color 0.12s, background 0.12s;
|
|
}
|
|
.cookbook-modeldir-add:hover { border-color: var(--accent, var(--red)); background: color-mix(in srgb, var(--accent, var(--red)) 10%, transparent); }
|
|
|
|
/* Cookbook serve optimizations */
|
|
.hwfit-serve-opts {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
margin: 4px 0;
|
|
}
|
|
.hwfit-apply-opts {
|
|
font-size: 11px !important;
|
|
min-width: auto !important;
|
|
white-space: nowrap;
|
|
}
|
|
.hwfit-opts-desc {
|
|
font-size: 9px;
|
|
opacity: 0.4;
|
|
font-style: italic;
|
|
}
|
|
|
|
/* Library modal tabs */
|
|
.lib-tabs,
|
|
.admin-tabs {
|
|
display: flex;
|
|
gap: 0;
|
|
margin-bottom: 8px;
|
|
border-bottom: 1px solid var(--border);
|
|
}
|
|
.lib-tab,
|
|
.admin-tab {
|
|
background: none;
|
|
border: none;
|
|
border-bottom: 2px solid transparent;
|
|
color: var(--fg-muted);
|
|
font-size: 12px;
|
|
font-family: inherit;
|
|
padding: 6px 14px;
|
|
cursor: pointer;
|
|
transition: color 0.1s, border-color 0.1s;
|
|
}
|
|
.lib-tab:hover,
|
|
.admin-tab:hover { color: var(--fg); }
|
|
.lib-tab.active,
|
|
.admin-tab.active {
|
|
color: var(--accent, var(--red));
|
|
border-bottom-color: var(--accent, var(--red));
|
|
}
|
|
|
|
/* Cookbook tab count badge */
|
|
.cookbook-tab-count {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
min-width: 16px;
|
|
height: 16px;
|
|
padding: 0 4px;
|
|
border-radius: 8px;
|
|
background: color-mix(in srgb, var(--accent, var(--red)) 20%, transparent);
|
|
color: var(--accent, var(--red));
|
|
font-size: 9px;
|
|
font-weight: 700;
|
|
margin-left: 4px;
|
|
line-height: 1;
|
|
}
|
|
|
|
/* ── Generate Visual Report button (deep research) ── */
|
|
.view-report-btn {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 6px;
|
|
margin-top: 12px;
|
|
padding: 8px 16px;
|
|
border: 1px solid color-mix(in srgb, var(--red) 30%, transparent);
|
|
border-radius: 8px;
|
|
background: color-mix(in srgb, var(--red) 10%, transparent);
|
|
color: var(--fg);
|
|
font-family: inherit;
|
|
font-size: 0.88em;
|
|
font-weight: 500;
|
|
transition: background 0.15s ease;
|
|
cursor: pointer;
|
|
}
|
|
.view-report-btn:hover:not(:disabled) {
|
|
background: color-mix(in srgb, var(--red) 18%, transparent);
|
|
}
|
|
.view-report-btn svg {
|
|
flex-shrink: 0;
|
|
}
|
|
.report-btn-wrap {
|
|
margin-top: 12px;
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 8px;
|
|
}
|
|
.view-report-btn.chat-about-btn {
|
|
border-color: color-mix(in srgb, var(--fg) 22%, transparent);
|
|
background: color-mix(in srgb, var(--fg) 6%, transparent);
|
|
}
|
|
.view-report-btn.chat-about-btn:hover:not(:disabled) {
|
|
background: color-mix(in srgb, var(--fg) 12%, transparent);
|
|
}
|
|
.view-report-btn:disabled {
|
|
opacity: 0.6;
|
|
cursor: progress;
|
|
}
|
|
.report-spinner {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 6px;
|
|
font-size: 0.88em;
|
|
color: var(--fg);
|
|
opacity: 0.7;
|
|
}
|
|
.report-spinner-text {
|
|
font-weight: 500;
|
|
}
|
|
.report-spinner-dots::after {
|
|
content: '';
|
|
animation: report-dots 1.4s steps(4, end) infinite;
|
|
}
|
|
@keyframes report-dots {
|
|
0% { content: ''; }
|
|
25% { content: '.'; }
|
|
50% { content: '..'; }
|
|
75% { content: '...'; }
|
|
100% { content: ''; }
|
|
}
|
|
|
|
/* ── Continue research hint ────────────────────────── */
|
|
.continue-research-wrap {
|
|
margin-top: 8px;
|
|
}
|
|
.continue-research-hint {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 6px;
|
|
font-size: 0.78em;
|
|
color: var(--fg-dim, var(--fg));
|
|
opacity: 0.5;
|
|
padding: 4px 0;
|
|
}
|
|
.continue-research-hint svg {
|
|
flex-shrink: 0;
|
|
opacity: 0.6;
|
|
}
|
|
|
|
/* ── Research reconnect & timer ────────────────────── */
|
|
.msg.research-reconnect {
|
|
border-left: 3px solid var(--red);
|
|
background: color-mix(in srgb, var(--red) 5%, transparent);
|
|
}
|
|
.research-timer {
|
|
font-size: 0.8em;
|
|
opacity: 0.6;
|
|
margin-top: 4px;
|
|
font-family: monospace;
|
|
color: var(--fg-dim, var(--fg));
|
|
}
|
|
|
|
/* ── Research synapse visualization ───────────────────────────────
|
|
Live SVG graph of an in-flight deep-research run. The query is the
|
|
central node; sub-questions branch off per round; sources are leaf
|
|
nodes that pop in as they're captured. Pulses indicate activity. */
|
|
.research-synapse {
|
|
margin: 6px 0 4px;
|
|
border: 1px solid color-mix(in srgb, var(--accent, var(--red)) 24%, var(--border));
|
|
border-radius: 7px;
|
|
background:
|
|
linear-gradient(color-mix(in srgb, var(--fg) 3%, transparent) 1px, transparent 1px),
|
|
linear-gradient(90deg, color-mix(in srgb, var(--fg) 3%, transparent) 1px, transparent 1px),
|
|
radial-gradient(ellipse at 50% 48%, color-mix(in srgb, var(--accent, var(--red)) 13%, transparent) 0%, transparent 62%),
|
|
color-mix(in srgb, var(--panel) 68%, var(--bg));
|
|
background-size: 18px 18px, 18px 18px, auto, auto;
|
|
box-shadow: inset 0 1px 0 color-mix(in srgb, var(--fg) 5%, transparent);
|
|
overflow: hidden;
|
|
}
|
|
.research-synapse .rs-stage {
|
|
height: 400px;
|
|
position: relative;
|
|
}
|
|
.research-synapse .rs-meta-header {
|
|
position: absolute;
|
|
z-index: 4;
|
|
top: 0;
|
|
left: 0;
|
|
right: 0;
|
|
border-top: 0;
|
|
border-bottom: 1px solid color-mix(in srgb, var(--border) 60%, transparent);
|
|
padding: 5px 8px 6px;
|
|
background: color-mix(in srgb, var(--panel) 76%, transparent);
|
|
}
|
|
.research-synapse-ascii-filter > .rs-stage > svg {
|
|
display: none;
|
|
}
|
|
.research-synapse-ascii-filter .rs-ascii-overlay {
|
|
position: absolute;
|
|
z-index: 3;
|
|
inset: 28px 10px 10px;
|
|
margin: 0;
|
|
overflow: hidden;
|
|
pointer-events: none;
|
|
color: color-mix(in srgb, var(--accent, var(--red)) 76%, var(--fg));
|
|
font: 600 10px/1.35 ui-monospace, SFMono-Regular, Consolas, monospace;
|
|
white-space: pre;
|
|
text-align: center;
|
|
opacity: 1;
|
|
text-shadow: 0 0 7px color-mix(in srgb, var(--accent, var(--red)) 55%, transparent);
|
|
mix-blend-mode: screen;
|
|
}
|
|
.research-synapse-compact .rs-meta { padding: 5px 8px 6px; font-size: 10px; }
|
|
.research-synapse-compact .rs-label-sub { font-size: 8px; }
|
|
.research-synapse .rs-stage-head {
|
|
position: absolute;
|
|
z-index: 2;
|
|
top: 7px;
|
|
left: 9px;
|
|
right: 9px;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 5px;
|
|
color: var(--fg-dim, var(--fg));
|
|
font: 600 8px/1 ui-monospace, "JetBrains Mono", monospace;
|
|
text-transform: uppercase;
|
|
pointer-events: none;
|
|
}
|
|
.research-synapse .rs-live-dot {
|
|
width: 5px;
|
|
height: 5px;
|
|
border-radius: 50%;
|
|
background: var(--accent, var(--red));
|
|
box-shadow: 0 0 6px color-mix(in srgb, var(--accent, var(--red)) 75%, transparent);
|
|
animation: rs-live 1.8s ease-in-out infinite;
|
|
}
|
|
.research-synapse .rs-phase-chip {
|
|
margin-left: auto;
|
|
padding: 3px 5px;
|
|
border: 1px solid color-mix(in srgb, var(--accent, var(--red)) 25%, var(--border));
|
|
border-radius: 3px;
|
|
background: color-mix(in srgb, var(--accent, var(--red)) 8%, var(--bg));
|
|
color: var(--accent, var(--red));
|
|
}
|
|
.research-synapse svg {
|
|
display: block;
|
|
width: 100%;
|
|
height: 100%;
|
|
}
|
|
.research-synapse .rs-edge {
|
|
stroke: color-mix(in srgb, var(--accent, var(--red)) 32%, var(--border));
|
|
stroke-width: 1.35;
|
|
fill: none;
|
|
opacity: 0.72;
|
|
}
|
|
.research-synapse .rs-node {
|
|
fill: var(--bg);
|
|
stroke: var(--accent, var(--red));
|
|
stroke-width: 1.5;
|
|
transition: all 0.3s ease;
|
|
transform-box: fill-box;
|
|
transform-origin: center;
|
|
}
|
|
.research-synapse .rs-source-index {
|
|
fill: var(--fg);
|
|
font: 650 4.5px/1 ui-monospace, "JetBrains Mono", monospace;
|
|
pointer-events: none;
|
|
opacity: 0.88;
|
|
}
|
|
.research-synapse .rs-node-expanded { animation: rs-hub-expand 0.75s ease-out; }
|
|
.research-synapse .rs-node-new {
|
|
animation: rs-pop 0.6s ease-out;
|
|
}
|
|
.research-synapse .rs-pulse {
|
|
fill: var(--accent, var(--red));
|
|
opacity: 0;
|
|
animation: rs-pulse 2.6s ease-out infinite;
|
|
transform-box: fill-box;
|
|
transform-origin: center;
|
|
}
|
|
.research-synapse .rs-label {
|
|
fill: var(--fg);
|
|
font-size: 10px;
|
|
font-family: ui-monospace, "JetBrains Mono", monospace;
|
|
pointer-events: none;
|
|
opacity: 0.85;
|
|
}
|
|
.research-synapse .rs-label-sub {
|
|
font-size: 9px;
|
|
opacity: 0.7;
|
|
}
|
|
.research-synapse .rs-meta {
|
|
display: grid;
|
|
grid-template-columns: minmax(0, 1fr) auto auto auto;
|
|
align-items: center;
|
|
gap: 10px;
|
|
padding: 6px 10px 8px;
|
|
font-family: ui-monospace, "JetBrains Mono", monospace;
|
|
font-size: 11px;
|
|
color: var(--fg-dim, var(--fg));
|
|
opacity: 0.92;
|
|
border-top: 1px solid color-mix(in srgb, var(--border) 60%, transparent);
|
|
background: color-mix(in srgb, var(--panel) 76%, transparent);
|
|
}
|
|
.research-synapse .rs-meta .rs-status {
|
|
display: none;
|
|
color: var(--accent, var(--red));
|
|
font-weight: 600;
|
|
min-width: 0;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
}
|
|
.research-synapse .rs-meta b {
|
|
color: var(--fg);
|
|
font-weight: 600;
|
|
}
|
|
.research-synapse.rs-complete .rs-meta .rs-status { color: var(--color-success, #4caf50); }
|
|
.research-synapse.rs-error .rs-meta .rs-status { color: var(--red); }
|