Initial scaffold: spec, architecture playbook, and M0 skeleton

This commit is contained in:
2026-06-04 22:14:33 +03:00
commit 056644450c
51 changed files with 13754 additions and 0 deletions
+58
View File
@@ -0,0 +1,58 @@
.app {
display: flex;
flex-direction: column;
height: 100%;
}
.header {
display: flex;
align-items: center;
gap: var(--space-3);
height: var(--header-height);
padding: 0 var(--space-4);
border-bottom: 1px solid var(--color-border);
background: var(--color-surface);
flex: 0 0 auto;
}
.title {
font-weight: 600;
font-size: 16px;
}
.version {
font-size: 11px;
color: var(--color-text-muted);
border: 1px solid var(--color-border);
border-radius: var(--radius);
padding: 1px var(--space-2);
}
.spacer {
flex: 1;
}
.panes {
display: flex;
flex: 1 1 auto;
min-height: 0;
}
.pane {
flex: 1;
min-width: 0;
padding: var(--space-4);
overflow: auto;
border-right: 1px solid var(--color-border);
}
.pane:last-child {
border-right: none;
}
.paneLabel {
font-size: 11px;
text-transform: uppercase;
letter-spacing: 0.05em;
color: var(--color-text-muted);
}
+33
View File
@@ -0,0 +1,33 @@
import styles from './App.module.css';
/**
* Application shell — the three-pane workspace from spec §01A
* (library · editor · preview) under a fixed header.
*
* This is the skeleton: panes are placeholders. Each milestone fills one in
* (see docs/IMPLEMENTATION-PLAN.md). Resizing, toggling, modals, routing, and
* shortcuts arrive in later milestones.
*/
export function App() {
return (
<div className={styles.app}>
<header className={styles.header}>
<span className={styles.title}>Astrolabe</span>
<span className={styles.version}>v{__APP_VERSION__}</span>
<span className={styles.spacer} />
</header>
<main className={styles.panes}>
<section className={styles.pane} aria-label="Snippet library">
<div className={styles.paneLabel}>Library</div>
</section>
<section className={styles.pane} aria-label="Spec editor">
<div className={styles.paneLabel}>Editor</div>
</section>
<section className={styles.pane} aria-label="Live preview">
<div className={styles.paneLabel}>Preview</div>
</section>
</main>
</div>
);
}
+41
View File
@@ -0,0 +1,41 @@
import { create } from 'zustand';
import type { UiTheme } from '@core/theme';
/**
* Centralized cross-cutting application state, as a Zustand store. Keep this
* lean — durable, feature-specific state (snippets, datasets, settings) lands
* in its own store module (e.g. stores/SnippetStore) as the app grows.
*
* Usable inside React via the `useAppStore` hook (with a selector) and outside
* React via `useAppStore.getState()` / `.setState()` / `.subscribe()` — see
* docs/architecture/01-state-and-stores.md.
*/
export type { UiTheme };
/** Which modal, if any, is currently open. At most one at a time (spec §01C). */
export type ModalName = 'datasets' | 'settings' | 'about' | 'donate' | 'chartBuilder' | 'extract';
export interface AppState {
/** Active UI theme; mirrored onto <html data-theme> by a subscriber. */
uiTheme: UiTheme;
/** The currently open modal, or null. */
activeModal: ModalName | null;
setTheme: (theme: UiTheme) => void;
/**
* Low-level modal setter — the single primitive that mutates `activeModal`.
* High-level open/close (snapshot for unsaved-change detection, URL sync,
* discard confirmation) lives in the modal coordinator (docs/architecture/03),
* which calls this; arrives with the modal system in M3.
*/
setActiveModal: (modal: ModalName | null) => void;
}
export const useAppStore = create<AppState>((set) => ({
uiTheme: 'light',
activeModal: null,
setTheme: (uiTheme) => set({ uiTheme }),
setActiveModal: (activeModal) => set({ activeModal }),
}));