feat(*): svelte 5 panel view sharing the gateway ui, obsidian theme, preview and tests

This commit is contained in:
hh
2026-08-29 18:30:31 +02:00
parent a1bf4db905
commit 04400c5076
33 changed files with 3378 additions and 1803 deletions
+365
View File
@@ -0,0 +1,365 @@
@layer theme, base, components, utilities;
@import "tailwindcss/theme.css" layer(theme);
@import "tailwindcss/utilities.css" layer(utilities) important;
@import "../../beaver-gateway/ui/src/lib/panel/panel.css";
@source "./";
@source "../../beaver-gateway/ui/src/lib";
@custom-variant dark (&:where(.theme-dark, .theme-dark *));
/* The panel's tokens are Obsidian's own variables, exactly as beaver-calendar
maps them onto .bcal-root: the theme, the accent, the font and the radii
come from the app and the user's theme. The msos palette never enters. */
.beaver-root {
--background: var(--background-primary);
--foreground: var(--text-normal);
--card: var(--background-primary-alt);
--card-foreground: var(--text-normal);
--popover: var(--background-secondary);
--popover-foreground: var(--text-normal);
--primary: var(--color-accent);
--primary-foreground: var(--text-on-accent);
--secondary: var(--background-modifier-hover);
--secondary-foreground: var(--text-normal);
--muted: var(--background-modifier-hover);
--muted-foreground: var(--text-muted);
--accent: var(--background-modifier-hover);
--accent-foreground: var(--text-normal);
--destructive: var(--text-error);
--destructive-foreground: var(--text-on-accent);
--border: var(--background-modifier-border);
--input: var(--background-modifier-border);
--ring: var(--color-accent);
--icon: var(--text-muted);
--signal: var(--color-accent);
--link: var(--link-color, var(--text-accent));
--note: oklch(0.52 0.13 78);
--warn: oklch(0.55 0.13 75);
--status-new: var(--color-accent);
--status-done: oklch(0.55 0.14 155);
--status-skip: var(--text-faint);
--status-reply: oklch(0.55 0.14 235);
--status-snooze: oklch(0.66 0.13 75);
--status-meeting: oklch(0.5 0.2 300);
--status-work: oklch(0.55 0.13 195);
--sidebar: var(--background-secondary);
--sidebar-accent: var(--background-modifier-hover);
--radius: 0.45rem;
}
.theme-dark .beaver-root {
--note: oklch(0.84 0.14 88);
--warn: oklch(0.8 0.13 75);
--status-done: oklch(0.74 0.14 155);
--status-reply: oklch(0.72 0.13 235);
--status-snooze: oklch(0.8 0.13 75);
--status-meeting: oklch(0.72 0.16 300);
--status-work: oklch(0.75 0.12 195);
}
@theme inline {
--font-sans: var(--font-interface);
--text-xs: 0.75rem;
--text-sm: 0.8125rem;
--text-base: 0.875rem;
--text-lg: 1rem;
--text-xl: 1.25rem;
--text-2xl: 1.5rem;
--color-icon: var(--icon);
--color-signal: var(--signal);
--color-note: var(--note);
--color-sidebar: var(--sidebar);
--color-sidebar-accent: var(--sidebar-accent);
--color-status-new: var(--status-new);
--color-status-done: var(--status-done);
--color-status-skip: var(--status-skip);
--color-status-reply: var(--status-reply);
--color-status-snooze: var(--status-snooze);
--color-status-meeting: var(--status-meeting);
--color-status-work: var(--status-work);
--color-ring: var(--ring);
--color-input: var(--input);
--color-border: var(--border);
--color-destructive-foreground: var(--destructive-foreground);
--color-destructive: var(--destructive);
--color-accent-foreground: var(--accent-foreground);
--color-accent: var(--accent);
--color-muted-foreground: var(--muted-foreground);
--color-muted: var(--muted);
--color-secondary-foreground: var(--secondary-foreground);
--color-secondary: var(--secondary);
--color-primary-foreground: var(--primary-foreground);
--color-primary: var(--primary);
--color-popover-foreground: var(--popover-foreground);
--color-popover: var(--popover);
--color-card-foreground: var(--card-foreground);
--color-card: var(--card);
--color-foreground: var(--foreground);
--color-background: var(--background);
--radius-sm: calc(var(--radius) * 0.6);
--radius-md: calc(var(--radius) * 0.8);
--radius-lg: var(--radius);
--radius-xl: calc(var(--radius) * 1.4);
--radius-2xl: calc(var(--radius) * 1.8);
}
.beaver-root {
height: 100%;
font-family: var(--font-interface);
font-size: var(--text-sm);
color: var(--foreground);
background: var(--background);
}
/* Portal layer for menus and dialogs: a zero-sized anchor at body level, above
the sidedocks, carrying the theme aliases so popovers stay on theme. */
.beaver-portal {
position: fixed;
top: 0;
left: 0;
z-index: var(--layer-menu, 65);
width: 0;
height: 0;
background: transparent;
}
.beaver-root.beaver-root *,
.beaver-root.beaver-root *::before,
.beaver-root.beaver-root *::after {
box-sizing: border-box;
border: 0 solid var(--border);
}
.beaver-root.beaver-root
:is(h1, h2, h3, h4, h5, h6, p, figure, blockquote, dl, dd) {
margin: 0;
font-size: inherit;
font-weight: inherit;
line-height: inherit;
}
.beaver-root.beaver-root :is(ul, ol) {
padding: 0;
margin: 0;
list-style: none;
}
.beaver-root.beaver-root :is(button, input, select, textarea) {
height: auto;
min-height: 0;
padding: 0;
margin: 0;
font: inherit;
color: inherit;
letter-spacing: inherit;
-webkit-appearance: none;
appearance: none;
outline: none;
background: transparent;
border: 0 solid var(--border);
border-radius: 0;
box-shadow: none;
}
.beaver-root.beaver-root :is(button, input, select, textarea):hover,
.beaver-root.beaver-root :is(button, input, select, textarea):focus,
.beaver-root.beaver-root :is(button, input, select, textarea):active {
box-shadow: none;
}
.beaver-root.beaver-root button {
text-align: inherit;
cursor: pointer;
}
.beaver-root.beaver-root button:hover {
background: transparent;
}
.beaver-root.beaver-root button:disabled {
cursor: default;
}
.beaver-root.beaver-root input::placeholder,
.beaver-root.beaver-root textarea::placeholder {
color: var(--muted-foreground);
}
.beaver-root.beaver-root :is(input, textarea) {
caret-color: var(--primary);
}
.beaver-root.beaver-root :focus-visible {
outline: 2px solid var(--ring);
outline-offset: 2px;
}
.beaver-root.beaver-root svg {
display: block;
flex-shrink: 0;
}
.beaver-root.beaver-root a,
.beaver-root.beaver-root a:hover {
color: inherit;
text-decoration: inherit;
}
.beaver-root.beaver-root img {
display: block;
max-width: 100%;
height: auto;
}
.beaver-root.beaver-root :is(code, pre, kbd) {
font-family: var(--font-monospace);
font-size: 0.8125rem;
}
/* Chat text rendered by Obsidian itself, kept compact for a sidedock. */
.beaver-root.beaver-root .beaver-md {
font-size: var(--text-sm);
line-height: 1.5;
}
.beaver-root.beaver-root .beaver-md :is(p, ul, ol, pre, blockquote, table) {
margin-block: 0.4em;
}
.beaver-root.beaver-root .beaver-md :is(h1, h2, h3, h4, h5, h6) {
margin-block: 0.8em 0.4em;
font-size: 1em;
font-weight: 600;
}
.beaver-root.beaver-root .beaver-md :is(ul, ol) {
padding-left: 1.25em;
list-style: disc;
}
.beaver-root.beaver-root .beaver-md ol {
list-style: decimal;
}
.beaver-root.beaver-root .beaver-md li + li {
margin-top: 0.15em;
}
.beaver-root.beaver-root .beaver-md a {
color: var(--link);
text-decoration: none;
}
.beaver-root.beaver-root .beaver-md a:hover {
text-decoration: underline;
}
.beaver-root.beaver-root .beaver-md a.is-unresolved {
color: var(--link-unresolved-color, var(--muted-foreground));
opacity: 0.75;
}
.beaver-root.beaver-root .beaver-md code {
padding: 0.1em 0.3em;
font-size: 0.9em;
font-weight: 500;
background: color-mix(in oklch, var(--muted) 70%, transparent);
border-radius: var(--radius-sm);
}
.beaver-root.beaver-root .beaver-md pre {
padding: 0.6em 0.8em;
overflow: auto;
background: var(--muted);
border-radius: var(--radius-md);
}
.beaver-root.beaver-root .beaver-md pre code {
padding: 0;
font-weight: 400;
background: transparent;
}
.beaver-root.beaver-root .beaver-md blockquote {
padding-left: 0.75em;
color: var(--muted-foreground);
border-left: 1px solid var(--border);
}
.beaver-root.beaver-root .beaver-md > :first-child {
margin-top: 0;
}
.beaver-root.beaver-root .beaver-md > :last-child {
margin-bottom: 0;
}
/* The luma primitives are pill-shaped for the browser; inside Obsidian every
control takes the app's own corner language, like beaver-calendar's. */
@layer utilities {
/* Utilities are !important here, so the hidden attribute needs its own weight. */
.beaver-root [hidden] {
display: none !important;
}
.beaver-root [data-slot="button"],
.beaver-root [data-slot="select-trigger"] {
border-radius: var(--button-radius, var(--radius-s)) !important;
}
.beaver-root [data-slot="input"],
.beaver-root [data-slot="tabs-trigger"],
.beaver-root [data-slot$="-item"] {
border-radius: var(--input-radius, var(--radius-s)) !important;
}
.beaver-root [data-slot="tabs-list"],
.beaver-root [data-slot="skeleton"] {
border-radius: var(--radius-m, 8px) !important;
}
/* The luma tabs mark the active trigger with the page colour, which on a
dark Obsidian theme is the same as the surface behind the list. */
.beaver-root [data-slot="tabs-list"] {
background: color-mix(
in oklab,
var(--text-normal) 6%,
transparent
) !important;
}
.beaver-root [data-slot="tabs-trigger"][data-state="active"] {
color: var(--text-normal) !important;
background: color-mix(
in oklab,
var(--text-normal) 14%,
transparent
) !important;
}
.beaver-root [data-slot="dialog-content"],
.beaver-root [data-slot="popover-content"],
.beaver-root [data-slot="dropdown-menu-content"],
.beaver-root [data-slot="context-menu-content"],
.beaver-root [data-slot="select-content"] {
border: 1px solid var(--border) !important;
border-radius: var(--radius-m, 8px) !important;
box-shadow: var(--shadow-s) !important;
}
.beaver-root [data-slot="dropdown-menu-content"],
.beaver-root [data-slot="context-menu-content"],
.beaver-root [data-slot="select-content"] {
background: var(--popover) !important;
}
.beaver-root [data-slot="dropdown-menu-content"]::before,
.beaver-root [data-slot="context-menu-content"]::before,
.beaver-root [data-slot="select-content"]::before {
display: none !important;
}
.beaver-root [data-slot="button"][data-variant="default"],
.beaver-root [data-slot="button"]:not([class*="bg-"]) {
font-weight: var(--font-medium, 500) !important;
}
}