mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-10-06 06:52:20 +02:00
Merge lab into refactor/settings-panel-modules
This commit is contained in:
+1
-1
@@ -37,7 +37,7 @@ import { UI_VIS_DEFAULT_OFF, resolveVisibility } from './js/ui_visibility.js?v=2
|
||||
import tasksModule from './js/tasks.js?v=20260914taskmodel1';
|
||||
import calendarModule from './js/calendar.js?v=20260914emailsource11';
|
||||
import notesModule from './js/notes.js?v=20260911notesselectioncancel1';
|
||||
import adminModule from './js/admin.js?v=20260921chatgptusage2';
|
||||
import adminModule from './js/admin.js?v=20260929csssplit';
|
||||
import settingsModule from './js/settings.js?v=20260912writingstyle3';
|
||||
// Eagerly bind unified minimize/restore behavior across all tool modals.
|
||||
import './js/modalManager.js';
|
||||
|
||||
@@ -0,0 +1,183 @@
|
||||
/* 00-tokens - 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.
|
||||
*/
|
||||
/* ============================================ */
|
||||
/* Odysseus UI — Consolidated Stylesheet */
|
||||
/* ============================================ */
|
||||
|
||||
|
||||
/* ── Variables ──
|
||||
*
|
||||
* Theme-public variables (override via theme.js or custom themes):
|
||||
* Core: --bg, --fg, --panel, --border, --red
|
||||
* Syntax: --hl-keyword, --hl-string, --hl-comment, --hl-function,
|
||||
* --hl-number, --hl-builtin, --hl-variable, --hl-params,
|
||||
* --hl-bg, --hl-fg
|
||||
* Accents: --accent-primary, --accent-error (set by theme.js)
|
||||
* Semantic: --color-error, --color-success, --color-warning, --color-danger,
|
||||
* --color-accent, --color-muted, --color-muted-alt
|
||||
*/
|
||||
|
||||
:root {
|
||||
/* Core palette */
|
||||
--bg: #282c34;
|
||||
--fg: #9cdef2;
|
||||
--panel: #111;
|
||||
--border: #355a66;
|
||||
--red: #e06c75;
|
||||
/* Were `var(--green)` / `var(--warn)` — self-referential, so they
|
||||
resolved to invalid and every site fell back to its own literal
|
||||
(or, for sites with no fallback, painted as transparent/inherit).
|
||||
Anchor them to real hex so the token layer actually works. */
|
||||
--green: #50fa7b;
|
||||
--warn: #f0ad4e;
|
||||
|
||||
/* Syntax highlighting */
|
||||
--hl-bg: #1e2228;
|
||||
--hl-fg: #9cdef2;
|
||||
--hl-keyword: #c678dd;
|
||||
--hl-string: #e5c07b;
|
||||
--hl-comment: #828997;
|
||||
--hl-function: #61afef;
|
||||
--hl-number: #d19a66;
|
||||
--hl-builtin: #56b6c2;
|
||||
--hl-variable: #abb2bf;
|
||||
--hl-params: #a8c0d4;
|
||||
|
||||
/* Semantic colors */
|
||||
--color-error: #ff4444;
|
||||
--color-error-light: #ff6666;
|
||||
--color-success: #4caf50;
|
||||
--color-warning: #f0ad4e;
|
||||
--color-danger: #c0392b;
|
||||
--color-recording: #ff3b30;
|
||||
--color-recording-hover: #d63031;
|
||||
--color-muted: #888;
|
||||
--color-muted-alt: #6b7280;
|
||||
--color-accent: #00aaff;
|
||||
--color-agent-active: #00ff00;
|
||||
--color-brand-blue: #3b82f6;
|
||||
--color-blind-orange: #ff9800;
|
||||
--color-save-green: var(--color-success);
|
||||
--color-link-hover: #66c7ff;
|
||||
--color-subheader: #6b8a94;
|
||||
--select-bg: var(--bg);
|
||||
--select-fg: var(--fg);
|
||||
--select-option-bg: color-mix(in srgb, var(--panel) 74%, var(--bg));
|
||||
--select-option-fg: var(--fg);
|
||||
--select-option-active-bg: color-mix(in srgb, var(--accent, var(--red)) 24%, var(--panel));
|
||||
/* Warm accent — used by the Goals/Today UI in Notes. Lives as a token so
|
||||
themes can override without touching the goal CSS. */
|
||||
--accent-warm: #d19a66;
|
||||
}
|
||||
|
||||
:root.light {
|
||||
--bg: #f5f5f5;
|
||||
--fg: #2b2b2b;
|
||||
--panel: #fff;
|
||||
--border: #bbb;
|
||||
--hl-bg: #f9f9f9;
|
||||
--hl-fg: #2b2b2b;
|
||||
--hl-keyword: #7928a1;
|
||||
--hl-string: #986801;
|
||||
--hl-comment: #6a737d;
|
||||
--hl-function: #005cc5;
|
||||
--hl-number: #986801;
|
||||
--hl-builtin: #0070a0;
|
||||
--hl-variable: #383a42;
|
||||
--hl-params: #4a4f5c;
|
||||
--select-bg: #eaeaea;
|
||||
--select-fg: var(--fg);
|
||||
--select-option-bg: var(--panel);
|
||||
--select-option-fg: var(--fg);
|
||||
--select-option-active-bg: color-mix(in srgb, var(--red) 16%, var(--panel));
|
||||
}
|
||||
|
||||
/* ── Reset & Base ── */
|
||||
|
||||
* { box-sizing: border-box; }
|
||||
html, body { overflow-x: hidden; height: 100%; margin: 0; overscroll-behavior: none; }
|
||||
body {
|
||||
background-color: var(--bg);
|
||||
color: var(--fg);
|
||||
/* Keep the base padding transition for older layout paths that still adjust
|
||||
the body directly. Edge docks reserve workspace room on the flex panes
|
||||
below so left + right docks can coexist without skewing the whole body. */
|
||||
transition: padding-left 160ms cubic-bezier(0.22, 0.61, 0.36, 1),
|
||||
padding-right 160ms cubic-bezier(0.22, 0.61, 0.36, 1);
|
||||
font-family: var(--font-family, 'Fira Code', monospace);
|
||||
display: flex;
|
||||
height: 100%;
|
||||
height: 100dvh; /* dynamic viewport height — adapts when mobile keyboard opens */
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* Self-hosted Fira Code font */
|
||||
@font-face { font-family: 'Fira Code'; font-weight: 300; font-style: normal; font-display: swap; src: url('/static/fonts/FiraCode-Light.woff2') format('woff2'); }
|
||||
@font-face { font-family: 'Fira Code'; font-weight: 400; font-style: normal; font-display: swap; src: url('/static/fonts/FiraCode-Regular.woff2') format('woff2'); }
|
||||
@font-face { font-family: 'Fira Code'; font-weight: 600; font-style: normal; font-display: swap; src: url('/static/fonts/FiraCode-SemiBold.woff2') format('woff2'); }
|
||||
|
||||
/* GohuFont is the default UI face across themes; keep the shipped font local
|
||||
so the default is available before custom-font discovery completes. */
|
||||
@font-face { font-family: 'GohuFont'; font-weight: 400; font-style: normal; font-display: swap; src: url('/static/fonts/custom/GohuFont.ttf') format('truetype'); }
|
||||
|
||||
/* Self-hosted OpenDyslexic — dyslexia-friendly accessibility font option (SIL OFL 1.1) */
|
||||
@font-face { font-family: 'OpenDyslexic'; font-weight: 400; font-style: normal; font-display: swap; src: url('/static/fonts/OpenDyslexic-Regular.woff2') format('woff2'); }
|
||||
@font-face { font-family: 'OpenDyslexic'; font-weight: 700; font-style: normal; font-display: swap; src: url('/static/fonts/OpenDyslexic-Bold.woff2') format('woff2'); }
|
||||
|
||||
/* Code block baseline */
|
||||
pre, code, .hljs {
|
||||
font-size: 11px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
/* Scrollbar styling */
|
||||
@supports selector(::-webkit-scrollbar) {
|
||||
::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
}
|
||||
::-webkit-scrollbar-track {
|
||||
background: var(--panel);
|
||||
}
|
||||
::-webkit-scrollbar-thumb {
|
||||
background-color: var(--red);
|
||||
border-radius: 4px;
|
||||
border: 2px solid var(--panel);
|
||||
}
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background-color: color-mix(in srgb, var(--red) 80%, white);
|
||||
}
|
||||
}
|
||||
|
||||
html {
|
||||
scrollbar-color: var(--red) var(--panel);
|
||||
scrollbar-width: thin;
|
||||
}
|
||||
|
||||
/* Utility */
|
||||
.red-text { color: var(--red); }
|
||||
|
||||
/* ── Density Overrides ── */
|
||||
|
||||
:root.density-compact { font-size: 13px; }
|
||||
:root.density-compact .msg { padding: 6px 10px; margin-bottom: 4px; }
|
||||
:root.density-compact .list-item { padding: 4px 8px; }
|
||||
:root.density-compact .sidebar .section { padding: 0; }
|
||||
:root.density-spacious { font-size: 16px; }
|
||||
:root.density-spacious .msg { padding: 14px 18px; margin-bottom: 12px; }
|
||||
:root.density-spacious .list-item { padding: 8px 12px; }
|
||||
:root.density-spacious .sidebar .section { padding: 0; }
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,839 @@
|
||||
/* 12-gallery - 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.
|
||||
*/
|
||||
.research-synapse.rs-phase-reading .rs-phase-chip,
|
||||
.research-synapse.rs-phase-analyzing .rs-phase-chip { color: var(--gold, #d4a72c); }
|
||||
.research-synapse.rs-phase-writing .rs-phase-chip { color: var(--color-success, #4caf50); }
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.research-synapse .rs-live-dot,
|
||||
.research-synapse .rs-pulse,
|
||||
.research-synapse .rs-node-new,
|
||||
.research-synapse .rs-node-expanded,
|
||||
.research-synapse .rs-edge-firing { animation: none !important; }
|
||||
}
|
||||
|
||||
/* ── Raw findings items (inside sources-style box) ── */
|
||||
.finding-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
}
|
||||
.finding-item .source-link {
|
||||
border-radius: 6px 6px 0 0;
|
||||
}
|
||||
.finding-summary {
|
||||
padding: 6px 8px 8px;
|
||||
font-size: 0.82em;
|
||||
line-height: 1.5;
|
||||
color: var(--fg-dim, var(--fg));
|
||||
opacity: 0.85;
|
||||
border-bottom: 1px solid color-mix(in srgb, var(--fg) 6%, transparent);
|
||||
margin-bottom: 4px;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
}
|
||||
.finding-item:last-child .finding-summary {
|
||||
border-bottom: none;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
/* ═══════════════════════════════════════════════════════════
|
||||
Gallery Editor
|
||||
═══════════════════════════════════════════════════════════ */
|
||||
|
||||
/* Tab bar */
|
||||
.gallery-tabs {
|
||||
display: flex;
|
||||
gap: 0;
|
||||
border-bottom: 1px solid var(--border);
|
||||
padding: 0 16px;
|
||||
background: var(--panel);
|
||||
}
|
||||
.gallery-tab {
|
||||
padding: 8px 18px;
|
||||
background: none;
|
||||
border: none;
|
||||
border-bottom: 2px solid transparent;
|
||||
color: var(--fg);
|
||||
opacity: 0.6;
|
||||
cursor: pointer;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
transition: opacity 0.15s, border-color 0.15s;
|
||||
}
|
||||
.gallery-tab:hover { opacity: 0.85; }
|
||||
.gallery-tab.active {
|
||||
opacity: 1;
|
||||
border-bottom-color: var(--red);
|
||||
}
|
||||
|
||||
/* Narrow modal tab strips should stay on one row. Resized docked windows can
|
||||
be much narrower than the viewport, so this cannot live only in mobile media
|
||||
queries. */
|
||||
.cookbook-tabs,
|
||||
.memory-tabs,
|
||||
.admin-tabs,
|
||||
.lib-tabs,
|
||||
.gallery-tabs,
|
||||
.preset-tabs {
|
||||
flex-wrap: nowrap !important;
|
||||
overflow-x: auto !important;
|
||||
overflow-y: hidden;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
overscroll-behavior-x: contain;
|
||||
scrollbar-width: none;
|
||||
}
|
||||
.cookbook-tabs::-webkit-scrollbar,
|
||||
.memory-tabs::-webkit-scrollbar,
|
||||
.admin-tabs::-webkit-scrollbar,
|
||||
.lib-tabs::-webkit-scrollbar,
|
||||
.gallery-tabs::-webkit-scrollbar,
|
||||
.preset-tabs::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
.cookbook-tabs > *,
|
||||
.memory-tabs > *,
|
||||
.admin-tabs > *,
|
||||
.lib-tabs > *,
|
||||
.gallery-tabs > *,
|
||||
.preset-tabs > * {
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
.cookbook-tab,
|
||||
.memory-tab,
|
||||
.admin-tab,
|
||||
.lib-tab,
|
||||
.gallery-tab,
|
||||
.preset-tab {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
white-space: nowrap;
|
||||
line-height: 1;
|
||||
}
|
||||
.gallery-tab {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
@container (max-width: 360px) {
|
||||
.cookbook-tab:has(svg),
|
||||
.memory-tab:has(svg),
|
||||
.admin-tab:has(svg),
|
||||
.lib-tab:has(svg),
|
||||
.gallery-tab:has(svg),
|
||||
.preset-tab:has(svg) {
|
||||
width: 34px;
|
||||
min-width: 34px;
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
font-size: 0;
|
||||
}
|
||||
|
||||
.cookbook-tab:has(svg) svg,
|
||||
.memory-tab:has(svg) svg,
|
||||
.admin-tab:has(svg) svg,
|
||||
.lib-tab:has(svg) svg,
|
||||
.gallery-tab:has(svg) svg,
|
||||
.preset-tab:has(svg) svg {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
margin-right: 0 !important;
|
||||
vertical-align: middle !important;
|
||||
}
|
||||
|
||||
.memory-tab:has(svg) .memory-count,
|
||||
.gallery-tab:has(svg) .gallery-tab-label,
|
||||
.gallery-tab:has(svg) .gallery-tab-close,
|
||||
.cookbook-tab:has(svg) .cookbook-tab-count,
|
||||
.preset-tab:has(svg) .preset-count {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
/* Icon + label layout inside each tab. */
|
||||
.gallery-tab {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
.gallery-tab-icon {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
opacity: 0.85;
|
||||
}
|
||||
.gallery-tab.active .gallery-tab-icon { opacity: 1; }
|
||||
/* Close × on the Edit tab — appears on hover. */
|
||||
.gallery-tab-close {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
margin-left: 2px;
|
||||
border-radius: 4px;
|
||||
opacity: 0;
|
||||
transition: opacity 0.12s, background 0.12s;
|
||||
/* Use a slightly larger glyph at a larger line-height so the × sits
|
||||
visually centered — the U+00D7 character has uneven em-box metrics. */
|
||||
font-size: 16px;
|
||||
line-height: 16px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
padding-bottom: 2px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.gallery-tab:hover .gallery-tab-close,
|
||||
.gallery-tab.active .gallery-tab-close { opacity: 0.65; }
|
||||
/* Hide the close × entirely on the Edit tab when there's no edit open. */
|
||||
.gallery-tab[data-tab="editor"]:not(.has-edit) .gallery-tab-close { display: none !important; }
|
||||
.gallery-tab-close:hover { opacity: 1 !important; background: color-mix(in srgb, var(--red) 25%, transparent); color: var(--red); }
|
||||
/* Inline rename input shown when the Edit tab is double-clicked. */
|
||||
.gallery-tab-rename-input {
|
||||
background: var(--bg);
|
||||
border: 1px solid var(--red);
|
||||
border-radius: 3px;
|
||||
color: var(--fg);
|
||||
font: inherit;
|
||||
font-size: 13px;
|
||||
padding: 1px 4px;
|
||||
width: 140px;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
/* Albums tab — grid of album cards with cover thumbnails. */
|
||||
.gallery-albums-container {
|
||||
padding: 12px 4px;
|
||||
max-height: calc(92vh - 120px);
|
||||
overflow-y: auto;
|
||||
border: 2px dashed transparent;
|
||||
border-radius: 8px;
|
||||
transition: border-color 0.15s, background 0.15s;
|
||||
}
|
||||
.gallery-settings-container {
|
||||
padding: 8px 4px;
|
||||
max-height: calc(92vh - 120px);
|
||||
overflow-y: auto;
|
||||
}
|
||||
.gallery-albums-container.gallery-dragover {
|
||||
border-color: var(--red);
|
||||
background: color-mix(in srgb, var(--red) 5%, transparent);
|
||||
}
|
||||
.gallery-albums-empty {
|
||||
display: flex; flex-direction: column; align-items: center; gap: 12px;
|
||||
padding: 48px 16px;
|
||||
opacity: 0.7;
|
||||
font-size: 13px;
|
||||
}
|
||||
.gallery-albums-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
|
||||
gap: 12px;
|
||||
}
|
||||
.gallery-album-card {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: var(--panel);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
cursor: pointer;
|
||||
transition: transform 0.12s, border-color 0.12s, box-shadow 0.12s;
|
||||
}
|
||||
.gallery-album-card:hover {
|
||||
transform: translateY(-2px);
|
||||
border-color: var(--red);
|
||||
box-shadow: 0 4px 14px color-mix(in srgb, var(--fg) 12%, transparent);
|
||||
}
|
||||
.gallery-album-menu-btn {
|
||||
position: absolute;
|
||||
top: 6px; right: 6px;
|
||||
width: 24px; height: 24px;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
background: color-mix(in srgb, var(--bg) 70%, transparent);
|
||||
color: var(--fg);
|
||||
border: 1px solid color-mix(in srgb, var(--border) 60%, transparent);
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
opacity: 0;
|
||||
transition: opacity 0.12s, background 0.12s;
|
||||
z-index: 2;
|
||||
backdrop-filter: blur(4px);
|
||||
/* Plain "…" text — the previous SVG was rendering as a flat black
|
||||
block on some themes when its currentColor didn't contrast. */
|
||||
font-size: 16px;
|
||||
line-height: 1;
|
||||
font-weight: 600;
|
||||
padding: 0 0 4px;
|
||||
}
|
||||
.gallery-album-card:hover .gallery-album-menu-btn,
|
||||
.gallery-album-menu-btn:focus-visible { opacity: 1; }
|
||||
.gallery-album-menu-btn:hover { background: var(--bg); }
|
||||
.gallery-album-menu-pop {
|
||||
position: absolute;
|
||||
top: 34px; right: 6px;
|
||||
min-width: 140px;
|
||||
background: var(--panel);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 4px 14px color-mix(in srgb, var(--fg) 18%, transparent);
|
||||
display: flex; flex-direction: column;
|
||||
padding: 4px;
|
||||
z-index: 3;
|
||||
}
|
||||
.gallery-album-menu-pop[hidden] { display: none; }
|
||||
.gallery-album-cover {
|
||||
aspect-ratio: 4 / 3;
|
||||
background: color-mix(in srgb, var(--fg) 5%, var(--bg));
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
overflow: hidden;
|
||||
}
|
||||
.gallery-album-placeholder {
|
||||
opacity: 0.4;
|
||||
font-size: 40px;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
width: 100%; height: 100%;
|
||||
}
|
||||
.gallery-album-info {
|
||||
padding: 8px 10px;
|
||||
display: flex; flex-direction: column; gap: 2px;
|
||||
}
|
||||
.gallery-album-name {
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
|
||||
}
|
||||
.gallery-album-count {
|
||||
font-size: 11px;
|
||||
opacity: 0.55;
|
||||
}
|
||||
.gallery-album-card-add {
|
||||
border-style: dashed;
|
||||
opacity: 0.7;
|
||||
}
|
||||
.gallery-album-card-add:hover { opacity: 1; }
|
||||
.gallery-album-action-tile {
|
||||
background: color-mix(in srgb, var(--fg) 3%, var(--bg));
|
||||
}
|
||||
|
||||
/* Edit-tab empty state — shown before any image is loaded. */
|
||||
.gallery-editor-landing {
|
||||
display: flex; flex-direction: column; align-items: center; justify-content: center;
|
||||
gap: 10px;
|
||||
padding: 28px 16px;
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
color: var(--fg);
|
||||
}
|
||||
.gallery-editor-landing h3 {
|
||||
margin: 0;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
}
|
||||
.gallery-editor-heading {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 7px;
|
||||
}
|
||||
.gallery-editor-heading > svg { opacity: 0.6; flex: 0 0 auto; }
|
||||
/* "vision model" link in the AI-tagging description → opens Settings. */
|
||||
.ge-vision-link {
|
||||
color: var(--accent, var(--red));
|
||||
text-decoration: underline;
|
||||
text-underline-offset: 2px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.ge-vision-link:hover { opacity: 0.8; }
|
||||
.ge-alpha-tag {
|
||||
font-size: 9px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.06em;
|
||||
text-transform: uppercase;
|
||||
vertical-align: middle;
|
||||
padding: 1px 5px;
|
||||
border-radius: 4px;
|
||||
color: var(--accent, var(--red));
|
||||
background: color-mix(in srgb, var(--accent, var(--red)) 15%, transparent);
|
||||
position: relative;
|
||||
top: -1px;
|
||||
}
|
||||
.gallery-editor-landing-actions {
|
||||
display: flex; gap: 10px;
|
||||
margin-top: 8px;
|
||||
}
|
||||
/* Bigger primary action buttons specifically inside the editor landing —
|
||||
the gallery-wide .gallery-select-btn is a compact toolbar style which is
|
||||
too small for the empty-state hero. */
|
||||
.gallery-editor-landing-actions .gallery-select-btn {
|
||||
padding: 7px 22px 17px;
|
||||
font-size: 13px;
|
||||
opacity: 0.85;
|
||||
}
|
||||
/* Visible template picker: every canvas ratio is inspectable without opening
|
||||
a native menu, and its SVG outline previews the resulting canvas shape. */
|
||||
.gallery-editor-template-picker {
|
||||
width: min(760px, 96%);
|
||||
margin-top: 10px;
|
||||
}
|
||||
.gallery-editor-template-picker h4 {
|
||||
margin: 0 0 7px;
|
||||
font-size: 11px;
|
||||
opacity: 0.55;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.06em;
|
||||
}
|
||||
.gallery-editor-template-options {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, minmax(105px, 1fr));
|
||||
gap: 6px;
|
||||
}
|
||||
.gallery-editor-template-option {
|
||||
height: 72px;
|
||||
min-width: 0;
|
||||
padding: 6px 7px;
|
||||
display: grid;
|
||||
grid-template-rows: 28px auto auto;
|
||||
justify-items: center;
|
||||
align-items: center;
|
||||
background: var(--panel);
|
||||
color: var(--fg);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 5px;
|
||||
font: inherit;
|
||||
font-size: 11px;
|
||||
cursor: pointer;
|
||||
overflow: hidden;
|
||||
}
|
||||
.gallery-editor-template-option svg { color: var(--accent, var(--red)); }
|
||||
.gallery-editor-template-option:hover,
|
||||
.gallery-editor-template-option:focus-visible {
|
||||
outline: none;
|
||||
border-color: var(--accent, var(--red));
|
||||
color: var(--accent, var(--red));
|
||||
background: color-mix(in srgb, var(--accent, var(--red)) 7%, var(--panel));
|
||||
}
|
||||
.gallery-editor-template-option span {
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
font-weight: 600;
|
||||
}
|
||||
.gallery-editor-template-option small {
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
font-size: 9px;
|
||||
opacity: 0.52;
|
||||
}
|
||||
@media (max-width: 620px) {
|
||||
.gallery-editor-landing {
|
||||
justify-content: flex-start;
|
||||
gap: 7px;
|
||||
padding: 18px 12px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.gallery-editor-landing-actions {
|
||||
width: 100%;
|
||||
gap: 6px;
|
||||
justify-content: center;
|
||||
}
|
||||
.gallery-editor-landing-actions .gallery-select-btn {
|
||||
flex: 1 1 0;
|
||||
min-width: 0;
|
||||
max-width: 180px;
|
||||
padding: 6px 8px 10px;
|
||||
font-size: 11px;
|
||||
}
|
||||
.gallery-editor-template-picker {
|
||||
width: 100%;
|
||||
margin-top: 4px;
|
||||
}
|
||||
.gallery-editor-template-options {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
/* The common square, widescreen, portrait, and Instagram presets are
|
||||
enough to start on a phone. Keep the print and 4K presets on desktop. */
|
||||
.gallery-editor-template-option:nth-child(n + 5) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
/* Saved-drafts grid on the editor landing. Surfaces every persisted
|
||||
in-progress project so the user can resume without re-opening the
|
||||
original photo. Each card: thumbnail + name + last-saved hint + ×. */
|
||||
.gallery-editor-drafts {
|
||||
width: min(720px, 92%);
|
||||
margin: 28px auto 0; /* centered */
|
||||
padding-top: 18px;
|
||||
border-top: 1px solid var(--border);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
}
|
||||
.gallery-editor-drafts {
|
||||
position: relative;
|
||||
}
|
||||
.gallery-editor-drafts-loading {
|
||||
position: absolute;
|
||||
inset: 30px 0 0 0;
|
||||
z-index: 4;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: var(--panel);
|
||||
border-radius: 6px;
|
||||
min-height: 80px;
|
||||
}
|
||||
.gallery-editor-drafts-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center; /* center the title / search / select */
|
||||
gap: 8px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.gallery-editor-drafts-title {
|
||||
margin: 0;
|
||||
font-size: 11px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.06em;
|
||||
opacity: 0.55;
|
||||
font-weight: 600;
|
||||
text-align: left;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.gallery-editor-drafts-search {
|
||||
flex: 1 1 auto;
|
||||
min-width: 0;
|
||||
max-width: none;
|
||||
height: 26px; /* same thickness as the Select button */
|
||||
box-sizing: border-box;
|
||||
padding: 0 8px;
|
||||
font-size: 12px;
|
||||
background: var(--bg);
|
||||
color: var(--fg);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 4px;
|
||||
}
|
||||
.gallery-editor-drafts-search:focus { outline: none; border-color: var(--red); }
|
||||
/* The shared .gallery-select-btn has a -4px top margin + tall asymmetric padding
|
||||
meant for the main gallery toolbar; in this header it makes the button a
|
||||
different height / offset from the search box. Normalize so it lines up. */
|
||||
.gallery-editor-drafts-header .gallery-select-btn {
|
||||
margin-top: 0 !important;
|
||||
height: 26px; /* match the search bar */
|
||||
box-sizing: border-box;
|
||||
padding: 0 10px;
|
||||
font-size: 12px;
|
||||
line-height: 1;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.cookbook-official-filter {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
flex: 0 1 auto;
|
||||
min-width: 0;
|
||||
max-width: 100%;
|
||||
color: var(--fg);
|
||||
font-size: 10px;
|
||||
opacity: 0.72;
|
||||
white-space: nowrap;
|
||||
position: relative;
|
||||
top: 1px;
|
||||
}
|
||||
.cookbook-official-filter > span:first-child {
|
||||
position: relative;
|
||||
top: 1px;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
font-size: 9px;
|
||||
}
|
||||
.cookbook-official-filter .toggle {
|
||||
width: 26px;
|
||||
height: 14px;
|
||||
}
|
||||
.cookbook-official-filter .toggle .slider:before {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
left: 2px;
|
||||
bottom: 2px;
|
||||
}
|
||||
.cookbook-official-filter .toggle input:checked + .slider:before {
|
||||
transform: translateX(12px);
|
||||
}
|
||||
#gallery-editor-drafts-select { margin-left: auto; flex-shrink: 0; }
|
||||
#gallery-editor-drafts-select.active {
|
||||
background: color-mix(in srgb, var(--accent, var(--red)) 18%, transparent);
|
||||
border-color: var(--accent, var(--red));
|
||||
color: var(--accent, var(--red));
|
||||
}
|
||||
/* Graceful exit when a project card is deleted — fade + shrink before re-render. */
|
||||
.gallery-editor-draft-card.gallery-draft-removing {
|
||||
opacity: 0;
|
||||
transform: scale(0.92);
|
||||
transition: opacity 0.24s ease, transform 0.24s ease;
|
||||
pointer-events: none;
|
||||
}
|
||||
/* Draft select dot uses the standard small .gallery-select-dot look (same as the
|
||||
photo grid) — no oversized white-ring checkbox. */
|
||||
.gallery-editor-drafts-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
|
||||
gap: 10px;
|
||||
}
|
||||
.gallery-drafts-just-opened > .gallery-editor-draft-card {
|
||||
animation: section-domino-in 0.36s cubic-bezier(0.22, 1.61, 0.36, 1) backwards;
|
||||
}
|
||||
.gallery-drafts-just-opened > :nth-child(1) { animation-delay: 0.02s; }
|
||||
.gallery-drafts-just-opened > :nth-child(2) { animation-delay: 0.04s; }
|
||||
.gallery-drafts-just-opened > :nth-child(3) { animation-delay: 0.06s; }
|
||||
.gallery-drafts-just-opened > :nth-child(4) { animation-delay: 0.08s; }
|
||||
.gallery-drafts-just-opened > :nth-child(5) { animation-delay: 0.10s; }
|
||||
.gallery-drafts-just-opened > :nth-child(6) { animation-delay: 0.12s; }
|
||||
.gallery-drafts-just-opened > :nth-child(7) { animation-delay: 0.14s; }
|
||||
.gallery-drafts-just-opened > :nth-child(8) { animation-delay: 0.16s; }
|
||||
.gallery-drafts-just-opened > :nth-child(9) { animation-delay: 0.18s; }
|
||||
.gallery-drafts-just-opened > :nth-child(10) { animation-delay: 0.20s; }
|
||||
.gallery-drafts-just-opened > :nth-child(11) { animation-delay: 0.22s; }
|
||||
.gallery-drafts-just-opened > :nth-child(12) { animation-delay: 0.24s; }
|
||||
.gallery-drafts-just-opened > :nth-child(13) { animation-delay: 0.26s; }
|
||||
.gallery-drafts-just-opened > :nth-child(14) { animation-delay: 0.28s; }
|
||||
.gallery-drafts-just-opened > :nth-child(15) { animation-delay: 0.30s; }
|
||||
.gallery-drafts-just-opened > :nth-child(16) { animation-delay: 0.32s; }
|
||||
.gallery-drafts-just-opened > :nth-child(17) { animation-delay: 0.34s; }
|
||||
.gallery-drafts-just-opened > :nth-child(18) { animation-delay: 0.36s; }
|
||||
.gallery-drafts-just-opened > :nth-child(19) { animation-delay: 0.38s; }
|
||||
.gallery-drafts-just-opened > :nth-child(20) { animation-delay: 0.40s; }
|
||||
.gallery-drafts-just-opened > :nth-child(n+21) { animation-delay: 0.42s; }
|
||||
.gallery-drafts-view-toggle {
|
||||
display: inline-flex;
|
||||
position: relative;
|
||||
top: 0;
|
||||
height: 24px;
|
||||
flex: 0 0 auto;
|
||||
overflow: hidden;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 5px;
|
||||
background: var(--bg);
|
||||
}
|
||||
.gallery-drafts-view-toggle button {
|
||||
width: 25px;
|
||||
height: 22px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: 0;
|
||||
border-right: 1px solid var(--border);
|
||||
background: transparent;
|
||||
color: color-mix(in srgb, var(--fg) 55%, transparent);
|
||||
cursor: pointer;
|
||||
line-height: 0;
|
||||
}
|
||||
.gallery-drafts-view-toggle button:last-child { border-right: 0; }
|
||||
.gallery-drafts-view-toggle button:hover { color: var(--fg); }
|
||||
.gallery-drafts-view-toggle button.active {
|
||||
height: 22px;
|
||||
color: var(--accent, var(--red));
|
||||
background: color-mix(in srgb, var(--accent, var(--red)) 12%, transparent);
|
||||
}
|
||||
.gallery-editor-drafts-grid.list-view .gallery-editor-draft-thumb {
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
aspect-ratio: auto;
|
||||
}
|
||||
.gallery-editor-drafts-grid.list-view .gallery-editor-draft-delete {
|
||||
position: static;
|
||||
opacity: 0.55;
|
||||
background: transparent;
|
||||
color: var(--fg);
|
||||
}
|
||||
.gallery-editor-draft-delete-trash { display: none; }
|
||||
.gallery-editor-drafts-grid.list-view .gallery-editor-draft-delete-trash {
|
||||
display: block;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
position: relative;
|
||||
top: -2px;
|
||||
}
|
||||
.gallery-editor-drafts-grid.list-view .gallery-editor-draft-delete:hover {
|
||||
opacity: 1;
|
||||
color: var(--color-error, #f44);
|
||||
background: color-mix(in srgb, var(--color-error, #f44) 10%, transparent);
|
||||
}
|
||||
.gallery-editor-draft-card {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
padding: 6px;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 8px;
|
||||
background: color-mix(in srgb, var(--panel) 70%, transparent);
|
||||
cursor: pointer;
|
||||
transition: border-color 0.15s, background 0.15s, transform 0.15s;
|
||||
text-align: left;
|
||||
}
|
||||
.gallery-editor-draft-card:hover {
|
||||
border-color: var(--accent, var(--red));
|
||||
background: color-mix(in srgb, var(--panel) 90%, transparent);
|
||||
}
|
||||
.gallery-editor-draft-thumb {
|
||||
width: 100%;
|
||||
aspect-ratio: 1;
|
||||
object-fit: cover;
|
||||
border-radius: 4px;
|
||||
background: var(--bg);
|
||||
display: block;
|
||||
}
|
||||
.gallery-editor-draft-thumb-empty {
|
||||
background: repeating-linear-gradient(
|
||||
45deg,
|
||||
color-mix(in srgb, var(--fg) 4%, transparent),
|
||||
color-mix(in srgb, var(--fg) 4%, transparent) 6px,
|
||||
transparent 6px,
|
||||
transparent 12px
|
||||
);
|
||||
}
|
||||
.gallery-editor-draft-info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
min-width: 0;
|
||||
}
|
||||
.gallery-editor-draft-name {
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.gallery-editor-draft-meta {
|
||||
font-size: 10px;
|
||||
opacity: 0.55;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.gallery-editor-draft-delete {
|
||||
position: absolute;
|
||||
top: -2px;
|
||||
right: 8px;
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
border-radius: 50%;
|
||||
border: none;
|
||||
background: rgba(0, 0, 0, 0.55);
|
||||
color: #fff;
|
||||
font-size: 14px;
|
||||
line-height: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
opacity: 0;
|
||||
transition: opacity 0.12s, background 0.12s;
|
||||
}
|
||||
.gallery-editor-draft-delete {
|
||||
padding-bottom: 2px;
|
||||
}
|
||||
.gallery-editor-draft-delete:hover { background: var(--red); }
|
||||
/* On very narrow phones, give project search its own row instead of
|
||||
compressing the title and view controls into an overflowing header. */
|
||||
@media (max-width: 360px) {
|
||||
.gallery-editor-drafts-header {
|
||||
flex-wrap: wrap;
|
||||
justify-content: flex-start;
|
||||
gap: 6px;
|
||||
}
|
||||
.gallery-editor-drafts-title {
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
.gallery-editor-drafts-search {
|
||||
order: 10;
|
||||
flex: 1 1 100%;
|
||||
width: 100%;
|
||||
max-width: none;
|
||||
}
|
||||
#gallery-editor-drafts-select {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
/* Editor layout */
|
||||
.gallery-editor {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
/* Prevent the editor's own scroll/touch from chaining out to the
|
||||
gallery modal or the page body — critical on mobile so touching
|
||||
the canvas doesn't slide the whole page. */
|
||||
overscroll-behavior: contain;
|
||||
touch-action: pan-y;
|
||||
}
|
||||
/* Pin the topbar to the top of the editor so its action buttons remain
|
||||
visible if the gallery modal body itself ends up scrolling. */
|
||||
.ge-topbar { position: sticky; top: 0; z-index: 5; }
|
||||
|
||||
/* Top bar */
|
||||
.ge-topbar {
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
scrollbar-width: thin;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 6px 10px;
|
||||
background: var(--panel);
|
||||
border-bottom: 1px solid var(--border);
|
||||
flex-shrink: 0;
|
||||
gap: 8px;
|
||||
}
|
||||
.ge-topbar-left, .ge-topbar-right {
|
||||
display: flex;
|
||||
flex: 0 0 auto;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
}
|
||||
.ge-topbar-right > * {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.ge-topbar .dropdown[popover] {
|
||||
position: fixed;
|
||||
margin: 0;
|
||||
bottom: auto;
|
||||
max-height: calc(100vh - 80px);
|
||||
overflow-y: auto;
|
||||
}
|
||||
.ge-draft-status {
|
||||
flex: 0 1 auto;
|
||||
min-width: 0;
|
||||
max-width: 92px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
color: color-mix(in srgb, var(--fg) 52%, transparent);
|
||||
font-size: 10px;
|
||||
line-height: 1;
|
||||
transition: color 0.15s, opacity 0.15s;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,22 +1,20 @@
|
||||
/* 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.
|
||||
* This is an eagerly loaded tail segment of the app stylesheet cascade. The
|
||||
* former static/style.css now lives in the numbered 00-17 fragments; these
|
||||
* three pre-existing panel files follow those fragments without changing
|
||||
* their relative order. Nothing here is lazy-loaded.
|
||||
*
|
||||
* LOAD ORDER IS PART OF THE CONTRACT. The <link> 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
|
||||
* LOAD ORDER IS PART OF THE CONTRACT. static/index.html and the PRECACHE list
|
||||
* in static/sw.js must agree:
|
||||
* 1. static/css/00-tokens.css through static/css/17-research.css,
|
||||
* in numeric order
|
||||
* 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.
|
||||
* Rules in this file retain their existing cascade position. Shared rules
|
||||
* formerly kept in static/style.css now live in the numbered fragments.
|
||||
*/
|
||||
|
||||
/* Cookbook already shows its own running/served-status dot
|
||||
@@ -28,10 +26,6 @@
|
||||
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);
|
||||
|
||||
@@ -1,22 +1,20 @@
|
||||
/* Document library and editor, image gallery, image editor.
|
||||
*
|
||||
* 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.
|
||||
* This is an eagerly loaded tail segment of the app stylesheet cascade. The
|
||||
* former static/style.css now lives in the numbered 00-17 fragments; these
|
||||
* three pre-existing panel files follow those fragments without changing
|
||||
* their relative order. Nothing here is lazy-loaded.
|
||||
*
|
||||
* LOAD ORDER IS PART OF THE CONTRACT. The <link> 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
|
||||
* LOAD ORDER IS PART OF THE CONTRACT. static/index.html and the PRECACHE list
|
||||
* in static/sw.js must agree:
|
||||
* 1. static/css/00-tokens.css through static/css/17-research.css,
|
||||
* in numeric order
|
||||
* 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.
|
||||
* Rules in this file retain their existing cascade position. Shared rules
|
||||
* formerly kept in static/style.css now live in the numbered fragments.
|
||||
*/
|
||||
|
||||
/* Document library search-term highlight — clear, high-contrast. */
|
||||
|
||||
@@ -1,22 +1,20 @@
|
||||
/* Email, calendar, notes and tasks panels.
|
||||
*
|
||||
* 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.
|
||||
* This is an eagerly loaded tail segment of the app stylesheet cascade. The
|
||||
* former static/style.css now lives in the numbered 00-17 fragments; these
|
||||
* three pre-existing panel files follow those fragments without changing
|
||||
* their relative order. Nothing here is lazy-loaded.
|
||||
*
|
||||
* LOAD ORDER IS PART OF THE CONTRACT. The <link> 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
|
||||
* LOAD ORDER IS PART OF THE CONTRACT. static/index.html and the PRECACHE list
|
||||
* in static/sw.js must agree:
|
||||
* 1. static/css/00-tokens.css through static/css/17-research.css,
|
||||
* in numeric order
|
||||
* 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.
|
||||
* Rules in this file retain their existing cascade position. Shared rules
|
||||
* formerly kept in static/style.css now live in the numbered fragments.
|
||||
*/
|
||||
|
||||
@keyframes email-notif-breathe {
|
||||
|
||||
+23
-6
@@ -246,15 +246,32 @@
|
||||
real request is discarded and the font fetched a second time. -->
|
||||
<link rel="preload" as="font" type="font/woff2" crossorigin href="/static/fonts/FiraCode-Regular.woff2">
|
||||
<link rel="preload" as="font" type="font/woff2" crossorigin href="/static/fonts/FiraCode-SemiBold.woff2">
|
||||
<link rel="stylesheet" href="/static/style.css?v=20260921chatgptusage2">
|
||||
<link rel="stylesheet" href="/static/css/00-tokens.css?v=20260929csssplit">
|
||||
<link rel="stylesheet" href="/static/css/01-agent-chat.css?v=20260929csssplit">
|
||||
<link rel="stylesheet" href="/static/css/02-compare.css?v=20260929csssplit">
|
||||
<link rel="stylesheet" href="/static/css/03-agent-chat.css?v=20260929csssplit">
|
||||
<link rel="stylesheet" href="/static/css/04-memory.css?v=20260929csssplit">
|
||||
<link rel="stylesheet" href="/static/css/05-documents.css?v=20260929csssplit">
|
||||
<link rel="stylesheet" href="/static/css/06-admin-settings.css?v=20260929csssplit">
|
||||
<link rel="stylesheet" href="/static/css/07-documents.css?v=20260929csssplit">
|
||||
<link rel="stylesheet" href="/static/css/08-skills.css?v=20260929csssplit">
|
||||
<link rel="stylesheet" href="/static/css/09-gallery.css?v=20260929csssplit">
|
||||
<link rel="stylesheet" href="/static/css/10-cookbook.css?v=20260929csssplit">
|
||||
<link rel="stylesheet" href="/static/css/11-tasks.css?v=20260929csssplit">
|
||||
<link rel="stylesheet" href="/static/css/12-gallery.css?v=20260929csssplit">
|
||||
<link rel="stylesheet" href="/static/css/13-image-editor.css?v=20260929csssplit">
|
||||
<link rel="stylesheet" href="/static/css/14-email.css?v=20260929csssplit">
|
||||
<link rel="stylesheet" href="/static/css/15-notes.css?v=20260929csssplit">
|
||||
<link rel="stylesheet" href="/static/css/16-calendar.css?v=20260929csssplit">
|
||||
<link rel="stylesheet" href="/static/css/17-research.css?v=20260929csssplit">
|
||||
<!-- Panel stylesheets split out of style.css for maintainability. They are
|
||||
eager and their order is load-bearing: the cascade is the concatenation of
|
||||
these files in this order, so they must stay in this order here and in the
|
||||
PRECACHE list in sw.js. None of them is lazy-loaded. -->
|
||||
<link rel="stylesheet" href="/static/css/documents-gallery-editor.css?v=20260923csssplit1">
|
||||
<link rel="stylesheet" href="/static/css/email-calendar-notes-tasks.css?v=20260923csssplit2">
|
||||
<link rel="stylesheet" href="/static/css/cookbook-research-memory-settings.css?v=20260923csssplit3">
|
||||
<link rel="modulepreload" href="/static/app.js?v=20260921chatgptusage2">
|
||||
<link rel="stylesheet" href="/static/css/documents-gallery-editor.css?v=20260929csssplit">
|
||||
<link rel="stylesheet" href="/static/css/email-calendar-notes-tasks.css?v=20260929csssplit">
|
||||
<link rel="stylesheet" href="/static/css/cookbook-research-memory-settings.css?v=20260929csssplit">
|
||||
<link rel="modulepreload" href="/static/app.js?v=20260929csssplit">
|
||||
<link rel="modulepreload" href="/static/js/chat.js?v=20260917toolttft1">
|
||||
<link rel="modulepreload" href="/static/js/ui.js?v=20260916largetoolscroll1">
|
||||
<link rel="modulepreload" href="/static/js/sessions.js">
|
||||
@@ -2639,7 +2656,7 @@
|
||||
<script type="module" src="/static/js/censor.js"></script>
|
||||
<script type="module" src="/static/js/settings.js?v=20260912writingstyle3"></script>
|
||||
<script type="module" src="/static/js/assistant.js?v=20260912firefoxjscleanup1"></script>
|
||||
<script type="module" src="/static/app.js?v=20260921chatgptusage2"></script> <!-- app.js must be LAST -->
|
||||
<script type="module" src="/static/app.js?v=20260929csssplit"></script> <!-- app.js must be LAST -->
|
||||
<script type="module" src="/static/js/init.js?v=20260829chatstyle12"></script>
|
||||
<script type="module" src="/static/js/a11y.js"></script>
|
||||
<script nonce="{{CSP_NONCE}}">if('serviceWorker' in navigator){navigator.serviceWorker.register('/static/sw.js?v=20260916autoscroll1').catch(()=>{});}</script>
|
||||
|
||||
@@ -3142,6 +3142,7 @@ function initMcpForm() {
|
||||
if (transport === 'stdio' && !command) { msg.textContent = 'Command is required for stdio'; msg.className = 'admin-error'; return; }
|
||||
if (transport === 'sse' && !url) { msg.textContent = 'URL is required for SSE'; msg.className = 'admin-error'; return; }
|
||||
try { JSON.parse(env); } catch { msg.textContent = 'Env must be valid JSON'; msg.className = 'admin-error'; return; }
|
||||
try { JSON.parse(args); } catch { msg.textContent = 'Args must be valid JSON, e.g. ["-y", "pkg"]'; msg.className = 'admin-error'; return; }
|
||||
const fd = new FormData();
|
||||
fd.append('name', name); fd.append('transport', transport); fd.append('command', command); fd.append('args', args); fd.append('env', env); fd.append('url', url);
|
||||
// If preset has oauthFile config, send credentials for file generation
|
||||
@@ -3162,6 +3163,10 @@ function initMcpForm() {
|
||||
try {
|
||||
const res = await fetch('/api/mcp/servers', { method: 'POST', body: fd, credentials: 'same-origin' });
|
||||
const data = await res.json();
|
||||
if (!res.ok) {
|
||||
msg.textContent = data.detail || `Failed (${res.status})`; msg.className = 'admin-error';
|
||||
return;
|
||||
}
|
||||
if (data.needs_oauth) {
|
||||
msg.innerHTML = `Added ${esc(name)} — <a href="/api/mcp/oauth/authorize/${data.id}" target="_blank" style="color:var(--red);font-weight:600;">Authorize with Google</a> to connect`;
|
||||
msg.className = 'admin-success';
|
||||
|
||||
+11
-2
@@ -4694,7 +4694,11 @@ async function initUnifiedIntegrations() {
|
||||
fd.append('transport', transport);
|
||||
if (transport === 'stdio') {
|
||||
fd.append('command', el('uf-mcp-cmd').value);
|
||||
let args = '[]'; try { args = JSON.stringify(JSON.parse(el('uf-mcp-args').value || '[]')); } catch (_) {}
|
||||
// Unlike env below, an unparseable args value is not silently
|
||||
// defaulted: it would spawn the subprocess with an empty argv.
|
||||
let args;
|
||||
try { args = JSON.stringify(JSON.parse(el('uf-mcp-args').value || '[]')); }
|
||||
catch (_) { el('uf-mcp-msg').textContent = 'Args must be valid JSON, e.g. ["-y", "pkg"]'; return; }
|
||||
let env = '{}'; try { env = JSON.stringify(JSON.parse(el('uf-mcp-env').value || '{}')); } catch (_) {}
|
||||
fd.append('args', args);
|
||||
fd.append('env', env);
|
||||
@@ -4716,7 +4720,12 @@ async function initUnifiedIntegrations() {
|
||||
} else if (r.ok) {
|
||||
el('uf-mcp-msg').textContent = 'Saved'; formEl.style.display = 'none'; await renderList();
|
||||
} else {
|
||||
el('uf-mcp-msg').textContent = `Failed (${r.status})`;
|
||||
// Surface the server's reason. The Args validation above rejects
|
||||
// unparseable JSON, but `"x"` and `{}` parse and are refused by
|
||||
// routes/mcp/mcp_routes.py with a message naming the expected
|
||||
// shape; a bare status code sends the user looking in the wrong
|
||||
// place. Matches what admin.js shows for the same endpoint.
|
||||
el('uf-mcp-msg').textContent = data.detail || `Failed (${r.status})`;
|
||||
}
|
||||
} catch (_) { el('uf-mcp-msg').textContent = 'Failed'; }
|
||||
finally { _setBtnLoading(saveBtn, false, _origLabel); if (cancelBtn) cancelBtn.disabled = false; }
|
||||
|
||||
@@ -40,7 +40,7 @@ export async function initDocumentWritingStyle() {
|
||||
if (msg) {
|
||||
msg.replaceChildren();
|
||||
try {
|
||||
const spinner = window.spinnerModule || (await import('./spinner.js')).default;
|
||||
const spinner = window.spinnerModule || (await import('../spinner.js')).default;
|
||||
whirlpool = spinner.createWhirlpool(14);
|
||||
whirlpool.element.style.cssText = 'display:inline-flex;width:14px;height:14px;margin-right:7px;';
|
||||
const label = document.createElement('span');
|
||||
|
||||
@@ -316,7 +316,7 @@ class SmoothPad {
|
||||
}
|
||||
|
||||
function _modal(innerHtml) {
|
||||
// Match the app's standard .modal pattern (defined in static/style.css).
|
||||
// Match the app's standard .modal pattern from the main stylesheet cascade.
|
||||
const overlay = document.createElement('div');
|
||||
overlay.className = 'modal sig-modal-overlay';
|
||||
overlay.style.cssText = 'pointer-events:auto;background:rgba(0,0,0,0.45);z-index:10100;';
|
||||
|
||||
+4
-4
@@ -106,7 +106,7 @@
|
||||
<style>
|
||||
@font-face { font-family: 'Fira Code'; font-weight: 400; font-style: normal; font-display: swap; src: url('static/fonts/FiraCode-Regular.woff2') format('woff2'); }
|
||||
@font-face { font-family: 'Fira Code'; font-weight: 600; font-style: normal; font-display: swap; src: url('static/fonts/FiraCode-SemiBold.woff2') format('woff2'); }
|
||||
/* Mirror the main app's :root defaults (static/style.css ~line 18) so an
|
||||
/* Mirror the main app's :root defaults from the stylesheet cascade so an
|
||||
uncustomized theme — or a fresh browser with no `odysseus-theme` in
|
||||
localStorage — renders the login page in the same palette as the rest
|
||||
of the app instead of a different greys+offwhite scheme. */
|
||||
@@ -115,7 +115,7 @@
|
||||
--red: #e06c75; --green: #50fa7b;
|
||||
--bg-effect-intensity: 1;
|
||||
}
|
||||
/* Background patterns — mirrors static/style.css "Background Patterns"
|
||||
/* Background patterns — mirrors the main app "Background Patterns" styles
|
||||
block so the theme's chosen bgPattern renders on login too. The
|
||||
canvas-based effects (rain, constellations, perlin-flow, petals,
|
||||
sparkles, embers) are spawned by the deferred theme.js import below;
|
||||
@@ -157,7 +157,7 @@
|
||||
font-size: 2rem; font-weight: 600;
|
||||
letter-spacing: 0.04em;
|
||||
/* Same gradient as the main app's welcome-screen "Odysseus" title
|
||||
(static/style.css #welcome-screen .welcome-name) so the brand
|
||||
(the main cascade's #welcome-screen .welcome-name) so the brand
|
||||
wordmark is consistent across login + first-launch screens. */
|
||||
background: linear-gradient(135deg, var(--brand-color, var(--red)), color-mix(in srgb, var(--brand-color, var(--red)) 60%, var(--fg)));
|
||||
-webkit-background-clip: text;
|
||||
@@ -175,7 +175,7 @@
|
||||
/* On touch devices keep inputs at >=16px so iOS Safari doesn't zoom the whole
|
||||
page when a field is focused (it auto-zooms any focused input under 16px).
|
||||
This page has its own inline styles, so it doesn't inherit the main app's
|
||||
equivalent rule in static/style.css; mirror it here. !important also lifts
|
||||
equivalent rule in the main stylesheet cascade; mirror it here. !important also lifts
|
||||
the dynamically-inserted 2FA input, which pins font-size:14px inline. */
|
||||
@media (hover: none) and (pointer: coarse) {
|
||||
input:not(.remember-check) { font-size: 16px !important; }
|
||||
|
||||
-47530
File diff suppressed because it is too large
Load Diff
+23
-6
@@ -39,11 +39,28 @@ const KATEX_FONTS = [
|
||||
// exact URL the browser requests, query string included.
|
||||
const PRECACHE = [
|
||||
'/',
|
||||
'/static/style.css?v=20260921chatgptusage2',
|
||||
'/static/css/documents-gallery-editor.css?v=20260923csssplit1',
|
||||
'/static/css/email-calendar-notes-tasks.css?v=20260923csssplit2',
|
||||
'/static/css/cookbook-research-memory-settings.css?v=20260923csssplit3',
|
||||
'/static/app.js?v=20260921chatgptusage2',
|
||||
'/static/css/00-tokens.css?v=20260929csssplit',
|
||||
'/static/css/01-agent-chat.css?v=20260929csssplit',
|
||||
'/static/css/02-compare.css?v=20260929csssplit',
|
||||
'/static/css/03-agent-chat.css?v=20260929csssplit',
|
||||
'/static/css/04-memory.css?v=20260929csssplit',
|
||||
'/static/css/05-documents.css?v=20260929csssplit',
|
||||
'/static/css/06-admin-settings.css?v=20260929csssplit',
|
||||
'/static/css/07-documents.css?v=20260929csssplit',
|
||||
'/static/css/08-skills.css?v=20260929csssplit',
|
||||
'/static/css/09-gallery.css?v=20260929csssplit',
|
||||
'/static/css/10-cookbook.css?v=20260929csssplit',
|
||||
'/static/css/11-tasks.css?v=20260929csssplit',
|
||||
'/static/css/12-gallery.css?v=20260929csssplit',
|
||||
'/static/css/13-image-editor.css?v=20260929csssplit',
|
||||
'/static/css/14-email.css?v=20260929csssplit',
|
||||
'/static/css/15-notes.css?v=20260929csssplit',
|
||||
'/static/css/16-calendar.css?v=20260929csssplit',
|
||||
'/static/css/17-research.css?v=20260929csssplit',
|
||||
'/static/css/documents-gallery-editor.css?v=20260929csssplit',
|
||||
'/static/css/email-calendar-notes-tasks.css?v=20260929csssplit',
|
||||
'/static/css/cookbook-research-memory-settings.css?v=20260929csssplit',
|
||||
'/static/app.js?v=20260929csssplit',
|
||||
'/static/js/storage.js',
|
||||
'/static/js/appConfig.js',
|
||||
'/static/js/ui.js?v=20260916largetoolscroll1',
|
||||
@@ -78,7 +95,7 @@ const PRECACHE = [
|
||||
'/static/js/theme.js?v=20260911organsrain1',
|
||||
'/static/js/censor.js',
|
||||
'/static/js/settings.js?v=20260912writingstyle3',
|
||||
'/static/js/admin.js?v=20260921chatgptusage2',
|
||||
'/static/js/admin.js?v=20260929csssplit',
|
||||
'/static/js/chatgptSubscriptionUsage.js',
|
||||
'/static/js/init.js?v=20260829chatstyle12',
|
||||
'/static/js/slashCommands.js?v=20260921chatgptusage1',
|
||||
|
||||
Reference in New Issue
Block a user