67 Commits

Author SHA1 Message Date
9781c9e676 Handle corrupt/empty notebook files, atomic writes
_read_notebook_file returns None on empty or unparseable JSON instead
of crashing with JSONDecodeError (surfaced as 500). save_notebook now
writes to a temp file and atomically renames, preventing empty files
from container restarts mid-write.
2026-03-05 19:33:44 -07:00
a2b76539da Replace hardcoded slate-* classes with theme-aware sb-* utilities
WaveformViewer, SchematicViewer, and SimulationLog now use CSS
variable-backed utility classes (bg-sb-surface, text-sb-muted,
border-sb-border, etc.) instead of hardcoded Tailwind slate colors.
Light mode embed theme now works through the variable system rather
than brute-force class overrides in embed-theme.css.
2026-03-05 19:26:23 -07:00
c86bb6e9f0 Add embedding docs and LTspice engine to llms.txt 2026-03-05 15:42:56 -07:00
fb70b39173 Open embed route to all origins, add embed snippet UI, enable LTspice
frame-ancestors * for /embed/* routes so any site can iframe notebooks.
Remove postMessage origin allowlist (theme toggle is cosmetic-only).
Add EmbedDialog popover with copy-paste iframe snippet and theme picker.
Enable ltspice in the engine dropdown now that the backend supports it.
2026-03-05 15:41:51 -07:00
896a8535cf Add LTspice simulation engine via mcltspice
Wire up LTspice as a second simulation engine using mcltspice (Wine-based
LTspice runner). The backend architecture already had the ABC + factory
pattern; this connects the ltspice branch.

- Extract shared raw_to_waveform() into raw_convert.py with Protocol typing
  so both ngspice and mcltspice RawFile objects work without coupling
- Add LtspiceEngine with deferred mcltspice import for graceful degradation
- Register "ltspice" in get_engine() with availability check
  (mcltspice + Wine + LTspice.exe must all be present)
- Add startup validation log for LTspice availability
- Add mcltspice as optional dependency: pip install spicebook[ltspice]
- Integration tests auto-skip when LTspice is unavailable (Docker/CI)
2026-03-05 15:06:41 -07:00
aafcff62b0 Use component-aware sizing for schematic element lengths
Inductors, capacitors, and diodes use compact length (3.0) while
resistors keep full length (4.0) for readable color-coded bands.
Reduces vertical stretch in inductor-heavy circuits like Hartley.
2026-03-02 02:10:57 -07:00
347f569968 Fix title parser and feedback component routing in schematics
Title parser: always use line 0 as title per SPICE convention instead
of skipping * comment lines (which consumed component definitions).

Layout engine: pass growing placed set to path tracing to prevent
feedback components (e.g. col→base cap) from being double-traced.
Route feedback paths from the output terminal horizontally right
instead of defaulting to "down" through the transistor body.
2026-03-01 23:30:57 -07:00
2faa581e0b Add fullscreen expansion to schematic and waveform viewers
Repurpose the Maximize2 button in SchematicViewer as a fullscreen
toggle (was reset-zoom; percentage label is now clickable for that).
Add matching fullscreen toggle to WaveformViewer alongside the scope
button. Both use a shared useFullscreen hook for Escape-to-exit and
body scroll lock. In fullscreen mode, the SVG canvas and plot areas
fill the viewport with proper dimension tracking via ResizeObserver.
2026-02-24 14:15:57 -07:00
8c66b10448 Fix PNP wire-through-body routing and inductor coil fill
The is_inverted flag incorrectly reversed stub wire directions for
PNP/PFET devices, causing wires to route through the transistor body
instead of away from it.  SchemDraw's BjtPnp already orients the
emitter (supply) terminal at the top, so stubs should always go UP
for supply and DOWN for ground regardless of polarity.

- Remove is_inverted direction flipping from _path_style and stubs
- Use output_term (always collector/drain) for output path detection
  instead of supply_term which varies by polarity
- Add .fill(_GRID_BG) to Inductor2 elements so the coil body has a
  solid background fill, visually breaking the through-wire
2026-02-24 13:28:59 -07:00
faebe1cee4 Increase component clearance and add text halo for label visibility
- _LEAD_STUB_LENGTH 1.0 → 2.0: prevents inductor/component bodies from
  overlapping the transistor symbol on complex circuits (Hartley, Colpitts)
- _DEVICE_LABEL_OFST 0.4 → 0.6: pushes Q1 label further from junction
  wires, especially important for PNP layouts
- Add _add_text_halos() SVG post-processor: applies paint-order stroke
  with white halo behind all text so net labels (tap, in, out) remain
  readable when wires cross underneath
2026-02-24 13:03:03 -07:00
b238c38bfa Fix vertical component labels using perpendicular SchemDraw loc values
SchemDraw's loc="left"/"right" on vertical elements places labels at the
element start/end (along the axis), not perpendicular to the body. Use
loc="top" for geometric-left and loc="bottom" for geometric-right to
position labels beside the component body instead of above/below it.

Affects _draw_vert_chain, _render_loop, and _draw_horiz_then_down.
2026-02-24 09:02:23 -07:00
1b8126cbb8 Merge feature/schematic-label-readability: improve label readability and eliminate wire crossings 2026-02-23 21:29:01 -07:00
b8b83fd282 Improve schematic label readability and eliminate wire crossings
Replace scattered magic numbers with named layout constants (_LABEL_OFST,
_NET_LABEL_FONTSIZE, _PARALLEL_PATH_SPACING, etc.) for consistent spacing.

Add "input_up" path classification that routes base/gate bias paths to
VCC left-then-up with local Vdd symbols, preventing wire crossings with
collector/drain vertical paths. Add _choose_label_side() for smart
alternating label placement and _sort_parallel_paths() to draw longest
chains closest to the device body.

Fix PNP/PFET polarity handling: supply terminal stub direction now
matches device polarity instead of always drawing upward.

Vertical chain label improvements:
- valign='bottom' for down-going chains pushes labels above midpoint
- Gap wire (0.5 units) before ground/Vdd terminators prevents overlap
- Minimum component length (_VERT_CHAIN_MIN_LEN) for readable labels
- valign='center' on device body and horizontal-turn labels
2026-02-23 20:51:07 -07:00
c0639c775c Add standalone /chat page with full-page conversation UI
Extract shared rendering (chat-render.ts) and streaming hook
(use-chat-stream.ts) from ChatWidget so both the floating panel
and the new page share identical markdown/KaTeX/SSE logic.

New page features:
- Responsive sidebar (inline desktop, Sheet drawer mobile)
- Conversation search/filter
- Notebook picker via cmdk command palette
- Auto-growing multi-line textarea input
- Pop-out button on widget header to open /chat

ChatLayout.astro omits the floating widget to avoid duplicate UI.
Chat store gains selectedNotebookId for page-level notebook context.
shadcn-ui primitives (ScrollArea, Sheet, Command, Popover, etc.)
wired to existing SpiceBook dark theme tokens.
2026-02-23 18:54:34 -07:00
70efde8aa6 Fix chat panel scroll: replace scrollIntoView with container.scrollTo
scrollIntoView walks up the DOM and scrolls every ancestor, including
the panel with overflow:hidden — this pushed the header and messages
area off-screen after long LLM responses. Using container.scrollTo
limits scrolling to only the messages div.
2026-02-23 14:56:41 -07:00
5db321c8e3 Add MCP chat reference architecture documentation
Comprehensive guide for building MCP-powered chat assistants with SSE
streaming, covering both Hamilton Archive (vanilla JS) and SpiceBook
(React + Zustand) implementations. Includes Caddy routing patterns,
security hardening checklist, and frontend lessons learned.
2026-02-23 14:26:05 -07:00
78350e02af Update docker-compose and astro config for chat/MCP integration
- Add ANTHROPIC_API_KEY env passthrough in docker-compose.yml
- Add astro telemetry and devToolbar disable settings
- Update CLAUDE.md with deployment notes
2026-02-23 14:25:58 -07:00
b89e520479 Consistent label offsets across all schematic layout engines
Add ofst=0.15 to all component labels for uniform clearance between
label text and component bodies. Previously, loop layout had no offsets,
grid layout was missing offsets on multi-terminal devices (BJT/FET), and
connected layout was missing offsets on transistor and horizontal labels.
2026-02-23 14:25:50 -07:00
6f239c185e Chat widget: markdown styling, KaTeX math rendering, SSE streaming fix
- Add comprehensive CSS for markdown elements in chat bubbles (headers,
  lists, tables, blockquotes, links, code blocks, horizontal rules)
- Integrate KaTeX via marked-katex-extension for inline ($) and display
  ($$) math rendering with DOMPurify whitelist for MathML/SVG elements
- Add normalizeDisplayMath() pre-processor to handle LLM output where $$
  delimiters appear inline rather than on their own lines
- Add dark theme KaTeX styles matching the SpiceBook color palette
- Fix asyncio.to_thread usage in chat SSE endpoint for streaming
2026-02-23 14:25:42 -07:00
99d1ca28d2 Add MCP server and chat assistant
- Mount FastMCP at /mcp with tools for notebook CRUD, simulation,
  and cell execution. Includes status resource and circuit_assistant
  prompt.
- Add SSE streaming chat endpoint at /api/chat/stream backed by
  GPU LLM gateway (qwen3). Chat widget sends notebook context
  (SPICE cells, markdown notes) so the assistant can reference the
  user's circuit.
- React floating chat panel with zustand-persisted conversation
  history, streaming token display, reasoning collapse, and
  keyboard shortcuts.
- Refactor main.py from deprecated on_event("startup") to lifespan
  context manager with combine_lifespans for MCP integration.
- Add notebook_id path traversal validation, decouple get_engine()
  from HTTPException for MCP compatibility, fix HTTP client init
  race condition with asyncio.Lock.
- Update Caddy labels for /mcp/* routing and SSE streaming on
  backend reverse proxy.
2026-02-22 16:49:15 -07:00
09fd59d570 Visual polish: card hover lift, gradient section dividers, editor spacing
- Cards (featured + gallery): hover adds -translate-y-1 + shadow-lg for
  subtle lift-off-surface effect
- Homepage sections: replace hard border-b with gradient-fade dividers
  that taper to transparent at edges
- Resistor button section: tighten vertical padding (py-10 → py-6)
- Notebook editor: cell spacing increased (space-y-0 → space-y-3),
  add-cell button slides up on hover instead of abrupt opacity flash,
  status bar gets more breathing room and lighter text
2026-02-21 15:28:08 -07:00
d65f23d6ee Add chevron-right to astro-icon include list
The 50Ω resistor button uses lucide:chevron-right which wasn't in the
explicit icon include list, causing SSR to crash mid-stream on the homepage.
2026-02-21 11:35:09 -07:00
26f9eb996e Move resistor color guide to /reference/resistor-colors, add 50Ω button on homepage
Promotes the full ResistorColorGuide component to a dedicated reference
page and replaces the homepage section with a compact inline SVG link
showing Green-Black-Black-Gold (50Ω ±5%) band colors.
2026-02-21 11:32:16 -07:00
0a9abd6771 Add flush_interval to prod frontend Caddy labels
Without flush_interval: -1, Caddy buffers SSR responses and can
truncate large pages (380KB homepage was cut to 70KB, dropping the
ResistorColorGuide section entirely).
2026-02-21 10:05:34 -07:00
5f5e0da3c4 Fix dev backend startup: use python -m uvicorn instead of uv run
uv pip install --system installs packages globally but uv run still
tries to create /app/.venv, which fails with permission denied.
Add PYTHONPATH=/app/src since the editable install .pth file is empty
when built without source directory present.
2026-02-21 09:17:03 -07:00
2ddc08cd3a Add schematic thumbnails, descriptions to notebook cards and resistor color guide
Notebook cards now show schematic SVG thumbnails (on Mims green background)
and description snippets extracted from the first markdown cell. The backend
extracts both from already-loaded notebook data at zero additional I/O cost.

New ResistorColorGuide section between the pipeline strip and featured
notebooks explains the 4-band color code with an annotated zigzag diagram
and reference table using the same BAND_HEX colors from the schematic renderer.

Also switches docker-compose.dev.yml from direct port mapping to Caddy labels,
matching the production routing pattern and avoiding port conflicts.
2026-02-21 08:56:48 -07:00
fe6d20afbd Add schematic pipeline step and fix vertical label spacing
Add ofst=0.15 to vertical resistor label calls preventing overlap with
color-coded zigzag bands. Add Schematic step 03 to homepage pipeline
with inline SVG voltage divider on Mims graph paper. Pipeline now
4-step with responsive 1col/2x2/4col layout.
2026-02-20 20:34:41 -07:00
65e7dbe68d Merge feature/color-coded-resistors: color-coded zigzag resistor symbols 2026-02-20 17:37:01 -07:00
7e10b87a13 Color-code resistor zigzag symbols with standard 4-band colors
Map physical resistor color bands onto the IEEE zigzag schematic symbol.
Each sub-segment is colored to match its band (digit, multiplier, tolerance),
with wire-colored entry/exit and a gap before tolerance mimicking real spacing.
Parseable values 0.01–1GΩ get color; parametric/out-of-range fall back to mono.
2026-02-20 17:36:50 -07:00
67bb47c0cd Fix oscilloscope CSS: use is:global for child component styles
Astro scopes <style> block imports to the authoring component.
Since OscilloscopeDisplay.astro is a child of index.astro, the
scoped selectors didn't match. Move the CSS import into the
component itself with is:global.
2026-02-20 16:31:20 -07:00
4b2ce896b5 Replace static hero scope with interactive XY-mode oscilloscope
Port the OscilloscopeDisplay from mcltspice docs — Web Audio API
renders stereo audio as Lissajous patterns on a canvas with teal
phosphor persistence. 6 tracks from Jerobeam Fenderson, volume
knob, skin switcher (465/545A), and the Outer Limits easter egg.

Audio: CC BY-NC-SA 4.0, oscilloscopemusic.com
Visual: Nick Watton (codepen.io/2Mogs), rsp2k
2026-02-20 16:25:13 -07:00
72cfd8191a Fix React hydration mismatch and clean up stale service workers
- Pin formatDate to timeZone: 'UTC' so server and client produce
  identical date strings (fixes React error #418)
- Add one-shot service worker unregistration since SpiceBook doesn't
  use one — clears phantom registrations from browser cache
2026-02-20 16:07:42 -07:00
3ee3cd6d8a Register arrow-right and check-circle-2 icons for pipeline strip
astro-icon requires explicit icon registration — these two were used
in PipelineStrip.astro but missing from the include list, causing
SSR render failures in production.
2026-02-20 15:31:21 -07:00
6350f15991 Merge homepage-redesign-show-dont-tell: animated scope hero, pipeline strip, featured notebooks 2026-02-20 15:26:31 -07:00
43789bdf24 Homepage redesign: show-don't-tell with animated scope hero
Restructure the homepage to lead with visuals instead of text:

- Hero: split layout with animated Tektronix 465 oscilloscope showing
  RC step response (CSS+SVG, zero JS) that links to the notebook
- Pipeline strip: 3-step Write → Simulate → Visualize with code/terminal
  previews and inline waveform SVG
- Featured notebooks: 3 curated circuits (RC, 555, common emitter) with
  pre-rendered waveform thumbnails
- Gallery cards: decorative graticule header strip, color-coded by engine
- Footer: updated copy with clearer call to action

All new sections are server-rendered Astro components. Total new client
JavaScript: zero bytes.
2026-02-20 15:16:53 -07:00
3c9c83742d Fix scope viewer: initialize div state from data range to prevent empty CRT
xDiv/yDiv were initialized as null and excluded from the main uPlot
effect's dependency array, causing the effect to bail on its null guard
and never re-run when the init effect set them to computed values.
2026-02-16 23:27:46 -07:00
ac82068b98 Merge feature/scope-skin: Tektronix oscilloscope waveform viewer 2026-02-15 18:11:21 -07:00
22d3e903db Add Tektronix oscilloscope skin for waveform viewer
Port 465/545A visual chrome from mcltspice docs into SpiceBook as a
toggleable waveform viewer skin. uPlot chart renders inside a CRT
screen with teal phosphor traces, graticule overlay, and scanlines.

Functional knobs control trace visibility (setSeries), X/Y zoom
(setScale) in standard 1-2-5 div steps. Digital readout bar shows
current trace, div values, and analysis type. Two switchable hardware
skins — 465 tan and 545A hammertone — persisted in localStorage.

New files: ScopeWaveformViewer.tsx, scope-skin.css
Modified: WaveformViewer.tsx (toggle), waveform-utils.ts (scope
palette, 1-2-5 sequence, stack-safe min/max), globals.css (scope vars)
2026-02-15 18:05:59 -07:00
b497d57890 Auto-generate schematics on mount, wire routing for grid layout
SPICE cells now auto-trigger schematic generation when they mount
without an existing diagram, so the schematic leads the UI. Template
and empty cells are skipped.

Grid fallback layout replaced with wire-connected rendering: BFS-based
node tier classification places components vertically between supply
and ground rails, then routes wires by node type (power/ground bus
rails, L-shaped signal wires, star topology for 3+ connections).
2026-02-15 14:40:25 -07:00
579f90487d Merge homepage-redesign: hero, features, categorized gallery 2026-02-14 20:46:49 -07:00
eae849fe7a Redesign homepage with hero, features, and categorized notebook gallery
SSR the notebook list in Astro frontmatter (eliminating the client-side
loading spinner). Add hero section with oscilloscope graticule background,
4-column feature highlights, and a React island gallery with category
filter pills, tag search, and grouped/flat view modes.
2026-02-14 20:45:24 -07:00
1e08be4409 Add /llms.txt API reference and POST /api/notebooks/compose endpoint
Machine-readable API docs at /llms.txt for LLM collaboration on circuit
design notebooks linked from Mims Electronics Reference Library.

Compose endpoint creates fully-populated notebooks in one call with
optional SPICE simulation. Per-cell try/except ensures partial simulation
failures don't lose the notebook.

Also extracts get_engine to spicebook.engine and makes
generate_notebook_id a public API.
2026-02-14 15:43:26 -07:00
c581786372 Add CLAUDE.md with deployment procedure and architecture notes 2026-02-14 13:34:15 -07:00
7d5d1b2d31 Use @fontsource/inter woff for OG renderer instead of broken TTF download
The Inter SemiBold TTF from GitHub was an HTML error page. Switch to
loading inter-latin-600-normal.woff from @fontsource/inter in
node_modules — always available, always valid, and Satori supports
woff natively.
2026-02-14 13:30:43 -07:00
db2ecf32c4 Fix font buffer slice and env var access for production SSR
Node.js Buffer pool shares an ArrayBuffer — slice to give Satori's
OpenType parser a standalone copy starting at byte 0. Use process.env
instead of import.meta.env for BACKEND_INTERNAL_URL since Vite only
exposes PUBLIC_* prefixed vars via import.meta.env.
2026-02-14 13:25:43 -07:00
ea66086b44 Remove named volume from prod compose, use bind mount for notebooks
The base docker-compose.yml already mounts ./notebooks:/app/notebooks.
The named Docker volume was shadowing it in prod, preventing example
notebooks from being visible. Bind mount ensures examples ship with
the repo and user notebooks persist on the host filesystem.
2026-02-14 13:16:18 -07:00
c9116c5d86 Add SEO meta tags, OG image generation, and astro-icon integration
Wire astro-seo-meta for OG, Twitter Card, and canonical tags on all
pages. Add Satori + resvg dynamic OG image endpoints at /og/[id].png
with branded dark-theme cards. Replace inline SVGs with zero-JS
astro-icon rendering. SSR fetches use 5s AbortController timeout and
shared ID validation across all dynamic routes.
2026-02-14 13:15:52 -07:00
99e47685aa Add Mims verification confirmation for embed bugfixes
All 4 embed bugs verified fixed by Mims library testing:
postMessage type sync, waveform theme colors, chart remount,
and shared component light-mode overrides. Thread closed.
2026-02-13 18:14:52 -07:00
e8ade01662 Fix embed integration bugs from Mims library testing
- postMessage type: 'theme-change' → 'spicebook-theme' to match
  SimulationEmbed.tsx namespace convention
- WaveformViewer: read axis/grid/tick colors from CSS custom
  properties via getComputedStyle instead of hardcoded dark hex
- Theme switch forces WaveformViewer remount via React key so
  uPlot picks up new CSS variable values
- Light-mode CSS overrides for shared components (SchematicViewer
  toolbar, SimulationLog borders, slate utility classes)
2026-02-13 16:31:32 -07:00
b0dc46edc2 Merge feature/schematic-phase1: schematics + Mims embed integration
Brings in auto-generated schematics from SPICE netlists with
click-to-edit values, graph paper backgrounds, and Mims-style
connected layout. Also adds embeddable notebook viewer for
cross-site iframe integration with the Mims library.
2026-02-13 15:46:43 -07:00