* web: pin Server/Client Settings modal header while body scrolls (#1698) Expanding enough accordion sections grew the Server Settings modal past the viewport, so the whole modal scrolled and carried the header (title + close) out of view. The header was a custom Group inside the modal body, and the modal used no `scrollAreaComponent`, so overflow was not confined to the body. Switch both the Server Settings and Client Settings modals from the default `<Modal>` (which always wraps children in `Modal.Body`) to the compound `Modal.Root`/`Modal.Content`/`Modal.Header`/`Modal.Body` API, moving the toggle/title/close row into `Modal.Header` (sticky by design) and setting `scrollAreaComponent={ScrollArea.Autosize}` so only the body scrolls. `Modal.Root` keeps the close-on-escape / close-on-click-outside / focus-trap / scroll-lock / portal defaults; `transitionProps` restores the fade-down animation the `<Modal>` wrapper otherwise supplies. Client Settings does not yet have enough content to scroll but is kept consistent per the issue. Closes #1698 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XNdjEPKLG637X8YmhDiEk5 * docs(pr-screenshots): Server Settings modal sticky-header proof (#1698) Two frames from the ServerSettingsModal story (all sections expanded, short viewport): header pinned at the top of the scroll and again scrolled down, showing it stays put while only the body scrolls. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XNdjEPKLG637X8YmhDiEk5 * web: move Modal.Root fade-down transition into the theme (#1698 review) Addresses the review note on PR #1753: the `transitionProps={{ transition: "fade-down", duration: 200 }}` literal was duplicated in both settings modals to restore the animation `<Modal>` defaults to but `Modal.Root` doesn't inherit. Move it to a `ThemeModalRoot` (`Modal.Root.extend`) registered under the `ModalRoot` theme key, mirroring the existing `ThemeScrollAreaAutosize` precedent. Both call sites drop the literal; any future compound `Modal.Root` now animates consistently with every plain `<Modal>` from one source of truth. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XNdjEPKLG637X8YmhDiEk5 --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
209 lines
4.3 KiB
TypeScript
209 lines
4.3 KiB
TypeScript
import { createTheme, type MantineColorsTuple } from "@mantine/core";
|
|
import {
|
|
ThemeAccordion,
|
|
ThemeActionIcon,
|
|
ThemeAlert,
|
|
ThemeAppShell,
|
|
ThemeAutocomplete,
|
|
ThemeBadge,
|
|
ThemeButton,
|
|
ThemeCard,
|
|
ThemeCode,
|
|
ThemeFlex,
|
|
ThemeGroup,
|
|
ThemeInput,
|
|
ThemeModal,
|
|
ThemeModalRoot,
|
|
ThemePaper,
|
|
ThemeScrollArea,
|
|
ThemeScrollAreaAutosize,
|
|
ThemeSelect,
|
|
ThemeSwitch,
|
|
ThemeText,
|
|
ThemeTextInput,
|
|
ThemeTitle,
|
|
ThemeUnstyledButton,
|
|
} from "./index";
|
|
|
|
// Accessible light/dark palette. Each tuple runs shade 0 (lightest) → 9
|
|
// (darkest) per the Mantine convention. These tuples are the single source of
|
|
// literal hex — every `--inspector-*` token in App.css references
|
|
// `--mantine-color-*`, so improving the tuples cascades through the whole UI.
|
|
const inspector: MantineColorsTuple = [
|
|
"#eef3ff",
|
|
"#dce6ff",
|
|
"#b7ccff",
|
|
"#8facfc",
|
|
"#6b8ef8",
|
|
"#4f78f3",
|
|
"#3d68ee",
|
|
"#2f59e0",
|
|
"#2749b8",
|
|
"#16265e",
|
|
];
|
|
|
|
// Cool-slate neutrals (light-mode surfaces, borders, text).
|
|
const gray: MantineColorsTuple = [
|
|
"#f7f8fa",
|
|
"#eef0f4",
|
|
"#e2e5ec",
|
|
"#cfd4de",
|
|
"#b0b7c5",
|
|
"#8d95a6",
|
|
"#6c7486",
|
|
"#515868",
|
|
"#3a3f4b",
|
|
"#23272f",
|
|
];
|
|
|
|
// Charcoal (dark-mode surfaces 9→6, text 0→2).
|
|
const dark: MantineColorsTuple = [
|
|
"#c9cdd6",
|
|
"#aeb4c0",
|
|
"#8b93a3",
|
|
"#6a7284",
|
|
"#464c5b",
|
|
"#363b47",
|
|
"#2b2f3a",
|
|
"#23262f",
|
|
"#1a1d24",
|
|
"#131519",
|
|
];
|
|
|
|
const green: MantineColorsTuple = [
|
|
"#e6f7ee",
|
|
"#c6efd7",
|
|
"#96e2b8",
|
|
"#5fd196",
|
|
"#33bd79",
|
|
"#1aa864",
|
|
"#109655",
|
|
"#0d7d47",
|
|
"#0a6339",
|
|
"#074a2b",
|
|
];
|
|
|
|
const red: MantineColorsTuple = [
|
|
"#ffecec",
|
|
"#ffd7d7",
|
|
"#ffb3b3",
|
|
"#ff8585",
|
|
"#f95f60",
|
|
"#ef4344",
|
|
"#e03236",
|
|
"#c62328",
|
|
"#a11a1f",
|
|
"#7d1418",
|
|
];
|
|
|
|
// Amber (Mantine's `yellow` slot — warning / connecting / cancelled).
|
|
const yellow: MantineColorsTuple = [
|
|
"#fff7e6",
|
|
"#ffecc2",
|
|
"#ffdb8a",
|
|
"#fbc94f",
|
|
"#f5b722",
|
|
"#e8a800",
|
|
"#d19700",
|
|
"#a87800",
|
|
"#855f00",
|
|
"#634700",
|
|
];
|
|
|
|
// Azure (Mantine's `blue` slot — info logs / running tasks).
|
|
const blue: MantineColorsTuple = [
|
|
"#e7f1ff",
|
|
"#cfe2ff",
|
|
"#a3c8ff",
|
|
"#6fa6fb",
|
|
"#4589f5",
|
|
"#2a75ee",
|
|
"#1b66e0",
|
|
"#1553bb",
|
|
"#124399",
|
|
"#0d2f6b",
|
|
];
|
|
|
|
const teal: MantineColorsTuple = [
|
|
"#e2f7f4",
|
|
"#bfeee8",
|
|
"#8adfd4",
|
|
"#4ecabb",
|
|
"#23b2a2",
|
|
"#109e8f",
|
|
"#0d8a7d",
|
|
"#0a6f65",
|
|
"#08574f",
|
|
"#06403a",
|
|
];
|
|
|
|
// Violet accent (docs / social glyphs, direction-badge incoming).
|
|
const violet: MantineColorsTuple = [
|
|
"#faf0ff",
|
|
"#f0dcff",
|
|
"#e0bbff",
|
|
"#cb91fb",
|
|
"#b264f5",
|
|
"#a03bf5",
|
|
"#9127e6",
|
|
"#7a1fc4",
|
|
"#63189e",
|
|
"#3f0f66",
|
|
];
|
|
|
|
export const theme = createTheme({
|
|
colors: {
|
|
inspector,
|
|
gray,
|
|
dark,
|
|
green,
|
|
red,
|
|
yellow,
|
|
blue,
|
|
teal,
|
|
violet,
|
|
},
|
|
/* ── Color ──────────────────────────────────────────────── */
|
|
primaryColor: "inspector",
|
|
primaryShade: { light: 7, dark: 8 },
|
|
autoContrast: true,
|
|
|
|
/* ── Shape ──────────────────────────────────────────────── */
|
|
defaultRadius: "md",
|
|
cursorType: "pointer",
|
|
|
|
/* ── Typography ─────────────────────────────────────────── */
|
|
fontFamily: '"Fredoka", sans-serif',
|
|
fontFamilyMonospace: '"Roboto Mono", monospace',
|
|
headings: {
|
|
fontFamily: '"Fredoka", sans-serif',
|
|
fontWeight: "600",
|
|
},
|
|
|
|
/* ── Component overrides ────────────────────────────────── */
|
|
components: {
|
|
Accordion: ThemeAccordion,
|
|
ActionIcon: ThemeActionIcon,
|
|
Alert: ThemeAlert,
|
|
AppShell: ThemeAppShell,
|
|
Autocomplete: ThemeAutocomplete,
|
|
Badge: ThemeBadge,
|
|
Button: ThemeButton,
|
|
Card: ThemeCard,
|
|
Code: ThemeCode,
|
|
Flex: ThemeFlex,
|
|
Group: ThemeGroup,
|
|
Input: ThemeInput,
|
|
Modal: ThemeModal,
|
|
ModalRoot: ThemeModalRoot,
|
|
Paper: ThemePaper,
|
|
ScrollArea: ThemeScrollArea,
|
|
ScrollAreaAutosize: ThemeScrollAreaAutosize,
|
|
Select: ThemeSelect,
|
|
Switch: ThemeSwitch,
|
|
Text: ThemeText,
|
|
TextInput: ThemeTextInput,
|
|
Title: ThemeTitle,
|
|
UnstyledButton: ThemeUnstyledButton,
|
|
},
|
|
});
|