a7e59a00f8
The mode select in the topbar posted with hx-post against a route that only answers PATCH, so every change returned 405 and the mode never moved. htmx shows nothing when a request fails, so the control looked like it worked: the select stayed where you put it and the server ignored you. It has never worked. Two more of the same kind. A mode could not be chosen at all until the chat existed, so reaching Plan meant sending something in Manual first and letting the model answer under the wrong rules. And the project directory box was real and submitted, but unlabelled and squeezed to a few characters by the select beside it, so it read as broken -- which is how it was reported. So the kind, the connection, the directory and the mode move out of the strip above the text and into one toolbar row beneath it, where attach and send already are. The directory becomes a button that opens a browser over SFTP, because a path is something you would rather find than spell. `scan_dir` is new beside `list_dir`: a picker has to tell a directory from a file before it can draw the row, and `list_dir` backs a tool whose contract is a list of names and must not change under a model mid-conversation. Browsing is a person clicking, not a model calling, so it does not pass through policy.py -- the same argument the terminal panel rests on. It does mean Manual mode has a second exception now. Also: .chip was two components with one name, and the attachment card won, so the Chat/Agent pills silently wore its padding. --radius-md was used twice and declared nowhere, so both fell back to 0. .btn.is-active has been set by syncToggles since the terminal landed and styled by nothing. Enter-to-send ignored isComposing, so committing an IME candidate sent the message. The terminal had five colours of a sixteen-colour palette, with fallbacks from a palette that no longer exists. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
297 lines
8.9 KiB
CSS
297 lines
8.9 KiB
CSS
/*
|
|
Design tokens.
|
|
|
|
Every colour, space and radius in the application resolves through a variable
|
|
declared here. Component CSS must never hard-code a hex value -- that is what
|
|
makes adding a theme a matter of writing one new block rather than auditing
|
|
every stylesheet.
|
|
|
|
Themes are selected with data-theme on <html>. `moria` is the default and is
|
|
declared on :root so the page is styled even before the theme script runs.
|
|
*/
|
|
|
|
:root {
|
|
/* --- Type ------------------------------------------------------------- */
|
|
--font-display: "Iowan Old Style", "Palatino Linotype", Palatino, Palladio,
|
|
"URW Palladio L", "Book Antiqua", Baskerville, Georgia, serif;
|
|
--font-body: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue",
|
|
Arial, sans-serif;
|
|
--font-mono: ui-monospace, "SF Mono", "JetBrains Mono", "Fira Code",
|
|
"Cascadia Code", Menlo, Consolas, monospace;
|
|
|
|
--text-xs: 0.75rem;
|
|
--text-sm: 0.8125rem;
|
|
--text-base: 0.9375rem;
|
|
--text-md: 1rem;
|
|
--text-lg: 1.125rem;
|
|
--text-xl: 1.375rem;
|
|
--text-2xl: 1.75rem;
|
|
--text-3xl: 2.25rem;
|
|
|
|
--leading-tight: 1.25;
|
|
--leading-normal: 1.6;
|
|
--leading-relaxed: 1.75;
|
|
|
|
/* --- Space (4px scale) ------------------------------------------------ */
|
|
--sp-1: 0.25rem;
|
|
--sp-2: 0.5rem;
|
|
--sp-3: 0.75rem;
|
|
--sp-4: 1rem;
|
|
--sp-5: 1.25rem;
|
|
--sp-6: 1.5rem;
|
|
--sp-8: 2rem;
|
|
--sp-10: 2.5rem;
|
|
--sp-12: 3rem;
|
|
--sp-16: 4rem;
|
|
|
|
/* --- Radius & shadow -------------------------------------------------- */
|
|
--radius-sm: 4px;
|
|
--radius: 8px;
|
|
--radius-md: 10px;
|
|
--radius-lg: 12px;
|
|
--radius-xl: 18px;
|
|
--radius-full: 999px;
|
|
|
|
/*
|
|
--- Controls ----------------------------------------------------------
|
|
Every button, input and select resolves its height from these. That is the
|
|
whole reason things line up: a row of mixed controls has one height, not
|
|
whatever each element's padding and font happened to add up to.
|
|
*/
|
|
--control-h: 2.25rem;
|
|
--control-h-sm: 1.75rem;
|
|
--control-h-lg: 2.75rem;
|
|
--control-px: 0.75rem;
|
|
--control-px-sm: 0.5rem;
|
|
|
|
/* --- Layout ----------------------------------------------------------- */
|
|
--sidebar-width: 17.5rem;
|
|
--inspector-width: 24rem;
|
|
/* Wider than the inspector because the content is not prose: eighty columns
|
|
of --font-mono do not fit in 24rem, and a terminal narrower than eighty
|
|
re-wraps everything a program prints. */
|
|
--terminal-width: 34rem;
|
|
--terminal-width-min: 24rem;
|
|
--thread-max-width: 48rem;
|
|
--header-height: 3.5rem;
|
|
|
|
/* The terminal's own type. xterm holds this as a number rather than reading
|
|
it from CSS, so terminal.js parses it back out -- it must stay a plain
|
|
pixel value. */
|
|
--terminal-font-size: 13px;
|
|
|
|
/*
|
|
--- Stacking ----------------------------------------------------------
|
|
These were four bare numbers scattered across two stylesheets, which is
|
|
fine until something new has to sit between two of them and nobody can
|
|
say what is already there.
|
|
*/
|
|
--z-raised: 5;
|
|
--z-handle: 10;
|
|
--z-dropdown: 30;
|
|
--z-panel: 40;
|
|
--z-overlay: 50;
|
|
--z-toast: 60;
|
|
|
|
--transition-fast: 120ms ease;
|
|
--transition: 200ms ease;
|
|
|
|
/* The focus treatment, written once. Three components spelled it out. It
|
|
resolves --accent-soft at the point of use, so it follows the theme even
|
|
though it is declared above them. */
|
|
--ring: 0 0 0 3px var(--accent-soft);
|
|
}
|
|
|
|
/*
|
|
---------------------------------------------------------------------------
|
|
MORIA (default, dark)
|
|
|
|
Deep stone and lamplight: the halls under the mountain. Surfaces are cool and
|
|
near-neutral so the mallorn and mithril accents carry all the colour.
|
|
---------------------------------------------------------------------------
|
|
*/
|
|
:root,
|
|
:root[data-theme="moria"] {
|
|
color-scheme: dark;
|
|
|
|
--bg: #101317;
|
|
--bg-sunken: #0B0E11;
|
|
--surface: #171B21;
|
|
--surface-raised: #1E242B;
|
|
--surface-hover: #232A32;
|
|
--surface-active: #2A323B;
|
|
|
|
--border: #2A313A;
|
|
--border-strong: #3A434E;
|
|
|
|
--ink: #E4E8EC;
|
|
--ink-muted: #A2ADB8;
|
|
--ink-faint: #6E7883;
|
|
--ink-inverse: #0B0E11;
|
|
|
|
/* Mithril: the cool primary, used for focus and interactive accents. */
|
|
--accent: #8FB3CC;
|
|
--accent-hover: #A9C6DA;
|
|
--accent-ink: #0B0E11;
|
|
--accent-soft: rgba(143, 179, 204, 0.14);
|
|
|
|
/*
|
|
Mallorn: the brand accent, and the assistant's mark. A yellow-leaning leaf
|
|
green, so it stays warm against the mithril blue rather than turning the
|
|
palette into two cool accents that compete.
|
|
*/
|
|
--leaf: #9BCC5A;
|
|
--leaf-hover: #B1DD74;
|
|
--leaf-soft: rgba(155, 204, 90, 0.14);
|
|
|
|
/* Ember: destructive actions and errors. */
|
|
--danger: #E2795A;
|
|
--danger-hover: #EC8E72;
|
|
--danger-soft: rgba(226, 121, 90, 0.14);
|
|
|
|
/*
|
|
Success leans teal rather than leaf. Two greens a hue apart read as one
|
|
colour rendered inconsistently -- an unread dot beside a brand badge has to
|
|
be tellable from it at a glance.
|
|
*/
|
|
--success: #5FBFA0;
|
|
--success-soft: rgba(95, 191, 160, 0.14);
|
|
--warning: #DFAE58;
|
|
--warning-soft: rgba(223, 174, 88, 0.14);
|
|
|
|
--bubble-user: #232B34;
|
|
--bubble-assistant: transparent;
|
|
--code-bg: #0C0F13;
|
|
--code-border: #262D36;
|
|
|
|
--shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.4);
|
|
--shadow: 0 4px 14px rgba(0, 0, 0, 0.45);
|
|
--shadow-lg: 0 12px 34px rgba(0, 0, 0, 0.55);
|
|
|
|
--scrim: rgba(6, 8, 10, 0.66);
|
|
|
|
/*
|
|
The sixteen ANSI colours, for the terminal panel.
|
|
|
|
A shell picks its own colours -- `ls --color`, a git diff, htop's meters --
|
|
and until these existed it got xterm's defaults, which are a different
|
|
palette from this one and read as a foreign window pasted into the
|
|
application. Where a slot has an obvious counterpart above it takes it, so
|
|
an error in the terminal is the same ember as an error anywhere else.
|
|
|
|
"black" is not #000: it is what a program picks for a dim background or
|
|
faint text, and on a near-black surface a true black is invisible.
|
|
*/
|
|
--ansi-black: #1A1F26;
|
|
--ansi-red: #E2795A;
|
|
--ansi-green: #9BCC5A;
|
|
--ansi-yellow: #DFAE58;
|
|
--ansi-blue: #8FB3CC;
|
|
--ansi-magenta: #C08FCC;
|
|
--ansi-cyan: #5FBFA0;
|
|
--ansi-white: #C6CDD4;
|
|
--ansi-bright-black: #4A535E;
|
|
--ansi-bright-red: #EC8E72;
|
|
--ansi-bright-green: #B1DD74;
|
|
--ansi-bright-yellow: #EFC87A;
|
|
--ansi-bright-blue: #A9C6DA;
|
|
--ansi-bright-magenta: #D4A9DE;
|
|
--ansi-bright-cyan: #7FD4BA;
|
|
--ansi-bright-white: #E4E8EC;
|
|
}
|
|
|
|
/*
|
|
---------------------------------------------------------------------------
|
|
SHIRE (light)
|
|
|
|
Parchment, ink and moss: warm, low-contrast, easy to read for a long time.
|
|
Backgrounds are deliberately off-white -- pure #FFF next to the leaf accent
|
|
reads as clinical rather than as paper.
|
|
---------------------------------------------------------------------------
|
|
*/
|
|
:root[data-theme="shire"] {
|
|
color-scheme: light;
|
|
|
|
--bg: #F6F1E4;
|
|
--bg-sunken: #EDE6D4;
|
|
--surface: #FDFBF5;
|
|
--surface-raised: #FFFFFF;
|
|
--surface-hover: #F1EADA;
|
|
--surface-active: #E7DEC9;
|
|
|
|
--border: #DED3BB;
|
|
--border-strong: #C6B896;
|
|
|
|
--ink: #2C2419;
|
|
--ink-muted: #6A5C48;
|
|
--ink-faint: #94856D;
|
|
--ink-inverse: #FDFBF5;
|
|
|
|
/* Hobbit-door blue-green: the cool primary. */
|
|
--accent: #3E6B7A;
|
|
--accent-hover: #325867;
|
|
--accent-ink: #FDFBF5;
|
|
--accent-soft: rgba(62, 107, 122, 0.12);
|
|
|
|
/* The same mallorn, darkened until it holds its own as text on parchment. */
|
|
--leaf: #4C7A22;
|
|
--leaf-hover: #3C6318;
|
|
--leaf-soft: rgba(76, 122, 34, 0.13);
|
|
|
|
--danger: #A6432B;
|
|
--danger-hover: #8C3722;
|
|
--danger-soft: rgba(166, 67, 43, 0.11);
|
|
|
|
--success: #2C7360;
|
|
--success-soft: rgba(44, 115, 96, 0.12);
|
|
--warning: #98701A;
|
|
--warning-soft: rgba(152, 112, 26, 0.13);
|
|
|
|
--bubble-user: #EDE4CF;
|
|
--bubble-assistant: transparent;
|
|
--code-bg: #F2EBD9;
|
|
--code-border: #DED3BB;
|
|
|
|
--shadow-sm: 0 1px 2px rgba(72, 58, 34, 0.09);
|
|
--shadow: 0 4px 14px rgba(72, 58, 34, 0.11);
|
|
--shadow-lg: 0 12px 34px rgba(72, 58, 34, 0.16);
|
|
|
|
--scrim: rgba(44, 36, 25, 0.4);
|
|
|
|
/*
|
|
The same sixteen on parchment, and the reason they are not the Moria set
|
|
lightened: every one of these has to be readable as *text* on #F2EBD9, so
|
|
the whole palette is darkened rather than brightened. "bright" therefore
|
|
means more emphatic here, not lighter -- a light terminal theme that made
|
|
bright yellow actually bright would render it invisible.
|
|
*/
|
|
--ansi-black: #2C2419;
|
|
--ansi-red: #A6432B;
|
|
--ansi-green: #4C7A22;
|
|
--ansi-yellow: #98701A;
|
|
--ansi-blue: #3E6B7A;
|
|
--ansi-magenta: #7A3E6B;
|
|
--ansi-cyan: #2C7360;
|
|
--ansi-white: #6A5C48;
|
|
--ansi-bright-black: #94856D;
|
|
--ansi-bright-red: #8C3722;
|
|
--ansi-bright-green: #3C6318;
|
|
--ansi-bright-yellow: #7A5A14;
|
|
--ansi-bright-blue: #325867;
|
|
--ansi-bright-magenta: #63325A;
|
|
--ansi-bright-cyan: #235C4D;
|
|
--ansi-bright-white: #453A2A;
|
|
}
|
|
|
|
/* Respect a stated preference for reduced motion everywhere, at once. */
|
|
@media (prefers-reduced-motion: reduce) {
|
|
*,
|
|
*::before,
|
|
*::after {
|
|
animation-duration: 0.01ms !important;
|
|
animation-iteration-count: 1 !important;
|
|
transition-duration: 0.01ms !important;
|
|
scroll-behavior: auto !important;
|
|
}
|
|
}
|