Implement M1 authoring loop: library, editor, live preview, persistence

M1 MVP from docs/IMPLEMENTATION-PLAN.md, with the /alignment pass applied.

- core: Snippet model + factory; prepareSpecForRender (copy-not-mutate); per-theme Vega chart config
- state/orchestration: SnippetStore with debounced auto-save; IndexedDB adapter + read-time migration; startup hydration + write-through persistence
- ui: SnippetLibrary, SpecEditor (Monaco edcore.main — full editor features, JSON-only languages), LivePreview
- build: Monaco/Vega manual chunks; raised PWA precache ceiling
- alignment: flush a valid draft on snippet switch (+regression tests); TODO breadcrumbs for the preview render race and the window.confirm delete
- housekeeping: gitignore .claude/projects/
This commit is contained in:
2026-06-05 00:16:24 +03:00
parent 056644450c
commit ca54bb66b1
34 changed files with 1557 additions and 74 deletions
@@ -0,0 +1,102 @@
.library {
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);
border-radius: var(--radius);
background: var(--color-accent);
color: var(--color-accent-contrast);
font: inherit;
font-weight: 600;
cursor: pointer;
text-align: left;
}
.createNew:hover {
filter: brightness(1.05);
}
.list {
list-style: none;
margin: 0;
padding: 0;
flex: 1 1 auto;
min-height: 0;
overflow: auto;
display: flex;
flex-direction: column;
gap: var(--space-1);
}
.empty {
color: var(--color-text-muted);
font-size: 13px;
padding: var(--space-2);
}
.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);
cursor: pointer;
}
.item:hover {
background: var(--color-surface);
}
.active {
background: var(--color-surface);
border-color: var(--color-accent);
}
.itemMain {
flex: 1 1 auto;
min-width: 0;
display: flex;
flex-direction: column;
gap: 2px;
}
.name {
font-size: 13px;
font-weight: 500;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.date {
font-size: 11px;
color: var(--color-text-muted);
}
.delete {
flex: 0 0 auto;
border: none;
background: none;
color: var(--color-text-muted);
cursor: pointer;
font-size: 12px;
padding: var(--space-1);
border-radius: var(--radius);
opacity: 0;
}
.item:hover .delete {
opacity: 1;
}
.delete:hover {
color: var(--color-error);
background: var(--color-bg);
}