mirror of
https://github.com/olehomelchenko/astrolabe.git
synced 2026-08-08 02:02:33 +00:00
Implement M1.5 visual design foundation: tokens, IBM Plex, theme toggle, chart themes
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
overflow: auto;
|
||||
background: var(--bg);
|
||||
}
|
||||
|
||||
.chart {
|
||||
@@ -9,16 +10,16 @@
|
||||
align-items: flex-start;
|
||||
justify-content: center;
|
||||
min-height: 100%;
|
||||
padding: var(--space-2);
|
||||
padding: var(--space-5);
|
||||
}
|
||||
|
||||
.error {
|
||||
margin: 0;
|
||||
padding: var(--space-3);
|
||||
padding: var(--space-5);
|
||||
font-family: var(--font-mono);
|
||||
font-size: 12px;
|
||||
line-height: 1.5;
|
||||
color: var(--color-error);
|
||||
line-height: 1.6;
|
||||
color: var(--support-error);
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
@@ -2,24 +2,26 @@
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
gap: var(--space-3);
|
||||
}
|
||||
|
||||
.createNew {
|
||||
flex: 0 0 auto;
|
||||
padding: var(--space-2) var(--space-3);
|
||||
border: 1px solid var(--color-border);
|
||||
margin: var(--space-4);
|
||||
height: 40px;
|
||||
padding: 0 var(--space-5);
|
||||
border: var(--border-width) solid transparent;
|
||||
border-radius: var(--radius);
|
||||
background: var(--color-accent);
|
||||
color: var(--color-accent-contrast);
|
||||
background: var(--accent);
|
||||
color: var(--accent-contrast);
|
||||
font: inherit;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
text-align: left;
|
||||
text-align: center;
|
||||
transition: background var(--dur-fast) var(--ease);
|
||||
}
|
||||
|
||||
.createNew:hover {
|
||||
filter: brightness(1.05);
|
||||
background: var(--accent-hover);
|
||||
}
|
||||
|
||||
.list {
|
||||
@@ -29,34 +31,36 @@
|
||||
flex: 1 1 auto;
|
||||
min-height: 0;
|
||||
overflow: auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-1);
|
||||
border-top: var(--border-width) solid var(--border);
|
||||
}
|
||||
|
||||
.empty {
|
||||
color: var(--color-text-muted);
|
||||
color: var(--text-secondary);
|
||||
font-size: 13px;
|
||||
padding: var(--space-2);
|
||||
padding: var(--space-5) var(--space-4);
|
||||
}
|
||||
|
||||
.item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--space-2);
|
||||
padding: var(--space-2) var(--space-3);
|
||||
border: 1px solid transparent;
|
||||
border-radius: var(--radius);
|
||||
gap: var(--space-3);
|
||||
padding: var(--space-3) var(--space-4);
|
||||
border-left: 2px solid transparent;
|
||||
cursor: pointer;
|
||||
transition: background var(--dur-fast) var(--ease);
|
||||
}
|
||||
|
||||
.item + .item {
|
||||
border-top: var(--border-width) solid var(--border);
|
||||
}
|
||||
|
||||
.item:hover {
|
||||
background: var(--color-surface);
|
||||
background: var(--layer-01);
|
||||
}
|
||||
|
||||
.active {
|
||||
background: var(--color-surface);
|
||||
border-color: var(--color-accent);
|
||||
background: var(--layer-01);
|
||||
border-left-color: var(--accent);
|
||||
}
|
||||
|
||||
.itemMain {
|
||||
@@ -64,7 +68,7 @@
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
gap: var(--space-1);
|
||||
}
|
||||
|
||||
.name {
|
||||
@@ -77,26 +81,30 @@
|
||||
|
||||
.date {
|
||||
font-size: 11px;
|
||||
color: var(--color-text-muted);
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.delete {
|
||||
flex: 0 0 auto;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: none;
|
||||
background: none;
|
||||
color: var(--color-text-muted);
|
||||
color: var(--text-secondary);
|
||||
cursor: pointer;
|
||||
font-size: 12px;
|
||||
padding: var(--space-1);
|
||||
border-radius: var(--radius);
|
||||
opacity: 0;
|
||||
transition: opacity var(--dur-fast) var(--ease);
|
||||
}
|
||||
|
||||
.item:hover .delete {
|
||||
.item:hover .delete,
|
||||
.delete:focus-visible {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.delete:hover {
|
||||
color: var(--color-error);
|
||||
background: var(--color-bg);
|
||||
color: var(--support-error);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
.editorPane {
|
||||
position: relative;
|
||||
height: 100%;
|
||||
background: var(--bg);
|
||||
}
|
||||
|
||||
.editor {
|
||||
@@ -15,8 +16,8 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: var(--color-text-muted);
|
||||
color: var(--text-secondary);
|
||||
font-size: 13px;
|
||||
background: var(--color-bg);
|
||||
background: var(--bg);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
@@ -42,6 +42,9 @@ export function SpecEditor() {
|
||||
language: 'json',
|
||||
automaticLayout: true,
|
||||
minimap: { enabled: false },
|
||||
// The editor is a Plex Mono surface per the design language (doc §3.1).
|
||||
// Monaco needs an explicit family string — it can't read the CSS token.
|
||||
fontFamily: "'IBM Plex Mono', ui-monospace, 'SF Mono', Menlo, monospace",
|
||||
fontSize: 13,
|
||||
tabSize: 2,
|
||||
wordWrap: 'on',
|
||||
@@ -78,7 +81,7 @@ export function SpecEditor() {
|
||||
|
||||
// Editor theme follows the UI theme (M1: light/dark stock themes).
|
||||
useEffect(() => {
|
||||
monaco.editor.setTheme(uiTheme === 'experimental' ? 'vs-dark' : 'vs');
|
||||
monaco.editor.setTheme(uiTheme === 'dark' ? 'vs-dark' : 'vs');
|
||||
}, [uiTheme]);
|
||||
|
||||
return (
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
.toggle {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
padding: 0;
|
||||
border: var(--border-width) solid transparent;
|
||||
border-radius: var(--radius);
|
||||
background: transparent;
|
||||
color: var(--text-secondary);
|
||||
cursor: pointer;
|
||||
transition:
|
||||
background var(--dur-fast) var(--ease),
|
||||
color var(--dur-fast) var(--ease);
|
||||
}
|
||||
|
||||
.toggle:hover {
|
||||
background: var(--layer-02);
|
||||
color: var(--text);
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* Theme toggle — a header control that flips light ⇄ dark (spec §07 Appearance).
|
||||
*
|
||||
* Interim home: the spec houses the UI-theme control inside the Settings modal,
|
||||
* which arrives in M5. Until then this header button is the control; it persists
|
||||
* through the same `ui.theme` settings key, so M5 can move it into Settings (or
|
||||
* keep it as a shortcut) without changing what's stored.
|
||||
*
|
||||
* The button shows the icon of the theme you'll switch *to* (moon when light,
|
||||
* sun when dark) and labels itself for screen readers. The focus ring comes from
|
||||
* the shared rule in base.css; chart + editor follow the theme via their own
|
||||
* store subscriptions, so flipping the store repaints everything.
|
||||
*/
|
||||
|
||||
import { useAppStore } from '../stores/AppStore';
|
||||
import styles from './ThemeToggle.module.css';
|
||||
|
||||
function MoonIcon() {
|
||||
return (
|
||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
|
||||
<path d="M21 12.8A9 9 0 1 1 11.2 3a7 7 0 0 0 9.8 9.8z" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
function SunIcon() {
|
||||
return (
|
||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
|
||||
<circle cx="12" cy="12" r="4.5" />
|
||||
<path d="M12 2v2M12 20v2M4.9 4.9l1.4 1.4M17.7 17.7l1.4 1.4M2 12h2M20 12h2M4.9 19.1l1.4-1.4M17.7 6.3l1.4-1.4" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export function ThemeToggle() {
|
||||
const uiTheme = useAppStore((s) => s.uiTheme);
|
||||
const toggleTheme = useAppStore((s) => s.toggleTheme);
|
||||
const target = uiTheme === 'dark' ? 'light' : 'dark';
|
||||
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
className={styles.toggle}
|
||||
onClick={toggleTheme}
|
||||
aria-label={`Switch to ${target} theme`}
|
||||
title={`Switch to ${target} theme`}
|
||||
>
|
||||
{uiTheme === 'dark' ? <SunIcon /> : <MoonIcon />}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user