Add Starlight docs site with API reference, guides, and embedding docs
Starlight site at docs-site/ following warehack.ing cookie-cutter pattern (3-compose split, Caddy prod stage, Makefile targets). 15 content pages covering simulation engines, waveform viewer, schematics, embedding with postMessage theme sync, and full REST API reference adapted from llms.txt. Blue accent theme matching SpiceBook's application palette. Adds docs link to homepage reference section.
This commit is contained in:
parent
b96f11b4af
commit
ad7e80c722
6
docs-site/.dockerignore
Normal file
6
docs-site/.dockerignore
Normal file
@ -0,0 +1,6 @@
|
||||
node_modules
|
||||
dist
|
||||
.astro
|
||||
.env
|
||||
.git
|
||||
*.md
|
||||
10
docs-site/.env.example
Normal file
10
docs-site/.env.example
Normal file
@ -0,0 +1,10 @@
|
||||
# SpiceBook docs environment configuration
|
||||
COMPOSE_PROJECT_NAME=spicebook-docs
|
||||
|
||||
# MODE: dev | prod
|
||||
MODE=dev
|
||||
|
||||
# Domain configuration
|
||||
DOMAIN=spicebook-docs.l.warehack.ing
|
||||
# Production: spicebook-docs.warehack.ing
|
||||
# Dev (local): spicebook-docs.l.warehack.ing
|
||||
4
docs-site/.gitignore
vendored
Normal file
4
docs-site/.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
node_modules/
|
||||
dist/
|
||||
.astro/
|
||||
.env
|
||||
68
docs-site/Dockerfile
Normal file
68
docs-site/Dockerfile
Normal file
@ -0,0 +1,68 @@
|
||||
# ── Dev target ────────────────────────────────────────────────
|
||||
FROM node:22-slim AS dev
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY package*.json ./
|
||||
COPY patches/ patches/
|
||||
RUN npm ci
|
||||
|
||||
COPY . .
|
||||
|
||||
EXPOSE 4321
|
||||
|
||||
CMD ["npm", "run", "dev"]
|
||||
|
||||
# ── Prod build stage ─────────────────────────────────────────
|
||||
FROM node:22-slim AS build
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY package*.json ./
|
||||
COPY patches/ patches/
|
||||
RUN npm ci
|
||||
|
||||
COPY . .
|
||||
RUN npm run build
|
||||
|
||||
# ── Prod serve stage ─────────────────────────────────────────
|
||||
FROM caddy:2-alpine AS prod
|
||||
|
||||
RUN addgroup -g 1001 -S app && \
|
||||
adduser -S app -u 1001 -G app
|
||||
|
||||
COPY --from=build /app/dist /srv
|
||||
COPY <<'CADDYFILE' /etc/caddy/Caddyfile
|
||||
:8080 {
|
||||
root * /srv
|
||||
file_server
|
||||
encode gzip zstd
|
||||
|
||||
header {
|
||||
X-Content-Type-Options nosniff
|
||||
X-Frame-Options DENY
|
||||
Referrer-Policy strict-origin-when-cross-origin
|
||||
Cache-Control "public, max-age=31536000, immutable"
|
||||
}
|
||||
|
||||
handle /health {
|
||||
respond "ok" 200
|
||||
}
|
||||
|
||||
handle_errors {
|
||||
rewrite * /404.html
|
||||
file_server
|
||||
}
|
||||
}
|
||||
CADDYFILE
|
||||
|
||||
RUN chown -R app:app /srv
|
||||
|
||||
USER app
|
||||
|
||||
EXPOSE 8080
|
||||
|
||||
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
|
||||
CMD wget -qO- http://127.0.0.1:8080/health || exit 1
|
||||
|
||||
CMD ["caddy", "run", "--config", "/etc/caddy/Caddyfile"]
|
||||
50
docs-site/Makefile
Normal file
50
docs-site/Makefile
Normal file
@ -0,0 +1,50 @@
|
||||
include .env
|
||||
export
|
||||
|
||||
COMPOSE_BASE := docker compose -f docker-compose.yml -f docker-compose.$(MODE).yml
|
||||
|
||||
.PHONY: dev prod build logs stop clean restart status help
|
||||
|
||||
## dev: start in dev mode with HMR and follow logs
|
||||
dev:
|
||||
@echo "Starting dev on $(DOMAIN) ..."
|
||||
MODE=dev docker compose -f docker-compose.yml -f docker-compose.dev.yml up --build -d
|
||||
MODE=dev docker compose -f docker-compose.yml -f docker-compose.dev.yml logs -f
|
||||
|
||||
## prod: build and start in production mode
|
||||
prod:
|
||||
@echo "Deploying prod on $(DOMAIN) ..."
|
||||
MODE=prod docker compose -f docker-compose.yml -f docker-compose.prod.yml up --build -d
|
||||
MODE=prod docker compose -f docker-compose.yml -f docker-compose.prod.yml logs --tail=40
|
||||
|
||||
## build: build containers using current MODE from .env
|
||||
build:
|
||||
$(COMPOSE_BASE) build
|
||||
|
||||
## logs: follow container logs
|
||||
logs:
|
||||
$(COMPOSE_BASE) logs -f
|
||||
|
||||
## stop: stop containers
|
||||
stop:
|
||||
$(COMPOSE_BASE) down
|
||||
|
||||
## down: alias for stop
|
||||
down: stop
|
||||
|
||||
## restart: rebuild and restart
|
||||
restart: stop
|
||||
$(COMPOSE_BASE) up --build -d
|
||||
$(COMPOSE_BASE) logs --tail=20
|
||||
|
||||
## clean: stop, remove volumes and local images
|
||||
clean:
|
||||
$(COMPOSE_BASE) down -v --rmi local
|
||||
|
||||
## status: show running container status
|
||||
status:
|
||||
$(COMPOSE_BASE) ps
|
||||
|
||||
## help: show available targets
|
||||
help:
|
||||
@grep '^## ' Makefile | sed 's/^## //' | column -t -s ':'
|
||||
79
docs-site/astro.config.mjs
Normal file
79
docs-site/astro.config.mjs
Normal file
@ -0,0 +1,79 @@
|
||||
import { defineConfig } from 'astro/config';
|
||||
import starlight from '@astrojs/starlight';
|
||||
import tailwindcss from '@tailwindcss/vite';
|
||||
|
||||
export default defineConfig({
|
||||
site: 'https://spicebook-docs.warehack.ing',
|
||||
telemetry: false,
|
||||
devToolbar: { enabled: false },
|
||||
integrations: [
|
||||
starlight({
|
||||
title: 'SpiceBook',
|
||||
tagline: 'Circuit simulation notebooks',
|
||||
social: [
|
||||
{ icon: 'external', label: 'SpiceBook', href: 'https://spicebook.warehack.ing' },
|
||||
{ icon: 'external', label: 'mcltspice', href: 'https://mcltspice.warehack.ing' },
|
||||
],
|
||||
customCss: ['./src/styles/custom.css'],
|
||||
expressiveCode: {
|
||||
themes: ['dracula', 'github-light'],
|
||||
},
|
||||
sidebar: [
|
||||
{
|
||||
label: 'Getting Started',
|
||||
items: [
|
||||
{ label: 'What is SpiceBook?', slug: '' },
|
||||
{ label: 'Quick Start', slug: 'getting-started/quick-start' },
|
||||
{ label: 'Editor Basics', slug: 'getting-started/editor-basics' },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Simulation',
|
||||
items: [
|
||||
{ label: 'Engines', slug: 'simulation/engines' },
|
||||
{ label: 'Running Simulations', slug: 'simulation/running' },
|
||||
{ label: 'Waveform Viewer', slug: 'simulation/waveforms' },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Schematics',
|
||||
items: [
|
||||
{ label: 'Auto-Generated Diagrams', slug: 'schematics/auto-generated' },
|
||||
{ label: 'Color-Coded Resistors', slug: 'schematics/resistor-colors' },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Embedding',
|
||||
items: [
|
||||
{ label: 'Embed a Notebook', slug: 'embedding/embed-notebook' },
|
||||
{ label: 'Theme Sync', slug: 'embedding/theme-sync' },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'API',
|
||||
collapsed: false,
|
||||
items: [
|
||||
{ label: 'REST API Overview', slug: 'api/overview' },
|
||||
{ label: 'Notebooks', slug: 'api/notebooks' },
|
||||
{ label: 'Cells', slug: 'api/cells' },
|
||||
{ label: 'Simulation', slug: 'api/simulation' },
|
||||
{ label: 'Waveforms & Schematics', slug: 'api/waveforms-schematics' },
|
||||
],
|
||||
},
|
||||
],
|
||||
}),
|
||||
],
|
||||
vite: {
|
||||
plugins: [tailwindcss()],
|
||||
server: {
|
||||
host: '0.0.0.0',
|
||||
...(process.env.VITE_HMR_HOST && {
|
||||
hmr: {
|
||||
host: process.env.VITE_HMR_HOST,
|
||||
protocol: 'wss',
|
||||
clientPort: 443,
|
||||
},
|
||||
}),
|
||||
},
|
||||
},
|
||||
});
|
||||
19
docs-site/docker-compose.dev.yml
Normal file
19
docs-site/docker-compose.dev.yml
Normal file
@ -0,0 +1,19 @@
|
||||
services:
|
||||
docs:
|
||||
environment:
|
||||
- VITE_HMR_HOST=${DOMAIN}
|
||||
volumes:
|
||||
- ./src:/app/src
|
||||
- ./public:/app/public
|
||||
- ./astro.config.mjs:/app/astro.config.mjs
|
||||
labels:
|
||||
caddy.reverse_proxy: "{{upstreams 4321}}"
|
||||
# HMR WebSocket support -- keeps Vite hot-reload alive through Caddy
|
||||
caddy.reverse_proxy.flush_interval: "-1"
|
||||
caddy.reverse_proxy.transport: http
|
||||
caddy.reverse_proxy.transport.read_timeout: "0"
|
||||
caddy.reverse_proxy.transport.write_timeout: "0"
|
||||
caddy.reverse_proxy.transport.keepalive: "5m"
|
||||
caddy.reverse_proxy.transport.keepalive_idle_conns: "10"
|
||||
caddy.reverse_proxy.stream_timeout: "24h"
|
||||
caddy.reverse_proxy.stream_close_delay: "5s"
|
||||
15
docs-site/docker-compose.prod.yml
Normal file
15
docs-site/docker-compose.prod.yml
Normal file
@ -0,0 +1,15 @@
|
||||
services:
|
||||
docs:
|
||||
labels:
|
||||
caddy.reverse_proxy: "{{upstreams 8080}}"
|
||||
caddy.header.Cache-Control: "public, max-age=31536000, immutable"
|
||||
caddy.header.X-Content-Type-Options: nosniff
|
||||
caddy.header.X-Frame-Options: DENY
|
||||
caddy.header.Referrer-Policy: strict-origin-when-cross-origin
|
||||
security_opt:
|
||||
- no-new-privileges:true
|
||||
read_only: true
|
||||
tmpfs:
|
||||
- /tmp:noexec,nosuid,size=50m
|
||||
- /config/caddy:size=10m
|
||||
- /data/caddy:size=10m
|
||||
24
docs-site/docker-compose.yml
Normal file
24
docs-site/docker-compose.yml
Normal file
@ -0,0 +1,24 @@
|
||||
services:
|
||||
docs:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
target: ${MODE:-dev}
|
||||
container_name: spicebook-docs
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- ASTRO_TELEMETRY_DISABLED=1
|
||||
networks:
|
||||
- caddy
|
||||
labels:
|
||||
caddy: ${DOMAIN}
|
||||
caddy.encode: gzip
|
||||
logging:
|
||||
driver: json-file
|
||||
options:
|
||||
max-size: "5m"
|
||||
max-file: "3"
|
||||
|
||||
networks:
|
||||
caddy:
|
||||
external: true
|
||||
7988
docs-site/package-lock.json
generated
Normal file
7988
docs-site/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
19
docs-site/package.json
Normal file
19
docs-site/package.json
Normal file
@ -0,0 +1,19 @@
|
||||
{
|
||||
"name": "spicebook-docs",
|
||||
"type": "module",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"postinstall": "node patches/fix-starlight-head.mjs",
|
||||
"dev": "astro dev --host 0.0.0.0",
|
||||
"build": "astro build",
|
||||
"preview": "astro preview --host 0.0.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"astro": "^5.17.2",
|
||||
"@astrojs/starlight": "^0.37.6",
|
||||
"@astrojs/starlight-tailwind": "^4.0.2",
|
||||
"@tailwindcss/vite": "^4.1.0",
|
||||
"tailwindcss": "^4.1.0",
|
||||
"sharp": "^0.33.0"
|
||||
}
|
||||
}
|
||||
35
docs-site/patches/fix-starlight-head.mjs
Normal file
35
docs-site/patches/fix-starlight-head.mjs
Normal file
@ -0,0 +1,35 @@
|
||||
/**
|
||||
* Patches @astrojs/starlight to handle undefined `data.head` in frontmatter.
|
||||
*
|
||||
* Starlight 0.37.x assumes docsSchema() defaults `head` to [] via Zod, but
|
||||
* the content loader doesn't always apply this default, causing:
|
||||
* "Cannot read properties of undefined (reading 'some')"
|
||||
*
|
||||
* This adds a nullish coalescing fallback: `data.head ?? []`
|
||||
*/
|
||||
|
||||
import { readFileSync, writeFileSync } from 'node:fs';
|
||||
|
||||
const file = 'node_modules/@astrojs/starlight/utils/head.ts';
|
||||
|
||||
try {
|
||||
let src = readFileSync(file, 'utf8');
|
||||
const target = 'config.head, data.head)';
|
||||
const replacement = 'config.head, data.head ?? [])';
|
||||
|
||||
if (src.includes(replacement)) {
|
||||
console.log('[patch] head.ts already patched, skipping.');
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
if (!src.includes(target)) {
|
||||
console.warn('[patch] Could not find target string in head.ts — Starlight may have been updated.');
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
src = src.replace(target, replacement);
|
||||
writeFileSync(file, src, 'utf8');
|
||||
console.log('[patch] Patched head.ts: data.head ?? []');
|
||||
} catch (err) {
|
||||
console.warn('[patch] Could not patch head.ts:', err.message);
|
||||
}
|
||||
4
docs-site/public/robots.txt
Normal file
4
docs-site/public/robots.txt
Normal file
@ -0,0 +1,4 @@
|
||||
User-agent: *
|
||||
Allow: /
|
||||
|
||||
Sitemap: https://spicebook-docs.warehack.ing/sitemap-index.xml
|
||||
7
docs-site/src/content.config.ts
Normal file
7
docs-site/src/content.config.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import { defineCollection } from 'astro:content';
|
||||
import { docsLoader } from '@astrojs/starlight/loaders';
|
||||
import { docsSchema } from '@astrojs/starlight/schema';
|
||||
|
||||
export const collections = {
|
||||
docs: defineCollection({ loader: docsLoader(), schema: docsSchema() }),
|
||||
};
|
||||
120
docs-site/src/content/docs/api/cells.mdx
Normal file
120
docs-site/src/content/docs/api/cells.mdx
Normal file
@ -0,0 +1,120 @@
|
||||
---
|
||||
title: Cells
|
||||
description: Add, update, delete, and reorder cells within SpiceBook notebooks via the REST API.
|
||||
---
|
||||
|
||||
import { Aside } from '@astrojs/starlight/components';
|
||||
|
||||
Cells are ordered elements within a notebook. Each cell has a `type` and `source` (text content). The API lets you manipulate cells individually without replacing the entire notebook.
|
||||
|
||||
## Cell Types
|
||||
|
||||
| Type | Description |
|
||||
|------|-------------|
|
||||
| `markdown` | Documentation text, rendered as formatted markdown |
|
||||
| `spice` | SPICE netlist, executable via the simulation endpoints |
|
||||
| `python` | Python code (execution planned for future release) |
|
||||
| `schematic` | Auto-generated circuit diagram (read-only display) |
|
||||
|
||||
## Add Cell
|
||||
|
||||
```
|
||||
POST /api/notebooks/{notebook_id}/cells
|
||||
```
|
||||
|
||||
**Request body:**
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "spice",
|
||||
"source": "V1 1 0 DC 5\nR1 1 0 1k\n.op\n.end",
|
||||
"after_cell_id": "cell-a1b2c3d4e5f6"
|
||||
}
|
||||
```
|
||||
|
||||
`after_cell_id` is optional — omit it to append the cell at the end.
|
||||
|
||||
**Response** `201`:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "cell-b2c3d4e5f6a7",
|
||||
"type": "spice",
|
||||
"source": "V1 1 0 DC 5\nR1 1 0 1k\n.op\n.end",
|
||||
"outputs": []
|
||||
}
|
||||
```
|
||||
|
||||
## Update Cell
|
||||
|
||||
```
|
||||
PUT /api/notebooks/{notebook_id}/cells/{cell_id}
|
||||
```
|
||||
|
||||
**Request body:**
|
||||
|
||||
```json
|
||||
{
|
||||
"source": "V1 1 0 DC 10\nR1 1 0 2k\n.op\n.end",
|
||||
"type": "spice"
|
||||
}
|
||||
```
|
||||
|
||||
Both fields are optional — only provided fields are updated. Changing `source` does not automatically re-run the simulation; call the [run endpoint](/api/simulation/#run-cell-in-notebook) to execute.
|
||||
|
||||
**Response** `200` — updated cell object.
|
||||
|
||||
## Delete Cell
|
||||
|
||||
```
|
||||
DELETE /api/notebooks/{notebook_id}/cells/{cell_id}
|
||||
```
|
||||
|
||||
**Response** `204` — no content.
|
||||
|
||||
## Reorder Cells
|
||||
|
||||
```
|
||||
PUT /api/notebooks/{notebook_id}/cells/reorder
|
||||
```
|
||||
|
||||
**Request body:**
|
||||
|
||||
```json
|
||||
{
|
||||
"cell_ids": ["cell-b2c3d4e5f6a7", "cell-a1b2c3d4e5f6"]
|
||||
}
|
||||
```
|
||||
|
||||
The array must include every cell ID in the notebook exactly once. This replaces the cell order entirely.
|
||||
|
||||
**Response** `200` — array of cell objects in the new order.
|
||||
|
||||
<Aside type="note">
|
||||
Cell IDs are generated server-side and are stable — they don't change when cells are reordered or when other cells are added/removed.
|
||||
</Aside>
|
||||
|
||||
## Cell Data Model
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "cell-a1b2c3d4e5f6",
|
||||
"type": "spice",
|
||||
"source": "V1 1 0 DC 5\n.op\n.end",
|
||||
"outputs": [
|
||||
{
|
||||
"output_type": "simulation_result",
|
||||
"data": {
|
||||
"success": true,
|
||||
"waveform": { "..." },
|
||||
"log": "...",
|
||||
"error": null,
|
||||
"elapsed_seconds": 0.3
|
||||
},
|
||||
"timestamp": "2026-02-13T18:35:00+00:00"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
The `outputs` array contains results from previous simulation runs. Each entry includes a timestamp so you can track when the cell was last executed.
|
||||
149
docs-site/src/content/docs/api/notebooks.mdx
Normal file
149
docs-site/src/content/docs/api/notebooks.mdx
Normal file
@ -0,0 +1,149 @@
|
||||
---
|
||||
title: Notebooks
|
||||
description: Create, read, update, delete, and compose SpiceBook notebooks via the REST API.
|
||||
---
|
||||
|
||||
import { Aside } from '@astrojs/starlight/components';
|
||||
|
||||
## List Notebooks
|
||||
|
||||
```
|
||||
GET /api/notebooks
|
||||
```
|
||||
|
||||
Returns an array of notebook summaries:
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"id": "rc-low-pass-a1b2c3d4",
|
||||
"title": "RC Low-Pass Filter",
|
||||
"engine": "ngspice",
|
||||
"tags": ["filter", "rc"],
|
||||
"cell_count": 3,
|
||||
"modified": "2026-02-13T18:30:00+00:00"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
## Create Notebook
|
||||
|
||||
```
|
||||
POST /api/notebooks
|
||||
```
|
||||
|
||||
**Request body:**
|
||||
|
||||
```json
|
||||
{
|
||||
"title": "My Circuit",
|
||||
"engine": "ngspice"
|
||||
}
|
||||
```
|
||||
|
||||
Both fields are optional. Defaults: title = `"Untitled Notebook"`, engine = `"ngspice"`. Supported engines: `"ngspice"`, `"ltspice"`.
|
||||
|
||||
**Response** `201` — the full notebook with an auto-generated `id`:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "my-circuit-f8e2a91b",
|
||||
"spicebook_version": "2026-02-13",
|
||||
"metadata": {
|
||||
"title": "My Circuit",
|
||||
"engine": "ngspice",
|
||||
"tags": [],
|
||||
"created": "2026-02-13T18:30:00+00:00",
|
||||
"modified": "2026-02-13T18:30:00+00:00"
|
||||
},
|
||||
"cells": [
|
||||
{
|
||||
"id": "cell-a1b2c3d4e5f6",
|
||||
"type": "markdown",
|
||||
"source": "# My Circuit\n\nAdd SPICE cells below to begin simulating.",
|
||||
"outputs": []
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
New notebooks start with a single markdown cell containing a heading.
|
||||
|
||||
## Get Notebook
|
||||
|
||||
```
|
||||
GET /api/notebooks/{notebook_id}
|
||||
```
|
||||
|
||||
**Response** `200` — full notebook object (same shape as the create response, without the top-level `id` wrapper).
|
||||
|
||||
## Update Notebook
|
||||
|
||||
```
|
||||
PUT /api/notebooks/{notebook_id}
|
||||
```
|
||||
|
||||
**Request body** — full notebook object. This replaces the entire notebook, including all cells and metadata.
|
||||
|
||||
**Response** `200` — the saved notebook.
|
||||
|
||||
## Delete Notebook
|
||||
|
||||
```
|
||||
DELETE /api/notebooks/{notebook_id}
|
||||
```
|
||||
|
||||
**Response** `204` — no content.
|
||||
|
||||
<Aside type="caution">
|
||||
Only user-created notebooks can be deleted. Built-in example notebooks return a `403` error.
|
||||
</Aside>
|
||||
|
||||
## Compose Notebook
|
||||
|
||||
```
|
||||
POST /api/notebooks/compose
|
||||
```
|
||||
|
||||
Creates a fully-populated notebook with multiple cells in a single call. This is the preferred way to create notebooks programmatically.
|
||||
|
||||
**Request body:**
|
||||
|
||||
```json
|
||||
{
|
||||
"title": "RC Low-Pass Filter",
|
||||
"engine": "ngspice",
|
||||
"tags": ["filter", "rc", "analog"],
|
||||
"cells": [
|
||||
{
|
||||
"type": "markdown",
|
||||
"source": "# RC Low-Pass Filter\n\nA simple first-order low-pass filter."
|
||||
},
|
||||
{
|
||||
"type": "spice",
|
||||
"source": "V1 in 0 AC 1\nR1 in out 1k\nC1 out 0 1u\n.ac dec 100 1 1meg\n.end"
|
||||
},
|
||||
{
|
||||
"type": "markdown",
|
||||
"source": "## Analysis\n\nThe -3dB cutoff is at f = 1/(2*pi*R*C) = 159 Hz."
|
||||
}
|
||||
],
|
||||
"run": false
|
||||
}
|
||||
```
|
||||
|
||||
| Field | Type | Default | Description |
|
||||
|-------|------|---------|-------------|
|
||||
| `title` | string | `"Untitled Notebook"` | Notebook title |
|
||||
| `engine` | string | `"ngspice"` | Simulation engine |
|
||||
| `tags` | string[] | `[]` | Searchable tags |
|
||||
| `cells` | object[] | (required) | Cells to create, each with `type` and `source` |
|
||||
| `run` | boolean | `false` | If `true`, execute each SPICE cell after creation |
|
||||
|
||||
**Response** `201` — same shape as `POST /api/notebooks`.
|
||||
|
||||
When `run` is `true`, each SPICE cell is executed sequentially using the notebook's engine. Simulation results are stored in each cell's `outputs` array. Non-SPICE cells are unaffected.
|
||||
|
||||
<Aside type="tip">
|
||||
The compose endpoint is what [mcltspice](https://mcltspice.warehack.ing) uses to publish simulation results as SpiceBook notebooks. Pass `run: true` to auto-execute all SPICE cells on creation.
|
||||
</Aside>
|
||||
66
docs-site/src/content/docs/api/overview.mdx
Normal file
66
docs-site/src/content/docs/api/overview.mdx
Normal file
@ -0,0 +1,66 @@
|
||||
---
|
||||
title: REST API Overview
|
||||
description: SpiceBook REST API basics — base URL, response format, and endpoint structure.
|
||||
---
|
||||
|
||||
import { LinkCard, CardGrid, Aside } from '@astrojs/starlight/components';
|
||||
|
||||
SpiceBook exposes a REST API for programmatic access to notebooks, simulation, and rendering. All endpoints accept and return JSON unless noted otherwise.
|
||||
|
||||
## Base URL
|
||||
|
||||
```
|
||||
https://spicebook.warehack.ing/api
|
||||
```
|
||||
|
||||
All endpoint paths below are relative to this base.
|
||||
|
||||
## Authentication
|
||||
|
||||
None. The API is open — no API keys or tokens are required. This makes it straightforward to integrate with scripts, CI pipelines, or AI assistants like [mcltspice](https://mcltspice.warehack.ing).
|
||||
|
||||
## Response Format
|
||||
|
||||
Successful responses return JSON with the appropriate HTTP status code (`200`, `201`, or `204`). Error responses include a detail message:
|
||||
|
||||
```json
|
||||
{
|
||||
"detail": "Notebook not found"
|
||||
}
|
||||
```
|
||||
|
||||
Common status codes:
|
||||
|
||||
| Code | Meaning |
|
||||
|------|---------|
|
||||
| `200` | Success |
|
||||
| `201` | Created (new notebook or cell) |
|
||||
| `204` | Deleted (no response body) |
|
||||
| `404` | Resource not found |
|
||||
| `422` | Validation error (malformed request body) |
|
||||
| `500` | Internal server error (simulation crash, etc.) |
|
||||
|
||||
## Health Check
|
||||
|
||||
```
|
||||
GET /health
|
||||
```
|
||||
|
||||
Returns the API version and status:
|
||||
|
||||
```json
|
||||
{"status": "ok", "version": "2026.02.13"}
|
||||
```
|
||||
|
||||
## Endpoint Groups
|
||||
|
||||
<CardGrid>
|
||||
<LinkCard title="Notebooks" description="Create, read, update, delete, and compose notebooks." href="/api/notebooks/" />
|
||||
<LinkCard title="Cells" description="Add, update, delete, and reorder cells within notebooks." href="/api/cells/" />
|
||||
<LinkCard title="Simulation" description="Run SPICE netlists standalone or within notebook cells." href="/api/simulation/" />
|
||||
<LinkCard title="Waveforms & Schematics" description="Generate SVG plots and circuit diagrams." href="/api/waveforms-schematics/" />
|
||||
</CardGrid>
|
||||
|
||||
<Aside type="tip">
|
||||
The SpiceBook backend also serves interactive API docs at [`/docs`](https://spicebook.warehack.ing/docs) (Swagger UI) and [`/openapi.json`](https://spicebook.warehack.ing/openapi.json) (OpenAPI spec).
|
||||
</Aside>
|
||||
113
docs-site/src/content/docs/api/simulation.mdx
Normal file
113
docs-site/src/content/docs/api/simulation.mdx
Normal file
@ -0,0 +1,113 @@
|
||||
---
|
||||
title: Simulation
|
||||
description: Run SPICE netlists standalone or within notebook cells via the SpiceBook REST API.
|
||||
---
|
||||
|
||||
import { Aside } from '@astrojs/starlight/components';
|
||||
|
||||
## Run Standalone Simulation
|
||||
|
||||
```
|
||||
POST /api/simulate
|
||||
```
|
||||
|
||||
Run a SPICE netlist without creating or modifying a notebook. Useful for one-off simulations or integration testing.
|
||||
|
||||
**Request body:**
|
||||
|
||||
```json
|
||||
{
|
||||
"netlist": "V1 1 0 DC 5\nR1 1 2 1k\nR2 2 0 2k\n.op\n.end",
|
||||
"engine": "ngspice"
|
||||
}
|
||||
```
|
||||
|
||||
**Response** `200`:
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"waveform": {
|
||||
"variables": [
|
||||
{"name": "v(1)", "type": "voltage"},
|
||||
{"name": "v(2)", "type": "voltage"}
|
||||
],
|
||||
"points": 1,
|
||||
"x_data": [0.0],
|
||||
"y_data": {"v(1)": [5.0], "v(2)": [3.333]},
|
||||
"x_type": "time",
|
||||
"is_complex": false,
|
||||
"y_magnitude_db": null,
|
||||
"y_phase_deg": null
|
||||
},
|
||||
"log": "ngspice output...",
|
||||
"error": null,
|
||||
"elapsed_seconds": 0.42
|
||||
}
|
||||
```
|
||||
|
||||
## Run Cell in Notebook
|
||||
|
||||
```
|
||||
POST /api/notebooks/{notebook_id}/cells/{cell_id}/run
|
||||
```
|
||||
|
||||
No request body. Uses the cell's `source` as the netlist and the notebook's `engine` setting.
|
||||
|
||||
**Response** `200` — same `SimulationResponse` shape as standalone. The cell's `outputs` array is updated in the saved notebook.
|
||||
|
||||
## SimulationResponse
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `success` | boolean | Whether the simulation completed without errors |
|
||||
| `waveform` | WaveformData \| null | Simulation results (null if simulation failed) |
|
||||
| `log` | string | Raw simulator output (stdout/stderr) |
|
||||
| `error` | string \| null | Error message if simulation failed |
|
||||
| `elapsed_seconds` | number | Wall-clock simulation time |
|
||||
|
||||
## WaveformData
|
||||
|
||||
```json
|
||||
{
|
||||
"variables": [{"name": "v(out)", "type": "voltage"}],
|
||||
"points": 100,
|
||||
"x_data": [0.0, 0.001, ...],
|
||||
"y_data": {"v(out)": [0.0, 0.5, ...]},
|
||||
"x_type": "time",
|
||||
"is_complex": false,
|
||||
"y_magnitude_db": null,
|
||||
"y_phase_deg": null
|
||||
}
|
||||
```
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `variables` | array | Signal names and types (voltage, current) |
|
||||
| `points` | integer | Number of data points |
|
||||
| `x_data` | number[] | Horizontal axis values (time or frequency) |
|
||||
| `y_data` | object | Signal name → value array mapping |
|
||||
| `x_type` | string | `"time"` or `"frequency"` |
|
||||
| `is_complex` | boolean | `true` for AC analysis |
|
||||
| `y_magnitude_db` | object \| null | Per-signal magnitude in dB (AC only) |
|
||||
| `y_phase_deg` | object \| null | Per-signal phase in degrees (AC only) |
|
||||
|
||||
<Aside type="note">
|
||||
For AC analysis (`is_complex: true`), use `y_magnitude_db` and `y_phase_deg` for Bode plots rather than `y_data`. The `x_type` will be `"frequency"` and `x_data` contains frequency values in Hz.
|
||||
</Aside>
|
||||
|
||||
## Error Handling
|
||||
|
||||
When a simulation fails (syntax error, convergence failure, missing model), the response still returns `200` but with `success: false`:
|
||||
|
||||
```json
|
||||
{
|
||||
"success": false,
|
||||
"waveform": null,
|
||||
"log": "Error: unknown subcircuit...",
|
||||
"error": "Simulation failed: unknown subcircuit 'LM741'",
|
||||
"elapsed_seconds": 0.1
|
||||
}
|
||||
```
|
||||
|
||||
Check the `log` field for the full simulator output — it often contains line numbers and detailed diagnostics that aren't in the `error` summary.
|
||||
129
docs-site/src/content/docs/api/waveforms-schematics.mdx
Normal file
129
docs-site/src/content/docs/api/waveforms-schematics.mdx
Normal file
@ -0,0 +1,129 @@
|
||||
---
|
||||
title: Waveforms & Schematics
|
||||
description: Generate SVG waveform plots and circuit schematic diagrams via the SpiceBook REST API.
|
||||
---
|
||||
|
||||
import { Aside } from '@astrojs/starlight/components';
|
||||
|
||||
## Generate Waveform SVG (JSON-Wrapped)
|
||||
|
||||
```
|
||||
POST /api/waveforms/svg
|
||||
```
|
||||
|
||||
Renders waveform data as an SVG plot, returned inside a JSON wrapper.
|
||||
|
||||
**Request body:**
|
||||
|
||||
```json
|
||||
{
|
||||
"waveform": { "...WaveformData from simulation response..." },
|
||||
"title": "Output Voltage",
|
||||
"width": 800,
|
||||
"height": 500,
|
||||
"signals": ["v(2)"]
|
||||
}
|
||||
```
|
||||
|
||||
| Field | Type | Default | Description |
|
||||
|-------|------|---------|-------------|
|
||||
| `waveform` | WaveformData | (required) | Simulation result data |
|
||||
| `title` | string | `""` | Plot title |
|
||||
| `width` | integer | `800` | SVG width in pixels |
|
||||
| `height` | integer | `500` | SVG height in pixels |
|
||||
| `signals` | string[] | all | Signals to include in the plot |
|
||||
|
||||
**Response** `200`:
|
||||
|
||||
```json
|
||||
{
|
||||
"svg": "<svg xmlns=\"http://www.w3.org/2000/svg\" ...>...</svg>"
|
||||
}
|
||||
```
|
||||
|
||||
## Generate Waveform SVG (Raw)
|
||||
|
||||
```
|
||||
POST /api/waveforms/svg/raw
|
||||
```
|
||||
|
||||
Same request body as above, but returns the raw SVG string with `Content-Type: image/svg+xml`. Useful for piping directly to a file:
|
||||
|
||||
```bash
|
||||
curl -X POST https://spicebook.warehack.ing/api/waveforms/svg/raw \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"waveform": { ...waveform data... },
|
||||
"title": "DC Sweep",
|
||||
"signals": ["v(2)"]
|
||||
}' \
|
||||
-o plot.svg
|
||||
```
|
||||
|
||||
## Generate Schematic
|
||||
|
||||
```
|
||||
POST /api/notebooks/{notebook_id}/cells/{cell_id}/schematic
|
||||
```
|
||||
|
||||
No request body. The cell must be type `spice`. Parses the cell's netlist and renders a circuit diagram.
|
||||
|
||||
**Response** `200`:
|
||||
|
||||
```json
|
||||
{
|
||||
"svg": "<svg xmlns=\"http://www.w3.org/2000/svg\" ...>...</svg>",
|
||||
"success": true,
|
||||
"error": null,
|
||||
"component_map": {
|
||||
"R1": "resistor",
|
||||
"V1": "voltage_source",
|
||||
"C1": "capacitor"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `svg` | string | SVG markup of the circuit diagram |
|
||||
| `success` | boolean | Whether generation succeeded |
|
||||
| `error` | string \| null | Error message if generation failed |
|
||||
| `component_map` | object | Component reference → type mapping |
|
||||
|
||||
The SVG includes standard IEEE symbols for components. Resistors with parseable values (0.01Ω–1GΩ) render with [color-coded bands](/schematics/resistor-colors/) on the zigzag symbol.
|
||||
|
||||
<Aside type="tip">
|
||||
Both waveform and schematic SVGs are self-contained — they include all styling inline and can be saved as standalone `.svg` files or embedded directly in HTML.
|
||||
</Aside>
|
||||
|
||||
## End-to-End Example
|
||||
|
||||
Simulate a circuit and generate both a waveform plot and schematic:
|
||||
|
||||
```bash
|
||||
# 1. Create a notebook with a SPICE cell and run it
|
||||
RESPONSE=$(curl -s -X POST https://spicebook.warehack.ing/api/notebooks/compose \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"title": "RC Filter",
|
||||
"cells": [
|
||||
{"type": "spice", "source": "V1 in 0 AC 1\nR1 in out 1k\nC1 out 0 1u\n.ac dec 100 1 1meg\n.end"}
|
||||
],
|
||||
"run": true
|
||||
}')
|
||||
|
||||
# 2. Extract IDs
|
||||
NB_ID=$(echo $RESPONSE | jq -r '.id')
|
||||
CELL_ID=$(echo $RESPONSE | jq -r '.cells[0].id')
|
||||
|
||||
# 3. Generate schematic
|
||||
curl -s -X POST "https://spicebook.warehack.ing/api/notebooks/$NB_ID/cells/$CELL_ID/schematic" \
|
||||
| jq -r '.svg' > schematic.svg
|
||||
|
||||
# 4. Generate waveform plot from simulation results
|
||||
WAVEFORM=$(echo $RESPONSE | jq '.cells[0].outputs[0].data.waveform')
|
||||
curl -s -X POST https://spicebook.warehack.ing/api/waveforms/svg/raw \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{\"waveform\": $WAVEFORM, \"title\": \"Bode Plot\"}" \
|
||||
-o bode.svg
|
||||
```
|
||||
54
docs-site/src/content/docs/embedding/embed-notebook.mdx
Normal file
54
docs-site/src/content/docs/embedding/embed-notebook.mdx
Normal file
@ -0,0 +1,54 @@
|
||||
---
|
||||
title: Embed a Notebook
|
||||
description: Embed interactive SpiceBook notebooks on any webpage with a simple iframe snippet.
|
||||
---
|
||||
|
||||
import { Aside, Code } from '@astrojs/starlight/components';
|
||||
|
||||
Any SpiceBook notebook can be embedded on a third-party page via iframe. No API key or authentication is required — embeds are public and read-only, but viewers can still run simulations and see results.
|
||||
|
||||
## Embed URL
|
||||
|
||||
```
|
||||
https://spicebook.warehack.ing/embed/{notebook_id}?theme=dark
|
||||
```
|
||||
|
||||
| Parameter | Values | Default | Description |
|
||||
|-----------|--------|---------|-------------|
|
||||
| `theme` | `dark`, `light` | `dark` | Initial color theme |
|
||||
|
||||
## iframe Snippet
|
||||
|
||||
```html
|
||||
<iframe
|
||||
src="https://spicebook.warehack.ing/embed/{notebook_id}?theme=dark"
|
||||
width="100%" height="600"
|
||||
style="border: 1px solid #334155; border-radius: 8px;"
|
||||
allow="clipboard-write"
|
||||
></iframe>
|
||||
```
|
||||
|
||||
Replace `{notebook_id}` with your notebook's ID (visible in the browser URL bar when editing, or returned by the API).
|
||||
|
||||
<Aside type="tip">
|
||||
The easiest way to get the embed snippet is from SpiceBook itself — open any notebook, click the **Embed** button in the toolbar, and copy the pre-filled iframe code.
|
||||
</Aside>
|
||||
|
||||
## What Viewers Can Do
|
||||
|
||||
Embedded notebooks render in a read-only mode with working simulation:
|
||||
|
||||
- **Read** markdown cells and SPICE netlist source
|
||||
- **Run** SPICE cells and see waveform results
|
||||
- **View** previously-generated schematics and plots
|
||||
- **Copy** code from cells via clipboard
|
||||
|
||||
Viewers cannot edit cell content, add or delete cells, or change notebook metadata.
|
||||
|
||||
## Sizing
|
||||
|
||||
The iframe adapts to the width of its container. Set `width="100%"` for responsive layouts. The `height` should be tall enough to show the notebook content without excessive scrolling — `600px` works well for notebooks with 2-4 cells.
|
||||
|
||||
## CORS
|
||||
|
||||
The SpiceBook backend allows framing from any origin by default. If your deployment uses a restricted `CORS_EXTRA_ORIGINS` configuration, ensure the embedding domain is included.
|
||||
77
docs-site/src/content/docs/embedding/theme-sync.mdx
Normal file
77
docs-site/src/content/docs/embedding/theme-sync.mdx
Normal file
@ -0,0 +1,77 @@
|
||||
---
|
||||
title: Theme Sync
|
||||
description: Control an embedded SpiceBook notebook's theme from the parent page using the postMessage API.
|
||||
---
|
||||
|
||||
import { Aside } from '@astrojs/starlight/components';
|
||||
|
||||
Embedded notebooks support runtime theme switching via the browser's `postMessage` API. This lets the parent page keep the embed's appearance in sync with its own dark/light mode toggle.
|
||||
|
||||
## Setting the Initial Theme
|
||||
|
||||
Use the `theme` query parameter on the embed URL:
|
||||
|
||||
```
|
||||
https://spicebook.warehack.ing/embed/{id}?theme=light
|
||||
```
|
||||
|
||||
This sets the theme when the iframe first loads. To change it after load, use `postMessage`.
|
||||
|
||||
## Switching Themes at Runtime
|
||||
|
||||
Send a message to the iframe's content window:
|
||||
|
||||
```javascript
|
||||
const iframe = document.querySelector('iframe');
|
||||
|
||||
iframe.contentWindow.postMessage(
|
||||
{ type: 'spicebook-theme', theme: 'light' },
|
||||
'*'
|
||||
);
|
||||
```
|
||||
|
||||
### Message Format
|
||||
|
||||
| Field | Type | Values | Description |
|
||||
|-------|------|--------|-------------|
|
||||
| `type` | string | `"spicebook-theme"` | Message identifier (required) |
|
||||
| `theme` | string | `"dark"`, `"light"` | Target theme |
|
||||
|
||||
Messages with any other `type` value are ignored by the embed.
|
||||
|
||||
## Example: Sync with Parent Toggle
|
||||
|
||||
A common pattern is to listen for your page's theme change and forward it to the embed:
|
||||
|
||||
```javascript
|
||||
// Assuming your page toggles a data-theme attribute on <html>
|
||||
const observer = new MutationObserver(() => {
|
||||
const theme = document.documentElement.dataset.theme || 'dark';
|
||||
iframe.contentWindow.postMessage(
|
||||
{ type: 'spicebook-theme', theme },
|
||||
'*'
|
||||
);
|
||||
});
|
||||
|
||||
observer.observe(document.documentElement, {
|
||||
attributes: true,
|
||||
attributeFilter: ['data-theme'],
|
||||
});
|
||||
```
|
||||
|
||||
<Aside type="note">
|
||||
The embed responds to theme messages immediately — there's no transition delay. The entire UI (backgrounds, text, code blocks, waveform plots) switches to the requested theme.
|
||||
</Aside>
|
||||
|
||||
## Security
|
||||
|
||||
The `postMessage` call uses `'*'` as the target origin for simplicity. If you want stricter security, specify the SpiceBook origin:
|
||||
|
||||
```javascript
|
||||
iframe.contentWindow.postMessage(
|
||||
{ type: 'spicebook-theme', theme: 'dark' },
|
||||
'https://spicebook.warehack.ing'
|
||||
);
|
||||
```
|
||||
|
||||
The embed only acts on messages with `type: 'spicebook-theme'` — all other messages are silently ignored.
|
||||
59
docs-site/src/content/docs/getting-started/editor-basics.mdx
Normal file
59
docs-site/src/content/docs/getting-started/editor-basics.mdx
Normal file
@ -0,0 +1,59 @@
|
||||
---
|
||||
title: Editor Basics
|
||||
description: Cell types, toolbar actions, and keyboard shortcuts in the SpiceBook notebook editor.
|
||||
---
|
||||
|
||||
import { Aside } from '@astrojs/starlight/components';
|
||||
|
||||
A SpiceBook notebook is a vertical sequence of cells. Each cell has a type that determines how it behaves and renders.
|
||||
|
||||
## Cell Types
|
||||
|
||||
| Type | Purpose | Editor |
|
||||
|------|---------|--------|
|
||||
| **Markdown** | Documentation, headings, LaTeX-style math | Rich text with live preview |
|
||||
| **SPICE** | Circuit netlists for simulation | Code editor with SPICE syntax highlighting |
|
||||
| **Python** | Computation and scripting | Code editor (execution planned) |
|
||||
| **Schematic** | Auto-generated circuit diagrams | Read-only SVG display |
|
||||
|
||||
Markdown cells render immediately as you type. SPICE cells require you to click **Run** to execute the netlist and see simulation results.
|
||||
|
||||
## Toolbar
|
||||
|
||||
The notebook toolbar sits at the top of the editor and provides:
|
||||
|
||||
- **Run All** — executes every SPICE cell in order, top to bottom
|
||||
- **Engine selector** — switch between ngspice and LTspice for the entire notebook
|
||||
- **Embed** — opens a popover with a ready-to-copy iframe snippet for sharing
|
||||
- **Tags** — add searchable tags to organize notebooks on the homepage
|
||||
|
||||
Each individual SPICE cell also has its own run and schematic buttons in the cell toolbar.
|
||||
|
||||
## Adding and Reordering Cells
|
||||
|
||||
Click **+ Add Cell** between any two cells to insert a new one. The dropdown lets you choose the cell type.
|
||||
|
||||
Cells can be reordered by dragging, or programmatically via the [reorder API endpoint](/api/cells/#reorder-cells).
|
||||
|
||||
To delete a cell, use the cell menu (three dots) and select **Delete**.
|
||||
|
||||
## Code Editor
|
||||
|
||||
SPICE cells use a code editor with:
|
||||
|
||||
- **Syntax highlighting** for SPICE netlist keywords (`.tran`, `.ac`, `.op`, etc.)
|
||||
- **Line numbers** for referencing specific netlist lines
|
||||
- **Auto-indentation** following SPICE conventions
|
||||
|
||||
<Aside type="note">
|
||||
The editor uses the SpiceBook dark theme by default — slate backgrounds with a blue cursor. The color scheme matches the rest of the application.
|
||||
</Aside>
|
||||
|
||||
## Notebook Metadata
|
||||
|
||||
Every notebook tracks:
|
||||
|
||||
- **Title** — displayed in the gallery and browser tab
|
||||
- **Engine** — `ngspice` or `ltspice`, applies to all SPICE cells
|
||||
- **Tags** — free-form labels for filtering in the notebook gallery
|
||||
- **Created / Modified** — ISO-8601 timestamps, set automatically
|
||||
67
docs-site/src/content/docs/getting-started/quick-start.mdx
Normal file
67
docs-site/src/content/docs/getting-started/quick-start.mdx
Normal file
@ -0,0 +1,67 @@
|
||||
---
|
||||
title: Quick Start
|
||||
description: Create a notebook, write a SPICE netlist, run a simulation, and see waveforms — in under five minutes.
|
||||
---
|
||||
|
||||
import { Steps, Aside } from '@astrojs/starlight/components';
|
||||
|
||||
This guide walks through creating your first SpiceBook notebook. You'll build a voltage divider, simulate it, and see the results — all in the browser.
|
||||
|
||||
<Steps>
|
||||
|
||||
1. **Open SpiceBook**
|
||||
|
||||
Go to [spicebook.warehack.ing](https://spicebook.warehack.ing) and click **New Notebook**. You'll get a blank notebook with a single markdown cell.
|
||||
|
||||
2. **Add a SPICE cell**
|
||||
|
||||
Click the **+ Add Cell** button and select **SPICE**. Paste this netlist:
|
||||
|
||||
```
|
||||
* Voltage Divider
|
||||
V1 1 0 DC 5
|
||||
R1 1 2 1k
|
||||
R2 2 0 2k
|
||||
.op
|
||||
.end
|
||||
```
|
||||
|
||||
This creates a 5V source with two resistors. The `.op` directive runs a DC operating point analysis — it computes the steady-state voltages at every node.
|
||||
|
||||
3. **Run the simulation**
|
||||
|
||||
Click the **Run** button on the SPICE cell (or use the keyboard shortcut). The backend sends the netlist to ngspice and returns the results:
|
||||
|
||||
- `V(1) = 5.0V` — the supply voltage
|
||||
- `V(2) = 3.333V` — the divided voltage: `5 × 2k / (1k + 2k)`
|
||||
|
||||
4. **Try a transient analysis**
|
||||
|
||||
Replace the netlist with a time-domain simulation:
|
||||
|
||||
```
|
||||
* RC Charging Curve
|
||||
V1 1 0 PULSE(0 5 0 1n 1n 5m 10m)
|
||||
R1 1 2 1k
|
||||
C1 2 0 1u
|
||||
.tran 1u 20m
|
||||
.end
|
||||
```
|
||||
|
||||
Run it again. This time you'll see a waveform plot showing the capacitor voltage rising exponentially toward 5V with a time constant of `RC = 1ms`.
|
||||
|
||||
5. **Generate a schematic**
|
||||
|
||||
Click the **Schematic** button on any SPICE cell. SpiceBook parses the netlist and draws a circuit diagram as an SVG — resistors render as IEEE zigzag symbols with color-coded bands matching their values.
|
||||
|
||||
</Steps>
|
||||
|
||||
<Aside type="tip">
|
||||
You can also create notebooks programmatically via the [compose endpoint](/api/notebooks/#compose-notebook), which accepts multiple cells and can auto-run SPICE cells in a single API call.
|
||||
</Aside>
|
||||
|
||||
## Next Steps
|
||||
|
||||
- **[Editor Basics](/getting-started/editor-basics/)** — cell types, toolbar actions, keyboard shortcuts
|
||||
- **[Engines](/simulation/engines/)** — choosing between ngspice and LTspice
|
||||
- **[Waveform Viewer](/simulation/waveforms/)** — interactive plots, oscilloscope mode, AC bode plots
|
||||
50
docs-site/src/content/docs/index.mdx
Normal file
50
docs-site/src/content/docs/index.mdx
Normal file
@ -0,0 +1,50 @@
|
||||
---
|
||||
title: What is SpiceBook?
|
||||
description: Notebook interface for SPICE circuit simulation — write netlists, simulate with ngspice or LTspice, and visualize waveforms in a single document.
|
||||
template: splash
|
||||
hero:
|
||||
tagline: Write SPICE netlists, simulate, and visualize waveforms — all in one document.
|
||||
actions:
|
||||
- text: Quick Start
|
||||
link: /getting-started/quick-start/
|
||||
icon: right-arrow
|
||||
variant: primary
|
||||
- text: Open SpiceBook
|
||||
link: https://spicebook.warehack.ing
|
||||
icon: external
|
||||
variant: minimal
|
||||
---
|
||||
|
||||
import { Card, CardGrid, LinkCard } from '@astrojs/starlight/components';
|
||||
|
||||
## What SpiceBook Does
|
||||
|
||||
SpiceBook is a notebook interface for SPICE circuit simulation. Each notebook is a sequence of cells — markdown for documentation, SPICE netlists for circuits, and auto-generated schematics and waveform plots for visualization.
|
||||
|
||||
You write a netlist, run it against ngspice or LTspice, and see the results inline. No file management, no separate plotting tools, no context switching between editor and simulator.
|
||||
|
||||
<CardGrid>
|
||||
<Card title="Dual Engines" icon="setting">
|
||||
Choose ngspice (built-in) or LTspice (via [mcltspice](https://mcltspice.warehack.ing)) per notebook. Same netlist format, different solver characteristics.
|
||||
</Card>
|
||||
<Card title="Waveform Plots" icon="line-chart">
|
||||
Transient, DC sweep, and AC analysis results render as interactive SVG plots with an oscilloscope display mode.
|
||||
</Card>
|
||||
<Card title="Auto Schematics" icon="puzzle">
|
||||
SPICE netlists are parsed into circuit diagrams automatically — resistors even render with IEEE zigzag symbols and 4-band color codes.
|
||||
</Card>
|
||||
<Card title="Embeddable" icon="laptop">
|
||||
Any notebook can be embedded on a third-party page via iframe. Viewers can run simulations directly in the embed.
|
||||
</Card>
|
||||
</CardGrid>
|
||||
|
||||
## How It Fits Together
|
||||
|
||||
SpiceBook is part of the [warehack.ing](https://warehack.ing) project family. It pairs with [mcltspice](https://mcltspice.warehack.ing), an MCP server that gives AI assistants access to LTspice simulation — notebooks created by mcltspice are viewable and editable in SpiceBook.
|
||||
|
||||
<CardGrid>
|
||||
<LinkCard title="Quick Start" description="Create a notebook and run your first simulation." href="/getting-started/quick-start/" />
|
||||
<LinkCard title="REST API" description="Programmatic access to notebooks, simulation, and rendering." href="/api/overview/" />
|
||||
<LinkCard title="Embedding" description="Embed interactive notebooks on any page." href="/embedding/embed-notebook/" />
|
||||
<LinkCard title="Engines" description="ngspice vs LTspice — differences and how to choose." href="/simulation/engines/" />
|
||||
</CardGrid>
|
||||
62
docs-site/src/content/docs/schematics/auto-generated.mdx
Normal file
62
docs-site/src/content/docs/schematics/auto-generated.mdx
Normal file
@ -0,0 +1,62 @@
|
||||
---
|
||||
title: Auto-Generated Diagrams
|
||||
description: How SpiceBook converts SPICE netlists into circuit diagram SVGs automatically.
|
||||
---
|
||||
|
||||
import { Aside } from '@astrojs/starlight/components';
|
||||
|
||||
SpiceBook can generate a schematic diagram from any SPICE cell. Click the **Schematic** button in the cell toolbar, and the backend parses the netlist into a circuit diagram rendered as an inline SVG.
|
||||
|
||||
## How It Works
|
||||
|
||||
The schematic generator:
|
||||
|
||||
1. **Parses the netlist** to identify components (resistors, capacitors, inductors, voltage sources, current sources) and their node connections
|
||||
2. **Determines layout** by walking the circuit graph and positioning components to minimize wire crossings
|
||||
3. **Renders SVG** with standard electrical engineering symbols — IEEE zigzag for resistors, parallel plates for capacitors, coil for inductors, ± symbols for sources
|
||||
4. **Labels components** with their reference designator (`R1`, `C1`, `V1`) and value
|
||||
|
||||
The result is a clean, readable diagram that matches what you'd draw by hand.
|
||||
|
||||
## Supported Components
|
||||
|
||||
| Component | Netlist Prefix | Symbol |
|
||||
|-----------|---------------|--------|
|
||||
| Resistor | `R` | IEEE zigzag (with optional color bands) |
|
||||
| Capacitor | `C` | Parallel plates |
|
||||
| Inductor | `L` | Coil |
|
||||
| Voltage source | `V` | Circle with ± |
|
||||
| Current source | `I` | Circle with arrow |
|
||||
| Ground | node `0` | Standard ground symbol |
|
||||
|
||||
## Component Map
|
||||
|
||||
The schematic endpoint returns a `component_map` alongside the SVG, listing each component and its type:
|
||||
|
||||
```json
|
||||
{
|
||||
"svg": "<svg ...>...</svg>",
|
||||
"success": true,
|
||||
"component_map": {
|
||||
"R1": "resistor",
|
||||
"V1": "voltage_source",
|
||||
"C1": "capacitor"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This is useful for programmatic tools that need to know what's in the circuit without parsing the SVG.
|
||||
|
||||
## API Access
|
||||
|
||||
Generate schematics programmatically:
|
||||
|
||||
```bash
|
||||
curl -X POST https://spicebook.warehack.ing/api/notebooks/{id}/cells/{cell_id}/schematic
|
||||
```
|
||||
|
||||
The cell must be type `spice`. No request body is needed — the cell's `source` netlist is used directly. See the [Waveforms & Schematics API reference](/api/waveforms-schematics/) for the full response format.
|
||||
|
||||
<Aside type="note">
|
||||
Schematic generation works for both ngspice and LTspice netlists. The generator parses the netlist text directly rather than relying on engine-specific features, so it works identically with either engine.
|
||||
</Aside>
|
||||
71
docs-site/src/content/docs/schematics/resistor-colors.mdx
Normal file
71
docs-site/src/content/docs/schematics/resistor-colors.mdx
Normal file
@ -0,0 +1,71 @@
|
||||
---
|
||||
title: Color-Coded Resistors
|
||||
description: IEEE zigzag resistor symbols with 4-band color codes rendered directly on schematic diagrams.
|
||||
---
|
||||
|
||||
import { Aside } from '@astrojs/starlight/components';
|
||||
|
||||
When SpiceBook generates a schematic, resistors with parseable values render as IEEE zigzag symbols with color-coded segments matching the standard 4-band resistor color code.
|
||||
|
||||
## How It Works
|
||||
|
||||
A resistor value like `1k` (1000Ω) is decomposed into:
|
||||
- **1st significant digit:** 1 → Brown
|
||||
- **2nd significant digit:** 0 → Black
|
||||
- **Multiplier:** ×100 → Red
|
||||
- **Tolerance:** ±5% → Gold (default)
|
||||
|
||||
These colors are applied directly to the zigzag segments in the SVG, mimicking how a physical resistor looks.
|
||||
|
||||
## Band-to-Segment Mapping
|
||||
|
||||
The IEEE zigzag symbol has 7 sub-segments. Each is assigned a color role:
|
||||
|
||||
| Segment | Role | Color |
|
||||
|---------|------|-------|
|
||||
| 0 (entry) | Wire | Default wire color |
|
||||
| 1 | 1st significant digit | Color band |
|
||||
| 2 | 2nd significant digit | Color band |
|
||||
| 3 | Multiplier | Color band |
|
||||
| 4 (gap) | Physical spacing | Default wire color |
|
||||
| 5 | Tolerance | Color band |
|
||||
| 6 (exit) | Wire | Default wire color |
|
||||
|
||||
The entry/exit segments and the gap before the tolerance band use the default wire color. This mimics the physical spacing on a real resistor that indicates reading direction — you read bands from the end closest to the first band.
|
||||
|
||||
## Color Code Reference
|
||||
|
||||
| Digit | Color | Hex |
|
||||
|-------|-------|-----|
|
||||
| 0 | Black | `#1a1a1a` |
|
||||
| 1 | Brown | `#8B4513` |
|
||||
| 2 | Red | `#cc0000` |
|
||||
| 3 | Orange | `#ff6600` |
|
||||
| 4 | Yellow | `#cccc00` |
|
||||
| 5 | Green | `#006400` |
|
||||
| 6 | Blue | `#0000cc` |
|
||||
| 7 | Violet | `#7b1fa2` |
|
||||
| 8 | Gray | `#555555` |
|
||||
| 9 | White | `#e0e0e0` |
|
||||
|
||||
**Multiplier bands** use the same color mapping (Black = ×1, Brown = ×10, Red = ×100, etc.).
|
||||
|
||||
**Tolerance:** Gold (±5%) is the default when no tolerance is specified.
|
||||
|
||||
## Value Range
|
||||
|
||||
Color-coded rendering applies to resistor values between **0.01Ω and 1GΩ**. Outside this range, or for parametric values (e.g., `{R_val}`), the resistor falls back to a standard monochrome zigzag.
|
||||
|
||||
<Aside type="tip">
|
||||
SpiceBook also has a dedicated [Resistor Color Codes reference page](https://spicebook.warehack.ing/reference/resistor-colors) in the main application where you can look up any value interactively.
|
||||
</Aside>
|
||||
|
||||
## Example
|
||||
|
||||
A `4.7k` resistor renders with:
|
||||
- **Yellow** (4) — 1st digit
|
||||
- **Violet** (7) — 2nd digit
|
||||
- **Red** (×100) — multiplier
|
||||
- **Gold** (±5%) — tolerance
|
||||
|
||||
The result is immediately recognizable to anyone who's read resistor bands by hand — the schematic tells you both the circuit topology and the component values at a glance.
|
||||
60
docs-site/src/content/docs/simulation/engines.mdx
Normal file
60
docs-site/src/content/docs/simulation/engines.mdx
Normal file
@ -0,0 +1,60 @@
|
||||
---
|
||||
title: Engines
|
||||
description: ngspice vs LTspice — differences, availability, and how to choose a simulation engine in SpiceBook.
|
||||
---
|
||||
|
||||
import { Aside } from '@astrojs/starlight/components';
|
||||
|
||||
SpiceBook supports two SPICE simulation engines. Each notebook is bound to one engine, selected when you create it (or changed later via the toolbar).
|
||||
|
||||
## ngspice
|
||||
|
||||
The default engine. ngspice is an open-source circuit simulator that runs natively on the SpiceBook backend — no additional setup required.
|
||||
|
||||
**Strengths:**
|
||||
- Always available — built into the backend container
|
||||
- Broad analysis support: `.op`, `.dc`, `.tran`, `.ac`, `.noise`, `.pz`
|
||||
- Well-documented netlist syntax with extensive model libraries
|
||||
- Fast for small-to-medium circuits
|
||||
|
||||
**Typical use:** General-purpose simulation, educational circuits, quick prototyping.
|
||||
|
||||
## LTspice
|
||||
|
||||
Available when the SpiceBook backend is configured with [mcltspice](https://mcltspice.warehack.ing), an MCP server that automates LTspice via Wine on Linux.
|
||||
|
||||
**Strengths:**
|
||||
- Proprietary solver with strong convergence for switching circuits
|
||||
- Extensive built-in component library (power semiconductors, op-amps, regulators)
|
||||
- Better performance on large circuits with many timesteps
|
||||
- Tight `.meas` directive support for automated measurements
|
||||
|
||||
**Typical use:** Power electronics, switching regulators, circuits using manufacturer models.
|
||||
|
||||
<Aside type="caution">
|
||||
LTspice availability depends on the server configuration. If mcltspice isn't running, selecting "ltspice" as the engine will return an error when you try to simulate. The engine selector in the toolbar shows which engines are available.
|
||||
</Aside>
|
||||
|
||||
## Differences That Matter
|
||||
|
||||
| Feature | ngspice | LTspice |
|
||||
|---------|---------|---------|
|
||||
| Availability | Always | Requires mcltspice |
|
||||
| Operating point (`.op`) | Yes | Yes |
|
||||
| Transient (`.tran`) | Yes | Yes |
|
||||
| AC sweep (`.ac`) | Yes | Yes |
|
||||
| Convergence on switchers | Fair | Excellent |
|
||||
| Built-in device models | Community libs | Extensive vendor libs |
|
||||
| Output format | Raw binary | Raw binary |
|
||||
|
||||
Both engines produce the same `WaveformData` structure in SpiceBook — the waveform viewer, SVG plots, and schematic generation work identically regardless of which engine ran the simulation.
|
||||
|
||||
## Choosing an Engine
|
||||
|
||||
For most circuits, ngspice works well and requires no configuration. Reach for LTspice when you need:
|
||||
|
||||
- A specific manufacturer model that ships with LTspice (LT1234, LTC3780, etc.)
|
||||
- Better convergence on circuits with fast switching edges
|
||||
- `.meas` directives for automated waveform measurements
|
||||
|
||||
You can switch engines on an existing notebook at any time — the netlist syntax is largely compatible. Re-run cells after switching to regenerate results with the new engine.
|
||||
118
docs-site/src/content/docs/simulation/running.mdx
Normal file
118
docs-site/src/content/docs/simulation/running.mdx
Normal file
@ -0,0 +1,118 @@
|
||||
---
|
||||
title: Running Simulations
|
||||
description: How to execute SPICE cells, interpret results, and use Run All for multi-cell notebooks.
|
||||
---
|
||||
|
||||
import { Aside } from '@astrojs/starlight/components';
|
||||
|
||||
## Running a Single Cell
|
||||
|
||||
Every SPICE cell has a **Run** button in its toolbar. Clicking it sends the cell's netlist to the backend, which:
|
||||
|
||||
1. Writes the netlist to a temp file
|
||||
2. Invokes the selected engine (ngspice or LTspice)
|
||||
3. Parses the raw output into structured `WaveformData`
|
||||
4. Returns results to the browser and saves them in the cell's `outputs` array
|
||||
|
||||
Results appear inline beneath the cell — either a data table (for `.op`) or a waveform plot (for `.tran`, `.dc`, `.ac`).
|
||||
|
||||
## Run All
|
||||
|
||||
The toolbar's **Run All** button executes every SPICE cell in the notebook from top to bottom, sequentially. Each cell's results are saved before the next cell runs.
|
||||
|
||||
This is useful for notebooks where later cells depend on understanding earlier results, or when you want to regenerate all outputs after changing the engine.
|
||||
|
||||
## Analysis Types
|
||||
|
||||
The analysis directive in your netlist determines what kind of results you get:
|
||||
|
||||
### DC Operating Point (`.op`)
|
||||
|
||||
```
|
||||
V1 1 0 DC 5
|
||||
R1 1 2 1k
|
||||
R2 2 0 2k
|
||||
.op
|
||||
.end
|
||||
```
|
||||
|
||||
Returns a single set of node voltages and branch currents. Displayed as a table.
|
||||
|
||||
### Transient (`.tran`)
|
||||
|
||||
```
|
||||
V1 1 0 PULSE(0 5 0 1n 1n 5u 10u)
|
||||
R1 1 2 1k
|
||||
C1 2 0 100n
|
||||
.tran 10n 50u
|
||||
.end
|
||||
```
|
||||
|
||||
Returns time-series data. Displayed as an XY waveform plot with time on the horizontal axis.
|
||||
|
||||
### DC Sweep (`.dc`)
|
||||
|
||||
```
|
||||
V1 1 0 DC 5
|
||||
R1 1 2 1k
|
||||
R2 2 0 2k
|
||||
.dc V1 0 10 0.5
|
||||
.end
|
||||
```
|
||||
|
||||
Sweeps a source value and returns node voltages at each step. Displayed as a waveform plot with the swept parameter on the horizontal axis.
|
||||
|
||||
### AC Analysis (`.ac`)
|
||||
|
||||
```
|
||||
V1 in 0 AC 1
|
||||
R1 in out 1k
|
||||
C1 out 0 1u
|
||||
.ac dec 100 1 1meg
|
||||
.end
|
||||
```
|
||||
|
||||
Returns complex frequency-domain data. Displayed as a Bode plot — magnitude in dB and phase in degrees versus frequency on a log scale.
|
||||
|
||||
<Aside type="tip">
|
||||
AC analysis results include `y_magnitude_db` and `y_phase_deg` fields in the waveform data. The viewer automatically switches to Bode plot mode when these are present.
|
||||
</Aside>
|
||||
|
||||
## Output Format
|
||||
|
||||
Every simulation returns a `SimulationResponse`:
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"waveform": {
|
||||
"variables": [{"name": "v(out)", "type": "voltage"}],
|
||||
"points": 100,
|
||||
"x_data": [0.0, 0.001, ...],
|
||||
"y_data": {"v(out)": [0.0, 0.5, ...]},
|
||||
"x_type": "time",
|
||||
"is_complex": false
|
||||
},
|
||||
"log": "ngspice output...",
|
||||
"error": null,
|
||||
"elapsed_seconds": 0.42
|
||||
}
|
||||
```
|
||||
|
||||
If the simulation fails (syntax error, convergence failure), `success` is `false` and `error` contains the diagnostic message. The simulator log is always available in the `log` field for debugging.
|
||||
|
||||
## Engineering Notation
|
||||
|
||||
SPICE uses its own suffix convention for component values — be aware that **`M` means milli** (10⁻³), not mega. Use `meg` for 10⁶.
|
||||
|
||||
| Suffix | Multiplier | Example |
|
||||
|--------|-----------|---------|
|
||||
| `T` | 10¹² | `1T` |
|
||||
| `G` | 10⁹ | `2.2G` |
|
||||
| `meg` | 10⁶ | `1meg` |
|
||||
| `k` | 10³ | `4.7k` |
|
||||
| `m` | 10⁻³ | `10m` |
|
||||
| `u` | 10⁻⁶ | `100u` |
|
||||
| `n` | 10⁻⁹ | `47n` |
|
||||
| `p` | 10⁻¹² | `10p` |
|
||||
| `f` | 10⁻¹⁵ | `1f` |
|
||||
76
docs-site/src/content/docs/simulation/waveforms.mdx
Normal file
76
docs-site/src/content/docs/simulation/waveforms.mdx
Normal file
@ -0,0 +1,76 @@
|
||||
---
|
||||
title: Waveform Viewer
|
||||
description: Interactive waveform plots, oscilloscope display mode, and AC Bode plots in SpiceBook.
|
||||
---
|
||||
|
||||
import { Aside } from '@astrojs/starlight/components';
|
||||
|
||||
After running a simulation, SpiceBook renders the results as interactive SVG plots directly beneath the SPICE cell. The viewer adapts to the analysis type automatically.
|
||||
|
||||
## Plot Types
|
||||
|
||||
### Time-Domain (Transient / DC Sweep)
|
||||
|
||||
Transient (`.tran`) and DC sweep (`.dc`) results display as standard XY plots:
|
||||
|
||||
- **X-axis:** time (seconds) or swept parameter value
|
||||
- **Y-axis:** node voltages and branch currents
|
||||
- **Multiple traces** are overlaid with distinct colors from an 8-color palette
|
||||
|
||||
Each trace is labeled with its signal name (`v(out)`, `i(R1)`, etc.).
|
||||
|
||||
### Frequency-Domain (AC Analysis)
|
||||
|
||||
AC analysis (`.ac`) results display as a Bode plot with two subplots:
|
||||
|
||||
- **Top:** Magnitude in dB vs frequency (log scale)
|
||||
- **Bottom:** Phase in degrees vs frequency (log scale)
|
||||
|
||||
The viewer detects AC data automatically when `is_complex` is `true` in the waveform data and switches to this dual-plot layout.
|
||||
|
||||
<Aside type="tip">
|
||||
The -3dB point on a magnitude plot marks the cutoff frequency of a filter. Look for where the magnitude trace drops 3dB below its passband level.
|
||||
</Aside>
|
||||
|
||||
### Oscilloscope Mode
|
||||
|
||||
The SpiceBook homepage features an animated oscilloscope display that renders waveform data in a CRT-style aesthetic — cyan traces on a dark background with phosphor-glow effects. This same rendering engine is available for waveform data throughout the application.
|
||||
|
||||
The oscilloscope uses a dedicated color palette:
|
||||
- **Axes:** `#475569` (slate-600)
|
||||
- **Grid:** `rgba(51, 65, 85, 0.5)` (slate-700 at 50%)
|
||||
- **Traces:** `#2dd4bf` (cyan-500 for the classic scope look)
|
||||
|
||||
## Trace Colors
|
||||
|
||||
Multi-signal plots use an 8-color palette designed for readability on both dark and light backgrounds:
|
||||
|
||||
| Trace | Color |
|
||||
|-------|-------|
|
||||
| 1 | Blue (`#2563eb`) |
|
||||
| 2 | Red (`#dc2626`) |
|
||||
| 3 | Green (`#16a34a`) |
|
||||
| 4 | Amber (`#ca8a04`) |
|
||||
| 5 | Cyan (`#0891b2`) |
|
||||
| 6 | Rose (`#e11d48`) |
|
||||
| 7 | Slate (`#4b5563`) |
|
||||
| 8 | Orange (`#ea580c`) |
|
||||
|
||||
## SVG Generation via API
|
||||
|
||||
Waveform plots can also be generated server-side via the REST API. Send the `waveform` data from a simulation response to the SVG endpoint:
|
||||
|
||||
```bash
|
||||
curl -X POST https://spicebook.warehack.ing/api/waveforms/svg/raw \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"waveform": { ...waveform data... },
|
||||
"title": "RC Filter Response",
|
||||
"width": 800,
|
||||
"height": 500,
|
||||
"signals": ["v(out)"]
|
||||
}' \
|
||||
-o plot.svg
|
||||
```
|
||||
|
||||
The `signals` array lets you select which traces to include. Omit it to plot all signals. See the [Waveforms & Schematics API reference](/api/waveforms-schematics/) for full details.
|
||||
43
docs-site/src/styles/custom.css
Normal file
43
docs-site/src/styles/custom.css
Normal file
@ -0,0 +1,43 @@
|
||||
/* SpiceBook docs -- blue accent, warm slate grays
|
||||
* Matches the SpiceBook application theme */
|
||||
|
||||
@import "@astrojs/starlight-tailwind";
|
||||
@import "tailwindcss";
|
||||
|
||||
:root {
|
||||
/* Dark mode (Starlight default: :root = dark)
|
||||
* Gray scale: gray-1 = highest contrast (lightest), gray-7 = lowest */
|
||||
--sl-color-accent-low: #172554; /* blue-950 — dark accent bg */
|
||||
--sl-color-accent: #60a5fa; /* blue-400 — bright on dark bg */
|
||||
--sl-color-accent-high: #dbeafe; /* blue-100 — light accent text */
|
||||
|
||||
--sl-color-gray-1: #f1f5f9; /* slate-100 */
|
||||
--sl-color-gray-2: #e2e8f0; /* slate-200 */
|
||||
--sl-color-gray-3: #94a3b8; /* slate-400 */
|
||||
--sl-color-gray-4: #475569; /* slate-600 */
|
||||
--sl-color-gray-5: #1e293b; /* slate-800 */
|
||||
--sl-color-gray-6: #0f172a; /* slate-900 */
|
||||
--sl-color-gray-7: #020617; /* slate-950 */
|
||||
}
|
||||
|
||||
:root[data-theme="light"] {
|
||||
/* Light mode: gray-1 = highest contrast (darkest) */
|
||||
--sl-color-accent-low: #dbeafe; /* blue-100 — light accent bg */
|
||||
--sl-color-accent: #2563eb; /* blue-600 — darker on light bg */
|
||||
--sl-color-accent-high: #1e3a5f; /* dark blue — dark accent text */
|
||||
|
||||
--sl-color-gray-1: #0f172a; /* slate-900 */
|
||||
--sl-color-gray-2: #1e293b; /* slate-800 */
|
||||
--sl-color-gray-3: #475569; /* slate-600 */
|
||||
--sl-color-gray-4: #94a3b8; /* slate-400 */
|
||||
--sl-color-gray-5: #cbd5e1; /* slate-300 */
|
||||
--sl-color-gray-6: #e2e8f0; /* slate-200 */
|
||||
--sl-color-gray-7: #f8fafc; /* slate-50 */
|
||||
}
|
||||
|
||||
/* Inline code styling */
|
||||
:not(pre) > code {
|
||||
background: var(--sl-color-gray-5);
|
||||
padding: 0.15em 0.35em;
|
||||
border-radius: 0.25em;
|
||||
}
|
||||
5
docs-site/tsconfig.json
Normal file
5
docs-site/tsconfig.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"extends": "astro/tsconfigs/strict",
|
||||
"include": ["src/**/*"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
@ -132,6 +132,21 @@ try {
|
||||
</div>
|
||||
<Icon name="lucide:chevron-right" class="w-4 h-4 text-slate-600 group-hover:text-slate-400 transition-colors" />
|
||||
</a>
|
||||
|
||||
<a href="https://spicebook-docs.warehack.ing" target="_blank" rel="noopener noreferrer"
|
||||
class="inline-flex items-center gap-4 px-5 py-3 rounded-lg border border-slate-700
|
||||
hover:border-slate-500 hover:bg-slate-800/40 transition-colors group">
|
||||
<Icon name="lucide:book-open" class="w-8 h-8 text-blue-400/60 flex-shrink-0" />
|
||||
<div>
|
||||
<span class="text-sm font-semibold text-slate-200 group-hover:text-slate-100">
|
||||
Docs
|
||||
</span>
|
||||
<span class="block text-xs text-slate-500">
|
||||
Guides, API reference, embedding
|
||||
</span>
|
||||
</div>
|
||||
<Icon name="lucide:chevron-right" class="w-4 h-4 text-slate-600 group-hover:text-slate-400 transition-colors" />
|
||||
</a>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user