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.
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.
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.
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
- _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
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.
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
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.
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.
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.
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.
- 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
- 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.
- 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
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.
Without flush_interval: -1, Caddy buffers SSR responses and can
truncate large pages (380KB homepage was cut to 70KB, dropping the
ResistorColorGuide section entirely).
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.
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.
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.
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.
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.
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
- 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
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.
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.
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.
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)
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).
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.
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.
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.
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.
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.
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.
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.
SchematicViewer now supports inline editing of component values
directly on the SVG. Clicking an editable value opens an overlay
input that updates the SPICE netlist on commit, triggering an
auto-redraw after 800ms debounce.
Added DOMPurify for SVG sanitization, netlist-utils for safe
value substitution in netlists, and wired schematic generation
through the notebook store with generation counters to discard
stale responses.
Sage-green grid (minor 10pt, major 50pt) on warm off-white canvas,
injected as nested SVG patterns behind all schematic content.
Works across all three renderers (loop, connected, grid).
- Add lead stub wires (0.75 unit) from collector and emitter pins
for clearance between transistor and first component
- Transistor label placed right of body, all chain labels on left
- Offset parallel paths (RE/CE) label on right, facing outward
- Wider parallel path spacing (2.5 units) for label breathing room
- Down-turning components label outward based on path direction
- Parameterized label_loc in _draw_vert_chain for context-aware placement
Replace the disconnected grid fallback with a topology-aware renderer
that places BJTs/MOSFETs at center and draws connected wire paths using
SchemDraw push/pop for branching at junction points.
Layout pipeline: trace component chains from each device terminal,
classify paths by direction (supply/ground/input/output), draw with
proper Vdd/Ground terminators and junction dots at branch points.
Fallback cascade: loop → connected → grid. Supply sources shown as Vdd
rail symbols; signal sources drawn inline as SourceV in input paths.