diff --git a/docs/frontend_architecture.md b/docs/frontend_architecture.md index ee56725..85dadf7 100644 --- a/docs/frontend_architecture.md +++ b/docs/frontend_architecture.md @@ -126,9 +126,8 @@ The application uses a **single-page template** (`index.html`) with tab-based na ┌─────────────────────────────────────────────────────────┐ │ Header (site-header) │ │ ├── Brand (icon + title + subtitle) │ -│ └── Ticker Badge (when analysis active) │ -├─────────────────────────────────────────────────────────┤ -│ Parameters bar [ticker] [Run] (collapse ▸/▾) │ ← sticky, not a tab +│ ├── Parameters bar [ticker] [Run] │ ← not a tab, always visible +│ └── Theme toggle │ ├─────────────────────────────────────────────────────────┤ │ Sidebar │ Main Panel │ │ (tab-nav) │ (tab-content) │ @@ -144,17 +143,17 @@ The application uses a **single-page template** (`index.html`) with tab-based na └───────────────┴─────────────────────────────────────────┘ ``` -The **Parameters bar** (`templates/partials/parameters_bar.html`, batch B6) renders -between the header and `.app-body`, is `position: sticky` under the header, and owns -exactly one visible input — `ticker` — plus the Run button and the ticker-validation -badges. Every other parameter lives in its module's toolbar (batch B7); the bar -also carries two hidden `start_time`/`end_time` inputs that `POST /` validates and -uses to size the readiness prefetch, kept in sync by `state/marketParamsState.js`. -It is **not** a tab: it survives tab switches. Collapsing it (the chevron toggle; -state persisted per viewer under `localStorage['parametersBarCollapsed']`, guarded -`try/catch`) hides the `#parameters-bar-body` fields group and the validation line, -leaving the toggle, a one-line `▸ ^SPX` summary, and Run. The chevron is an inline -SVG (Font Awesome is not loaded on this page) rotated by `[data-collapsed]`. +The **Parameters bar** (`templates/partials/parameters_bar.html`, batch B6; folded into +the header post-B10) renders inside `.site-header`, between the brand and the theme +toggle, and owns exactly one visible input — `ticker` — plus the Run button and the +ticker-validation badges. Every other parameter lives in its module's toolbar (batch +B7); the bar also carries two hidden `start_time`/`end_time` inputs that `POST /` +validates and uses to size the readiness prefetch, kept in sync by +`state/marketParamsState.js`. It is **not** a tab: it survives tab switches, and it is +always visible — there is no collapse toggle. Because `.site-header` stays dark in both +themes, the bar's own controls (label, input) use light-on-dark styling rather than the +page's light-theme tokens; the ticker-validation badges and the error alert keep their +own self-contained colors, so they read the same as before. ### Peek Sidebar diff --git a/static/parametersBar.js b/static/parametersBar.js deleted file mode 100644 index 19dda8c..0000000 --- a/static/parametersBar.js +++ /dev/null @@ -1,85 +0,0 @@ -/* parametersBar.js — persistent Parameters bar: collapse state + summary. - * - * Contract (docs/frontend_architecture.md, batch B6): - * - the bar owns the shared `ticker` input and the Run button; it is NOT a tab, - * so it stays visible while the user switches tabs; - * - collapsing is a per-viewer convenience persisted in localStorage, and the - * collapsed bar shows the current ticker as `▸ ^SPX`; - * - CONSTRAINT: every localStorage access is guarded — private mode / disabled - * storage must degrade to "not persisted", never throw (no build step, no - * polyfills; see ADR 0006). - */ -(function (root) { - 'use strict'; - - var STORAGE_KEY = 'parametersBarCollapsed'; - var BAR_SELECTOR = '.parameters-bar'; - - function _read() { - try { - return root.localStorage.getItem(STORAGE_KEY); - } catch (_) { - return null; - } - } - - function _write(value) { - try { - root.localStorage.setItem(STORAGE_KEY, value); - } catch (_) { - /* not fatal: the bar just will not remember its state */ - } - } - - function setCollapsed(bar, collapsed) { - bar.dataset.collapsed = collapsed ? 'true' : 'false'; - var toggle = document.getElementById('parameters-bar-toggle'); - if (!toggle) return; - toggle.setAttribute('aria-expanded', collapsed ? 'false' : 'true'); - // The chevron is an inline SVG rotated by CSS via [data-collapsed]; only - // the label needs updating here. - toggle.title = collapsed ? 'Expand parameters' : 'Collapse parameters'; - } - - function updateSummary(bar) { - var summary = document.getElementById('parameters-bar-summary'); - var input = document.getElementById('ticker'); - if (!summary || !input) return; - var value = (input.value || '').trim(); - summary.textContent = value ? '▸ ' + value : ''; - } - - function init() { - var bar = document.querySelector(BAR_SELECTOR); - if (!bar) return; - - setCollapsed(bar, _read() === 'true'); - - var toggle = document.getElementById('parameters-bar-toggle'); - if (toggle) { - toggle.addEventListener('click', function () { - var next = bar.dataset.collapsed !== 'true'; - setCollapsed(bar, next); - _write(next ? 'true' : 'false'); - }); - } - - var input = document.getElementById('ticker'); - if (input) input.addEventListener('input', function () { updateSummary(bar); }); - updateSummary(bar); - } - - if (document.readyState === 'loading') { - document.addEventListener('DOMContentLoaded', init); - } else { - init(); - } - - // Exposed for the jsdom unit tests (no bundler; plain global, like theme.js). - root.parametersBar = { - init: init, - setCollapsed: setCollapsed, - updateSummary: updateSummary, - STORAGE_KEY: STORAGE_KEY, - }; -})(typeof window !== 'undefined' ? window : this); diff --git a/static/styles.css b/static/styles.css index 340c311..a1cb8c1 100644 --- a/static/styles.css +++ b/static/styles.css @@ -149,7 +149,7 @@ body { top: 0; z-index: 200; background: var(--slate-900); - height: var(--header-h); + min-height: var(--header-h); display: flex; align-items: center; box-shadow: 0 1px 0 rgba(255, 255, 255, .06), var(--shadow-md); @@ -159,11 +159,12 @@ body { width: 100%; max-width: 1600px; margin: 0 auto; - padding: 0 1.5rem; + padding: .6rem 1.5rem; display: flex; + flex-wrap: wrap; align-items: center; justify-content: space-between; - gap: 1rem; + gap: .6rem 1rem; } .header-brand { @@ -200,29 +201,12 @@ body { font-weight: 400; } -.header-badge { - display: flex; - flex-direction: column; - align-items: flex-end; - gap: 2px; -} - -.badge-ticker { - background: var(--blue); - color: #fff; - padding: 2px 10px; - border-radius: 999px; - font-size: .8rem; - font-weight: 700; - letter-spacing: .04em; -} - .badge-meta { font-size: .7rem; color: var(--slate-300); } -/* ── Header actions (ticker badge + theme toggle) ── */ +/* ── Header actions (theme toggle) ── */ .header-actions { display: flex; align-items: center; @@ -1131,6 +1115,13 @@ body { .badge-meta { display: none; } + + /* Brand + theme toggle share the first line; the ticker form (flex-basis + 100%) is forced onto its own line below, same as the old separate bar. */ + .parameters-bar { + order: 3; + flex-basis: 100%; + } } @media (max-width: 480px) { @@ -3174,63 +3165,30 @@ textarea:focus-visible, /* ============================================================ - Parameters bar (batch B6) — persistent `ticker` bar above the panes. - Not a tab: it stays put across tab switches (sticky under the header) - and collapses to a one-line summary (`▸ ^SPX`). Tokens only, so the - Onyx override layer below themes it for free. + Parameters bar — the `ticker` input, lives inside the header + (.site-header is dark in both themes, see the Onyx override layer), + so its controls use light-on-dark styling rather than the page's + light-theme tokens. Not a tab: it stays put across tab switches, and + it is always visible — no collapse toggle. ============================================================ */ .parameters-bar { - position: sticky; - top: var(--header-h); - z-index: 100; display: flex; flex-direction: column; - gap: 6px; - max-width: 1600px; - margin: 0 auto; - padding: 10px 1.5rem; - background: var(--slate-50); - border-bottom: 1px solid var(--slate-200); + gap: 4px; + flex: 1 1 320px; + min-width: 0; font-family: var(--font); } .parameters-bar-main { display: flex; + flex-wrap: wrap; align-items: center; gap: 10px; } -.parameters-bar-toggle { - display: inline-flex; - align-items: center; - background: transparent; - border: 1px solid var(--slate-200); - border-radius: var(--radius-sm); - color: var(--slate-700); - cursor: pointer; - padding: 5px 9px; - line-height: 1; -} - -.parameters-bar-chevron { - display: block; - transition: transform .15s ease; -} - -.parameters-bar[data-collapsed="true"] .parameters-bar-chevron { - transform: rotate(-90deg); -} - -/* The collapsible region: label + ticker input + validation badges. */ -.parameters-bar-fields { - display: flex; - align-items: center; - gap: 10px; - min-width: 0; -} - .parameters-bar-label { - color: var(--slate-700); + color: var(--slate-300); font-size: 12px; font-weight: 600; letter-spacing: .02em; @@ -3238,36 +3196,36 @@ textarea:focus-visible, } .parameters-bar-ticker { - flex: 0 1 320px; - min-width: 180px; + flex: 1 1 220px; + min-width: 140px; + max-width: 320px; padding: 6px 10px; - border: 1px solid var(--slate-200); + border: 1px solid rgb(255 255 255 / .25); border-radius: var(--radius-sm); + background: rgb(255 255 255 / .08); + color: #fff; font-family: inherit; font-size: 14px; } -.parameters-bar-summary { - color: var(--slate-500); - font-size: 13px; - font-variant-numeric: tabular-nums; -} - -/* Collapsed → only the toggle, the one-line summary and Run remain. Expanded → - the input already shows the ticker, so the summary is redundant. */ -.parameters-bar[data-collapsed="true"] .parameters-bar-fields, -.parameters-bar[data-collapsed="true"] .ticker-validation { - display: none; +.parameters-bar-ticker::placeholder { + color: var(--slate-400); } -.parameters-bar[data-collapsed="false"] .parameters-bar-summary { - display: none; +.parameters-bar-ticker:focus { + outline: 2px solid var(--blue); + outline-offset: 1px; + background: rgb(255 255 255 / .12); } .parameters-bar-alert { margin: 0; } +.parameters-bar .ticker-validation { + color: var(--slate-300); +} + .parameters-bar .btn-primary { margin-left: auto; } @@ -3417,11 +3375,6 @@ textarea:focus-visible, border: 1px solid var(--blue-muted); } -:root:not([data-theme="light"]) .badge-ticker { - background: var(--blue); - color: #0a0a0a; -} - /* All surfaces that were hardcoded #fff → onyx surface-1 */ :root:not([data-theme="light"]) .tab-nav, :root:not([data-theme="light"]) .form-card, diff --git a/templates/index.html b/templates/index.html index 087584d..1681b99 100644 --- a/templates/index.html +++ b/templates/index.html @@ -105,20 +105,18 @@

Market Dashboard

Options Strategy & Market Analysis

+ + {# The `ticker` input lives in the header now: it is the one input + every data module shares, plus Run and validation feedback. Not + a tab, so it survives tab switches; always visible — no collapse + toggle. Module params moved to the tab toolbars in B7. #} + {% include 'partials/parameters_bar.html' %} +
- {% if ticker %} - {# Analysis parameters are per-module now (batch B7): frequency / - horizon / side-bias differ per tab, so there is no single - value to show here. The badge is just the active ticker. #} -
- {{ ticker }} -
- {% endif %} + mobile. Icons show the CURRENT theme (dark → moon, light → + sun; CSS swaps them per theme), while the aria-label + describes the action. -->
- {# Persistent Parameters bar (batch B6): above the panes so it survives tab - switches. Owns `ticker` + Run; module params move to the tab toolbars in B7. #} - {% include 'partials/parameters_bar.html' %} -