Compare commits
2 Commits
fe6d20afbd
...
5f5e0da3c4
| Author | SHA1 | Date | |
|---|---|---|---|
| 5f5e0da3c4 | |||
| 2ddc08cd3a |
@ -47,6 +47,8 @@ class NotebookSummary(BaseModel):
|
|||||||
tags: list[str]
|
tags: list[str]
|
||||||
cell_count: int
|
cell_count: int
|
||||||
modified: str
|
modified: str
|
||||||
|
description: str | None = None
|
||||||
|
schematic_svg: str | None = None
|
||||||
|
|
||||||
|
|
||||||
class CreateNotebookRequest(BaseModel):
|
class CreateNotebookRequest(BaseModel):
|
||||||
|
|||||||
@ -51,6 +51,44 @@ def _notebook_id_from_path(path: Path) -> str:
|
|||||||
return path.stem
|
return path.stem
|
||||||
|
|
||||||
|
|
||||||
|
def _extract_card_data(nb: Notebook) -> tuple[str | None, str | None]:
|
||||||
|
"""Extract a description snippet and schematic SVG from notebook cells.
|
||||||
|
|
||||||
|
- description: text from the first markdown cell, with the title header
|
||||||
|
stripped and truncated to ~200 characters.
|
||||||
|
- schematic_svg: SVG string from the first successful schematic output.
|
||||||
|
"""
|
||||||
|
description: str | None = None
|
||||||
|
schematic_svg: str | None = None
|
||||||
|
|
||||||
|
for cell in nb.cells:
|
||||||
|
if description is None and cell.type == CellType.MARKDOWN and cell.source.strip():
|
||||||
|
lines = cell.source.strip().splitlines()
|
||||||
|
# Strip leading "# Title" header line
|
||||||
|
if lines and lines[0].lstrip().startswith("#"):
|
||||||
|
lines = lines[1:]
|
||||||
|
text = " ".join(line.strip() for line in lines if line.strip())
|
||||||
|
# Strip basic markdown syntax
|
||||||
|
text = re.sub(r"[*_`~\[\]#>]", "", text).strip()
|
||||||
|
if text:
|
||||||
|
description = text[:200].rstrip()
|
||||||
|
|
||||||
|
if schematic_svg is None:
|
||||||
|
for output in cell.outputs:
|
||||||
|
if (
|
||||||
|
output.output_type == "schematic"
|
||||||
|
and output.data.get("success")
|
||||||
|
and output.data.get("svg")
|
||||||
|
):
|
||||||
|
schematic_svg = output.data["svg"]
|
||||||
|
break
|
||||||
|
|
||||||
|
if description is not None and schematic_svg is not None:
|
||||||
|
break
|
||||||
|
|
||||||
|
return description, schematic_svg
|
||||||
|
|
||||||
|
|
||||||
def list_notebooks(directory: Path) -> list[NotebookSummary]:
|
def list_notebooks(directory: Path) -> list[NotebookSummary]:
|
||||||
"""List all notebooks in the directory and its examples/ subdirectory."""
|
"""List all notebooks in the directory and its examples/ subdirectory."""
|
||||||
summaries: list[NotebookSummary] = []
|
summaries: list[NotebookSummary] = []
|
||||||
@ -66,6 +104,7 @@ def list_notebooks(directory: Path) -> list[NotebookSummary]:
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
nb = _read_notebook_file(path)
|
nb = _read_notebook_file(path)
|
||||||
|
description, schematic_svg = _extract_card_data(nb)
|
||||||
summaries.append(NotebookSummary(
|
summaries.append(NotebookSummary(
|
||||||
id=nb_id,
|
id=nb_id,
|
||||||
title=nb.metadata.title,
|
title=nb.metadata.title,
|
||||||
@ -73,6 +112,8 @@ def list_notebooks(directory: Path) -> list[NotebookSummary]:
|
|||||||
tags=nb.metadata.tags,
|
tags=nb.metadata.tags,
|
||||||
cell_count=len(nb.cells),
|
cell_count=len(nb.cells),
|
||||||
modified=nb.metadata.modified,
|
modified=nb.metadata.modified,
|
||||||
|
description=description,
|
||||||
|
schematic_svg=schematic_svg,
|
||||||
))
|
))
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.warning("Skipping corrupt notebook: %s", path)
|
logger.warning("Skipping corrupt notebook: %s", path)
|
||||||
|
|||||||
@ -4,23 +4,50 @@ services:
|
|||||||
context: ./backend
|
context: ./backend
|
||||||
dockerfile: Dockerfile
|
dockerfile: Dockerfile
|
||||||
target: dev
|
target: dev
|
||||||
ports:
|
expose:
|
||||||
- "${BACKEND_PORT:-8099}:8000"
|
- "8000"
|
||||||
volumes:
|
volumes:
|
||||||
- ./backend/src:/app/src
|
- ./backend/src:/app/src
|
||||||
- ./notebooks:/app/notebooks
|
- ./notebooks:/app/notebooks
|
||||||
# Mount mcltspice for live development
|
# Mount mcltspice for live development
|
||||||
- ../mcp-ltspice/src/mcltspice:/app/mcltspice-lib:ro
|
- ../mcp-ltspice/src/mcltspice:/app/mcltspice-lib:ro
|
||||||
command: ["uv", "run", "uvicorn", "spicebook.main:app", "--host", "0.0.0.0", "--port", "8000", "--reload"]
|
environment:
|
||||||
|
- PYTHONPATH=/app/src
|
||||||
|
command: ["python", "-m", "uvicorn", "spicebook.main:app", "--host", "0.0.0.0", "--port", "8000", "--reload"]
|
||||||
|
networks:
|
||||||
|
- default
|
||||||
|
- caddy
|
||||||
|
labels:
|
||||||
|
caddy: "${SPICEBOOK_DOMAIN:-spicebook.localhost}"
|
||||||
|
caddy.@api.path: "/api/* /health /docs /openapi.json /redoc"
|
||||||
|
caddy.reverse_proxy_0: "@api {{upstreams 8000}}"
|
||||||
|
|
||||||
frontend:
|
frontend:
|
||||||
build:
|
build:
|
||||||
context: ./frontend
|
context: ./frontend
|
||||||
dockerfile: Dockerfile
|
dockerfile: Dockerfile
|
||||||
target: dev
|
target: dev
|
||||||
ports:
|
expose:
|
||||||
- "${FRONTEND_PORT:-4321}:4321"
|
- "4321"
|
||||||
volumes:
|
volumes:
|
||||||
- ./frontend/src:/app/src
|
- ./frontend/src:/app/src
|
||||||
- ./frontend/public:/app/public
|
- ./frontend/public:/app/public
|
||||||
command: ["npm", "run", "dev", "--", "--host", "0.0.0.0"]
|
command: ["npm", "run", "dev", "--", "--host", "0.0.0.0"]
|
||||||
|
networks:
|
||||||
|
- default
|
||||||
|
- caddy
|
||||||
|
labels:
|
||||||
|
caddy: "${SPICEBOOK_DOMAIN:-spicebook.localhost}"
|
||||||
|
caddy.reverse_proxy_1: "{{upstreams 4321}}"
|
||||||
|
caddy.reverse_proxy_1.flush_interval: "-1"
|
||||||
|
caddy.reverse_proxy_1.transport: "http"
|
||||||
|
caddy.reverse_proxy_1.transport.read_timeout: "0"
|
||||||
|
caddy.reverse_proxy_1.transport.write_timeout: "0"
|
||||||
|
caddy.reverse_proxy_1.transport.keepalive: "5m"
|
||||||
|
caddy.reverse_proxy_1.transport.keepalive_idle_conns: "10"
|
||||||
|
caddy.reverse_proxy_1.stream_timeout: "24h"
|
||||||
|
caddy.reverse_proxy_1.stream_close_delay: "5s"
|
||||||
|
|
||||||
|
networks:
|
||||||
|
caddy:
|
||||||
|
external: true
|
||||||
|
|||||||
@ -25,14 +25,21 @@ interface NotebookCardProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function NotebookCard({ notebook }: NotebookCardProps) {
|
export function NotebookCard({ notebook }: NotebookCardProps) {
|
||||||
const { id, title, engine, tags, cell_count, modified } = notebook;
|
const { id, title, engine, tags, cell_count, modified, description, schematic_svg } = notebook;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<a
|
<a
|
||||||
href={`/notebook/${encodeURIComponent(id)}`}
|
href={`/notebook/${encodeURIComponent(id)}`}
|
||||||
className="group block rounded-lg border border-slate-800 bg-slate-900/50 overflow-hidden hover:border-blue-500/40 hover:bg-slate-800/70 transition-all"
|
className="group block rounded-lg border border-slate-800 bg-slate-900/50 overflow-hidden hover:border-blue-500/40 hover:bg-slate-800/70 transition-all"
|
||||||
>
|
>
|
||||||
<div className="notebook-card-strip" data-engine={engine} />
|
{schematic_svg ? (
|
||||||
|
<div
|
||||||
|
className="w-full h-[120px] bg-[#f8faf6] border-b border-slate-800/60 overflow-hidden flex items-center justify-center [&_svg]:max-w-full [&_svg]:max-h-full [&_svg]:w-auto [&_svg]:h-auto"
|
||||||
|
dangerouslySetInnerHTML={{ __html: schematic_svg }}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<div className="notebook-card-strip" data-engine={engine} />
|
||||||
|
)}
|
||||||
<div className="p-5">
|
<div className="p-5">
|
||||||
<div className="flex items-start justify-between mb-2">
|
<div className="flex items-start justify-between mb-2">
|
||||||
<h3 className="text-base font-semibold text-slate-100 group-hover:text-blue-400 transition-colors line-clamp-1">
|
<h3 className="text-base font-semibold text-slate-100 group-hover:text-blue-400 transition-colors line-clamp-1">
|
||||||
@ -43,6 +50,12 @@ export function NotebookCard({ notebook }: NotebookCardProps) {
|
|||||||
</Badge>
|
</Badge>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{description && (
|
||||||
|
<p className="text-sm text-slate-400 leading-relaxed mb-2 line-clamp-2">
|
||||||
|
{description}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
|
||||||
<div className="flex items-center gap-3 text-xs text-slate-500 mb-3">
|
<div className="flex items-center gap-3 text-xs text-slate-500 mb-3">
|
||||||
<span className="inline-flex items-center gap-1">
|
<span className="inline-flex items-center gap-1">
|
||||||
<LayoutGrid className="w-3 h-3" />
|
<LayoutGrid className="w-3 h-3" />
|
||||||
|
|||||||
199
frontend/src/components/ResistorColorGuide.astro
Normal file
199
frontend/src/components/ResistorColorGuide.astro
Normal file
@ -0,0 +1,199 @@
|
|||||||
|
---
|
||||||
|
/**
|
||||||
|
* Resistor Color Code Guide — educational reference section.
|
||||||
|
*
|
||||||
|
* Left column: annotated SVG showing a 10k resistor zigzag with labeled bands.
|
||||||
|
* Right column: compact reference table mapping colors to digits and multipliers.
|
||||||
|
*
|
||||||
|
* Colors match BAND_HEX from resistor_colors.py so dots match schematic rendering.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// BAND_HEX values from backend/src/spicebook/engine/resistor_colors.py
|
||||||
|
const bandHex: Record<string, string> = {
|
||||||
|
black: '#1a1a1a',
|
||||||
|
brown: '#8B4513',
|
||||||
|
red: '#CC0000',
|
||||||
|
orange: '#E67300',
|
||||||
|
yellow: '#B8960B',
|
||||||
|
green: '#006400',
|
||||||
|
blue: '#0000CC',
|
||||||
|
violet: '#7B00B3',
|
||||||
|
gray: '#666666',
|
||||||
|
white: '#D0D0D0',
|
||||||
|
gold: '#B8860B',
|
||||||
|
silver: '#808080',
|
||||||
|
};
|
||||||
|
|
||||||
|
const digitBands = [
|
||||||
|
{ color: 'Black', hex: bandHex.black, digit: 0, multiplier: '×1 \u03A9' },
|
||||||
|
{ color: 'Brown', hex: bandHex.brown, digit: 1, multiplier: '×10 \u03A9' },
|
||||||
|
{ color: 'Red', hex: bandHex.red, digit: 2, multiplier: '×100 \u03A9' },
|
||||||
|
{ color: 'Orange', hex: bandHex.orange, digit: 3, multiplier: '×1 k\u03A9' },
|
||||||
|
{ color: 'Yellow', hex: bandHex.yellow, digit: 4, multiplier: '×10 k\u03A9' },
|
||||||
|
{ color: 'Green', hex: bandHex.green, digit: 5, multiplier: '×100 k\u03A9' },
|
||||||
|
{ color: 'Blue', hex: bandHex.blue, digit: 6, multiplier: '×1 M\u03A9' },
|
||||||
|
{ color: 'Violet', hex: bandHex.violet, digit: 7, multiplier: '×10 M\u03A9' },
|
||||||
|
{ color: 'Gray', hex: bandHex.gray, digit: 8, multiplier: '\u2014' },
|
||||||
|
{ color: 'White', hex: bandHex.white, digit: 9, multiplier: '\u2014' },
|
||||||
|
];
|
||||||
|
|
||||||
|
const toleranceBands = [
|
||||||
|
{ color: 'Gold', hex: bandHex.gold, value: '\u00B15%' },
|
||||||
|
{ color: 'Silver', hex: bandHex.silver, value: '\u00B110%' },
|
||||||
|
];
|
||||||
|
---
|
||||||
|
|
||||||
|
<section class="max-w-6xl mx-auto px-6 py-16">
|
||||||
|
<p class="text-sm font-semibold tracking-widest text-blue-400 uppercase mb-2">Reference</p>
|
||||||
|
<h2 class="text-2xl font-bold text-slate-100 mb-3">Reading Resistor Color Codes</h2>
|
||||||
|
<p class="text-sm text-slate-400 mb-8 max-w-2xl leading-relaxed">
|
||||||
|
Resistors in SpiceBook schematics use color-coded bands — the same EIA standard
|
||||||
|
printed on physical components. Each colored segment of the zigzag symbol encodes
|
||||||
|
a digit, multiplier, or tolerance value.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="grid gap-8 lg:grid-cols-[1fr_1fr] items-start">
|
||||||
|
|
||||||
|
<!-- Left: Annotated resistor diagram -->
|
||||||
|
<div class="rcg-diagram-card">
|
||||||
|
<svg viewBox="0 0 460 200" xmlns="http://www.w3.org/2000/svg" class="w-full h-auto" aria-label="Annotated 10k ohm resistor showing 4 color bands">
|
||||||
|
|
||||||
|
<!-- Mims graph paper background -->
|
||||||
|
<defs>
|
||||||
|
<pattern id="rcg-minor" width="10" height="10" patternUnits="userSpaceOnUse">
|
||||||
|
<path d="M 10 0 V 10 H 0" fill="none" stroke="#d4e4d4" stroke-width="0.3"/>
|
||||||
|
</pattern>
|
||||||
|
<pattern id="rcg-major" width="50" height="50" patternUnits="userSpaceOnUse">
|
||||||
|
<path d="M 50 0 V 50 H 0" fill="none" stroke="#b0ccb0" stroke-width="0.5"/>
|
||||||
|
</pattern>
|
||||||
|
</defs>
|
||||||
|
<rect width="460" height="200" fill="#f8faf6" rx="4"/>
|
||||||
|
<rect width="460" height="200" fill="url(#rcg-minor)" rx="4"/>
|
||||||
|
<rect width="460" height="200" fill="url(#rcg-major)" rx="4"/>
|
||||||
|
|
||||||
|
<!-- Lead wires -->
|
||||||
|
<line x1="40" y1="100" x2="100" y2="100" stroke="#555" stroke-width="2"/>
|
||||||
|
<line x1="360" y1="100" x2="420" y2="100" stroke="#555" stroke-width="2"/>
|
||||||
|
|
||||||
|
<!-- Zigzag body — 7 segments like backend mapping -->
|
||||||
|
<!-- seg 0: entry half-rise (wire) -->
|
||||||
|
<polyline points="100,100 120,72" fill="none" stroke="#555" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<!-- seg 1: Band 1 - Brown (1st digit = 1) -->
|
||||||
|
<polyline points="120,72 160,128" fill="none" stroke={bandHex.brown} stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<!-- seg 2: Band 2 - Black (2nd digit = 0) -->
|
||||||
|
<polyline points="160,128 200,72" fill="none" stroke={bandHex.black} stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<!-- seg 3: Band 3 - Orange (multiplier = ×1kΩ) -->
|
||||||
|
<polyline points="200,72 240,128" fill="none" stroke={bandHex.orange} stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<!-- seg 4: gap (wire) -->
|
||||||
|
<polyline points="240,128 280,72" fill="none" stroke="#555" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<!-- seg 5: Band 4 - Gold (tolerance = ±5%) -->
|
||||||
|
<polyline points="280,72 320,128" fill="none" stroke={bandHex.gold} stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<!-- seg 6: exit half-rise (wire) -->
|
||||||
|
<polyline points="320,128 340,100" fill="none" stroke="#555" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
|
||||||
|
<!-- Lead wire continuation -->
|
||||||
|
<line x1="340" y1="100" x2="360" y2="100" stroke="#555" stroke-width="2"/>
|
||||||
|
|
||||||
|
<!-- Callout labels with lines -->
|
||||||
|
<!-- Band 1: Brown -->
|
||||||
|
<line x1="140" y1="68" x2="140" y2="36" stroke="#888" stroke-width="0.8" stroke-dasharray="2,2"/>
|
||||||
|
<text x="140" y="30" text-anchor="middle" font-family="ui-monospace, monospace" font-size="10" fill="#555" font-weight="600">1st digit</text>
|
||||||
|
<text x="140" y="18" text-anchor="middle" font-family="ui-monospace, monospace" font-size="8.5" fill={bandHex.brown}>Brown = 1</text>
|
||||||
|
|
||||||
|
<!-- Band 2: Black -->
|
||||||
|
<line x1="180" y1="132" x2="180" y2="164" stroke="#888" stroke-width="0.8" stroke-dasharray="2,2"/>
|
||||||
|
<text x="180" y="175" text-anchor="middle" font-family="ui-monospace, monospace" font-size="10" fill="#555" font-weight="600">2nd digit</text>
|
||||||
|
<text x="180" y="188" text-anchor="middle" font-family="ui-monospace, monospace" font-size="8.5" fill="#444">Black = 0</text>
|
||||||
|
|
||||||
|
<!-- Band 3: Orange -->
|
||||||
|
<line x1="220" y1="68" x2="220" y2="36" stroke="#888" stroke-width="0.8" stroke-dasharray="2,2"/>
|
||||||
|
<text x="220" y="30" text-anchor="middle" font-family="ui-monospace, monospace" font-size="10" fill="#555" font-weight="600">Multiplier</text>
|
||||||
|
<text x="220" y="18" text-anchor="middle" font-family="ui-monospace, monospace" font-size="8.5" fill={bandHex.orange}>Orange = ×1kΩ</text>
|
||||||
|
|
||||||
|
<!-- Band 4: Gold -->
|
||||||
|
<line x1="300" y1="132" x2="300" y2="164" stroke="#888" stroke-width="0.8" stroke-dasharray="2,2"/>
|
||||||
|
<text x="300" y="175" text-anchor="middle" font-family="ui-monospace, monospace" font-size="10" fill="#555" font-weight="600">Tolerance</text>
|
||||||
|
<text x="300" y="188" text-anchor="middle" font-family="ui-monospace, monospace" font-size="8.5" fill={bandHex.gold}>Gold = ±5%</text>
|
||||||
|
|
||||||
|
<!-- Result value label -->
|
||||||
|
<rect x="360" y="58" width="90" height="24" rx="3" fill="#f0f2ee" stroke="#b0ccb0" stroke-width="0.8"/>
|
||||||
|
<text x="405" y="74" text-anchor="middle" font-family="ui-monospace, monospace" font-size="11" fill="#333" font-weight="700">10 kΩ ±5%</text>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Right: Color code reference table -->
|
||||||
|
<div class="rcg-table-card">
|
||||||
|
<table class="w-full text-sm">
|
||||||
|
<thead>
|
||||||
|
<tr class="border-b border-slate-700/60">
|
||||||
|
<th class="rcg-th text-left py-2.5 pr-2">Color</th>
|
||||||
|
<th class="rcg-th text-center py-2.5 px-2">Digit</th>
|
||||||
|
<th class="rcg-th text-left py-2.5 pl-2">Multiplier</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{digitBands.map((band) => (
|
||||||
|
<tr class="border-b border-slate-800/40">
|
||||||
|
<td class="py-1.5 pr-2">
|
||||||
|
<span class="inline-flex items-center gap-2">
|
||||||
|
<span class="rcg-dot" style={`background: ${band.hex};`}></span>
|
||||||
|
<span class="text-slate-300 text-xs">{band.color}</span>
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td class="text-center py-1.5 px-2 text-slate-300 font-mono text-xs">{band.digit}</td>
|
||||||
|
<td class="py-1.5 pl-2 text-slate-400 font-mono text-xs">{band.multiplier}</td>
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<!-- Tolerance row -->
|
||||||
|
<div class="mt-4 pt-3 border-t border-slate-700/60">
|
||||||
|
<p class="text-xs font-semibold text-slate-500 uppercase tracking-wider mb-2">Tolerance</p>
|
||||||
|
<div class="flex gap-6">
|
||||||
|
{toleranceBands.map((band) => (
|
||||||
|
<span class="inline-flex items-center gap-2">
|
||||||
|
<span class="rcg-dot" style={`background: ${band.hex};`}></span>
|
||||||
|
<span class="text-slate-300 text-xs">{band.color}</span>
|
||||||
|
<span class="text-slate-400 font-mono text-xs">{band.value}</span>
|
||||||
|
</span>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.rcg-diagram-card {
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
border: 1px solid #1e293b;
|
||||||
|
overflow: hidden;
|
||||||
|
background: #f8faf6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rcg-table-card {
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
border: 1px solid #1e293b;
|
||||||
|
background: rgba(15, 23, 42, 0.5);
|
||||||
|
padding: 1.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rcg-th {
|
||||||
|
font-size: 0.7rem;
|
||||||
|
font-weight: 600;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.06em;
|
||||||
|
color: #64748b;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rcg-dot {
|
||||||
|
display: inline-block;
|
||||||
|
width: 10px;
|
||||||
|
height: 10px;
|
||||||
|
border-radius: 50%;
|
||||||
|
flex-shrink: 0;
|
||||||
|
border: 1px solid rgba(148, 163, 184, 0.2);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -34,6 +34,8 @@ export interface NotebookSummary {
|
|||||||
tags: string[];
|
tags: string[];
|
||||||
cell_count: number;
|
cell_count: number;
|
||||||
modified: string;
|
modified: string;
|
||||||
|
description?: string;
|
||||||
|
schematic_svg?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface WaveformVariable {
|
export interface WaveformVariable {
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import { Icon } from 'astro-icon/components';
|
|||||||
import NotebookLayout from '../layouts/NotebookLayout.astro';
|
import NotebookLayout from '../layouts/NotebookLayout.astro';
|
||||||
import NotebookGallery from '../components/NotebookGallery';
|
import NotebookGallery from '../components/NotebookGallery';
|
||||||
import PipelineStrip from '../components/PipelineStrip.astro';
|
import PipelineStrip from '../components/PipelineStrip.astro';
|
||||||
|
import ResistorColorGuide from '../components/ResistorColorGuide.astro';
|
||||||
import FeaturedNotebooks from '../components/FeaturedNotebooks.astro';
|
import FeaturedNotebooks from '../components/FeaturedNotebooks.astro';
|
||||||
import OscilloscopeDisplay from '../components/OscilloscopeDisplay.astro';
|
import OscilloscopeDisplay from '../components/OscilloscopeDisplay.astro';
|
||||||
import { fetchNotebookList } from '../lib/server-api';
|
import { fetchNotebookList } from '../lib/server-api';
|
||||||
@ -72,6 +73,11 @@ try {
|
|||||||
<PipelineStrip />
|
<PipelineStrip />
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<!-- Resistor Color Code Guide -->
|
||||||
|
<section class="border-b border-slate-800/60">
|
||||||
|
<ResistorColorGuide />
|
||||||
|
</section>
|
||||||
|
|
||||||
<!-- Featured Notebooks -->
|
<!-- Featured Notebooks -->
|
||||||
<section class="border-b border-slate-800/60">
|
<section class="border-b border-slate-800/60">
|
||||||
<FeaturedNotebooks />
|
<FeaturedNotebooks />
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user