diff --git a/static/css/cookbook-research-memory-settings.css b/static/css/cookbook-research-memory-settings.css new file mode 100644 index 000000000..3bcbb08cd --- /dev/null +++ b/static/css/cookbook-research-memory-settings.css @@ -0,0 +1,1423 @@ +/* Cookbook (including What Fits?), deep research, memory, settings and admin. + * + * Split out of style.css for maintainability, not for performance: this file + * is loaded eagerly from index.html immediately after style.css, so the rules + * land in the cascade in exactly the order they had inside the single file. + * Nothing here is lazy-loaded. + * + * LOAD ORDER IS PART OF THE CONTRACT. The tags in static/index.html and + * the PRECACHE list in static/sw.js must both list the stylesheets in this + * order: + * 1. static/style.css + * 2. static/css/documents-gallery-editor.css + * 3. static/css/email-calendar-notes-tasks.css + * 4. static/css/cookbook-research-memory-settings.css + * + * Rules were moved verbatim. A rule stays in style.css when its selector group + * covers more than one panel, when the class is shared design language used by + * modules outside this panel, or when moving it would change which of two + * equally specific rules wins. + */ + +/* Cookbook already shows its own running/served-status dot + (#cookbook-notif-dot, toggled with .cookbook-notif-active on the + button). Don't stack the tabbed-down pulse on top of it — the two + dots overlap. Suppress the minimized dot while the status dot is up. */ +.list-item.rail-minimized.cookbook-notif-active::after { display: none; } +@keyframes research-done-pulse { + 0%, 100% { transform: scale(1); opacity: 1; } + 50% { transform: scale(1.5); opacity: 0.7; } + } +@keyframes research-pulse { + 0%, 100% { background: color-mix(in srgb, var(--red) 12%, transparent); } + 50% { background: color-mix(in srgb, var(--red) 22%, transparent); } + } +#cookbook-gguf-delete-overlay { + background:rgba(0,0,0,0.5); + backdrop-filter:blur(4px); + pointer-events:auto !important; + z-index:99999 !important; + position:fixed !important; + inset:0 !important; + } +.cookbook-gguf-delete-actions { + flex-wrap:wrap; + } +/* ── Processing pulse animation (reused by session-star) ── */ +@keyframes research-pulse { + 0%, 100% { opacity: 0.3; transform: scale(0.8); } + 50% { opacity: 1; transform: scale(1.2); } +} +@keyframes memory-synapse-pulse { + 0%, 100% { opacity: 0.35; transform: scale(1); } + 50% { opacity: 0.65; transform: scale(1.02); } +} +.memory-add-input::placeholder { + color: color-mix(in srgb, var(--fg) 40%, transparent); +} +.memory-toolbar-toggle:has(input:not(:checked)) > span { + text-decoration: line-through; + text-decoration-color: color-mix(in srgb, var(--fg) 30%, transparent); +} +.memory-sort-label { white-space: nowrap; } +.memory-sort-btn[aria-expanded="true"] .memory-sort-caret { transform: rotate(180deg); } +@keyframes memory-synapse-sweep { + /* Sweep traverses left → right in the first ~12% of the cycle (≈0.7s of + a 6.2s loop), then waits offscreen. */ + 0% { --sweep: -20%; } + 12% { --sweep: 120%; } + 13%, 100% { --sweep: 120%; } +} +.memory-item-edit-input:focus { + outline: none; +} +.memory-edit-cat-select:focus { + outline: none; +} +.memory-add-row .memory-edit-cat-select:focus { + border-color: var(--red); +} +.memory-item-source::before, +.memory-item-time::before { + content: '\00b7 '; +} +.adm-check-dot::after { + content: ''; + position: absolute; + left: 50%; + top: 45%; + width: 5px; + height: 2.5px; + border-left: 1.5px solid #fff; + border-bottom: 1.5px solid #fff; + transform: translate(-50%, -50%) rotate(-45deg) scale(0); + transform-origin: center; + transition: transform 0.2s cubic-bezier(0.34, 1.56, 0.64, 1); +} +.adm-model-row:active .adm-check-dot { + transform: scale(0.9); +} +.adm-cb-hidden:checked + .adm-check-dot::after { + transform: translate(-50%, -50%) rotate(-45deg) scale(1); +} +.adm-cb-hidden:checked + .adm-endpoint-select-dot::after { + display: none; +} +/* Most recently added endpoint — brief accent glow so the user can + spot the new row immediately after Adding / Find. Fades out cleanly. */ +@keyframes adm-ep-just-added-glow { + 0% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--accent, var(--red)) 55%, transparent); background: color-mix(in srgb, var(--accent, var(--red)) 18%, transparent); } + 60% { box-shadow: 0 0 0 6px color-mix(in srgb, var(--accent, var(--red)) 0%, transparent); background: color-mix(in srgb, var(--accent, var(--red)) 8%, transparent); } + 100% { box-shadow: 0 0 0 0 transparent; background: transparent; } +} +.adm-section-toggle:focus-visible { outline: 1px solid var(--red); outline-offset: 1px; } +/* Collapsed: hide the form body and point the caret right. */ +.adm-add-section.collapsed .admin-model-form { display: none; } +.adm-add-section.collapsed .adm-section-caret { transform: rotate(-90deg); } +.adm-quickstart-section { + margin-top: 7px; +} +.adm-quickstart-section:not(.collapsed) .adm-quickstart-toggle { + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; +} +.adm-quickstart-section.collapsed .adm-quickstart-body { display: none; } +.adm-quickstart-section.collapsed .adm-section-caret { transform: rotate(-90deg); } +.adm-provider-name { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } +@keyframes admin-spin { + to { transform: rotate(360deg); } +} +.admin-rag-item .admin-btn-delete { font-size: 10px; padding: 2px 6px; } +/* Cookbook's cached-model list: NO inner-scroll cap. Two nested scroll + surfaces (this + the outer .admin-card) trapped the wheel so an expanded + serve panel couldn't be reached on tall content. Let the outer + .admin-card (overflow-y:auto) be the single scroll surface. */ +.hwfit-cached-list { + max-height: none !important; + overflow-y: visible !important; +} +/* The Servers list reuses .memory-toolbar for layout but must grow with every + added server — the 120px cap above was clipping manually-added servers. */ +.memory-toolbar.cookbook-servers-toolbar { + max-height: none; + overflow: visible; +} +#hwfit-cached-list { + position: relative; + top: -6px; +} +#hwfit-server-select, +#hwfit-search { + position: relative; + top: -2px !important; +} +#hwfit-search { + height: 30px; + min-height: 30px; + font-family: inherit; + font-size: 11px; + background-image: url("data:image/svg+xml;utf8,"); + background-repeat: no-repeat; + background-position: 9px center; + padding-left: 28px; +} +.cookbook-body::-webkit-scrollbar { + width: 4px; +} +.cookbook-body::-webkit-scrollbar-thumb { + background: color-mix(in srgb, var(--fg) 15%, transparent); + border-radius: 4px; +} +.cookbook-body::-webkit-scrollbar-track { + background: transparent; + padding: 4px 0; +} +#hwfit-dl-server.cookbook-server-select-colored, +#hwfit-server-select.cookbook-server-select-colored, +#hwfit-cache-server.cookbook-server-select-colored, +#hwfit-deps-server.cookbook-server-select-colored { + color: var(--cookbook-server-color, var(--fg)); + border-color: color-mix(in srgb, var(--cookbook-server-color) 55%, var(--border)); + background-color: color-mix(in srgb, var(--cookbook-server-color) 12%, var(--bg)); +} +.cookbook-output-wrap:hover .cookbook-output-kill { opacity: 0.7; } +.cookbook-tab-error-dot { --notif-glow: var(--color-error, #f44); } +/* Brief highlight on the matched task card when jumping from the + running pill, so the user can spot it among a long list. */ +.cookbook-task-flash { + animation: cookbook-task-flash-anim 1.6s ease-out; +} +@keyframes cookbook-task-flash-anim { + 0% { box-shadow: 0 0 0 2px var(--accent, var(--red)); } + 100% { box-shadow: 0 0 0 2px transparent; } +} +/* Cookbook header "downloading" status label sits 2px too far left + against the rest of the cookbook chrome — nudge it right. */ +#cookbook-bg-status { + left: 2px; +} +/* Keep both GPU action labels on one line — they wrapped to two rows on + mobile, which looked broken. */ +.cookbook-gpu-probe, .cookbook-gpu-clear { white-space: nowrap; } +#cookbook-hf-latest-list .cookbook-hf-link:hover { + opacity: 1; +} +#cookbook-hf-latest-list { + min-height: 0; + max-height: min(52vh, 480px); + overflow-y: auto; + overflow-x: hidden; + overscroll-behavior: contain; + scrollbar-width: thin; +} +.cookbook-section-header .cookbook-section-title { + flex: 1; +} +.cookbook-section-header:hover .cookbook-section-chevron { opacity: 1; } +.cookbook-slot-wrap:hover .cookbook-slot-del { opacity: 1; } +/* Keep dependency pills visually centered against the package description. + The row itself stays stable; only the controls move down slightly. */ +.cookbook-dep-row > .cookbook-dep-tag { + transform: translateY(2px); +} +/* Conditional line under the Download h2: only when the section is folded + (collapsed). When expanded, the body content provides separation; the + underline reads as clutter. */ +#cookbook-dl-tab-fold { border-bottom: none !important; padding-bottom: 0 !important; } +#cookbook-dl-tab-fold.is-folded { + border-bottom: 1px solid color-mix(in srgb, var(--border) 40%, transparent) !important; + padding-bottom: 6px !important; +} +/* Smooth open/close for the Direct Download body — replaces the + instant display:none toggle with a max-height + opacity slide so + auto-fold and auto-expand don't feel jarring. 1200px is a safe + upper bound for the body height; the slide only fires up to the + actual content height because content stops there. */ +#cookbook-dl-tab-fold-body { + overflow: hidden; + max-height: 1200px; + opacity: 1; + transition: max-height 0.28s ease, opacity 0.18s ease, margin 0.28s ease; +} +#cookbook-dl-tab-fold-body.is-folded { + max-height: 0; + opacity: 0; + margin-top: 0; + margin-bottom: 0; + pointer-events: none; +} +.cookbook-dep-installed-btn .cookbook-dep-installed-label { padding: 0 8px; } +.cookbook-dep-installed-btn:hover { filter: brightness(1.15); } +.hwfit-serve-topline .hwfit-serve-runtime-note { + flex: 1 1 auto; + min-width: 0; +} +/* Expanded serve panel — make sure it can be scrolled past when it + grows taller than the visible viewport. Caps panel height to viewport + minus chrome and gives it its own internal scroll, so the surrounding + cached-list scroll isn't the only escape route. */ +.hwfit-cached-item .hwfit-serve-panel { + max-height: calc(100vh - 180px); + overflow-y: auto; + overscroll-behavior: contain; +} +@media (max-width: 768px) { + .hwfit-cached-item .hwfit-serve-panel { + max-height: calc(100svh - 120px); + } + .hwfit-serve-preset-row { + justify-content: flex-end; + margin: -2px 0 0; + } + .hwfit-serve-topline { + flex-wrap: wrap; + } + .hwfit-serve-topline .hwfit-serve-runtime-note { + flex-basis: 100%; + } + .hwfit-serve-topline .hwfit-serve-runtime-text { + white-space: normal; + } +} +.hwfit-serve-row label select, +.hwfit-serve-row label input { + display: block; + margin-top: 2px; +} +.hwfit-extra-env-label { + grid-column: 1 / -1; +} +.hwfit-serve-panel[data-backend-active="vllm"] .hwfit-extra-env-label { + grid-column: 2 / -1; +} +.hwfit-sf.hwfit-sf-full { grid-column: 1 / -1; } +.hwfit-spec-tokens-bare::-webkit-outer-spin-button, +.hwfit-spec-tokens-bare::-webkit-inner-spin-button { + -webkit-appearance: none; + margin: 0; + display: none; +} +.hwfit-numstep .hwfit-spec-tokens::-webkit-outer-spin-button, +.hwfit-numstep .hwfit-spec-tokens::-webkit-inner-spin-button { + -webkit-appearance: none; + margin: 0; +} +.hwfit-numstep-btn:focus { outline: none; } +.hwfit-spec-group:has(input[type="checkbox"]:not(:checked)) .hwfit-spec-method, +.hwfit-spec-group:has(input[type="checkbox"]:not(:checked)) .hwfit-spec-tokens, +.hwfit-spec-group:has(input[type="checkbox"]:not(:checked)) .hwfit-numstep { + opacity: 0.45; + pointer-events: none; +} +.hwfit-serve-extra { + margin-bottom: 6px; +} +.hwfit-serve-cmd-details { + margin: 4px 0 0; +} +.hwfit-serve-cmd-summary::-webkit-details-marker { + display: none; +} +.hwfit-serve-cmd-summary::after { + content: ''; + margin-left: auto; + width: 0; + height: 0; + border-left: 4px solid currentColor; + border-top: 3px solid transparent; + border-bottom: 3px solid transparent; + opacity: 0.6; + transform: rotate(0deg); + transition: transform 0.18s ease; +} +.hwfit-serve-cmd-details[open] > .hwfit-serve-cmd-summary::after { + transform: rotate(90deg); +} +.hwfit-serve-cmd-details[open] > .hwfit-serve-cmd-summary { + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; +} +/* Same z-index treatment as .cookbook-task-dropdown — cookbook modal's + auto-stack climbs past low values; popups append to body and need to + sit above the modal regardless. */ +:where(.hwfit-cached-dropdown, +.cookbook-gpu-split-menu, +.cookbook-launch-actions-menu, +.cookbook-saved-menu, +.cookbook-dep-menu) { + z-index: 10000; +} +.hwfit-serve-cmd-wrap .hwfit-serve-cmd { + /* Just enough breathing room so a cursor at line-end doesn't actually + touch the Copy icon — text otherwise uses the full width of the box. */ + padding-right: 32px; +} +.hwfit-serve-cmd-details[open] .hwfit-serve-cmd { + border-color: color-mix(in srgb, var(--accent, var(--red)) 48%, var(--border)); + box-shadow: 0 0 0 1px color-mix(in srgb, var(--accent, var(--red)) 16%, transparent), + 0 0 12px color-mix(in srgb, var(--accent, var(--red)) 13%, transparent); +} +.cookbook-gpu-split .cookbook-gpu-split-main { + border-top-right-radius: 0 !important; + border-bottom-right-radius: 0 !important; +} +.cookbook-gpu-split .cookbook-gpu-split-arrow { + border-top-left-radius: 0 !important; + border-bottom-left-radius: 0 !important; + border-left-width: 0 !important; + padding: 0 6px !important; + min-width: 24px; +} +.cookbook-task[data-type="download"][data-status="running"] .cookbook-task-type[data-type="download"] { + background: color-mix(in srgb, var(--color-accent, #00aaff) 18%, transparent); + color: var(--color-accent, #00aaff); +} +@keyframes cookbook-wave-pulse { + 0%, 100% { opacity: 0.45; } + 50% { opacity: 1; } +} +.cookbook-task[data-status="done"] .cookbook-task-check-ico { display: inline; } +.cookbook-task[data-status="done"] .cookbook-task-clear-ico { display: none; } +.cookbook-task-header { + cursor: pointer; +} +.cookbook-env-row > .cookbook-field-label { + flex: 1 1 140px; + min-width: 120px; +} +.cookbook-extra-label { grid-column: 1 / -1; } +.cookbook-slider::-webkit-slider-thumb { + -webkit-appearance: none; + width: 16px; + height: 16px; + border-radius: 50%; + background: var(--fg); + border: 2px solid var(--panel); + box-shadow: 0 1px 4px rgba(0,0,0,0.3); + cursor: pointer; + transition: transform 0.1s, box-shadow 0.1s; +} +.cookbook-slider:hover::-webkit-slider-thumb { + transform: scale(1.15); + box-shadow: 0 0 0 4px color-mix(in srgb, var(--accent, var(--red)) 20%, transparent); +} +.cookbook-slider:active::-webkit-slider-thumb { + transform: scale(1.25); + box-shadow: 0 0 0 6px color-mix(in srgb, var(--accent, var(--red)) 25%, transparent); +} +.cookbook-slider::-moz-range-thumb { + width: 14px; + height: 14px; + border-radius: 50%; + background: var(--fg); + border: 2px solid var(--panel); + box-shadow: 0 1px 4px rgba(0,0,0,0.3); + cursor: pointer; +} +.cookbook-slider::-moz-range-progress { + background: var(--accent, var(--red)); + height: 6px; + border-radius: 4px; +} +.cookbook-slider::-webkit-slider-runnable-track { + height: 6px; + border-radius: 4px; +} +.cookbook-slider::-moz-range-track { + height: 6px; + border-radius: 4px; + background: color-mix(in srgb, var(--fg) 12%, transparent); +} +.cookbook-checkbox:checked::after { + transform: translateX(14px); +} +/* Sliders span full row for alignment */ +.cookbook-field-slider { + grid-column: 1 / -1; +} +.cookbook-diag-body { + margin-top: 7px; +} +/* Add Server collapsible */ +.cookbook-server-details { + margin: 4px 0 6px; +} +.cookbook-server-toggle::-webkit-details-marker { display: none; } +.cookbook-srv-status:hover { transform: scale(1.3); } +@keyframes cookbook-srv-pulse { + 0%, 100% { opacity: 0.45; } + 50% { opacity: 1; } +} +.cookbook-server-entry::after { + content: ''; + position: absolute; + inset: 0; + z-index: 2; + border-radius: inherit; + padding: 1px; + pointer-events: none; + background: linear-gradient( + to right, + transparent 0%, + transparent calc(var(--sweep, -20%) - 8%), + color-mix(in srgb, var(--cookbook-server-color, var(--red)) 85%, transparent) var(--sweep, -20%), + transparent calc(var(--sweep, -20%) + 8%), + transparent 100% + ); + -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0); + -webkit-mask-composite: xor; + mask-composite: exclude; + animation: memory-synapse-sweep 6.2s linear infinite; + animation-delay: 0.4s; +} +#cookbook-servers-list .cookbook-server-entry:nth-child(2n)::after { animation-duration: 7.4s; animation-delay: 1.6s; } +#cookbook-servers-list .cookbook-server-entry:nth-child(3n)::after { animation-duration: 8.8s; animation-delay: 3.2s; } +#cookbook-servers-list .cookbook-server-entry:nth-child(5n)::after { animation-duration: 9.3s; animation-delay: 4.7s; } +#cookbook-servers-list .cookbook-server-entry:nth-child(7n)::after { animation-duration: 5.5s; animation-delay: 2.3s; } +.cookbook-server-entry:hover::after { animation: none; opacity: 0; } +@media (prefers-reduced-motion: reduce) { + .cookbook-server-entry::after { animation: none; opacity: 0; } +} +/* Keep the row inside the card on narrow screens — the fixed-width host/path + inputs would otherwise overflow the card's background to the right. */ +@media (max-width: 768px) { + .cookbook-server-row .cookbook-srv-host, + .cookbook-server-row .cookbook-srv-path { flex: 1 1 100% !important; width: auto !important; min-width: 0 !important; } +} +.cookbook-server-row .cookbook-srv-name { width: 60px; flex-shrink: 0; flex-grow: 0; } +.cookbook-server-row select.cookbook-srv-color { + display: none !important; +} +.cookbook-server-row .cookbook-srv-color-btn:focus-visible { + outline: 2px solid color-mix(in srgb, var(--cookbook-server-color, var(--accent, var(--red))) 70%, transparent); + outline-offset: 2px; +} +.cookbook-server-row .cookbook-srv-host { flex: 1; min-width: 100px; } +.cookbook-server-row .cookbook-srv-host[readonly] { opacity: 0.4; cursor: default; } +.cookbook-server-row .cookbook-srv-port { width: 40px; flex-shrink: 0; flex-grow: 0; } +.cookbook-server-row .cookbook-srv-env { width: 65px; flex-shrink: 0; flex-grow: 0; } +.cookbook-server-row .cookbook-srv-path { flex: 1; min-width: 80px; } +.cookbook-server-row .cookbook-srv-status { width: 8px; height: 8px; align-self: center; } +/* Brief highlight on the serve command box when a saved config is loaded, so + the click clearly registers (loading is otherwise silent). */ +.cookbook-cmd-flash { + animation: cookbookCmdFlash 0.6s ease; +} +.hwfit-toolbar:has(.hwfit-hw-manual-btn) { + gap: 8px; +} +.hwfit-toolbar .hwfit-server-select { min-width: 70px; flex-shrink: 0; } +.hwfit-toolbar .hwfit-usecase { min-width: 70px; flex-shrink: 0; } +.hwfit-toolbar .hwfit-engine { min-width: 0; width: 86px; flex-shrink: 0; font-size: 10px; } +.hwfit-toolbar .hwfit-search { flex: 1; min-width: 80px; } +.hwfit-advanced-panel .hwfit-engine-menu { + min-width: 132px; +} +/* Lower-opacity "Search models..." placeholder so it reads as a hint, not + a label — matches the muted form-field feel of the inline filters. */ +.hwfit-search::placeholder { opacity: 0.5; } +.hwfit-search::-webkit-input-placeholder { opacity: 0.5; } +.hwfit-search::-moz-placeholder { opacity: 0.5; } +.hwfit-quant-wrap .hwfit-quant, +.hwfit-engine-wrap .hwfit-engine { + /* Make room for the ? on the right edge, in addition to the native chevron. */ + padding-right: 32px; +} +.hwfit-toolbar .hwfit-host { width: 110px; flex-shrink: 0; } +.hwfit-env-row .hwfit-envtype { width: auto; min-width: 70px; flex-shrink: 0; } +.hwfit-env-row .hwfit-envpath { flex: 1; min-width: 100px; } +.hwfit-env-row .hwfit-gpus { width: 90px; flex-shrink: 0; } +.hwfit-hw-chip-dismiss { cursor: pointer; } +.hwfit-hw-chip-row[data-hw-chip="gpu"] .hwfit-hw-chip-x { + transform: translateY(-3px); +} +.hwfit-manual-panel .hwfit-hw-manual-save, +.hwfit-manual-panel .hwfit-hw-manual-clear { + /* -3 (was -2) — 1px more up to optically align with the labeled + inputs to their left. */ + transform: translateY(-3px); +} +.cookbook-model-list-fade { + animation: cookbook-model-list-fade-in 180ms ease-out both; +} +@keyframes cookbook-model-list-fade-in { + from { opacity: 0; transform: translateY(3px); } + to { opacity: 1; transform: translateY(0); } +} +@media (prefers-reduced-motion: reduce) { + .cookbook-model-list-fade, + .hwfit-list.cookbook-hwfit-models-just-loaded > .hwfit-row:not(.hwfit-header) { animation: none; } +} +.hwfit-row:has(.hwfit-dl-dot):hover { opacity: 0.9; } +.hwfit-row:has(.hwfit-dl-dot) .hwfit-dl-dot { opacity: 1; } +.hwfit-header .hwfit-name { font-size: 9px; } +.hwfit-sortable { cursor: pointer; user-select: none; } +.hwfit-c-params { width: 42px; } +.hwfit-c-quant { width: 52px; } +.hwfit-c-vram { width: 42px; } +.hwfit-c-ctx { width: 32px; } +.hwfit-c-speed { width: 44px; } +.hwfit-c-mode { width: 72px; } +@keyframes hwfit-panel-in { + from { opacity: 0; transform: translateY(-4px); } + to { opacity: 1; transform: translateY(0); } +} +.hwfit-cb { cursor: pointer; } +.settings-appearance-panel { + flex-direction: column; +} +@keyframes cookbook-modal-enter { + 0% { + opacity: 0; + transform: translateY(12px) scale(0.94); + filter: saturate(0.85); + } + 65% { + opacity: 1; + transform: translateY(-2px) scale(1.012); + filter: saturate(1.05); + } + 100% { + opacity: 1; + transform: translateY(0) scale(1); + filter: none; + } +} +@keyframes cookbook-modal-enter-mobile { + 0% { opacity: 0; transform: translateY(100%); } + 100% { opacity: 1; transform: translateY(0); } +} +/* Cookbook Serve Advanced fold — wraps the rarely-touched tuning rows + (KV/Attention/Swap/Env for vLLM, llama.cpp batch/cache/split, VRAM + monitor, speculative, extra args). Matches the existing .hwfit-panel- + advanced look: muted-gray label, no caps, no letter-spacing, no + warning-y opacity. Content flows into the parent's existing scroll + surface (no inner max-height) and inner rows reset their margin so + stacking gaps don't double when the fold opens. */ +/* Styled to match the Add Models page collapsible sections + (.adm-section-toggle) — same border/background/caret pattern, so the + two folds across the app read consistently. */ +details.hwfit-serve-advanced { + margin-top: 8px; + overflow: visible; +} +details.hwfit-serve-advanced > summary.hwfit-serve-advanced-summary { + cursor: pointer; + user-select: none; + list-style: none; + display: flex; + align-items: center; + gap: 6px; + font-size: 11px; + color: var(--fg); + opacity: 0.8; + border: 1px solid var(--border); + border-radius: 6px; + padding: 6px 9px; + background: color-mix(in srgb, var(--fg) 4%, transparent); + transition: border-color 0.12s, background 0.12s, opacity 0.12s, border-radius 0s; +} +details.hwfit-serve-advanced > summary.hwfit-serve-advanced-summary::-webkit-details-marker { + display: none; +} +details.hwfit-serve-advanced > summary.hwfit-serve-advanced-summary:hover { + opacity: 1; + border-color: var(--red); + background: color-mix(in srgb, var(--red) 8%, transparent); +} +/* Caret on the right, rotates open/closed. SVG-style rectangles via + borders keep this glyph-free + crisp at small sizes. */ +details.hwfit-serve-advanced > summary.hwfit-serve-advanced-summary::after { + content: ''; + margin-left: auto; + width: 0; + height: 0; + border-left: 4px solid currentColor; + border-top: 3px solid transparent; + border-bottom: 3px solid transparent; + opacity: 0.6; + transform: rotate(90deg); + transition: transform 0.18s ease; +} +details.hwfit-serve-advanced:not([open]) > summary.hwfit-serve-advanced-summary::after { + transform: rotate(0deg); +} +/* Body rows below the header — tight rhythm so the fold doesn't + feel airy. The cookbook modal's existing .cookbook-body is the + scroll surface; nothing inside the fold should add its own scroll. */ +details.hwfit-serve-advanced[open] > summary.hwfit-serve-advanced-summary { + margin-bottom: 6px; +} +details.hwfit-serve-advanced > .hwfit-serve-row.hwfit-vram-monitor .hwfit-vram-readout { + min-width: 0; + overflow: hidden; + text-overflow: ellipsis; +} +details.hwfit-serve-advanced > .hwfit-serve-row label select, +details.hwfit-serve-advanced > .hwfit-serve-row label input { + margin-top: 1px; +} +details.hwfit-serve-advanced label:has(.hwfit-sf[data-field="vllm_attn_backend"]) { + position: relative; + left: -63px; +} +details.hwfit-serve-advanced label:has(.hwfit-sf[data-field="vllm_block_size"]) { + position: relative; + left: -75px; +} +details.hwfit-serve-advanced label:has(.hwfit-sf[data-field="swap"]) { + position: relative; + left: -73px; +} +details.hwfit-serve-advanced label:has(.hwfit-sf[data-field="vllm_kv_cache_dtype"]) { + position: relative; + left: 2px; +} +@media (max-width: 768px) { + details.hwfit-serve-advanced label:has(.hwfit-sf[data-field="vllm_block_size"]) { + left: 1px; + } + details.hwfit-serve-advanced label:has(.hwfit-sf[data-field="swap"]) { + left: -3px; + } + details.hwfit-serve-advanced > .hwfit-serve-checks .hwfit-sf-cb { + flex: 1 1 100%; + } +} +/* Hide the native number-input spinner arrows (e.g. SMTP/IMAP Port) — they + render unstyled and ugly. Field still accepts only numbers. */ +.settings-input[type="number"]::-webkit-outer-spin-button, +.settings-input[type="number"]::-webkit-inner-spin-button { + -webkit-appearance: none; + margin: 0; +} +.settings-input[type="number"] { + -moz-appearance: textfield; + appearance: textfield; +} +.cookbook-output-wrap:hover .cookbook-output-copy { + opacity: 0.5; +} +.cookbook-output-wrap .cookbook-output-copy:hover { + opacity: 1; +} +#cookbook-dl-btn-search { + position: relative; + top: -4px; + padding-top: 0; + padding-bottom: 0; + height: 28px; +} +#cookbook-dl-btn { + position: relative; + top: -4px; +} +.research-synapse-compact .rs-stage { height: 288px; } +@keyframes rs-live { + 0%, 100% { opacity: 0.45; } + 50% { opacity: 1; } +} +.research-synapse .rs-guide ellipse { + fill: none; + stroke: color-mix(in srgb, var(--fg) 7%, transparent); + stroke-width: 1; + stroke-dasharray: 2 7; +} +.research-synapse .rs-edge-tone-1 { stroke: color-mix(in srgb, var(--gold, #d4a72c) 45%, var(--border)); } +.research-synapse .rs-edge-tone-2 { stroke: color-mix(in srgb, #5aa7c7 42%, var(--border)); } +.research-synapse .rs-edge.rs-edge-firing { + stroke: var(--accent, var(--red)); + stroke-width: 2; + opacity: 1; + filter: drop-shadow(0 0 4px var(--accent, var(--red))); + animation: rs-fire 1.1s ease-out; +} +@keyframes rs-fire { + 0% { stroke-dasharray: 4 200; stroke-dashoffset: 200; opacity: 0; } + 20% { opacity: 1; } + 100% { stroke-dasharray: 200 4; stroke-dashoffset: -200; opacity: 0.55; } +} +.research-synapse .rs-node-root { + fill: var(--accent, var(--red)); + stroke: var(--accent, var(--red)); + filter: drop-shadow(0 0 5px color-mix(in srgb, var(--accent, var(--red)) 65%, transparent)); +} +.research-synapse .rs-root-halo { + fill: none; + stroke: color-mix(in srgb, var(--accent, var(--red)) 28%, transparent); + stroke-width: 1; +} +/* Sub & leaf nodes stay inside the accent palette so the whole graph + reads as one organism. Leaves are softer/lower-contrast dots that + float around their sub — not green-on-stem (which read as palm fronds). */ +.research-synapse .rs-node-sub { + fill: color-mix(in srgb, var(--accent, var(--red)) 10%, var(--bg)); + stroke: color-mix(in srgb, var(--accent, var(--red)) 70%, var(--fg)); +} +.research-synapse .rs-node-sub.rs-node-tone-1 { + fill: color-mix(in srgb, var(--gold, #d4a72c) 11%, var(--bg)); + stroke: color-mix(in srgb, var(--gold, #d4a72c) 76%, var(--fg)); +} +.research-synapse .rs-node-sub.rs-node-tone-2 { + fill: color-mix(in srgb, #5aa7c7 11%, var(--bg)); + stroke: color-mix(in srgb, #5aa7c7 72%, var(--fg)); +} +.research-synapse .rs-node-leaf { + stroke: color-mix(in srgb, var(--accent, var(--red)) 55%, transparent); + fill: color-mix(in srgb, var(--accent, var(--red)) 42%, var(--bg)); +} +.research-synapse .rs-node-leaf.rs-node-tone-1 { fill: color-mix(in srgb, var(--gold, #d4a72c) 48%, var(--bg)); } +.research-synapse .rs-node-leaf.rs-node-tone-2 { fill: color-mix(in srgb, #5aa7c7 48%, var(--bg)); } +.research-synapse .rs-source-node { cursor: help; } +@keyframes rs-hub-expand { + 0% { filter: drop-shadow(0 0 0 transparent); } + 35% { filter: drop-shadow(0 0 7px var(--accent, var(--red))); } + 100% { filter: drop-shadow(0 0 0 transparent); } +} +@keyframes rs-pop { + 0% { transform: scale(0); opacity: 0; } + 60% { transform: scale(1.25); opacity: 1; } + 100% { opacity: 1; } +} +@keyframes rs-pulse { + 0% { transform: scale(1); opacity: 0.65; } + 100% { transform: scale(5); opacity: 0; } +} +.research-synapse .rs-meta .rs-sep { display: none; } +.research-synapse.rs-complete .rs-pulse { animation: none; opacity: 0; } +.research-synapse.rs-complete .rs-node-root { + fill: var(--color-success, #4caf50); + stroke: var(--color-success, #4caf50); +} +@media (max-width: 480px) { + .research-synapse .rs-meta { grid-template-columns: 1fr auto auto; row-gap: 6px; } + .research-synapse .rs-meta .rs-status { display: none; } +} +.hwfit-cached-menu-btn svg { fill: currentColor; } +.settings-email-preferences-card > h2 { + display: none; +} +[data-settings-panel="email"] > .settings-email-preferences-card { + padding-top: 0; +} +.cookbook-task-menu-btn { + top: 0 !important; + transform: translateY(2px) scale(1.15) !important; +} +/* Settings "+ Add server" matches the model-dir "+ Add" path button (22px). */ +#cookbook-server-add.cal-add-btn-text { + height: 28px; + min-width: 62px; + border-radius: 14px; + padding: 0 12px 0 8px; + position: relative; + top: 1px; + font-size: 12px; +} +@keyframes research-pane-enter { + from { opacity: 0; } + to { opacity: 1; } +} +.research-pane-header:active { cursor:grabbing; } +.research-pane-header h4 { + margin: 0; + font-size: 1rem; + font-weight: 600; + color: var(--red); + display: inline-flex; + align-items: center; + gap: 6px; +} +.research-tab-panel.hidden, +.research-tab-panel[hidden] { display: none; } +.research-tab-heading h2 { + margin: 0; + font-size: 14px; + font-weight: 600; + line-height: 1; +} +.research-tab-panel:not([data-research-panel="research"]) .research-jobs-list { + flex: 1 1 auto; + min-height: 0; + overflow-y: auto; +} +/* Hide the in-body "Research" subtitle on mobile to save vertical space — + the toolbar/header carries enough context already. */ +/* Match the .admin-card h2 styling used in Documents/Library so the + "Research" sub-title reads the same as those modals' titles. */ +.research-new-job h2 { + font-size: 14px; + font-weight: 600; + letter-spacing: 0; +} +.research-new-job > .research-new-job-desc { + margin: 6px 0 0; + display: flex; + align-items: center; + gap: 6px; + flex-wrap: wrap; +} +@media (max-width: 600px) { + /* Keep the "Research" title visible on mobile (matches the Cookbook tab + titles, which show on mobile). It used to be hidden here. */ + .research-new-job h2 { + font-size: 14px; + } +} +.research-setting:has(#research-model) .research-setting-label { + position: relative; + top: 1px; +} +.research-setting:has(#research-rounds) select, +.research-setting:has(#research-category) select { + position: relative; + top: -4px; +} +.research-setting:has(#research-rounds) .research-setting-label, +.research-setting:has(#research-category) .research-setting-label { + position: relative; + top: 4px; +} +.research-setting select { + font-size: 11px; padding: 4px 6px; + background: var(--bg); + color: var(--fg); + border: 1px solid var(--border); border-radius: 4px; +} +.research-picker-btn:focus-visible, +.research-picker-option:focus-visible { + outline: 2px solid color-mix(in srgb, var(--accent, var(--red)) 65%, transparent); + outline-offset: 1px; +} +.research-picker-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } +.research-picker.open .research-picker-btn > svg { transform: rotate(180deg); } +.research-picker-menu[hidden] { display: none; } +.research-picker-option > span:nth-child(2):not(.research-picker-check), +.research-picker-option > span:first-child:not(.research-picker-icon) { + overflow: hidden; text-overflow: ellipsis; +} +.research-picker-option.active .research-picker-check { opacity: 1; } +.research-run-mode-popover.rrm-up { transform-origin: bottom right; } +.research-run-mode-popover .research-run-mode-row:hover { + background: color-mix(in srgb, var(--accent-primary, var(--red)) 14%, transparent); +} +.research-job-card.done { cursor:pointer; } +/* Past (library) research used to be dimmed to 0.65 which read as "greyed out". + They now look the same as the rest — folding them under "Past research" + handles the de-clutter instead. */ +.research-job-card.done.from-library { opacity:1; } +.research-job-thumb-frame[role="button"] { cursor: pointer; } +.research-job-thumb-frame[role="button"]:hover { + outline: 1px solid color-mix(in srgb, var(--accent, var(--red)) 65%, transparent); + outline-offset: 1px; +} +.research-job-report-body h1, .research-job-report-body h2, .research-job-report-body h3 { + color: var(--cat-color, var(--accent, var(--fg))); +} +.research-hero::after { + content: ''; position: absolute; right: -40px; top: -40px; + width: 160px; height: 160px; border-radius: 50%; + background: radial-gradient(circle, color-mix(in srgb, var(--cat-color, var(--accent)) 15%, transparent) 0%, transparent 60%); + pointer-events: none; +} +.research-hero-icon svg { width: 100%; height: 100%; } +/* Product: callout-style bullets */ +.research-body-product ul { list-style: none; padding-left: 0; } +.research-body-product ul li { + padding: 6px 10px 6px 28px; margin: 4px 0; + border-left: 2px solid color-mix(in srgb, #5b8abf 40%, transparent); + background: color-mix(in srgb, #5b8abf 4%, transparent); + border-radius: 0 4px 4px 0; position: relative; +} +.research-body-product ul li::before { + content: '▸'; position: absolute; left: 10px; + color: #5b8abf; font-weight: bold; +} +/* Comparison: styled table */ +.research-body-comparison table { + width: 100%; border-collapse: collapse; margin: 12px 0; + border: 1px solid color-mix(in srgb, #e5a33a 25%, transparent); + border-radius: 6px; overflow: hidden; +} +.research-body-comparison th { + background: color-mix(in srgb, #e5a33a 18%, transparent); + color: #e5a33a; font-weight: 700; + padding: 8px 12px; text-align: left; + border-bottom: 2px solid color-mix(in srgb, #e5a33a 40%, transparent); +} +.research-body-comparison td { + padding: 8px 12px; + border-bottom: 1px solid color-mix(in srgb, #e5a33a 12%, transparent); +} +.research-body-comparison tr:nth-child(even) td { + background: color-mix(in srgb, #e5a33a 3%, transparent); +} +/* How-to: big numbered steps */ +.research-body-howto ol { counter-reset: howto-step; list-style: none; padding-left: 0; } +.research-body-howto ol > li { + counter-increment: howto-step; position: relative; + padding: 10px 12px 10px 52px; margin: 8px 0; + background: color-mix(in srgb, #82c882 5%, transparent); + border-radius: 8px; border-left: 2px solid #82c882; +} +.research-body-howto ol > li::before { + content: counter(howto-step); + position: absolute; left: 10px; top: 14px; + width: 30px; height: 30px; border-radius: 50%; + background: #82c882; color: white; + font-weight: 700; font-size: 13px; + display: flex; align-items: center; justify-content: center; + box-shadow: 0 2px 6px color-mix(in srgb, #82c882 40%, transparent); +} +/* Landscape: section banners */ +.research-body-landscape h3 { + padding: 8px 14px; + background: linear-gradient(90deg, color-mix(in srgb, #a07ae0 15%, transparent), transparent); + border-left: 3px solid #a07ae0; + border-radius: 0 6px 6px 0; + margin: 14px 0 8px; +} +/* Fact-check: verdict emphasis */ +.research-body-factcheck blockquote { + border-left: 3px solid var(--red); + background: color-mix(in srgb, var(--red) 6%, transparent); + padding: 10px 14px; margin: 10px 0; + border-radius: 0 6px 6px 0; +} +.research-body-factcheck strong { + color: var(--red); + padding: 1px 6px; border-radius: 4px; + background: color-mix(in srgb, var(--red) 12%, transparent); +} +.research-job-cancel:hover, +.research-job-remove:hover, +.research-job-report-link:hover { opacity:1; } +.research-synapse-toggle:hover, +.research-synapse-toggle.active { opacity:1; } +.research-job-synapse-host.synapse-collapsed { display:none; } +.research-job-summary .research-job-thumb-frame { + transform: translateY(-4px); +} +.research-job-summary .research-job-failnote { + margin: 2px 0 0; +} +.research-job-thumb-frame::after { + content: ""; + position: absolute; + inset: 0; + pointer-events: none; + background: radial-gradient(ellipse at center, + transparent 42%, + color-mix(in srgb, var(--accent, var(--red)) 28%, transparent) 100%); + opacity: 0.75; +} +/* Push the first dim (dismiss/delete) button — and everything after it — to + the right edge of the actions row. `:first-of-type` would match the very + first