Initial commit: Forrest Mims Electronics Reference Library
Astro site with e-book reader for classic electronics notebooks. 15 Mims notebooks + 1 Ugly's Electrical Reference, served via Docker/Caddy at mims.l.supported.systems. PDFs tracked with git-lfs.
2
.gitattributes
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
*.pdf filter=lfs diff=lfs merge=lfs -text
|
||||
*.PDF filter=lfs diff=lfs merge=lfs -text
|
||||
22
.gitignore
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
# Dependencies
|
||||
node_modules/
|
||||
|
||||
# Build output
|
||||
dist/
|
||||
.astro/
|
||||
|
||||
# Environment
|
||||
.env
|
||||
.env.local
|
||||
|
||||
# IDE
|
||||
.vscode/
|
||||
.idea/
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# Logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
BIN
notebooks/01_Basic_Semiconductor_Circuits.pdf
(Stored with Git LFS)
Normal file
BIN
notebooks/02_Op_Amp_IC_Circuits.pdf
(Stored with Git LFS)
Normal file
BIN
notebooks/03_555_Timer_Circuits.pdf
(Stored with Git LFS)
Normal file
BIN
notebooks/04_Formulas_Tables_Basic_Circuits.pdf
(Stored with Git LFS)
Normal file
BIN
notebooks/05_Communications_Projects.pdf
(Stored with Git LFS)
Normal file
BIN
notebooks/06_Solar_Cell_Projects.pdf
(Stored with Git LFS)
Normal file
BIN
notebooks/07_Optoelectronics_Circuits.pdf
(Stored with Git LFS)
Normal file
BIN
notebooks/08_Sensor_Projects.pdf
(Stored with Git LFS)
Normal file
BIN
notebooks/09_Magnet_And_Sensor_Projects.pdf
(Stored with Git LFS)
Normal file
BIN
notebooks/10_Optoelectronic_Circuits_1986.pdf
(Stored with Git LFS)
Normal file
BIN
notebooks/11_Basic_Semiconductor_1993.pdf
(Stored with Git LFS)
Normal file
BIN
notebooks/12_Op_Amp_IC_1985.pdf
(Stored with Git LFS)
Normal file
193
notebooks/README.md
Normal file
@ -0,0 +1,193 @@
|
||||
# Forrest M. Mims III - Engineer's Mini-Notebook Collection
|
||||
|
||||
A collection of the legendary hand-illustrated electronics notebooks originally published by Radio Shack in the 1980s-1990s. These notebooks are considered classics in electronics education, featuring Mims' distinctive hand-drawn circuit diagrams and clear explanations.
|
||||
|
||||
## Downloaded PDFs (12 files, 68MB total)
|
||||
|
||||
| # | Filename | Size | Description |
|
||||
|---|----------|------|-------------|
|
||||
| 01 | `01_Basic_Semiconductor_Circuits.pdf` | 4.3 MB | Transistors, diodes, basic circuits |
|
||||
| 02 | `02_Op_Amp_IC_Circuits.pdf` | 4.3 MB | Operational amplifier projects |
|
||||
| 03 | `03_555_Timer_Circuits.pdf` | 2.9 MB | 555 timer applications |
|
||||
| 04 | `04_Formulas_Tables_Basic_Circuits.pdf` | 4.2 MB | Reference formulas & tables |
|
||||
| 05 | `05_Communications_Projects.pdf` | 10.7 MB | Radio and communications |
|
||||
| 06 | `06_Solar_Cell_Projects.pdf` | 4.5 MB | Solar/photovoltaic circuits |
|
||||
| 07 | `07_Optoelectronics_Circuits.pdf` | 4.2 MB | LEDs, phototransistors, fiber optics |
|
||||
| 08 | `08_Sensor_Projects.pdf` | 4.4 MB | Various sensor circuits |
|
||||
| 09 | `09_Magnet_And_Sensor_Projects.pdf` | 4.3 MB | Magnetic sensors & projects |
|
||||
| 10 | `10_Optoelectronic_Circuits_1986.pdf` | 10.7 MB | Earlier edition with bonus content |
|
||||
| 11 | `11_Basic_Semiconductor_1993.pdf` | 4.4 MB | Revised 1993 edition |
|
||||
| 12 | `12_Op_Amp_IC_1985.pdf` | 10.7 MB | Original 1985 edition |
|
||||
|
||||
---
|
||||
|
||||
## Complete Archive.org Details
|
||||
|
||||
### 1. Basic Semiconductor Circuits
|
||||
**Local File:** `01_Basic_Semiconductor_Circuits.pdf`
|
||||
**Archive.org:** [View Online](https://archive.org/details/electronics_-_Forrest_Mims-engineers_mini-notebook_basic_semiconductor_circuits_)
|
||||
|
||||
| Format | Filename |
|
||||
|--------|----------|
|
||||
| PDF (Image) | `electronics - Forrest Mims-engineer's mini-notebook basic semiconductor circuits (radio shack electronics).pdf` |
|
||||
| PDF (Text Layer) | `electronics - Forrest Mims-engineer's mini-notebook basic semiconductor circuits (radio shack electronics)_text.pdf` |
|
||||
| DjVu | `electronics - Forrest Mims-engineer's mini-notebook basic semiconductor circuits (radio shack electronics).djvu` |
|
||||
| Plain Text | `electronics - Forrest Mims-engineer's mini-notebook basic semiconductor circuits (radio shack electronics)_djvu.txt` |
|
||||
|
||||
---
|
||||
|
||||
### 2. Op Amp IC Circuits
|
||||
**Local File:** `02_Op_Amp_IC_Circuits.pdf`
|
||||
**Archive.org:** [View Online](https://archive.org/details/Forrest_Mims-Engineers_Mini-Notebook_Op_Amp_Ic_Circuits_Radio_Shack_Electronics)
|
||||
|
||||
| Format | Filename |
|
||||
|--------|----------|
|
||||
| PDF (Image) | `Forrest Mims-Engineer's Mini-Notebook Op Amp Ic Circuits (Radio Shack Electronics)(1).pdf` |
|
||||
| PDF (Text Layer) | `Forrest Mims-Engineer's Mini-Notebook Op Amp Ic Circuits (Radio Shack Electronics)(1)_text.pdf` |
|
||||
| DjVu | `Forrest Mims-Engineer's Mini-Notebook Op Amp Ic Circuits (Radio Shack Electronics)(1).djvu` |
|
||||
| Plain Text | `Forrest Mims-Engineer's Mini-Notebook Op Amp Ic Circuits (Radio Shack Electronics)(1)_djvu.txt` |
|
||||
|
||||
---
|
||||
|
||||
### 3. 555 Timer Circuits
|
||||
**Local File:** `03_555_Timer_Circuits.pdf`
|
||||
**Archive.org:** [View Online](https://archive.org/details/electronics_-_Forrest_Mims-engineers_mini-notebook_555_timer_circuits_radio_sha)
|
||||
|
||||
| Format | Filename |
|
||||
|--------|----------|
|
||||
| PDF (Image) | `electronics - Forrest Mims-engineer's mini-notebook 555 timer circuits (radio shack electronics).pdf` |
|
||||
| PDF (Text Layer) | `electronics - Forrest Mims-engineer's mini-notebook 555 timer circuits (radio shack electronics)_text.pdf` |
|
||||
| DjVu | `electronics - Forrest Mims-engineer's mini-notebook 555 timer circuits (radio shack electronics).djvu` |
|
||||
| Plain Text | `electronics - Forrest Mims-engineer's mini-notebook 555 timer circuits (radio shack electronics)_djvu.txt` |
|
||||
|
||||
---
|
||||
|
||||
### 4. Formulas, Tables & Basic Circuits
|
||||
**Local File:** `04_Formulas_Tables_Basic_Circuits.pdf`
|
||||
**Archive.org:** [View Online](https://archive.org/details/Forrest_Mims-Engineers_Mini-Notebook_Formulas_Tables_Basic_Circuits_Radio_Shack)
|
||||
|
||||
| Format | Filename |
|
||||
|--------|----------|
|
||||
| PDF (Image) | `Forrest Mims-Engineer's Mini-Notebook Formulas Tables Basic Circuits (Radio Shack Electronics).pdf` |
|
||||
| PDF (Text Layer) | `Forrest Mims-Engineer's Mini-Notebook Formulas Tables Basic Circuits (Radio Shack Electronics)_text.pdf` |
|
||||
| DjVu | `Forrest Mims-Engineer's Mini-Notebook Formulas Tables Basic Circuits (Radio Shack Electronics).djvu` |
|
||||
| Plain Text | `Forrest Mims-Engineer's Mini-Notebook Formulas Tables Basic Circuits (Radio Shack Electronics)_djvu.txt` |
|
||||
|
||||
---
|
||||
|
||||
### 5. Communications Projects
|
||||
**Local File:** `05_Communications_Projects.pdf`
|
||||
**Archive.org:** [View Online](https://archive.org/details/Forrest_Mims-Engineers_Mini-Notebook_-_Communications_Projects_Radio_Shack_Elec)
|
||||
|
||||
| Format | Filename |
|
||||
|--------|----------|
|
||||
| PDF (Image) | `Forrest Mims-Engineer's Mini-Notebook - Communications Projects (Radio Shack Electronics).pdf` |
|
||||
| PDF (Text Layer) | `Forrest Mims-Engineer's Mini-Notebook - Communications Projects (Radio Shack Electronics)_text.pdf` |
|
||||
| DjVu | `Forrest Mims-Engineer's Mini-Notebook - Communications Projects (Radio Shack Electronics).djvu` |
|
||||
| Plain Text | `Forrest Mims-Engineer's Mini-Notebook - Communications Projects (Radio Shack Electronics)_djvu.txt` |
|
||||
|
||||
---
|
||||
|
||||
### 6. Solar Cell Projects
|
||||
**Local File:** `06_Solar_Cell_Projects.pdf`
|
||||
**Archive.org:** [View Online](https://archive.org/details/Forrest_Mims-Engineers_Mini-Notebook_Solar_Cell_Projects_Radio_Shack_Electronic)
|
||||
|
||||
| Format | Filename |
|
||||
|--------|----------|
|
||||
| PDF (Image) | `Forrest Mims-Engineer's Mini-Notebook Solar Cell Projects (Radio Shack Electronics).pdf` |
|
||||
| PDF (Text Layer) | `Forrest Mims-Engineer's Mini-Notebook Solar Cell Projects (Radio Shack Electronics)_text.pdf` |
|
||||
| DjVu | `Forrest Mims-Engineer's Mini-Notebook Solar Cell Projects (Radio Shack Electronics).djvu` |
|
||||
| Plain Text | `Forrest Mims-Engineer's Mini-Notebook Solar Cell Projects (Radio Shack Electronics)_djvu.txt` |
|
||||
|
||||
---
|
||||
|
||||
### 7. Optoelectronics Circuits
|
||||
**Local File:** `07_Optoelectronics_Circuits.pdf`
|
||||
**Archive.org:** [View Online](https://archive.org/details/Forrest_Mims-Engineers_Mini-Notebook_Optoelectronics_Circuits_Radio_Shack_Elect)
|
||||
|
||||
| Format | Filename |
|
||||
|--------|----------|
|
||||
| PDF (Image) | `Forrest Mims-Engineer's Mini-Notebook Optoelectronics Circuits (Radio Shack Electronics).pdf` |
|
||||
| PDF (Text Layer) | `Forrest Mims-Engineer's Mini-Notebook Optoelectronics Circuits (Radio Shack Electronics)_text.pdf` |
|
||||
| DjVu | `Forrest Mims-Engineer's Mini-Notebook Optoelectronics Circuits (Radio Shack Electronics).djvu` |
|
||||
| Plain Text | `Forrest Mims-Engineer's Mini-Notebook Optoelectronics Circuits (Radio Shack Electronics)_djvu.txt` |
|
||||
|
||||
---
|
||||
|
||||
### 8. Sensor Projects
|
||||
**Local File:** `08_Sensor_Projects.pdf`
|
||||
**Archive.org:** [View Online](https://archive.org/details/Forrest_Mims-engineers_mini-notebook_sensor_projects_radio_shack_electronics)
|
||||
|
||||
| Format | Filename |
|
||||
|--------|----------|
|
||||
| PDF (Image) | `Forrest Mims-engineer's mini-notebook sensor projects (radio shack electronics).pdf` |
|
||||
| PDF (Text Layer) | `Forrest Mims-engineer's mini-notebook sensor projects (radio shack electronics)_text.pdf` |
|
||||
| DjVu | `Forrest Mims-engineer's mini-notebook sensor projects (radio shack electronics).djvu` |
|
||||
| Plain Text | `Forrest Mims-engineer's mini-notebook sensor projects (radio shack electronics)_djvu.txt` |
|
||||
|
||||
---
|
||||
|
||||
### 9. Magnet and Sensor Projects
|
||||
**Local File:** `09_Magnet_And_Sensor_Projects.pdf`
|
||||
**Archive.org:** [View Online](https://archive.org/details/Forrest_Mims-Engineers_Mini-Notebook_Magnet_And_Sensor_Projects_Radio_Shack_Ele)
|
||||
|
||||
| Format | Filename |
|
||||
|--------|----------|
|
||||
| PDF (Image) | `Forrest Mims-Engineer's Mini-Notebook Magnet And Sensor Projects (Radio Shack Electronics).pdf` |
|
||||
| PDF (Text Layer) | `Forrest Mims-Engineer's Mini-Notebook Magnet And Sensor Projects (Radio Shack Electronics)_text.pdf` |
|
||||
| DjVu | `Forrest Mims-Engineer's Mini-Notebook Magnet And Sensor Projects (Radio Shack Electronics).djvu` |
|
||||
| Plain Text | `Forrest Mims-Engineer's Mini-Notebook Magnet And Sensor Projects (Radio Shack Electronics)_djvu.txt` |
|
||||
|
||||
---
|
||||
|
||||
### 10. Optoelectronic Circuits (1986 Edition)
|
||||
**Local File:** `10_Optoelectronic_Circuits_1986.pdf`
|
||||
**Archive.org:** [View Online](https://archive.org/details/Radio_Shack_Engineers_Mini-Notebook_-_Optoelectronic_Circuits_-_F._Mims_1986_W)
|
||||
|
||||
| Format | Filename |
|
||||
|--------|----------|
|
||||
| PDF (Image) | `Radio Shack Engineer's Mini-Notebook - Optoelectronic Circuits - F. Mims (1986) WW.pdf` |
|
||||
| PDF (Text Layer) | `Radio Shack Engineer's Mini-Notebook - Optoelectronic Circuits - F. Mims (1986) WW_text.pdf` |
|
||||
| DjVu | `Radio Shack Engineer's Mini-Notebook - Optoelectronic Circuits - F. Mims (1986) WW.djvu` |
|
||||
| Plain Text | `Radio Shack Engineer's Mini-Notebook - Optoelectronic Circuits - F. Mims (1986) WW_djvu.txt` |
|
||||
|
||||
---
|
||||
|
||||
### 11. Basic Semiconductor Circuits (1993 Edition)
|
||||
**Local File:** `11_Basic_Semiconductor_1993.pdf`
|
||||
**Archive.org:** [View Online](https://archive.org/details/Radio_Shack_Engineers_Mini-Notebook_-_Basic_Semiconductor_Circuits_-_F._Mims_19)
|
||||
|
||||
| Format | Filename |
|
||||
|--------|----------|
|
||||
| PDF (Image) | `Radio Shack Engineer's Mini-Notebook - Basic Semiconductor Circuits - F. Mims (1993) WW.pdf` |
|
||||
| PDF (Text Layer) | `Radio Shack Engineer's Mini-Notebook - Basic Semiconductor Circuits - F. Mims (1993) WW_text.pdf` |
|
||||
| DjVu | `Radio Shack Engineer's Mini-Notebook - Basic Semiconductor Circuits - F. Mims (1993) WW.djvu` |
|
||||
| Plain Text | `Radio Shack Engineer's Mini-Notebook - Basic Semiconductor Circuits - F. Mims (1993) WW_djvu.txt` |
|
||||
|
||||
---
|
||||
|
||||
### 12. Op Amp IC Circuits (1985 Edition)
|
||||
**Local File:** `12_Op_Amp_IC_1985.pdf`
|
||||
**Archive.org:** [View Online](https://archive.org/details/Radio_Shack_Engineers_Mini-Notebook_-_Op_Amp_IC_Circuits_-_F._Mims_1985_WW)
|
||||
|
||||
| Format | Filename |
|
||||
|--------|----------|
|
||||
| PDF (Image) | `Radio Shack Engineer's Mini-Notebook - Op Amp IC Circuits - F. Mims (1985) WW.pdf` |
|
||||
| PDF (Text Layer) | `Radio Shack Engineer's Mini-Notebook - Op Amp IC Circuits - F. Mims (1985) WW_text.pdf` |
|
||||
| DjVu | `Radio Shack Engineer's Mini-Notebook - Op Amp IC Circuits - F. Mims (1985) WW.djvu` |
|
||||
| Plain Text | `Radio Shack Engineer's Mini-Notebook - Op Amp IC Circuits - F. Mims (1985) WW_djvu.txt` |
|
||||
|
||||
---
|
||||
|
||||
## About Forrest M. Mims III
|
||||
|
||||
Forrest M. Mims III is an American amateur scientist, author, and inventor known for his hand-illustrated electronics books. His distinctive style—featuring hand-lettered text and meticulously drawn circuit diagrams—became iconic in the Radio Shack "Engineer's Mini-Notebook" series.
|
||||
|
||||
These notebooks taught countless hobbyists and students the fundamentals of electronics through practical, buildable projects.
|
||||
|
||||
## Source Collection
|
||||
|
||||
All materials sourced from the [Folkscanomy: Tandy and Radio Shack Books](https://archive.org/details/folkscanomy_tandy) collection on Archive.org.
|
||||
|
||||
---
|
||||
*Downloaded: January 2026*
|
||||
43
notebooks/download_mims.sh
Executable file
@ -0,0 +1,43 @@
|
||||
#!/bin/bash
|
||||
# Forrest Mims Mini-Notebook Downloader
|
||||
|
||||
NOTEBOOKS=(
|
||||
"electronics_-_Forrest_Mims-engineers_mini-notebook_basic_semiconductor_circuits_"
|
||||
"Forrest_Mims-Engineers_Mini-Notebook_Op_Amp_Ic_Circuits_Radio_Shack_Electronics"
|
||||
"electronics_-_Forrest_Mims-engineers_mini-notebook_555_timer_circuits_radio_sha"
|
||||
"Forrest_Mims-Engineers_Mini-Notebook_Formulas_Tables_Basic_Circuits_Radio_Shack"
|
||||
"Forrest_Mims-Engineers_Mini-Notebook_-_Communications_Projects_Radio_Shack_Elec"
|
||||
"Forrest_Mims-engineers_mini-notebook_basic_semiconductor_circuits_radio_shack_e"
|
||||
"Forrest_Mims-Engineers_Mini-Notebook_Solar_Cell_Projects_Radio_Shack_Electronic"
|
||||
"Forrest_Mims-Engineers_Mini-Notebook_Magnet_and_Sensor_Projects_Radio_Shack_Ele"
|
||||
"Forrest_Mims-Engineers_Mini-Notebook_Optoelectronics_Circuits_Radio_Shack_Elect"
|
||||
"Forrest_Mims-Engineers_Mini-Notebook_-_Sensor_Projects_Radio_Shack_Electronics"
|
||||
"RadioShackEngineersMiniNotebookOpAmpICCircuits"
|
||||
"Forrest_Mims-Engineers_Mini-Notebook_Formulas_Tables_and_Basic_Circuits_Radio_S"
|
||||
"RadioShackEngineersMiniNotebookBasicSemiconductorCircuits"
|
||||
"RadioShackEngineersMiniNotebookOptoelectronicCircuits"
|
||||
)
|
||||
|
||||
echo "📚 Downloading Forrest Mims Mini-Notebooks from Archive.org"
|
||||
echo "============================================================"
|
||||
|
||||
for id in "${NOTEBOOKS[@]}"; do
|
||||
echo ""
|
||||
echo "📖 Processing: $id"
|
||||
|
||||
# Get metadata JSON
|
||||
curl -s "https://archive.org/metadata/$id" > "${id}_metadata.json" 2>/dev/null
|
||||
|
||||
# Try to download PDF (try common naming patterns)
|
||||
for pattern in "${id}.pdf" "${id}_text.pdf" "$(echo $id | sed 's/_/-/g').pdf"; do
|
||||
url="https://archive.org/download/$id/$pattern"
|
||||
if curl -sI "$url" 2>/dev/null | grep -q "200 OK"; then
|
||||
echo " ⬇️ Downloading PDF: $pattern"
|
||||
curl -s -L -o "${id}.pdf" "$url"
|
||||
break
|
||||
fi
|
||||
done
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "✅ Download complete!"
|
||||
12
site/.env.example
Normal file
@ -0,0 +1,12 @@
|
||||
# Docker Compose Configuration
|
||||
COMPOSE_PROJECT_NAME=mims-library
|
||||
|
||||
# Mode: development or production
|
||||
MODE=production
|
||||
NODE_ENV=production
|
||||
|
||||
# Caddy reverse proxy hostname
|
||||
CADDY_HOST=mims.localhost
|
||||
|
||||
# Dev mode: uncomment to mount src for hot reload
|
||||
# DEV_MOUNT=./src
|
||||
24
site/.gitignore
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
# build output
|
||||
dist/
|
||||
# generated types
|
||||
.astro/
|
||||
|
||||
# dependencies
|
||||
node_modules/
|
||||
|
||||
# logs
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
|
||||
|
||||
# environment variables
|
||||
.env
|
||||
.env.production
|
||||
|
||||
# macOS-specific files
|
||||
.DS_Store
|
||||
|
||||
# jetbrains setting folder
|
||||
.idea/
|
||||
20
site/Caddyfile
Normal file
@ -0,0 +1,20 @@
|
||||
:4321 {
|
||||
root * /srv
|
||||
file_server
|
||||
|
||||
# Handle clean URLs - try file, then directory, then .html extension
|
||||
try_files {path} {path}/ {path}.html /index.html
|
||||
|
||||
# Compression
|
||||
encode gzip
|
||||
|
||||
# Cache static assets
|
||||
@static {
|
||||
path *.jpg *.jpeg *.png *.gif *.ico *.css *.js *.pdf *.svg *.woff *.woff2
|
||||
}
|
||||
header @static Cache-Control "public, max-age=604800, immutable"
|
||||
|
||||
# Security headers
|
||||
header X-Frame-Options "SAMEORIGIN"
|
||||
header X-Content-Type-Options "nosniff"
|
||||
}
|
||||
33
site/Dockerfile
Normal file
@ -0,0 +1,33 @@
|
||||
# Build stage
|
||||
FROM ghcr.io/astral-sh/uv:python3.11-bookworm-slim AS base
|
||||
WORKDIR /app
|
||||
|
||||
# Install Node.js
|
||||
RUN apt-get update && apt-get install -y curl && \
|
||||
curl -fsSL https://deb.nodesource.com/setup_20.x | bash - && \
|
||||
apt-get install -y nodejs && \
|
||||
apt-get clean && rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Development target
|
||||
FROM base AS development
|
||||
WORKDIR /app
|
||||
COPY package*.json ./
|
||||
RUN npm install
|
||||
COPY . .
|
||||
EXPOSE 4321
|
||||
CMD ["npm", "run", "dev", "--", "--host", "0.0.0.0"]
|
||||
|
||||
# Build stage for production
|
||||
FROM base AS builder
|
||||
WORKDIR /app
|
||||
COPY package*.json ./
|
||||
RUN npm ci
|
||||
COPY . .
|
||||
RUN npm run build
|
||||
|
||||
# Production target - serve static files with Caddy
|
||||
FROM caddy:alpine AS production
|
||||
COPY --from=builder /app/dist /srv
|
||||
COPY Caddyfile /etc/caddy/Caddyfile
|
||||
EXPOSE 4321
|
||||
CMD ["caddy", "run", "--config", "/etc/caddy/Caddyfile"]
|
||||
37
site/Makefile
Normal file
@ -0,0 +1,37 @@
|
||||
.PHONY: dev prod build up down logs clean
|
||||
|
||||
# Development mode with hot reload
|
||||
dev:
|
||||
MODE=development DEV_MOUNT=./src docker compose up --build
|
||||
|
||||
# Production mode
|
||||
prod:
|
||||
MODE=production docker compose up -d --build
|
||||
|
||||
# Build only
|
||||
build:
|
||||
docker compose build
|
||||
|
||||
# Start containers
|
||||
up:
|
||||
docker compose up -d
|
||||
|
||||
# Stop containers
|
||||
down:
|
||||
docker compose down
|
||||
|
||||
# View logs
|
||||
logs:
|
||||
docker compose logs -f
|
||||
|
||||
# Clean up
|
||||
clean:
|
||||
docker compose down -v --rmi local
|
||||
|
||||
# Local development without Docker
|
||||
local:
|
||||
npm run dev
|
||||
|
||||
# Build static site
|
||||
static:
|
||||
npm run build
|
||||
43
site/README.md
Normal file
@ -0,0 +1,43 @@
|
||||
# Astro Starter Kit: Minimal
|
||||
|
||||
```sh
|
||||
npm create astro@latest -- --template minimal
|
||||
```
|
||||
|
||||
> 🧑🚀 **Seasoned astronaut?** Delete this file. Have fun!
|
||||
|
||||
## 🚀 Project Structure
|
||||
|
||||
Inside of your Astro project, you'll see the following folders and files:
|
||||
|
||||
```text
|
||||
/
|
||||
├── public/
|
||||
├── src/
|
||||
│ └── pages/
|
||||
│ └── index.astro
|
||||
└── package.json
|
||||
```
|
||||
|
||||
Astro looks for `.astro` or `.md` files in the `src/pages/` directory. Each page is exposed as a route based on its file name.
|
||||
|
||||
There's nothing special about `src/components/`, but that's where we like to put any Astro/React/Vue/Svelte/Preact components.
|
||||
|
||||
Any static assets, like images, can be placed in the `public/` directory.
|
||||
|
||||
## 🧞 Commands
|
||||
|
||||
All commands are run from the root of the project, from a terminal:
|
||||
|
||||
| Command | Action |
|
||||
| :------------------------ | :----------------------------------------------- |
|
||||
| `npm install` | Installs dependencies |
|
||||
| `npm run dev` | Starts local dev server at `localhost:4321` |
|
||||
| `npm run build` | Build your production site to `./dist/` |
|
||||
| `npm run preview` | Preview your build locally, before deploying |
|
||||
| `npm run astro ...` | Run CLI commands like `astro add`, `astro check` |
|
||||
| `npm run astro -- --help` | Get help using the Astro CLI |
|
||||
|
||||
## 👀 Want to learn more?
|
||||
|
||||
Feel free to check [our documentation](https://docs.astro.build) or jump into our [Discord server](https://astro.build/chat).
|
||||
24
site/astro.config.mjs
Normal file
@ -0,0 +1,24 @@
|
||||
// @ts-check
|
||||
import { defineConfig } from 'astro/config';
|
||||
|
||||
import react from '@astrojs/react';
|
||||
import tailwindcss from '@tailwindcss/vite';
|
||||
|
||||
// https://astro.build/config
|
||||
export default defineConfig({
|
||||
integrations: [react()],
|
||||
|
||||
// Production site URL for correct link generation
|
||||
site: process.env.SITE_URL || 'https://mims.l.supported.systems',
|
||||
|
||||
// Disable telemetry and devToolbar
|
||||
telemetry: false,
|
||||
devToolbar: { enabled: false },
|
||||
|
||||
vite: {
|
||||
plugins: [tailwindcss()],
|
||||
server: {
|
||||
host: '0.0.0.0',
|
||||
}
|
||||
}
|
||||
});
|
||||
22
site/components.json
Normal file
@ -0,0 +1,22 @@
|
||||
{
|
||||
"$schema": "https://ui.shadcn.com/schema.json",
|
||||
"style": "new-york",
|
||||
"rsc": false,
|
||||
"tsx": true,
|
||||
"tailwind": {
|
||||
"config": "",
|
||||
"css": "src/styles/global.css",
|
||||
"baseColor": "neutral",
|
||||
"cssVariables": true,
|
||||
"prefix": ""
|
||||
},
|
||||
"iconLibrary": "lucide",
|
||||
"aliases": {
|
||||
"components": "@/components",
|
||||
"utils": "@/lib/utils",
|
||||
"ui": "@/components/ui",
|
||||
"lib": "@/lib",
|
||||
"hooks": "@/hooks"
|
||||
},
|
||||
"registries": {}
|
||||
}
|
||||
24
site/docker-compose.yml
Normal file
@ -0,0 +1,24 @@
|
||||
services:
|
||||
mims-library:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
target: ${MODE:-production}
|
||||
container_name: mims-library
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- NODE_ENV=${NODE_ENV:-production}
|
||||
- HOST=0.0.0.0
|
||||
- PORT=4321
|
||||
volumes:
|
||||
# Dev mode: mount source for hot reload
|
||||
- ${DEV_MOUNT:-/dev/null}:/app/src:ro
|
||||
networks:
|
||||
- caddy
|
||||
labels:
|
||||
caddy: ${CADDY_HOST:-mims.localhost}
|
||||
caddy.reverse_proxy: "{{upstreams 4321}}"
|
||||
|
||||
networks:
|
||||
caddy:
|
||||
external: true
|
||||
7346
site/package-lock.json
generated
Normal file
32
site/package.json
Normal file
@ -0,0 +1,32 @@
|
||||
{
|
||||
"name": "site",
|
||||
"type": "module",
|
||||
"version": "0.0.1",
|
||||
"scripts": {
|
||||
"dev": "astro dev",
|
||||
"build": "astro build",
|
||||
"preview": "astro preview",
|
||||
"astro": "astro"
|
||||
},
|
||||
"dependencies": {
|
||||
"@astrojs/react": "^4.4.2",
|
||||
"@radix-ui/react-dropdown-menu": "^2.1.16",
|
||||
"@radix-ui/react-slot": "^1.2.4",
|
||||
"@radix-ui/react-tabs": "^1.1.13",
|
||||
"@tailwindcss/vite": "^4.1.18",
|
||||
"@types/react": "^19.2.7",
|
||||
"@types/react-dom": "^19.2.3",
|
||||
"astro": "^5.16.7",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"lucide-react": "^0.562.0",
|
||||
"react": "^19.2.3",
|
||||
"react-dom": "^19.2.3",
|
||||
"react-pdf": "^10.3.0",
|
||||
"tailwind-merge": "^3.4.0",
|
||||
"tailwindcss": "^4.1.18"
|
||||
},
|
||||
"devDependencies": {
|
||||
"tw-animate-css": "^1.4.0"
|
||||
}
|
||||
}
|
||||
BIN
site/public/covers/mims/01_Basic_Semiconductor_Circuits-01.jpg
Normal file
|
After Width: | Height: | Size: 519 KiB |
BIN
site/public/covers/mims/02_Op_Amp_IC_Circuits-01.jpg
Normal file
|
After Width: | Height: | Size: 403 KiB |
BIN
site/public/covers/mims/03_555_Timer_Circuits-01.jpg
Normal file
|
After Width: | Height: | Size: 355 KiB |
BIN
site/public/covers/mims/04_Formulas_Tables_Basic_Circuits-01.jpg
Normal file
|
After Width: | Height: | Size: 102 KiB |
BIN
site/public/covers/mims/05_Communications_Projects-01.jpg
Normal file
|
After Width: | Height: | Size: 240 KiB |
BIN
site/public/covers/mims/06_Solar_Cell_Projects-01.jpg
Normal file
|
After Width: | Height: | Size: 647 KiB |
BIN
site/public/covers/mims/07_Optoelectronics_Circuits-01.jpg
Normal file
|
After Width: | Height: | Size: 378 KiB |
BIN
site/public/covers/mims/08_Sensor_Projects-01.jpg
Normal file
|
After Width: | Height: | Size: 372 KiB |
BIN
site/public/covers/mims/09_Magnet_And_Sensor_Projects-01.jpg
Normal file
|
After Width: | Height: | Size: 362 KiB |
BIN
site/public/covers/mims/10_Optoelectronic_Circuits_1986-01.jpg
Normal file
|
After Width: | Height: | Size: 255 KiB |
BIN
site/public/covers/mims/11_Basic_Semiconductor_1993-01.jpg
Normal file
|
After Width: | Height: | Size: 519 KiB |
BIN
site/public/covers/mims/12_Op_Amp_IC_1985-01.jpg
Normal file
|
After Width: | Height: | Size: 266 KiB |
|
After Width: | Height: | Size: 368 KiB |
|
After Width: | Height: | Size: 418 KiB |
|
After Width: | Height: | Size: 212 KiB |
BIN
site/public/covers/uglys/Uglys_Electrical_Reference_2005-01.jpg
Normal file
|
After Width: | Height: | Size: 73 KiB |
4
site/public/favicon.svg
Normal file
@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="#3b5998" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z"/>
|
||||
<path d="M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 296 B |
BIN
site/public/pdfs/mims/01_Basic_Semiconductor_Circuits.pdf
(Stored with Git LFS)
Normal file
BIN
site/public/pdfs/mims/02_Op_Amp_IC_Circuits.pdf
(Stored with Git LFS)
Normal file
BIN
site/public/pdfs/mims/03_555_Timer_Circuits.pdf
(Stored with Git LFS)
Normal file
BIN
site/public/pdfs/mims/04_Formulas_Tables_Basic_Circuits.pdf
(Stored with Git LFS)
Normal file
BIN
site/public/pdfs/mims/05_Communications_Projects.pdf
(Stored with Git LFS)
Normal file
BIN
site/public/pdfs/mims/06_Solar_Cell_Projects.pdf
(Stored with Git LFS)
Normal file
BIN
site/public/pdfs/mims/07_Optoelectronics_Circuits.pdf
(Stored with Git LFS)
Normal file
BIN
site/public/pdfs/mims/08_Sensor_Projects.pdf
(Stored with Git LFS)
Normal file
BIN
site/public/pdfs/mims/09_Magnet_And_Sensor_Projects.pdf
(Stored with Git LFS)
Normal file
BIN
site/public/pdfs/mims/10_Optoelectronic_Circuits_1986.pdf
(Stored with Git LFS)
Normal file
BIN
site/public/pdfs/mims/11_Basic_Semiconductor_1993.pdf
(Stored with Git LFS)
Normal file
BIN
site/public/pdfs/mims/12_Op_Amp_IC_1985.pdf
(Stored with Git LFS)
Normal file
BIN
site/public/pdfs/mims/Engineers_Mini_Notebook_Environmental_Projects.pdf
(Stored with Git LFS)
Normal file
BIN
site/public/pdfs/mims/Engineers_Mini_Notebook_Schematic_Symbols.pdf
(Stored with Git LFS)
Normal file
BIN
site/public/pdfs/mims/Engineers_Notebook_II_IC_Applications.pdf
(Stored with Git LFS)
Normal file
BIN
site/public/pdfs/uglys/Uglys_Electrical_Reference_2005.pdf
(Stored with Git LFS)
Normal file
69
site/src/components/BookCard.astro
Normal file
@ -0,0 +1,69 @@
|
||||
---
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import type { CollectionEntry } from 'astro:content';
|
||||
|
||||
interface Props {
|
||||
book: CollectionEntry<'books'>;
|
||||
}
|
||||
|
||||
const { book } = Astro.props;
|
||||
const { title, shortTitle, description, topics, localPdf, coverImage, year, collection } = book.data;
|
||||
---
|
||||
|
||||
<a
|
||||
href={`/${collection}/${book.slug.split('/').pop()}`}
|
||||
class="book-card block bg-card rounded-lg border border-border overflow-hidden hover:border-primary/50"
|
||||
>
|
||||
<div class="relative">
|
||||
{coverImage ? (
|
||||
<img
|
||||
src={coverImage}
|
||||
alt={`Cover of ${shortTitle}`}
|
||||
class="w-full aspect-[3/4] object-cover object-top"
|
||||
loading="lazy"
|
||||
/>
|
||||
) : (
|
||||
<div class="w-full aspect-[3/4] bg-muted flex items-center justify-center">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="text-muted-foreground">
|
||||
<path d="M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z"/>
|
||||
<path d="M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z"/>
|
||||
</svg>
|
||||
</div>
|
||||
)}
|
||||
{year && (
|
||||
<div class="absolute top-2 right-2 bg-card/90 backdrop-blur-sm text-xs font-medium px-2 py-1 rounded border border-border">
|
||||
{year}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div class="p-4 space-y-3">
|
||||
<div>
|
||||
<h3 class="font-semibold text-foreground title-accent line-clamp-2 leading-tight">
|
||||
{shortTitle}
|
||||
</h3>
|
||||
<p class="text-sm text-muted-foreground mt-1 line-clamp-2">
|
||||
{description}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-wrap gap-1">
|
||||
{topics.slice(0, 3).map((topic) => (
|
||||
<Badge variant="secondary" className="text-xs">
|
||||
{topic.replace(/-/g, ' ')}
|
||||
</Badge>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div class="pt-2 border-t border-border flex items-center justify-between text-xs text-muted-foreground">
|
||||
<span class="flex items-center gap-1">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/>
|
||||
<polyline points="14 2 14 8 20 8"/>
|
||||
</svg>
|
||||
PDF
|
||||
</span>
|
||||
<span class="text-primary font-medium">View →</span>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
27
site/src/components/BookGrid.astro
Normal file
@ -0,0 +1,27 @@
|
||||
---
|
||||
import BookCard from './BookCard.astro';
|
||||
import type { CollectionEntry } from 'astro:content';
|
||||
|
||||
interface Props {
|
||||
books: CollectionEntry<'books'>[];
|
||||
title?: string;
|
||||
description?: string;
|
||||
}
|
||||
|
||||
const { books, title, description } = Astro.props;
|
||||
---
|
||||
|
||||
<section class="space-y-6">
|
||||
{(title || description) && (
|
||||
<div class="space-y-2">
|
||||
{title && <h2 class="text-2xl font-bold title-accent">{title}</h2>}
|
||||
{description && <p class="text-muted-foreground">{description}</p>}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6">
|
||||
{books.map((book) => (
|
||||
<BookCard book={book} />
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
310
site/src/components/EBookReader.tsx
Normal file
@ -0,0 +1,310 @@
|
||||
import { useState, useRef, useEffect } from 'react';
|
||||
|
||||
interface EBookReaderProps {
|
||||
pdfUrl: string;
|
||||
title: string;
|
||||
coverImage?: string;
|
||||
}
|
||||
|
||||
export default function EBookReader({ pdfUrl, title, coverImage }: EBookReaderProps) {
|
||||
const [isFullscreen, setIsFullscreen] = useState(false);
|
||||
const [brightness, setBrightness] = useState(100);
|
||||
const [isWarmLight, setIsWarmLight] = useState(true);
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
// Handle escape key to exit fullscreen
|
||||
useEffect(() => {
|
||||
const handleEscape = (e: KeyboardEvent) => {
|
||||
if (e.key === 'Escape' && isFullscreen) {
|
||||
setIsFullscreen(false);
|
||||
}
|
||||
};
|
||||
window.addEventListener('keydown', handleEscape);
|
||||
return () => window.removeEventListener('keydown', handleEscape);
|
||||
}, [isFullscreen]);
|
||||
|
||||
// Lock scroll when fullscreen
|
||||
useEffect(() => {
|
||||
if (isFullscreen) {
|
||||
document.body.style.overflow = 'hidden';
|
||||
} else {
|
||||
document.body.style.overflow = '';
|
||||
}
|
||||
return () => {
|
||||
document.body.style.overflow = '';
|
||||
};
|
||||
}, [isFullscreen]);
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* Fullscreen overlay */}
|
||||
{isFullscreen && (
|
||||
<div
|
||||
className="fixed inset-0 bg-stone-900/95 z-50 flex items-center justify-center p-2 md:p-4"
|
||||
onClick={(e) => e.target === e.currentTarget && setIsFullscreen(false)}
|
||||
>
|
||||
<div className="w-full max-w-[95vw] h-full flex flex-col">
|
||||
{/* Fullscreen header */}
|
||||
<div className="flex items-center justify-between py-3 px-4 text-stone-300">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="w-2 h-2 rounded-full bg-amber-500 animate-pulse" />
|
||||
<span className="text-sm font-medium">{title}</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-4">
|
||||
{/* Brightness control */}
|
||||
<div className="flex items-center gap-2">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className="text-stone-400">
|
||||
<circle cx="12" cy="12" r="4"/>
|
||||
<path d="M12 2v2"/>
|
||||
<path d="M12 20v2"/>
|
||||
<path d="m4.93 4.93 1.41 1.41"/>
|
||||
<path d="m17.66 17.66 1.41 1.41"/>
|
||||
<path d="M2 12h2"/>
|
||||
<path d="M20 12h2"/>
|
||||
<path d="m6.34 17.66-1.41 1.41"/>
|
||||
<path d="m19.07 4.93-1.41 1.41"/>
|
||||
</svg>
|
||||
<input
|
||||
type="range"
|
||||
min="50"
|
||||
max="100"
|
||||
value={brightness}
|
||||
onChange={(e) => setBrightness(Number(e.target.value))}
|
||||
className="w-20 h-1 bg-stone-700 rounded-lg appearance-none cursor-pointer accent-amber-500"
|
||||
/>
|
||||
</div>
|
||||
{/* Warm light toggle */}
|
||||
<button
|
||||
onClick={() => setIsWarmLight(!isWarmLight)}
|
||||
className={`p-2 rounded-lg transition-colors ${isWarmLight ? 'bg-amber-500/20 text-amber-400' : 'text-stone-400 hover:text-stone-300'}`}
|
||||
title="Warm light"
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||||
<path d="M8 2h8"/>
|
||||
<path d="M9 2v2.789a4 4 0 0 1-.672 2.219l-.656.984A4 4 0 0 0 7 10.212V14a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2v-3.788a4 4 0 0 0-.672-2.219l-.656-.984A4 4 0 0 1 15 4.788V2"/>
|
||||
<path d="M9 16v3a2 2 0 0 0 2 2h2a2 2 0 0 0 2-2v-3"/>
|
||||
</svg>
|
||||
</button>
|
||||
{/* Close button */}
|
||||
<button
|
||||
onClick={() => setIsFullscreen(false)}
|
||||
className="p-2 rounded-lg text-stone-400 hover:text-stone-200 hover:bg-stone-800 transition-colors"
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||||
<path d="M18 6 6 18"/>
|
||||
<path d="m6 6 12 12"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Fullscreen reader */}
|
||||
<div
|
||||
className="flex-1 rounded-lg overflow-hidden shadow-2xl"
|
||||
style={{
|
||||
filter: `brightness(${brightness}%)`,
|
||||
backgroundColor: isWarmLight ? '#f5f0e6' : '#fff'
|
||||
}}
|
||||
>
|
||||
<iframe
|
||||
src={`${pdfUrl}#toolbar=0&navpanes=0&scrollbar=1&view=FitH`}
|
||||
className="w-full h-full border-0"
|
||||
title={title}
|
||||
style={{
|
||||
filter: isWarmLight ? 'sepia(15%)' : 'none'
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Fullscreen footer hint */}
|
||||
<div className="text-center py-2 text-stone-500 text-xs">
|
||||
Press <kbd className="px-1.5 py-0.5 bg-stone-800 rounded text-stone-400">Esc</kbd> or click outside to exit
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* E-Reader Device Frame */}
|
||||
<div
|
||||
ref={containerRef}
|
||||
className="ereader-device relative w-full"
|
||||
>
|
||||
{/* Device outer shell */}
|
||||
<div className="relative bg-gradient-to-b from-stone-800 via-stone-900 to-stone-950 rounded-2xl md:rounded-[2rem] p-2 md:p-3 shadow-[0_25px_50px_-12px_rgba(0,0,0,0.5),inset_0_1px_1px_rgba(255,255,255,0.1)]">
|
||||
|
||||
{/* Device bezel with subtle texture */}
|
||||
<div className="relative bg-gradient-to-b from-stone-700 to-stone-800 rounded-xl md:rounded-[1.5rem] p-[2px] md:p-[3px] shadow-[inset_0_2px_4px_rgba(0,0,0,0.3)]">
|
||||
|
||||
{/* Inner bezel */}
|
||||
<div className="bg-stone-900 rounded-xl md:rounded-[1.4rem] p-1.5 md:p-2">
|
||||
|
||||
{/* Screen area */}
|
||||
<div className="relative rounded-xl overflow-hidden bg-stone-950 shadow-[inset_0_0_30px_rgba(0,0,0,0.5)]">
|
||||
|
||||
{/* Status bar */}
|
||||
<div className="flex items-center justify-between px-4 py-2 bg-stone-900/90 border-b border-stone-800">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="w-1.5 h-1.5 rounded-full bg-emerald-500" />
|
||||
<span className="text-[10px] text-stone-400 font-medium tracking-wide uppercase">Reading</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-3 text-stone-500">
|
||||
<span className="text-[10px]">{title}</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
{/* Battery indicator */}
|
||||
<div className="flex items-center gap-1">
|
||||
<div className="w-5 h-2.5 rounded-sm border border-stone-600 p-[1px]">
|
||||
<div className="w-full h-full rounded-[1px] bg-emerald-500" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* PDF Display with paper texture */}
|
||||
<div
|
||||
className="relative"
|
||||
style={{
|
||||
background: isWarmLight
|
||||
? 'linear-gradient(to bottom, #faf8f3, #f5f0e6)'
|
||||
: 'linear-gradient(to bottom, #ffffff, #fafafa)',
|
||||
filter: `brightness(${brightness}%)`
|
||||
}}
|
||||
>
|
||||
{/* Paper texture overlay */}
|
||||
<div
|
||||
className="absolute inset-0 opacity-30 pointer-events-none mix-blend-multiply"
|
||||
style={{
|
||||
backgroundImage: `url("data:image/svg+xml,%3Csvg viewBox='0 0 200 200' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noiseFilter'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noiseFilter)'/%3E%3C/svg%3E")`,
|
||||
}}
|
||||
/>
|
||||
|
||||
<iframe
|
||||
src={`${pdfUrl}#toolbar=0&navpanes=0&scrollbar=1&view=FitH`}
|
||||
className="w-full border-0 relative z-10"
|
||||
style={{
|
||||
height: 'max(75vh, 600px)',
|
||||
filter: isWarmLight ? 'sepia(10%)' : 'none'
|
||||
}}
|
||||
title={title}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Bottom control bar */}
|
||||
<div className="flex items-center justify-between px-4 py-3 bg-stone-900/90 border-t border-stone-800">
|
||||
{/* Left controls */}
|
||||
<div className="flex items-center gap-1">
|
||||
<button
|
||||
onClick={() => setIsWarmLight(!isWarmLight)}
|
||||
className={`p-2 rounded-lg transition-all ${isWarmLight ? 'bg-amber-500/20 text-amber-400' : 'text-stone-500 hover:text-stone-300'}`}
|
||||
title="Warm reading light"
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||||
<path d="M8 2h8"/>
|
||||
<path d="M9 2v2.789a4 4 0 0 1-.672 2.219l-.656.984A4 4 0 0 0 7 10.212V14a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2v-3.788a4 4 0 0 0-.672-2.219l-.656-.984A4 4 0 0 1 15 4.788V2"/>
|
||||
<path d="M9 16v3a2 2 0 0 0 2 2h2a2 2 0 0 0 2-2v-3"/>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
{/* Brightness slider */}
|
||||
<div className="flex items-center gap-2 px-2">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className="text-stone-500">
|
||||
<circle cx="12" cy="12" r="4"/>
|
||||
<path d="M12 2v2"/>
|
||||
<path d="M12 20v2"/>
|
||||
<path d="m4.93 4.93 1.41 1.41"/>
|
||||
<path d="m17.66 17.66 1.41 1.41"/>
|
||||
<path d="M2 12h2"/>
|
||||
<path d="M20 12h2"/>
|
||||
<path d="m6.34 17.66-1.41 1.41"/>
|
||||
<path d="m19.07 4.93-1.41 1.41"/>
|
||||
</svg>
|
||||
<input
|
||||
type="range"
|
||||
min="50"
|
||||
max="100"
|
||||
value={brightness}
|
||||
onChange={(e) => setBrightness(Number(e.target.value))}
|
||||
className="w-16 h-1 bg-stone-700 rounded-lg appearance-none cursor-pointer accent-amber-500"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Center - Fullscreen */}
|
||||
<button
|
||||
onClick={() => setIsFullscreen(true)}
|
||||
className="flex items-center gap-2 px-4 py-2 rounded-lg bg-stone-800 hover:bg-stone-700 text-stone-300 transition-all text-sm font-medium"
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||||
<path d="M8 3H5a2 2 0 0 0-2 2v3"/>
|
||||
<path d="M21 8V5a2 2 0 0 0-2-2h-3"/>
|
||||
<path d="M3 16v3a2 2 0 0 0 2 2h3"/>
|
||||
<path d="M16 21h3a2 2 0 0 0 2-2v-3"/>
|
||||
</svg>
|
||||
<span className="hidden sm:inline">Immersive Mode</span>
|
||||
</button>
|
||||
|
||||
{/* Right controls */}
|
||||
<div className="flex items-center gap-1">
|
||||
<a
|
||||
href={pdfUrl}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="p-2 rounded-lg text-stone-500 hover:text-stone-300 hover:bg-stone-800 transition-all"
|
||||
title="Open in new tab"
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||||
<path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/>
|
||||
<polyline points="15 3 21 3 21 9"/>
|
||||
<line x1="10" x2="21" y1="14" y2="3"/>
|
||||
</svg>
|
||||
</a>
|
||||
<a
|
||||
href={pdfUrl}
|
||||
download
|
||||
className="p-2 rounded-lg text-stone-500 hover:text-stone-300 hover:bg-stone-800 transition-all"
|
||||
title="Download PDF"
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||||
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/>
|
||||
<polyline points="7 10 12 15 17 10"/>
|
||||
<line x1="12" x2="12" y1="15" y2="3"/>
|
||||
</svg>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Device bottom chin with logo */}
|
||||
<div className="flex items-center justify-center py-3">
|
||||
<div className="flex items-center gap-2 text-stone-600">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round">
|
||||
<path d="M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z"/>
|
||||
<path d="M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z"/>
|
||||
</svg>
|
||||
<span className="text-[10px] font-medium tracking-widest uppercase">Mims Reader</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Device shadow */}
|
||||
<div className="absolute -bottom-4 left-1/2 -translate-x-1/2 w-3/4 h-8 bg-black/20 blur-xl rounded-full" />
|
||||
</div>
|
||||
|
||||
{/* Fallback for non-embedded viewing */}
|
||||
<div className="mt-4 text-center">
|
||||
<p className="text-sm text-muted-foreground">
|
||||
PDF not displaying correctly?{' '}
|
||||
<a href={pdfUrl} target="_blank" rel="noopener noreferrer" className="text-primary hover:underline">
|
||||
Open in browser
|
||||
</a>{' '}
|
||||
or{' '}
|
||||
<a href={pdfUrl} download className="text-primary hover:underline">
|
||||
download the file
|
||||
</a>.
|
||||
</p>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
106
site/src/components/PdfViewer.tsx
Normal file
@ -0,0 +1,106 @@
|
||||
import { useState } from 'react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
|
||||
interface PdfViewerProps {
|
||||
pdfUrl: string;
|
||||
}
|
||||
|
||||
export default function PdfViewer({ pdfUrl }: PdfViewerProps) {
|
||||
const [isFullscreen, setIsFullscreen] = useState(false);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col">
|
||||
{/* Controls */}
|
||||
<div className="flex items-center justify-between p-3 border-b border-border bg-muted/30">
|
||||
<div className="flex items-center gap-2 text-sm text-muted-foreground">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||||
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/>
|
||||
<polyline points="14 2 14 8 20 8"/>
|
||||
</svg>
|
||||
<span>PDF Document</span>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-2">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => setIsFullscreen(!isFullscreen)}
|
||||
className="h-8"
|
||||
>
|
||||
{isFullscreen ? (
|
||||
<>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className="mr-1.5">
|
||||
<path d="M8 3v3a2 2 0 0 1-2 2H3"/>
|
||||
<path d="M21 8h-3a2 2 0 0 1-2-2V3"/>
|
||||
<path d="M3 16h3a2 2 0 0 1 2 2v3"/>
|
||||
<path d="M16 21v-3a2 2 0 0 1 2-2h3"/>
|
||||
</svg>
|
||||
<span className="hidden sm:inline">Compact</span>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className="mr-1.5">
|
||||
<path d="M8 3H5a2 2 0 0 0-2 2v3"/>
|
||||
<path d="M21 8V5a2 2 0 0 0-2-2h-3"/>
|
||||
<path d="M3 16v3a2 2 0 0 0 2 2h3"/>
|
||||
<path d="M16 21h3a2 2 0 0 0 2-2v-3"/>
|
||||
</svg>
|
||||
<span className="hidden sm:inline">Expand</span>
|
||||
</>
|
||||
)}
|
||||
</Button>
|
||||
|
||||
<a
|
||||
href={pdfUrl}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline-flex items-center gap-1.5 px-3 py-1.5 text-sm border border-border rounded-md hover:bg-muted transition-colors"
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||||
<path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/>
|
||||
<polyline points="15 3 21 3 21 9"/>
|
||||
<line x1="10" x2="21" y1="14" y2="3"/>
|
||||
</svg>
|
||||
<span className="hidden sm:inline">Open in new tab</span>
|
||||
</a>
|
||||
|
||||
<a
|
||||
href={pdfUrl}
|
||||
download
|
||||
className="inline-flex items-center gap-1.5 px-3 py-1.5 text-sm bg-primary text-primary-foreground rounded-md hover:bg-primary/90 transition-colors"
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||||
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/>
|
||||
<polyline points="7 10 12 15 17 10"/>
|
||||
<line x1="12" x2="12" y1="15" y2="3"/>
|
||||
</svg>
|
||||
<span className="hidden sm:inline">Download</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* PDF Embed */}
|
||||
<div className={`bg-neutral-200 dark:bg-neutral-800 ${isFullscreen ? 'h-[85vh]' : 'h-[600px]'} transition-all duration-300`}>
|
||||
<iframe
|
||||
src={`${pdfUrl}#toolbar=1&navpanes=1&scrollbar=1`}
|
||||
className="w-full h-full border-0"
|
||||
title="PDF Viewer"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Fallback message */}
|
||||
<div className="p-4 text-center text-sm text-muted-foreground bg-muted/30 border-t border-border">
|
||||
<p>
|
||||
PDF not displaying? Try{' '}
|
||||
<a href={pdfUrl} target="_blank" rel="noopener noreferrer" className="text-primary hover:underline">
|
||||
opening directly
|
||||
</a>{' '}
|
||||
or{' '}
|
||||
<a href={pdfUrl} download className="text-primary hover:underline">
|
||||
downloading the file
|
||||
</a>.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
46
site/src/components/ui/badge.tsx
Normal file
@ -0,0 +1,46 @@
|
||||
import * as React from "react"
|
||||
import { Slot } from "@radix-ui/react-slot"
|
||||
import { cva, type VariantProps } from "class-variance-authority"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
const badgeVariants = cva(
|
||||
"inline-flex items-center justify-center rounded-full border px-2 py-0.5 text-xs font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-[color,box-shadow] overflow-hidden",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default:
|
||||
"border-transparent bg-primary text-primary-foreground [a&]:hover:bg-primary/90",
|
||||
secondary:
|
||||
"border-transparent bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90",
|
||||
destructive:
|
||||
"border-transparent bg-destructive text-white [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
|
||||
outline:
|
||||
"text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: "default",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
function Badge({
|
||||
className,
|
||||
variant,
|
||||
asChild = false,
|
||||
...props
|
||||
}: React.ComponentProps<"span"> &
|
||||
VariantProps<typeof badgeVariants> & { asChild?: boolean }) {
|
||||
const Comp = asChild ? Slot : "span"
|
||||
|
||||
return (
|
||||
<Comp
|
||||
data-slot="badge"
|
||||
className={cn(badgeVariants({ variant }), className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export { Badge, badgeVariants }
|
||||
62
site/src/components/ui/button.tsx
Normal file
@ -0,0 +1,62 @@
|
||||
import * as React from "react"
|
||||
import { Slot } from "@radix-ui/react-slot"
|
||||
import { cva, type VariantProps } from "class-variance-authority"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
const buttonVariants = cva(
|
||||
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default: "bg-primary text-primary-foreground hover:bg-primary/90",
|
||||
destructive:
|
||||
"bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
|
||||
outline:
|
||||
"border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
|
||||
secondary:
|
||||
"bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
||||
ghost:
|
||||
"hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
|
||||
link: "text-primary underline-offset-4 hover:underline",
|
||||
},
|
||||
size: {
|
||||
default: "h-9 px-4 py-2 has-[>svg]:px-3",
|
||||
sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
|
||||
lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
|
||||
icon: "size-9",
|
||||
"icon-sm": "size-8",
|
||||
"icon-lg": "size-10",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: "default",
|
||||
size: "default",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
function Button({
|
||||
className,
|
||||
variant = "default",
|
||||
size = "default",
|
||||
asChild = false,
|
||||
...props
|
||||
}: React.ComponentProps<"button"> &
|
||||
VariantProps<typeof buttonVariants> & {
|
||||
asChild?: boolean
|
||||
}) {
|
||||
const Comp = asChild ? Slot : "button"
|
||||
|
||||
return (
|
||||
<Comp
|
||||
data-slot="button"
|
||||
data-variant={variant}
|
||||
data-size={size}
|
||||
className={cn(buttonVariants({ variant, size, className }))}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export { Button, buttonVariants }
|
||||
92
site/src/components/ui/card.tsx
Normal file
@ -0,0 +1,92 @@
|
||||
import * as React from "react"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
function Card({ className, ...props }: React.ComponentProps<"div">) {
|
||||
return (
|
||||
<div
|
||||
data-slot="card"
|
||||
className={cn(
|
||||
"bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
|
||||
return (
|
||||
<div
|
||||
data-slot="card-header"
|
||||
className={cn(
|
||||
"@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-2 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function CardTitle({ className, ...props }: React.ComponentProps<"div">) {
|
||||
return (
|
||||
<div
|
||||
data-slot="card-title"
|
||||
className={cn("leading-none font-semibold", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function CardDescription({ className, ...props }: React.ComponentProps<"div">) {
|
||||
return (
|
||||
<div
|
||||
data-slot="card-description"
|
||||
className={cn("text-muted-foreground text-sm", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function CardAction({ className, ...props }: React.ComponentProps<"div">) {
|
||||
return (
|
||||
<div
|
||||
data-slot="card-action"
|
||||
className={cn(
|
||||
"col-start-2 row-span-2 row-start-1 self-start justify-self-end",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function CardContent({ className, ...props }: React.ComponentProps<"div">) {
|
||||
return (
|
||||
<div
|
||||
data-slot="card-content"
|
||||
className={cn("px-6", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function CardFooter({ className, ...props }: React.ComponentProps<"div">) {
|
||||
return (
|
||||
<div
|
||||
data-slot="card-footer"
|
||||
className={cn("flex items-center px-6 [.border-t]:pt-6", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export {
|
||||
Card,
|
||||
CardHeader,
|
||||
CardFooter,
|
||||
CardTitle,
|
||||
CardAction,
|
||||
CardDescription,
|
||||
CardContent,
|
||||
}
|
||||
255
site/src/components/ui/dropdown-menu.tsx
Normal file
@ -0,0 +1,255 @@
|
||||
import * as React from "react"
|
||||
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu"
|
||||
import { CheckIcon, ChevronRightIcon, CircleIcon } from "lucide-react"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
function DropdownMenu({
|
||||
...props
|
||||
}: React.ComponentProps<typeof DropdownMenuPrimitive.Root>) {
|
||||
return <DropdownMenuPrimitive.Root data-slot="dropdown-menu" {...props} />
|
||||
}
|
||||
|
||||
function DropdownMenuPortal({
|
||||
...props
|
||||
}: React.ComponentProps<typeof DropdownMenuPrimitive.Portal>) {
|
||||
return (
|
||||
<DropdownMenuPrimitive.Portal data-slot="dropdown-menu-portal" {...props} />
|
||||
)
|
||||
}
|
||||
|
||||
function DropdownMenuTrigger({
|
||||
...props
|
||||
}: React.ComponentProps<typeof DropdownMenuPrimitive.Trigger>) {
|
||||
return (
|
||||
<DropdownMenuPrimitive.Trigger
|
||||
data-slot="dropdown-menu-trigger"
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function DropdownMenuContent({
|
||||
className,
|
||||
sideOffset = 4,
|
||||
...props
|
||||
}: React.ComponentProps<typeof DropdownMenuPrimitive.Content>) {
|
||||
return (
|
||||
<DropdownMenuPrimitive.Portal>
|
||||
<DropdownMenuPrimitive.Content
|
||||
data-slot="dropdown-menu-content"
|
||||
sideOffset={sideOffset}
|
||||
className={cn(
|
||||
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 max-h-(--radix-dropdown-menu-content-available-height) min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border p-1 shadow-md",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
</DropdownMenuPrimitive.Portal>
|
||||
)
|
||||
}
|
||||
|
||||
function DropdownMenuGroup({
|
||||
...props
|
||||
}: React.ComponentProps<typeof DropdownMenuPrimitive.Group>) {
|
||||
return (
|
||||
<DropdownMenuPrimitive.Group data-slot="dropdown-menu-group" {...props} />
|
||||
)
|
||||
}
|
||||
|
||||
function DropdownMenuItem({
|
||||
className,
|
||||
inset,
|
||||
variant = "default",
|
||||
...props
|
||||
}: React.ComponentProps<typeof DropdownMenuPrimitive.Item> & {
|
||||
inset?: boolean
|
||||
variant?: "default" | "destructive"
|
||||
}) {
|
||||
return (
|
||||
<DropdownMenuPrimitive.Item
|
||||
data-slot="dropdown-menu-item"
|
||||
data-inset={inset}
|
||||
data-variant={variant}
|
||||
className={cn(
|
||||
"focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:*:[svg]:!text-destructive [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function DropdownMenuCheckboxItem({
|
||||
className,
|
||||
children,
|
||||
checked,
|
||||
...props
|
||||
}: React.ComponentProps<typeof DropdownMenuPrimitive.CheckboxItem>) {
|
||||
return (
|
||||
<DropdownMenuPrimitive.CheckboxItem
|
||||
data-slot="dropdown-menu-checkbox-item"
|
||||
className={cn(
|
||||
"focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||
className
|
||||
)}
|
||||
checked={checked}
|
||||
{...props}
|
||||
>
|
||||
<span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center">
|
||||
<DropdownMenuPrimitive.ItemIndicator>
|
||||
<CheckIcon className="size-4" />
|
||||
</DropdownMenuPrimitive.ItemIndicator>
|
||||
</span>
|
||||
{children}
|
||||
</DropdownMenuPrimitive.CheckboxItem>
|
||||
)
|
||||
}
|
||||
|
||||
function DropdownMenuRadioGroup({
|
||||
...props
|
||||
}: React.ComponentProps<typeof DropdownMenuPrimitive.RadioGroup>) {
|
||||
return (
|
||||
<DropdownMenuPrimitive.RadioGroup
|
||||
data-slot="dropdown-menu-radio-group"
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function DropdownMenuRadioItem({
|
||||
className,
|
||||
children,
|
||||
...props
|
||||
}: React.ComponentProps<typeof DropdownMenuPrimitive.RadioItem>) {
|
||||
return (
|
||||
<DropdownMenuPrimitive.RadioItem
|
||||
data-slot="dropdown-menu-radio-item"
|
||||
className={cn(
|
||||
"focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center">
|
||||
<DropdownMenuPrimitive.ItemIndicator>
|
||||
<CircleIcon className="size-2 fill-current" />
|
||||
</DropdownMenuPrimitive.ItemIndicator>
|
||||
</span>
|
||||
{children}
|
||||
</DropdownMenuPrimitive.RadioItem>
|
||||
)
|
||||
}
|
||||
|
||||
function DropdownMenuLabel({
|
||||
className,
|
||||
inset,
|
||||
...props
|
||||
}: React.ComponentProps<typeof DropdownMenuPrimitive.Label> & {
|
||||
inset?: boolean
|
||||
}) {
|
||||
return (
|
||||
<DropdownMenuPrimitive.Label
|
||||
data-slot="dropdown-menu-label"
|
||||
data-inset={inset}
|
||||
className={cn(
|
||||
"px-2 py-1.5 text-sm font-medium data-[inset]:pl-8",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function DropdownMenuSeparator({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof DropdownMenuPrimitive.Separator>) {
|
||||
return (
|
||||
<DropdownMenuPrimitive.Separator
|
||||
data-slot="dropdown-menu-separator"
|
||||
className={cn("bg-border -mx-1 my-1 h-px", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function DropdownMenuShortcut({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<"span">) {
|
||||
return (
|
||||
<span
|
||||
data-slot="dropdown-menu-shortcut"
|
||||
className={cn(
|
||||
"text-muted-foreground ml-auto text-xs tracking-widest",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function DropdownMenuSub({
|
||||
...props
|
||||
}: React.ComponentProps<typeof DropdownMenuPrimitive.Sub>) {
|
||||
return <DropdownMenuPrimitive.Sub data-slot="dropdown-menu-sub" {...props} />
|
||||
}
|
||||
|
||||
function DropdownMenuSubTrigger({
|
||||
className,
|
||||
inset,
|
||||
children,
|
||||
...props
|
||||
}: React.ComponentProps<typeof DropdownMenuPrimitive.SubTrigger> & {
|
||||
inset?: boolean
|
||||
}) {
|
||||
return (
|
||||
<DropdownMenuPrimitive.SubTrigger
|
||||
data-slot="dropdown-menu-sub-trigger"
|
||||
data-inset={inset}
|
||||
className={cn(
|
||||
"focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
<ChevronRightIcon className="ml-auto size-4" />
|
||||
</DropdownMenuPrimitive.SubTrigger>
|
||||
)
|
||||
}
|
||||
|
||||
function DropdownMenuSubContent({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof DropdownMenuPrimitive.SubContent>) {
|
||||
return (
|
||||
<DropdownMenuPrimitive.SubContent
|
||||
data-slot="dropdown-menu-sub-content"
|
||||
className={cn(
|
||||
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-hidden rounded-md border p-1 shadow-lg",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export {
|
||||
DropdownMenu,
|
||||
DropdownMenuPortal,
|
||||
DropdownMenuTrigger,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuGroup,
|
||||
DropdownMenuLabel,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuCheckboxItem,
|
||||
DropdownMenuRadioGroup,
|
||||
DropdownMenuRadioItem,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuShortcut,
|
||||
DropdownMenuSub,
|
||||
DropdownMenuSubTrigger,
|
||||
DropdownMenuSubContent,
|
||||
}
|
||||
66
site/src/components/ui/tabs.tsx
Normal file
@ -0,0 +1,66 @@
|
||||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import * as TabsPrimitive from "@radix-ui/react-tabs"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
function Tabs({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof TabsPrimitive.Root>) {
|
||||
return (
|
||||
<TabsPrimitive.Root
|
||||
data-slot="tabs"
|
||||
className={cn("flex flex-col gap-2", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function TabsList({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof TabsPrimitive.List>) {
|
||||
return (
|
||||
<TabsPrimitive.List
|
||||
data-slot="tabs-list"
|
||||
className={cn(
|
||||
"bg-muted text-muted-foreground inline-flex h-9 w-fit items-center justify-center rounded-lg p-[3px]",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function TabsTrigger({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof TabsPrimitive.Trigger>) {
|
||||
return (
|
||||
<TabsPrimitive.Trigger
|
||||
data-slot="tabs-trigger"
|
||||
className={cn(
|
||||
"data-[state=active]:bg-background dark:data-[state=active]:text-foreground focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:outline-ring dark:data-[state=active]:border-input dark:data-[state=active]:bg-input/30 text-foreground dark:text-muted-foreground inline-flex h-[calc(100%-1px)] flex-1 items-center justify-center gap-1.5 rounded-md border border-transparent px-2 py-1 text-sm font-medium whitespace-nowrap transition-[color,box-shadow] focus-visible:ring-[3px] focus-visible:outline-1 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:shadow-sm [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function TabsContent({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof TabsPrimitive.Content>) {
|
||||
return (
|
||||
<TabsPrimitive.Content
|
||||
data-slot="tabs-content"
|
||||
className={cn("flex-1 outline-none", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export { Tabs, TabsList, TabsTrigger, TabsContent }
|
||||
20
site/src/content/books/mims/555-timer-circuits.md
Normal file
@ -0,0 +1,20 @@
|
||||
---
|
||||
title: "Engineer's Mini-Notebook: 555 Timer IC Circuits"
|
||||
shortTitle: "555 Timer Circuits"
|
||||
collection: "mims"
|
||||
year: 1986
|
||||
description: "Everything about the legendary 555 timer IC. Monostable and astable modes, pulse generators, oscillators, and creative timing applications."
|
||||
topics: ["555-timer", "oscillators", "timing", "integrated-circuits"]
|
||||
archiveOrgId: "electronics_-_Forrest_Mims-engineers_mini-notebook_555_timer_circuits_radio_sha"
|
||||
archiveOrgUrl: "https://archive.org/details/electronics_-_Forrest_Mims-engineers_mini-notebook_555_timer_circuits_radio_sha"
|
||||
localPdf: "/pdfs/mims/03_555_Timer_Circuits.pdf"
|
||||
coverImage: "/covers/mims/03_555_Timer_Circuits-01.jpg"
|
||||
sortOrder: 3
|
||||
formats:
|
||||
- type: "PDF"
|
||||
filename: "03_555_Timer_Circuits.pdf"
|
||||
- type: "DjVu"
|
||||
filename: "electronics - Forrest Mims-engineer's mini-notebook 555 timer circuits (radio shack electronics).djvu"
|
||||
---
|
||||
|
||||
The 555 timer is one of the most versatile ICs ever made. This notebook shows you dozens of ways to use it, from simple LED blinkers to complex timing circuits.
|
||||
20
site/src/content/books/mims/basic-semiconductor-1993.md
Normal file
@ -0,0 +1,20 @@
|
||||
---
|
||||
title: "Engineer's Mini-Notebook: Basic Semiconductor Circuits (1993 Edition)"
|
||||
shortTitle: "Semiconductors (1993)"
|
||||
collection: "mims"
|
||||
year: 1993
|
||||
description: "Revised 1993 edition with updated component values and additional circuits. Reflects improvements in semiconductor technology over 7 years."
|
||||
topics: ["semiconductors", "transistors", "diodes", "updated"]
|
||||
archiveOrgId: "Radio_Shack_Engineers_Mini-Notebook_-_Basic_Semiconductor_Circuits_-_F._Mims_19"
|
||||
archiveOrgUrl: "https://archive.org/details/Radio_Shack_Engineers_Mini-Notebook_-_Basic_Semiconductor_Circuits_-_F._Mims_19"
|
||||
localPdf: "/pdfs/mims/11_Basic_Semiconductor_1993.pdf"
|
||||
coverImage: "/covers/mims/11_Basic_Semiconductor_1993-01.jpg"
|
||||
sortOrder: 11
|
||||
formats:
|
||||
- type: "PDF"
|
||||
filename: "11_Basic_Semiconductor_1993.pdf"
|
||||
- type: "DjVu"
|
||||
filename: "Radio Shack Engineer's Mini-Notebook - Basic Semiconductor Circuits - F. Mims (1993) WW.djvu"
|
||||
---
|
||||
|
||||
The updated 1993 edition of the semiconductor basics notebook. Seven years of refinement produced improved circuits and clearer explanations.
|
||||
22
site/src/content/books/mims/basic-semiconductor-circuits.md
Normal file
@ -0,0 +1,22 @@
|
||||
---
|
||||
title: "Engineer's Mini-Notebook: Basic Semiconductor Circuits"
|
||||
shortTitle: "Basic Semiconductor Circuits"
|
||||
collection: "mims"
|
||||
year: 1986
|
||||
description: "Fundamental transistor and diode circuits with Mims' signature hand-drawn illustrations. Covers NPN/PNP transistors, rectifiers, voltage regulators, and basic amplifier configurations."
|
||||
topics: ["semiconductors", "transistors", "diodes", "amplifiers"]
|
||||
archiveOrgId: "electronics_-_Forrest_Mims-engineers_mini-notebook_basic_semiconductor_circuits_"
|
||||
archiveOrgUrl: "https://archive.org/details/electronics_-_Forrest_Mims-engineers_mini-notebook_basic_semiconductor_circuits_"
|
||||
localPdf: "/pdfs/mims/01_Basic_Semiconductor_Circuits.pdf"
|
||||
coverImage: "/covers/mims/01_Basic_Semiconductor_Circuits-01.jpg"
|
||||
sortOrder: 1
|
||||
formats:
|
||||
- type: "PDF"
|
||||
filename: "01_Basic_Semiconductor_Circuits.pdf"
|
||||
- type: "DjVu"
|
||||
filename: "electronics - Forrest Mims-engineer's mini-notebook basic semiconductor circuits (radio shack electronics).djvu"
|
||||
- type: "Text"
|
||||
filename: "electronics - Forrest Mims-engineer's mini-notebook basic semiconductor circuits (radio shack electronics)_djvu.txt"
|
||||
---
|
||||
|
||||
The foundational notebook covering transistor basics, diode applications, and simple semiconductor circuits. Perfect for beginners learning the fundamentals of solid-state electronics.
|
||||
20
site/src/content/books/mims/communications-projects.md
Normal file
@ -0,0 +1,20 @@
|
||||
---
|
||||
title: "Engineer's Mini-Notebook: Communications Projects"
|
||||
shortTitle: "Communications Projects"
|
||||
collection: "mims"
|
||||
year: 1986
|
||||
description: "Radio and communications circuits including transmitters, receivers, modulators, and antenna projects. Great for amateur radio enthusiasts and experimenters."
|
||||
topics: ["communications", "radio", "transmitters", "receivers"]
|
||||
archiveOrgId: "Forrest_Mims-Engineers_Mini-Notebook_-_Communications_Projects_Radio_Shack_Elec"
|
||||
archiveOrgUrl: "https://archive.org/details/Forrest_Mims-Engineers_Mini-Notebook_-_Communications_Projects_Radio_Shack_Elec"
|
||||
localPdf: "/pdfs/mims/05_Communications_Projects.pdf"
|
||||
coverImage: "/covers/mims/05_Communications_Projects-01.jpg"
|
||||
sortOrder: 5
|
||||
formats:
|
||||
- type: "PDF"
|
||||
filename: "05_Communications_Projects.pdf"
|
||||
- type: "DjVu"
|
||||
filename: "Forrest Mims-Engineer's Mini-Notebook - Communications Projects (Radio Shack Electronics).djvu"
|
||||
---
|
||||
|
||||
Build your own radio circuits! From simple crystal radios to FM transmitters and receivers, this notebook covers the fundamentals of wireless communication.
|
||||
24
site/src/content/books/mims/engineers-notebook-ii.md
Normal file
@ -0,0 +1,24 @@
|
||||
---
|
||||
title: "Engineer's Notebook II: A Handbook of Integrated Circuit Applications"
|
||||
shortTitle: "Engineer's Notebook II"
|
||||
collection: "mims"
|
||||
year: 1982
|
||||
description: "The foundational handbook covering integrated circuit applications. Features Mims' iconic hand-drawn schematics for op-amps, timers, audio circuits, power supplies, and dozens of practical IC projects."
|
||||
topics: ["integrated circuits", "op-amps", "IC applications", "handbook"]
|
||||
archiveOrgId: "ENGINEERSNOTEBOOKII"
|
||||
archiveOrgUrl: "https://archive.org/details/ENGINEERSNOTEBOOKII"
|
||||
localPdf: "/pdfs/mims/Engineers_Notebook_II_IC_Applications.pdf"
|
||||
coverImage: "/covers/mims/Engineers_Notebook_II_IC_Applications-01.jpg"
|
||||
sortOrder: 1
|
||||
formats:
|
||||
- type: "PDF"
|
||||
filename: "Engineers_Notebook_II_IC_Applications.pdf"
|
||||
- type: "EPUB"
|
||||
filename: "ENGINEERS_NOTEBOOK_II.epub"
|
||||
---
|
||||
|
||||
Engineer's Notebook II is one of the foundational volumes in Forrest Mims' legendary series. Published in 1982, this handbook focuses on practical integrated circuit applications with Mims' signature hand-drawn schematics.
|
||||
|
||||
The book covers a wide range of IC applications including operational amplifiers, 555 timers, audio circuits, power supply designs, and many more practical projects. Each circuit is accompanied by clear explanations and component values, making it easy to build and experiment.
|
||||
|
||||
This is essential reading for anyone interested in understanding how integrated circuits work and how to use them in real-world applications.
|
||||
22
site/src/content/books/mims/environmental-projects.md
Normal file
@ -0,0 +1,22 @@
|
||||
---
|
||||
title: "Engineer's Mini-Notebook: Environmental Projects"
|
||||
shortTitle: "Environmental Projects"
|
||||
collection: "mims"
|
||||
year: 1990
|
||||
description: "Electronics projects for environmental monitoring and measurement. Covers temperature sensors, humidity detectors, rain gauges, light meters, and other circuits for observing and measuring the natural world."
|
||||
topics: ["environmental", "sensors", "monitoring", "projects"]
|
||||
archiveOrgId: "engineers-mini-notebook-555-timer-circuits"
|
||||
archiveOrgUrl: "https://archive.org/details/engineers-mini-notebook-555-timer-circuits"
|
||||
localPdf: "/pdfs/mims/Engineers_Mini_Notebook_Environmental_Projects.pdf"
|
||||
coverImage: "/covers/mims/Engineers_Mini_Notebook_Environmental_Projects-01.jpg"
|
||||
sortOrder: 14
|
||||
formats:
|
||||
- type: "PDF"
|
||||
filename: "Engineers_Mini_Notebook_Environmental_Projects.pdf"
|
||||
---
|
||||
|
||||
This Mini-Notebook focuses on electronics projects for environmental monitoring - a fascinating intersection of electronics and nature observation.
|
||||
|
||||
Projects include temperature measurement circuits, humidity sensors, rain detectors, light intensity meters, wind speed indicators, and more. Each project is designed to be buildable with commonly available components and includes Mims' characteristic clear hand-drawn schematics.
|
||||
|
||||
Perfect for makers interested in weather stations, environmental science, or outdoor monitoring applications.
|
||||
@ -0,0 +1,20 @@
|
||||
---
|
||||
title: "Engineer's Mini-Notebook: Formulas, Tables & Basic Circuits"
|
||||
shortTitle: "Formulas & Tables"
|
||||
collection: "mims"
|
||||
year: 1986
|
||||
description: "Essential reference of electronics formulas, component values, schematic symbols, and fundamental circuits. The perfect pocket reference for any electronics workbench."
|
||||
topics: ["reference", "formulas", "schematic-symbols", "fundamentals"]
|
||||
archiveOrgId: "Forrest_Mims-Engineers_Mini-Notebook_Formulas_Tables_Basic_Circuits_Radio_Shack"
|
||||
archiveOrgUrl: "https://archive.org/details/Forrest_Mims-Engineers_Mini-Notebook_Formulas_Tables_Basic_Circuits_Radio_Shack"
|
||||
localPdf: "/pdfs/mims/04_Formulas_Tables_Basic_Circuits.pdf"
|
||||
coverImage: "/covers/mims/04_Formulas_Tables_Basic_Circuits-01.jpg"
|
||||
sortOrder: 4
|
||||
formats:
|
||||
- type: "PDF"
|
||||
filename: "04_Formulas_Tables_Basic_Circuits.pdf"
|
||||
- type: "DjVu"
|
||||
filename: "Forrest Mims-Engineer's Mini-Notebook Formulas Tables Basic Circuits (Radio Shack Electronics).djvu"
|
||||
---
|
||||
|
||||
The ultimate pocket reference. Ohm's Law, resistor color codes, capacitor values, schematic symbols, and dozens of basic circuits - all in Mims' clear, hand-drawn style.
|
||||
20
site/src/content/books/mims/magnet-sensor-projects.md
Normal file
@ -0,0 +1,20 @@
|
||||
---
|
||||
title: "Engineer's Mini-Notebook: Magnet and Sensor Projects"
|
||||
shortTitle: "Magnet & Sensor Projects"
|
||||
collection: "mims"
|
||||
year: 1986
|
||||
description: "Magnetic sensing and related projects. Hall effect sensors, reed switches, magnetic field detectors, and creative magnet-based circuits."
|
||||
topics: ["magnets", "hall-effect", "sensors", "magnetic-fields"]
|
||||
archiveOrgId: "Forrest_Mims-Engineers_Mini-Notebook_Magnet_And_Sensor_Projects_Radio_Shack_Ele"
|
||||
archiveOrgUrl: "https://archive.org/details/Forrest_Mims-Engineers_Mini-Notebook_Magnet_And_Sensor_Projects_Radio_Shack_Ele"
|
||||
localPdf: "/pdfs/mims/09_Magnet_And_Sensor_Projects.pdf"
|
||||
coverImage: "/covers/mims/09_Magnet_And_Sensor_Projects-01.jpg"
|
||||
sortOrder: 9
|
||||
formats:
|
||||
- type: "PDF"
|
||||
filename: "09_Magnet_And_Sensor_Projects.pdf"
|
||||
- type: "DjVu"
|
||||
filename: "Forrest Mims-Engineer's Mini-Notebook Magnet And Sensor Projects (Radio Shack Electronics).djvu"
|
||||
---
|
||||
|
||||
Magnets and electronics make a powerful combination. Build magnetic field detectors, proximity sensors, and position sensing circuits.
|
||||
20
site/src/content/books/mims/op-amp-ic-1985.md
Normal file
@ -0,0 +1,20 @@
|
||||
---
|
||||
title: "Engineer's Mini-Notebook: Op Amp IC Circuits (1985 Edition)"
|
||||
shortTitle: "Op Amps (1985)"
|
||||
collection: "mims"
|
||||
year: 1985
|
||||
description: "The original 1985 first printing of the Op Amp notebook. Features the earliest versions of Mims' iconic operational amplifier circuits."
|
||||
topics: ["op-amps", "integrated-circuits", "amplifiers", "vintage"]
|
||||
archiveOrgId: "Radio_Shack_Engineers_Mini-Notebook_-_Op_Amp_IC_Circuits_-_F._Mims_1985_WW"
|
||||
archiveOrgUrl: "https://archive.org/details/Radio_Shack_Engineers_Mini-Notebook_-_Op_Amp_IC_Circuits_-_F._Mims_1985_WW"
|
||||
localPdf: "/pdfs/mims/12_Op_Amp_IC_1985.pdf"
|
||||
coverImage: "/covers/mims/12_Op_Amp_IC_1985-01.jpg"
|
||||
sortOrder: 12
|
||||
formats:
|
||||
- type: "PDF"
|
||||
filename: "12_Op_Amp_IC_1985.pdf"
|
||||
- type: "DjVu"
|
||||
filename: "Radio Shack Engineer's Mini-Notebook - Op Amp IC Circuits - F. Mims (1985) WW.djvu"
|
||||
---
|
||||
|
||||
The first edition from 1985 - where it all began for the Op Amp notebook. A piece of electronics education history.
|
||||
20
site/src/content/books/mims/op-amp-ic-circuits.md
Normal file
@ -0,0 +1,20 @@
|
||||
---
|
||||
title: "Engineer's Mini-Notebook: Op Amp IC Circuits"
|
||||
shortTitle: "Op Amp IC Circuits"
|
||||
collection: "mims"
|
||||
year: 1986
|
||||
description: "Comprehensive guide to operational amplifier circuits. Covers inverting/non-inverting amplifiers, comparators, integrators, differentiators, and practical applications."
|
||||
topics: ["op-amps", "integrated-circuits", "amplifiers", "analog"]
|
||||
archiveOrgId: "Forrest_Mims-Engineers_Mini-Notebook_Op_Amp_Ic_Circuits_Radio_Shack_Electronics"
|
||||
archiveOrgUrl: "https://archive.org/details/Forrest_Mims-Engineers_Mini-Notebook_Op_Amp_Ic_Circuits_Radio_Shack_Electronics"
|
||||
localPdf: "/pdfs/mims/02_Op_Amp_IC_Circuits.pdf"
|
||||
coverImage: "/covers/mims/02_Op_Amp_IC_Circuits-01.jpg"
|
||||
sortOrder: 2
|
||||
formats:
|
||||
- type: "PDF"
|
||||
filename: "02_Op_Amp_IC_Circuits.pdf"
|
||||
- type: "DjVu"
|
||||
filename: "Forrest Mims-Engineer's Mini-Notebook Op Amp Ic Circuits (Radio Shack Electronics)(1).djvu"
|
||||
---
|
||||
|
||||
Master the versatile operational amplifier with circuits ranging from simple gain stages to precision instrumentation amplifiers.
|
||||
20
site/src/content/books/mims/optoelectronic-circuits-1986.md
Normal file
@ -0,0 +1,20 @@
|
||||
---
|
||||
title: "Engineer's Mini-Notebook: Optoelectronic Circuits (1986 Edition)"
|
||||
shortTitle: "Optoelectronics (1986)"
|
||||
collection: "mims"
|
||||
year: 1986
|
||||
description: "Earlier edition of the optoelectronics notebook with additional content and variations. A collector's edition with unique circuits."
|
||||
topics: ["optoelectronics", "LEDs", "phototransistors", "vintage"]
|
||||
archiveOrgId: "Radio_Shack_Engineers_Mini-Notebook_-_Optoelectronic_Circuits_-_F._Mims_1986_W"
|
||||
archiveOrgUrl: "https://archive.org/details/Radio_Shack_Engineers_Mini-Notebook_-_Optoelectronic_Circuits_-_F._Mims_1986_W"
|
||||
localPdf: "/pdfs/mims/10_Optoelectronic_Circuits_1986.pdf"
|
||||
coverImage: "/covers/mims/10_Optoelectronic_Circuits_1986-01.jpg"
|
||||
sortOrder: 10
|
||||
formats:
|
||||
- type: "PDF"
|
||||
filename: "10_Optoelectronic_Circuits_1986.pdf"
|
||||
- type: "DjVu"
|
||||
filename: "Radio Shack Engineer's Mini-Notebook - Optoelectronic Circuits - F. Mims (1986) WW.djvu"
|
||||
---
|
||||
|
||||
The original 1986 printing of the optoelectronics notebook. Compare with the later edition to see how Mims refined his presentations.
|
||||
20
site/src/content/books/mims/optoelectronics-circuits.md
Normal file
@ -0,0 +1,20 @@
|
||||
---
|
||||
title: "Engineer's Mini-Notebook: Optoelectronics Circuits"
|
||||
shortTitle: "Optoelectronics Circuits"
|
||||
collection: "mims"
|
||||
year: 1986
|
||||
description: "Light-based electronics including LEDs, phototransistors, optocouplers, fiber optics, and laser circuits. The intersection of light and electronics."
|
||||
topics: ["optoelectronics", "LEDs", "phototransistors", "fiber-optics"]
|
||||
archiveOrgId: "Forrest_Mims-Engineers_Mini-Notebook_Optoelectronics_Circuits_Radio_Shack_Elect"
|
||||
archiveOrgUrl: "https://archive.org/details/Forrest_Mims-Engineers_Mini-Notebook_Optoelectronics_Circuits_Radio_Shack_Elect"
|
||||
localPdf: "/pdfs/mims/07_Optoelectronics_Circuits.pdf"
|
||||
coverImage: "/covers/mims/07_Optoelectronics_Circuits-01.jpg"
|
||||
sortOrder: 7
|
||||
formats:
|
||||
- type: "PDF"
|
||||
filename: "07_Optoelectronics_Circuits.pdf"
|
||||
- type: "DjVu"
|
||||
filename: "Forrest Mims-Engineer's Mini-Notebook Optoelectronics Circuits (Radio Shack Electronics).djvu"
|
||||
---
|
||||
|
||||
LEDs, lasers, and light sensors come alive in this notebook. Build everything from simple LED drivers to sophisticated light-based communication systems.
|
||||
22
site/src/content/books/mims/schematic-symbols.md
Normal file
@ -0,0 +1,22 @@
|
||||
---
|
||||
title: "Engineer's Mini-Notebook: Schematic Symbols, Device Packages, Design & Testing"
|
||||
shortTitle: "Schematic Symbols & Testing"
|
||||
collection: "mims"
|
||||
year: 1988
|
||||
description: "Essential reference for reading and drawing electronic schematics. Covers standard schematic symbols, component package types, circuit design basics, and testing procedures with hand-drawn clarity."
|
||||
topics: ["schematic symbols", "reference", "design", "testing"]
|
||||
archiveOrgId: "engineers-mini-notebook-555-timer-circuits"
|
||||
archiveOrgUrl: "https://archive.org/details/engineers-mini-notebook-555-timer-circuits"
|
||||
localPdf: "/pdfs/mims/Engineers_Mini_Notebook_Schematic_Symbols.pdf"
|
||||
coverImage: "/covers/mims/Engineers_Mini_Notebook_Schematic_Symbols-01.jpg"
|
||||
sortOrder: 15
|
||||
formats:
|
||||
- type: "PDF"
|
||||
filename: "Engineers_Mini_Notebook_Schematic_Symbols.pdf"
|
||||
---
|
||||
|
||||
This Mini-Notebook is an essential reference for anyone learning to read or draw electronic schematics. It provides a comprehensive guide to standard schematic symbols used in circuit diagrams.
|
||||
|
||||
Beyond symbols, the book covers common component package types (DIPs, TO-92, etc.), basic circuit design principles, and testing procedures for verifying circuit operation. This is the kind of foundational knowledge that every electronics hobbyist needs.
|
||||
|
||||
Keep this one handy on your workbench - you'll reference it constantly when building circuits from schematics or designing your own.
|
||||
20
site/src/content/books/mims/sensor-projects.md
Normal file
@ -0,0 +1,20 @@
|
||||
---
|
||||
title: "Engineer's Mini-Notebook: Sensor Projects"
|
||||
shortTitle: "Sensor Projects"
|
||||
collection: "mims"
|
||||
year: 1986
|
||||
description: "Interface with the physical world using sensors. Temperature, light, sound, touch, and motion sensors with practical circuits for each."
|
||||
topics: ["sensors", "temperature", "light-sensing", "interfacing"]
|
||||
archiveOrgId: "Forrest_Mims-engineers_mini-notebook_sensor_projects_radio_shack_electronics"
|
||||
archiveOrgUrl: "https://archive.org/details/Forrest_Mims-engineers_mini-notebook_sensor_projects_radio_shack_electronics"
|
||||
localPdf: "/pdfs/mims/08_Sensor_Projects.pdf"
|
||||
coverImage: "/covers/mims/08_Sensor_Projects-01.jpg"
|
||||
sortOrder: 8
|
||||
formats:
|
||||
- type: "PDF"
|
||||
filename: "08_Sensor_Projects.pdf"
|
||||
- type: "DjVu"
|
||||
filename: "Forrest Mims-engineer's mini-notebook sensor projects (radio shack electronics).djvu"
|
||||
---
|
||||
|
||||
Sensors let your circuits interact with the real world. This notebook covers thermistors, photocells, microphones, and more - essential for any maker project.
|
||||
20
site/src/content/books/mims/solar-cell-projects.md
Normal file
@ -0,0 +1,20 @@
|
||||
---
|
||||
title: "Engineer's Mini-Notebook: Solar Cell Projects"
|
||||
shortTitle: "Solar Cell Projects"
|
||||
collection: "mims"
|
||||
year: 1986
|
||||
description: "Harness the sun with photovoltaic circuits. Solar battery chargers, light meters, solar-powered gadgets, and energy harvesting projects."
|
||||
topics: ["solar", "photovoltaic", "energy", "green-tech"]
|
||||
archiveOrgId: "Forrest_Mims-Engineers_Mini-Notebook_Solar_Cell_Projects_Radio_Shack_Electronic"
|
||||
archiveOrgUrl: "https://archive.org/details/Forrest_Mims-Engineers_Mini-Notebook_Solar_Cell_Projects_Radio_Shack_Electronic"
|
||||
localPdf: "/pdfs/mims/06_Solar_Cell_Projects.pdf"
|
||||
coverImage: "/covers/mims/06_Solar_Cell_Projects-01.jpg"
|
||||
sortOrder: 6
|
||||
formats:
|
||||
- type: "PDF"
|
||||
filename: "06_Solar_Cell_Projects.pdf"
|
||||
- type: "DjVu"
|
||||
filename: "Forrest Mims-Engineer's Mini-Notebook Solar Cell Projects (Radio Shack Electronics).djvu"
|
||||
---
|
||||
|
||||
Solar power was cutting-edge in the 80s, and Mims was ahead of his time. Learn to build solar-powered circuits that are still relevant today.
|
||||
22
site/src/content/books/uglys/electrical-reference-2005.md
Normal file
@ -0,0 +1,22 @@
|
||||
---
|
||||
title: "Ugly's Electrical References (2005 Edition)"
|
||||
shortTitle: "Ugly's Electrical References"
|
||||
collection: "uglys"
|
||||
year: 2005
|
||||
description: "The pocket-sized bible for electricians. Packed with tables, formulas, wiring diagrams, NEC code references, and quick calculations for electrical work. An essential field reference."
|
||||
topics: ["electrical", "wiring", "NEC-code", "reference", "formulas"]
|
||||
archiveOrgId: "Ugly_sElectricalReference2005"
|
||||
archiveOrgUrl: "https://archive.org/details/Ugly_sElectricalReference2005"
|
||||
localPdf: "/pdfs/uglys/Uglys_Electrical_Reference_2005.pdf"
|
||||
coverImage: "/covers/uglys/Uglys_Electrical_Reference_2005-01.jpg"
|
||||
sortOrder: 1
|
||||
formats:
|
||||
- type: "PDF"
|
||||
filename: "Uglys_Electrical_Reference_2005.pdf"
|
||||
- type: "PDF (Text)"
|
||||
filename: "Ugly_sElectricalReference2005_text.pdf"
|
||||
---
|
||||
|
||||
Ugly's Electrical References has been the go-to pocket reference for electricians since 1970. This spiral-bound guide fits in your back pocket and contains everything you need on the job site - wire ampacities, conduit fill tables, motor calculations, NEC code references, and much more.
|
||||
|
||||
Like the Mims notebooks, this is another example of practical, no-nonsense reference material that gets the job done.
|
||||
28
site/src/content/config.ts
Normal file
@ -0,0 +1,28 @@
|
||||
import { defineCollection, z } from 'astro:content';
|
||||
|
||||
const booksCollection = defineCollection({
|
||||
type: 'content',
|
||||
schema: z.object({
|
||||
title: z.string(),
|
||||
shortTitle: z.string(),
|
||||
collection: z.enum(['mims', 'uglys', 'other']),
|
||||
year: z.number().optional(),
|
||||
description: z.string(),
|
||||
topics: z.array(z.string()),
|
||||
archiveOrgId: z.string().optional(),
|
||||
archiveOrgUrl: z.string().url().optional(),
|
||||
localPdf: z.string(),
|
||||
coverImage: z.string().optional(),
|
||||
pageCount: z.number().optional(),
|
||||
sortOrder: z.number().default(0),
|
||||
formats: z.array(z.object({
|
||||
type: z.string(),
|
||||
filename: z.string(),
|
||||
url: z.string().optional()
|
||||
})).optional()
|
||||
})
|
||||
});
|
||||
|
||||
export const collections = {
|
||||
books: booksCollection
|
||||
};
|
||||
86
site/src/layouts/Layout.astro
Normal file
@ -0,0 +1,86 @@
|
||||
---
|
||||
import '@/styles/global.css';
|
||||
|
||||
interface Props {
|
||||
title: string;
|
||||
description?: string;
|
||||
}
|
||||
|
||||
const { title, description = "Classic electronics reference notebooks from Forrest M. Mims III" } = Astro.props;
|
||||
---
|
||||
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<meta name="description" content={description} />
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
<title>{title} | Electronics Reference Library</title>
|
||||
</head>
|
||||
<body class="min-h-screen graph-paper-large">
|
||||
<header class="border-b border-border bg-card/80 backdrop-blur-sm sticky top-0 z-50">
|
||||
<div class="container mx-auto px-4 py-4">
|
||||
<nav class="flex items-center justify-between">
|
||||
<a href="/" class="flex items-center gap-3 hover:opacity-80 transition-opacity">
|
||||
<div class="w-10 h-10 rounded-lg bg-primary flex items-center justify-center">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="text-primary-foreground">
|
||||
<path d="M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z"/>
|
||||
<path d="M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<h1 class="text-lg font-semibold title-accent text-foreground">Electronics Library</h1>
|
||||
<p class="text-xs text-muted-foreground">Classic Reference Collection</p>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
<div class="flex items-center gap-4">
|
||||
<a
|
||||
href="/mims"
|
||||
class="text-sm font-medium text-muted-foreground hover:text-foreground transition-colors"
|
||||
>
|
||||
Mims
|
||||
</a>
|
||||
<a
|
||||
href="/uglys"
|
||||
class="text-sm font-medium text-muted-foreground hover:text-foreground transition-colors"
|
||||
>
|
||||
Ugly's
|
||||
</a>
|
||||
<a
|
||||
href="https://archive.org"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="text-sm font-medium text-muted-foreground hover:text-foreground transition-colors flex items-center gap-1"
|
||||
>
|
||||
Archive.org
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/>
|
||||
<polyline points="15 3 21 3 21 9"/>
|
||||
<line x1="10" x2="21" y1="14" y2="3"/>
|
||||
</svg>
|
||||
</a>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main class="container mx-auto px-4 py-8">
|
||||
<slot />
|
||||
</main>
|
||||
|
||||
<footer class="border-t border-border bg-card/50 mt-16">
|
||||
<div class="container mx-auto px-4 py-8">
|
||||
<div class="flex flex-col md:flex-row items-center justify-between gap-4 text-sm text-muted-foreground">
|
||||
<p>
|
||||
Preserving classic electronics and electrical references
|
||||
</p>
|
||||
<p>
|
||||
Materials sourced from <a href="https://archive.org" target="_blank" rel="noopener noreferrer" class="underline hover:text-foreground">Archive.org</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
6
site/src/lib/utils.ts
Normal file
@ -0,0 +1,6 @@
|
||||
import { clsx, type ClassValue } from "clsx"
|
||||
import { twMerge } from "tailwind-merge"
|
||||
|
||||
export function cn(...inputs: ClassValue[]) {
|
||||
return twMerge(clsx(inputs))
|
||||
}
|
||||
256
site/src/pages/index.astro
Normal file
@ -0,0 +1,256 @@
|
||||
---
|
||||
import Layout from '@/layouts/Layout.astro';
|
||||
import BookGrid from '@/components/BookGrid.astro';
|
||||
import { getCollection } from 'astro:content';
|
||||
|
||||
const allBooks = await getCollection('books');
|
||||
|
||||
const mimsBooks = allBooks
|
||||
.filter(book => book.data.collection === 'mims')
|
||||
.sort((a, b) => a.data.sortOrder - b.data.sortOrder);
|
||||
|
||||
const uglysBooks = allBooks
|
||||
.filter(book => book.data.collection === 'uglys')
|
||||
.sort((a, b) => a.data.sortOrder - b.data.sortOrder);
|
||||
|
||||
const featuredMimsBooks = mimsBooks.slice(0, 4);
|
||||
const totalBooks = allBooks.length;
|
||||
---
|
||||
|
||||
<Layout title="Home">
|
||||
<!-- Hero Section -->
|
||||
<section class="text-center py-12 space-y-6">
|
||||
<div class="inline-flex items-center gap-2 px-4 py-2 rounded-full bg-accent/20 text-accent-foreground text-sm font-medium">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/>
|
||||
</svg>
|
||||
Classic Electronics & Electrical Education
|
||||
</div>
|
||||
|
||||
<h1 class="text-4xl md:text-5xl lg:text-6xl font-bold title-accent max-w-4xl mx-auto leading-tight">
|
||||
The Hand-Drawn References That
|
||||
<span class="text-primary">Taught Generations</span>
|
||||
</h1>
|
||||
|
||||
<p class="text-lg text-muted-foreground max-w-2xl mx-auto">
|
||||
Rediscover legendary electronics and electrical references. From Forrest Mims' hand-illustrated
|
||||
circuit notebooks to Ugly's essential electrician's guides - timeless knowledge preserved digitally.
|
||||
</p>
|
||||
|
||||
<div class="flex flex-wrap items-center justify-center gap-4 pt-4">
|
||||
<a
|
||||
href="#collections"
|
||||
class="inline-flex items-center gap-2 px-6 py-3 bg-primary text-primary-foreground rounded-lg font-medium hover:bg-primary/90 transition-colors"
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z"/>
|
||||
<path d="M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z"/>
|
||||
</svg>
|
||||
Browse Collections
|
||||
</a>
|
||||
<a
|
||||
href="https://archive.org"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="inline-flex items-center gap-2 px-6 py-3 border border-border text-foreground rounded-lg font-medium hover:bg-muted transition-colors"
|
||||
>
|
||||
About Archive.org
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/>
|
||||
<polyline points="15 3 21 3 21 9"/>
|
||||
<line x1="10" x2="21" y1="14" y2="3"/>
|
||||
</svg>
|
||||
</a>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Stats -->
|
||||
<section class="grid grid-cols-2 md:grid-cols-4 gap-4 py-8">
|
||||
<div class="text-center p-6 rounded-lg bg-card border border-border">
|
||||
<div class="text-3xl font-bold text-primary">{totalBooks}</div>
|
||||
<div class="text-sm text-muted-foreground">References</div>
|
||||
</div>
|
||||
<div class="text-center p-6 rounded-lg bg-card border border-border">
|
||||
<div class="text-3xl font-bold text-primary">2</div>
|
||||
<div class="text-sm text-muted-foreground">Collections</div>
|
||||
</div>
|
||||
<div class="text-center p-6 rounded-lg bg-card border border-border">
|
||||
<div class="text-3xl font-bold text-primary">100+</div>
|
||||
<div class="text-sm text-muted-foreground">Circuit Projects</div>
|
||||
</div>
|
||||
<div class="text-center p-6 rounded-lg bg-card border border-border">
|
||||
<div class="text-3xl font-bold text-primary">Free</div>
|
||||
<div class="text-sm text-muted-foreground">PDF Downloads</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Collection Cards -->
|
||||
<section id="collections" class="py-12 scroll-mt-8">
|
||||
<h2 class="text-2xl font-bold title-accent text-center mb-8">Our Collections</h2>
|
||||
|
||||
<div class="grid md:grid-cols-2 gap-6">
|
||||
<!-- Mims Collection Card -->
|
||||
<a href="/mims" class="group block p-6 rounded-lg bg-card border border-border hover:border-primary/50 transition-all hover:shadow-lg">
|
||||
<div class="flex items-start gap-4">
|
||||
<div class="w-14 h-14 rounded-lg bg-primary/10 flex items-center justify-center shrink-0 group-hover:bg-primary/20 transition-colors">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="text-primary">
|
||||
<path d="M12 20h9"/>
|
||||
<path d="M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4L16.5 3.5z"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="flex-1">
|
||||
<div class="flex items-center justify-between">
|
||||
<h3 class="text-xl font-semibold group-hover:text-primary transition-colors">Forrest Mims Mini-Notebooks</h3>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="text-muted-foreground group-hover:text-primary transition-colors">
|
||||
<polyline points="9 18 15 12 9 6"/>
|
||||
</svg>
|
||||
</div>
|
||||
<p class="text-sm text-muted-foreground mt-1">
|
||||
The legendary Radio Shack Engineer's Mini-Notebook series. Hand-illustrated electronics
|
||||
education that taught a generation.
|
||||
</p>
|
||||
<div class="flex items-center gap-4 mt-4 text-xs text-muted-foreground">
|
||||
<span class="flex items-center gap-1">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z"/>
|
||||
<path d="M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z"/>
|
||||
</svg>
|
||||
{mimsBooks.length} notebooks
|
||||
</span>
|
||||
<span>1985-1993</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
<!-- Ugly's Collection Card -->
|
||||
<a href="/uglys" class="group block p-6 rounded-lg bg-card border border-border hover:border-chart-2/50 transition-all hover:shadow-lg">
|
||||
<div class="flex items-start gap-4">
|
||||
<div class="w-14 h-14 rounded-lg bg-chart-2/10 flex items-center justify-center shrink-0 group-hover:bg-chart-2/20 transition-colors">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="text-chart-2">
|
||||
<path d="M13 2 3 14h9l-1 8 10-12h-9l1-8z"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="flex-1">
|
||||
<div class="flex items-center justify-between">
|
||||
<h3 class="text-xl font-semibold group-hover:text-chart-2 transition-colors">Ugly's Electrical References</h3>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="text-muted-foreground group-hover:text-chart-2 transition-colors">
|
||||
<polyline points="9 18 15 12 9 6"/>
|
||||
</svg>
|
||||
</div>
|
||||
<p class="text-sm text-muted-foreground mt-1">
|
||||
The pocket-sized bible for electricians. Packed with tables, formulas, wiring diagrams,
|
||||
and NEC code references.
|
||||
</p>
|
||||
<div class="flex items-center gap-4 mt-4 text-xs text-muted-foreground">
|
||||
<span class="flex items-center gap-1">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z"/>
|
||||
<path d="M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z"/>
|
||||
</svg>
|
||||
{uglysBooks.length} reference{uglysBooks.length !== 1 ? 's' : ''}
|
||||
</span>
|
||||
<span>Since 1970</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Featured Mims Collection -->
|
||||
<section class="py-8">
|
||||
<div class="flex items-center justify-between mb-6">
|
||||
<div>
|
||||
<h2 class="text-2xl font-bold title-accent">Featured: Mims Mini-Notebooks</h2>
|
||||
<p class="text-muted-foreground">Hand-illustrated electronics that shaped a generation</p>
|
||||
</div>
|
||||
<a
|
||||
href="/mims"
|
||||
class="text-sm font-medium text-primary hover:underline flex items-center gap-1"
|
||||
>
|
||||
View all {mimsBooks.length} notebooks
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polyline points="9 18 15 12 9 6"/>
|
||||
</svg>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<BookGrid books={featuredMimsBooks} />
|
||||
</section>
|
||||
|
||||
<!-- About Section -->
|
||||
<section class="py-12 mt-8 border-t border-border">
|
||||
<div class="grid md:grid-cols-2 gap-8 items-center">
|
||||
<div class="space-y-4">
|
||||
<h2 class="text-2xl font-bold title-accent">Preserving Electronics Heritage</h2>
|
||||
<p class="text-muted-foreground leading-relaxed">
|
||||
These references represent the golden age of hands-on electronics education. From Forrest Mims'
|
||||
distinctive hand-drawn notebooks that taught millions through Radio Shack, to Ugly's practical
|
||||
field guides trusted by electricians everywhere.
|
||||
</p>
|
||||
<p class="text-muted-foreground leading-relaxed">
|
||||
What makes these works special is their approach: clear explanations, practical focus, and
|
||||
a personal touch that made complex concepts accessible. Many engineers credit these materials
|
||||
with sparking their interest in electronics - reading them under the covers with a flashlight,
|
||||
building circuits on breadboards, learning schematic symbols for the first time.
|
||||
</p>
|
||||
<p class="text-muted-foreground leading-relaxed">
|
||||
All materials have been preserved and made available through Archive.org. Click any item
|
||||
to view it with our embedded PDF reader, or download it in various formats.
|
||||
</p>
|
||||
</div>
|
||||
<div class="circuit-border p-6 bg-card">
|
||||
<div class="space-y-4">
|
||||
<div class="flex items-start gap-3">
|
||||
<div class="w-8 h-8 rounded bg-chart-1/20 flex items-center justify-center shrink-0">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="text-chart-1">
|
||||
<path d="M12 20h9"/>
|
||||
<path d="M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4L16.5 3.5z"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<h4 class="font-semibold">Hand-Illustrated</h4>
|
||||
<p class="text-sm text-muted-foreground">Every diagram drawn by hand with meticulous care</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-start gap-3">
|
||||
<div class="w-8 h-8 rounded bg-accent/20 flex items-center justify-center shrink-0">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="text-accent">
|
||||
<circle cx="12" cy="12" r="10"/>
|
||||
<polyline points="12 6 12 12 16 14"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<h4 class="font-semibold">Timeless Knowledge</h4>
|
||||
<p class="text-sm text-muted-foreground">Fundamentals that remain relevant decades later</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-start gap-3">
|
||||
<div class="w-8 h-8 rounded bg-primary/20 flex items-center justify-center shrink-0">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="text-primary">
|
||||
<path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"/>
|
||||
<path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<h4 class="font-semibold">Practical Projects</h4>
|
||||
<p class="text-sm text-muted-foreground">Real circuits you can build and learn from</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-start gap-3">
|
||||
<div class="w-8 h-8 rounded bg-chart-2/20 flex items-center justify-center shrink-0">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="text-chart-2">
|
||||
<path d="M13 2 3 14h9l-1 8 10-12h-9l1-8z"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<h4 class="font-semibold">Field-Ready</h4>
|
||||
<p class="text-sm text-muted-foreground">Pocket references built for real-world use</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</Layout>
|
||||
181
site/src/pages/mims/[slug].astro
Normal file
@ -0,0 +1,181 @@
|
||||
---
|
||||
import Layout from '@/layouts/Layout.astro';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import EBookReader from '@/components/EBookReader';
|
||||
import { getCollection, type CollectionEntry } from 'astro:content';
|
||||
|
||||
export async function getStaticPaths() {
|
||||
const books = await getCollection('books');
|
||||
return books
|
||||
.filter(book => book.data.collection === 'mims')
|
||||
.map(book => ({
|
||||
params: { slug: book.slug.split('/').pop() },
|
||||
props: { book }
|
||||
}));
|
||||
}
|
||||
|
||||
interface Props {
|
||||
book: CollectionEntry<'books'>;
|
||||
}
|
||||
|
||||
const { book } = Astro.props;
|
||||
const { title, shortTitle, description, topics, localPdf, coverImage, year, archiveOrgUrl, formats } = book.data;
|
||||
|
||||
// Get all mims books for navigation
|
||||
const allBooks = await getCollection('books');
|
||||
const mimsBooks = allBooks
|
||||
.filter(b => b.data.collection === 'mims')
|
||||
.sort((a, b) => a.data.sortOrder - b.data.sortOrder);
|
||||
|
||||
const currentIndex = mimsBooks.findIndex(b => b.slug === book.slug);
|
||||
const prevBook = currentIndex > 0 ? mimsBooks[currentIndex - 1] : null;
|
||||
const nextBook = currentIndex < mimsBooks.length - 1 ? mimsBooks[currentIndex + 1] : null;
|
||||
---
|
||||
|
||||
<Layout title={shortTitle} description={description}>
|
||||
<div class="space-y-8">
|
||||
<!-- Breadcrumb -->
|
||||
<div class="flex items-center gap-2 text-sm text-muted-foreground">
|
||||
<a href="/" class="hover:text-foreground transition-colors">Home</a>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polyline points="9 18 15 12 9 6"/>
|
||||
</svg>
|
||||
<a href="/mims" class="hover:text-foreground transition-colors">Mims Collection</a>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polyline points="9 18 15 12 9 6"/>
|
||||
</svg>
|
||||
<span class="text-foreground">{shortTitle}</span>
|
||||
</div>
|
||||
|
||||
<!-- Compact Header -->
|
||||
<div class="flex flex-col md:flex-row md:items-start gap-6">
|
||||
<!-- Book Info -->
|
||||
<div class="flex-1 space-y-4">
|
||||
<div>
|
||||
{year && (
|
||||
<div class="inline-flex items-center gap-2 text-sm text-muted-foreground mb-2">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<rect width="18" height="18" x="3" y="4" rx="2" ry="2"/>
|
||||
<line x1="16" x2="16" y1="2" y2="6"/>
|
||||
<line x1="8" x2="8" y1="2" y2="6"/>
|
||||
<line x1="3" x2="21" y1="10" y2="10"/>
|
||||
</svg>
|
||||
Published {year}
|
||||
</div>
|
||||
)}
|
||||
<h1 class="text-2xl md:text-3xl font-bold title-accent">{title}</h1>
|
||||
<p class="text-muted-foreground mt-2">{description}</p>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-wrap gap-2">
|
||||
{topics.slice(0, 5).map((topic) => (
|
||||
<Badge variant="secondary" className="text-xs">
|
||||
{topic.replace(/-/g, ' ')}
|
||||
</Badge>
|
||||
))}
|
||||
{topics.length > 5 && (
|
||||
<Badge variant="outline" className="text-xs">
|
||||
+{topics.length - 5} more
|
||||
</Badge>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Quick Actions -->
|
||||
<div class="flex flex-row md:flex-col gap-2">
|
||||
<a
|
||||
href={localPdf}
|
||||
download
|
||||
class="inline-flex items-center justify-center gap-2 px-4 py-2.5 bg-primary text-primary-foreground rounded-lg font-medium hover:bg-primary/90 transition-colors text-sm"
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/>
|
||||
<polyline points="7 10 12 15 17 10"/>
|
||||
<line x1="12" x2="12" y1="15" y2="3"/>
|
||||
</svg>
|
||||
<span>Download</span>
|
||||
</a>
|
||||
|
||||
{archiveOrgUrl && (
|
||||
<a
|
||||
href={archiveOrgUrl}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="inline-flex items-center justify-center gap-2 px-4 py-2.5 border border-border rounded-lg font-medium hover:bg-muted transition-colors text-sm"
|
||||
>
|
||||
<span>Archive.org</span>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/>
|
||||
<polyline points="15 3 21 3 21 9"/>
|
||||
<line x1="10" x2="21" y1="14" y2="3"/>
|
||||
</svg>
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- E-Book Reader -->
|
||||
<div class="mt-6">
|
||||
<EBookReader
|
||||
pdfUrl={localPdf}
|
||||
title={shortTitle}
|
||||
coverImage={coverImage}
|
||||
client:load
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Navigation -->
|
||||
<div class="flex items-center justify-between pt-8 border-t border-border">
|
||||
{prevBook ? (
|
||||
<a
|
||||
href={`/mims/${prevBook.slug.split('/').pop()}`}
|
||||
class="group flex items-center gap-3 p-3 -m-3 rounded-lg hover:bg-muted/50 transition-colors"
|
||||
>
|
||||
<div class="w-8 h-8 rounded-full bg-muted flex items-center justify-center group-hover:bg-primary/10 transition-colors">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="text-muted-foreground group-hover:text-primary transition-colors">
|
||||
<polyline points="15 18 9 12 15 6"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="text-left">
|
||||
<div class="text-xs text-muted-foreground">Previous</div>
|
||||
<div class="text-sm font-medium text-foreground hidden sm:block">{prevBook.data.shortTitle}</div>
|
||||
</div>
|
||||
</a>
|
||||
) : (
|
||||
<div></div>
|
||||
)}
|
||||
|
||||
<a
|
||||
href="/mims"
|
||||
class="flex items-center gap-2 px-4 py-2 rounded-lg border border-border hover:bg-muted transition-colors text-sm"
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<rect width="7" height="7" x="3" y="3" rx="1"/>
|
||||
<rect width="7" height="7" x="14" y="3" rx="1"/>
|
||||
<rect width="7" height="7" x="14" y="14" rx="1"/>
|
||||
<rect width="7" height="7" x="3" y="14" rx="1"/>
|
||||
</svg>
|
||||
<span class="hidden sm:inline">All Notebooks</span>
|
||||
</a>
|
||||
|
||||
{nextBook ? (
|
||||
<a
|
||||
href={`/mims/${nextBook.slug.split('/').pop()}`}
|
||||
class="group flex items-center gap-3 p-3 -m-3 rounded-lg hover:bg-muted/50 transition-colors"
|
||||
>
|
||||
<div class="text-right">
|
||||
<div class="text-xs text-muted-foreground">Next</div>
|
||||
<div class="text-sm font-medium text-foreground hidden sm:block">{nextBook.data.shortTitle}</div>
|
||||
</div>
|
||||
<div class="w-8 h-8 rounded-full bg-muted flex items-center justify-center group-hover:bg-primary/10 transition-colors">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="text-muted-foreground group-hover:text-primary transition-colors">
|
||||
<polyline points="9 18 15 12 9 6"/>
|
||||
</svg>
|
||||
</div>
|
||||
</a>
|
||||
) : (
|
||||
<div></div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</Layout>
|
||||
85
site/src/pages/mims/index.astro
Normal file
@ -0,0 +1,85 @@
|
||||
---
|
||||
import Layout from '@/layouts/Layout.astro';
|
||||
import BookGrid from '@/components/BookGrid.astro';
|
||||
import { getCollection } from 'astro:content';
|
||||
|
||||
const allBooks = await getCollection('books');
|
||||
const mimsBooks = allBooks
|
||||
.filter(book => book.data.collection === 'mims')
|
||||
.sort((a, b) => a.data.sortOrder - b.data.sortOrder);
|
||||
|
||||
// Get unique topics for filtering
|
||||
const allTopics = [...new Set(mimsBooks.flatMap(book => book.data.topics))].sort();
|
||||
---
|
||||
|
||||
<Layout
|
||||
title="Mims Mini-Notebooks"
|
||||
description="The complete collection of Forrest M. Mims III's Radio Shack Engineer's Mini-Notebooks"
|
||||
>
|
||||
<div class="space-y-8">
|
||||
<!-- Header -->
|
||||
<div class="space-y-4">
|
||||
<div class="flex items-center gap-2 text-sm text-muted-foreground">
|
||||
<a href="/" class="hover:text-foreground transition-colors">Home</a>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polyline points="9 18 15 12 9 6"/>
|
||||
</svg>
|
||||
<span>Mims Collection</span>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col md:flex-row md:items-end justify-between gap-4">
|
||||
<div>
|
||||
<h1 class="text-3xl md:text-4xl font-bold title-accent">
|
||||
Forrest Mims Mini-Notebooks
|
||||
</h1>
|
||||
<p class="text-muted-foreground mt-2 max-w-2xl">
|
||||
The complete Radio Shack Engineer's Mini-Notebook series. Hand-illustrated electronics
|
||||
education that taught a generation of engineers and hobbyists.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-2 text-sm text-muted-foreground">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z"/>
|
||||
<path d="M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z"/>
|
||||
</svg>
|
||||
<span>{mimsBooks.length} notebooks</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Topic tags -->
|
||||
<div class="flex flex-wrap gap-2">
|
||||
{allTopics.map((topic) => (
|
||||
<span class="px-3 py-1 text-xs font-medium rounded-full bg-muted text-muted-foreground">
|
||||
{topic.replace(/-/g, ' ')}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<!-- Grid -->
|
||||
<BookGrid books={mimsBooks} />
|
||||
|
||||
<!-- Info box -->
|
||||
<div class="mt-12 p-6 rounded-lg bg-card border border-border">
|
||||
<div class="flex items-start gap-4">
|
||||
<div class="w-10 h-10 rounded-lg bg-primary/10 flex items-center justify-center shrink-0">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="text-primary">
|
||||
<circle cx="12" cy="12" r="10"/>
|
||||
<line x1="12" x2="12" y1="16" y2="12"/>
|
||||
<line x1="12" x2="12.01" y1="8" y2="8"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<h3 class="font-semibold text-foreground">About This Collection</h3>
|
||||
<p class="text-sm text-muted-foreground mt-1">
|
||||
These notebooks were originally published by Radio Shack between 1985 and 1993.
|
||||
All materials have been preserved and made available through Archive.org's
|
||||
Folkscanomy project. Click any notebook to view it with our embedded PDF reader,
|
||||
or download it in various formats.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Layout>
|
||||
181
site/src/pages/uglys/[slug].astro
Normal file
@ -0,0 +1,181 @@
|
||||
---
|
||||
import Layout from '@/layouts/Layout.astro';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import EBookReader from '@/components/EBookReader';
|
||||
import { getCollection, type CollectionEntry } from 'astro:content';
|
||||
|
||||
export async function getStaticPaths() {
|
||||
const books = await getCollection('books');
|
||||
return books
|
||||
.filter(book => book.data.collection === 'uglys')
|
||||
.map(book => ({
|
||||
params: { slug: book.slug.split('/').pop() },
|
||||
props: { book }
|
||||
}));
|
||||
}
|
||||
|
||||
interface Props {
|
||||
book: CollectionEntry<'books'>;
|
||||
}
|
||||
|
||||
const { book } = Astro.props;
|
||||
const { title, shortTitle, description, topics, localPdf, coverImage, year, archiveOrgUrl, formats } = book.data;
|
||||
|
||||
// Get all uglys books for navigation
|
||||
const allBooks = await getCollection('books');
|
||||
const uglysBooks = allBooks
|
||||
.filter(b => b.data.collection === 'uglys')
|
||||
.sort((a, b) => a.data.sortOrder - b.data.sortOrder);
|
||||
|
||||
const currentIndex = uglysBooks.findIndex(b => b.slug === book.slug);
|
||||
const prevBook = currentIndex > 0 ? uglysBooks[currentIndex - 1] : null;
|
||||
const nextBook = currentIndex < uglysBooks.length - 1 ? uglysBooks[currentIndex + 1] : null;
|
||||
---
|
||||
|
||||
<Layout title={shortTitle} description={description}>
|
||||
<div class="space-y-8">
|
||||
<!-- Breadcrumb -->
|
||||
<div class="flex items-center gap-2 text-sm text-muted-foreground">
|
||||
<a href="/" class="hover:text-foreground transition-colors">Home</a>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polyline points="9 18 15 12 9 6"/>
|
||||
</svg>
|
||||
<a href="/uglys" class="hover:text-foreground transition-colors">Ugly's Collection</a>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polyline points="9 18 15 12 9 6"/>
|
||||
</svg>
|
||||
<span class="text-foreground">{shortTitle}</span>
|
||||
</div>
|
||||
|
||||
<!-- Compact Header -->
|
||||
<div class="flex flex-col md:flex-row md:items-start gap-6">
|
||||
<!-- Book Info -->
|
||||
<div class="flex-1 space-y-4">
|
||||
<div>
|
||||
{year && (
|
||||
<div class="inline-flex items-center gap-2 text-sm text-muted-foreground mb-2">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<rect width="18" height="18" x="3" y="4" rx="2" ry="2"/>
|
||||
<line x1="16" x2="16" y1="2" y2="6"/>
|
||||
<line x1="8" x2="8" y1="2" y2="6"/>
|
||||
<line x1="3" x2="21" y1="10" y2="10"/>
|
||||
</svg>
|
||||
{year} Edition
|
||||
</div>
|
||||
)}
|
||||
<h1 class="text-2xl md:text-3xl font-bold title-accent">{title}</h1>
|
||||
<p class="text-muted-foreground mt-2">{description}</p>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-wrap gap-2">
|
||||
{topics.slice(0, 5).map((topic) => (
|
||||
<Badge variant="secondary" className="text-xs">
|
||||
{topic.replace(/-/g, ' ')}
|
||||
</Badge>
|
||||
))}
|
||||
{topics.length > 5 && (
|
||||
<Badge variant="outline" className="text-xs">
|
||||
+{topics.length - 5} more
|
||||
</Badge>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Quick Actions -->
|
||||
<div class="flex flex-row md:flex-col gap-2">
|
||||
<a
|
||||
href={localPdf}
|
||||
download
|
||||
class="inline-flex items-center justify-center gap-2 px-4 py-2.5 bg-primary text-primary-foreground rounded-lg font-medium hover:bg-primary/90 transition-colors text-sm"
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/>
|
||||
<polyline points="7 10 12 15 17 10"/>
|
||||
<line x1="12" x2="12" y1="15" y2="3"/>
|
||||
</svg>
|
||||
<span>Download</span>
|
||||
</a>
|
||||
|
||||
{archiveOrgUrl && (
|
||||
<a
|
||||
href={archiveOrgUrl}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="inline-flex items-center justify-center gap-2 px-4 py-2.5 border border-border rounded-lg font-medium hover:bg-muted transition-colors text-sm"
|
||||
>
|
||||
<span>Archive.org</span>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/>
|
||||
<polyline points="15 3 21 3 21 9"/>
|
||||
<line x1="10" x2="21" y1="14" y2="3"/>
|
||||
</svg>
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- E-Book Reader -->
|
||||
<div class="mt-6">
|
||||
<EBookReader
|
||||
pdfUrl={localPdf}
|
||||
title={shortTitle}
|
||||
coverImage={coverImage}
|
||||
client:load
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Navigation -->
|
||||
<div class="flex items-center justify-between pt-8 border-t border-border">
|
||||
{prevBook ? (
|
||||
<a
|
||||
href={`/uglys/${prevBook.slug.split('/').pop()}`}
|
||||
class="group flex items-center gap-3 p-3 -m-3 rounded-lg hover:bg-muted/50 transition-colors"
|
||||
>
|
||||
<div class="w-8 h-8 rounded-full bg-muted flex items-center justify-center group-hover:bg-primary/10 transition-colors">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="text-muted-foreground group-hover:text-primary transition-colors">
|
||||
<polyline points="15 18 9 12 15 6"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="text-left">
|
||||
<div class="text-xs text-muted-foreground">Previous</div>
|
||||
<div class="text-sm font-medium text-foreground hidden sm:block">{prevBook.data.shortTitle}</div>
|
||||
</div>
|
||||
</a>
|
||||
) : (
|
||||
<div></div>
|
||||
)}
|
||||
|
||||
<a
|
||||
href="/uglys"
|
||||
class="flex items-center gap-2 px-4 py-2 rounded-lg border border-border hover:bg-muted transition-colors text-sm"
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<rect width="7" height="7" x="3" y="3" rx="1"/>
|
||||
<rect width="7" height="7" x="14" y="3" rx="1"/>
|
||||
<rect width="7" height="7" x="14" y="14" rx="1"/>
|
||||
<rect width="7" height="7" x="3" y="14" rx="1"/>
|
||||
</svg>
|
||||
<span class="hidden sm:inline">All References</span>
|
||||
</a>
|
||||
|
||||
{nextBook ? (
|
||||
<a
|
||||
href={`/uglys/${nextBook.slug.split('/').pop()}`}
|
||||
class="group flex items-center gap-3 p-3 -m-3 rounded-lg hover:bg-muted/50 transition-colors"
|
||||
>
|
||||
<div class="text-right">
|
||||
<div class="text-xs text-muted-foreground">Next</div>
|
||||
<div class="text-sm font-medium text-foreground hidden sm:block">{nextBook.data.shortTitle}</div>
|
||||
</div>
|
||||
<div class="w-8 h-8 rounded-full bg-muted flex items-center justify-center group-hover:bg-primary/10 transition-colors">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="text-muted-foreground group-hover:text-primary transition-colors">
|
||||
<polyline points="9 18 15 12 9 6"/>
|
||||
</svg>
|
||||
</div>
|
||||
</a>
|
||||
) : (
|
||||
<div></div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</Layout>
|
||||
86
site/src/pages/uglys/index.astro
Normal file
@ -0,0 +1,86 @@
|
||||
---
|
||||
import Layout from '@/layouts/Layout.astro';
|
||||
import BookGrid from '@/components/BookGrid.astro';
|
||||
import { getCollection } from 'astro:content';
|
||||
|
||||
const allBooks = await getCollection('books');
|
||||
const uglysBooks = allBooks
|
||||
.filter(book => book.data.collection === 'uglys')
|
||||
.sort((a, b) => a.data.sortOrder - b.data.sortOrder);
|
||||
|
||||
// Get unique topics for filtering
|
||||
const allTopics = [...new Set(uglysBooks.flatMap(book => book.data.topics))].sort();
|
||||
---
|
||||
|
||||
<Layout
|
||||
title="Ugly's Electrical References"
|
||||
description="The essential pocket reference for electricians - packed with tables, formulas, and NEC code references"
|
||||
>
|
||||
<div class="space-y-8">
|
||||
<!-- Header -->
|
||||
<div class="space-y-4">
|
||||
<div class="flex items-center gap-2 text-sm text-muted-foreground">
|
||||
<a href="/" class="hover:text-foreground transition-colors">Home</a>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polyline points="9 18 15 12 9 6"/>
|
||||
</svg>
|
||||
<span>Ugly's Collection</span>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col md:flex-row md:items-end justify-between gap-4">
|
||||
<div>
|
||||
<h1 class="text-3xl md:text-4xl font-bold title-accent">
|
||||
Ugly's Electrical References
|
||||
</h1>
|
||||
<p class="text-muted-foreground mt-2 max-w-2xl">
|
||||
The pocket-sized bible for electricians. Packed with tables, formulas, wiring diagrams,
|
||||
NEC code references, and quick calculations for electrical work. An essential field reference.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-2 text-sm text-muted-foreground">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z"/>
|
||||
<path d="M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z"/>
|
||||
</svg>
|
||||
<span>{uglysBooks.length} reference{uglysBooks.length !== 1 ? 's' : ''}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Topic tags -->
|
||||
{allTopics.length > 0 && (
|
||||
<div class="flex flex-wrap gap-2">
|
||||
{allTopics.map((topic) => (
|
||||
<span class="px-3 py-1 text-xs font-medium rounded-full bg-muted text-muted-foreground">
|
||||
{topic.replace(/-/g, ' ')}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<!-- Grid -->
|
||||
<BookGrid books={uglysBooks} />
|
||||
|
||||
<!-- Info box -->
|
||||
<div class="mt-12 p-6 rounded-lg bg-card border border-border">
|
||||
<div class="flex items-start gap-4">
|
||||
<div class="w-10 h-10 rounded-lg bg-chart-2/20 flex items-center justify-center shrink-0">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="text-chart-2">
|
||||
<path d="M13 2 3 14h9l-1 8 10-12h-9l1-8z"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<h3 class="font-semibold text-foreground">About This Collection</h3>
|
||||
<p class="text-sm text-muted-foreground mt-1">
|
||||
Ugly's Electrical References has been the go-to pocket reference for electricians since 1970.
|
||||
This spiral-bound guide fits in your back pocket and contains everything you need on the
|
||||
job site - wire ampacities, conduit fill tables, motor calculations, NEC code references,
|
||||
and much more. Like the Mims notebooks, this is practical, no-nonsense reference material
|
||||
that gets the job done.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Layout>
|
||||
196
site/src/styles/global.css
Normal file
@ -0,0 +1,196 @@
|
||||
@import "tailwindcss";
|
||||
@import "tw-animate-css";
|
||||
|
||||
@custom-variant dark (&:is(.dark *));
|
||||
|
||||
@theme inline {
|
||||
--radius-sm: calc(var(--radius) - 4px);
|
||||
--radius-md: calc(var(--radius) - 2px);
|
||||
--radius-lg: var(--radius);
|
||||
--radius-xl: calc(var(--radius) + 4px);
|
||||
--radius-2xl: calc(var(--radius) + 8px);
|
||||
--radius-3xl: calc(var(--radius) + 12px);
|
||||
--radius-4xl: calc(var(--radius) + 16px);
|
||||
--color-background: var(--background);
|
||||
--color-foreground: var(--foreground);
|
||||
--color-card: var(--card);
|
||||
--color-card-foreground: var(--card-foreground);
|
||||
--color-popover: var(--popover);
|
||||
--color-popover-foreground: var(--popover-foreground);
|
||||
--color-primary: var(--primary);
|
||||
--color-primary-foreground: var(--primary-foreground);
|
||||
--color-secondary: var(--secondary);
|
||||
--color-secondary-foreground: var(--secondary-foreground);
|
||||
--color-muted: var(--muted);
|
||||
--color-muted-foreground: var(--muted-foreground);
|
||||
--color-accent: var(--accent);
|
||||
--color-accent-foreground: var(--accent-foreground);
|
||||
--color-destructive: var(--destructive);
|
||||
--color-border: var(--border);
|
||||
--color-input: var(--input);
|
||||
--color-ring: var(--ring);
|
||||
--color-chart-1: var(--chart-1);
|
||||
--color-chart-2: var(--chart-2);
|
||||
--color-chart-3: var(--chart-3);
|
||||
--color-chart-4: var(--chart-4);
|
||||
--color-chart-5: var(--chart-5);
|
||||
--color-sidebar: var(--sidebar);
|
||||
--color-sidebar-foreground: var(--sidebar-foreground);
|
||||
--color-sidebar-primary: var(--sidebar-primary);
|
||||
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
|
||||
--color-sidebar-accent: var(--sidebar-accent);
|
||||
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
||||
--color-sidebar-border: var(--sidebar-border);
|
||||
--color-sidebar-ring: var(--sidebar-ring);
|
||||
}
|
||||
|
||||
:root {
|
||||
--radius: 0.625rem;
|
||||
/* Warm cream paper background */
|
||||
--background: oklch(0.97 0.01 85);
|
||||
--foreground: oklch(0.25 0.02 250);
|
||||
/* Card with slightly lighter cream */
|
||||
--card: oklch(0.98 0.008 85);
|
||||
--card-foreground: oklch(0.25 0.02 250);
|
||||
--popover: oklch(0.98 0.008 85);
|
||||
--popover-foreground: oklch(0.25 0.02 250);
|
||||
/* Deep ink blue primary */
|
||||
--primary: oklch(0.35 0.08 250);
|
||||
--primary-foreground: oklch(0.98 0.008 85);
|
||||
/* Muted cream secondary */
|
||||
--secondary: oklch(0.94 0.015 85);
|
||||
--secondary-foreground: oklch(0.30 0.03 250);
|
||||
--muted: oklch(0.92 0.012 85);
|
||||
--muted-foreground: oklch(0.50 0.02 250);
|
||||
/* Warm amber accent */
|
||||
--accent: oklch(0.75 0.15 65);
|
||||
--accent-foreground: oklch(0.25 0.02 250);
|
||||
--destructive: oklch(0.577 0.245 27.325);
|
||||
/* Graph paper blue lines */
|
||||
--border: oklch(0.85 0.04 230);
|
||||
--input: oklch(0.90 0.02 85);
|
||||
--ring: oklch(0.55 0.12 250);
|
||||
/* Circuit board green for charts */
|
||||
--chart-1: oklch(0.55 0.15 145);
|
||||
--chart-2: oklch(0.65 0.12 250);
|
||||
--chart-3: oklch(0.70 0.14 65);
|
||||
--chart-4: oklch(0.60 0.10 30);
|
||||
--chart-5: oklch(0.50 0.08 300);
|
||||
--sidebar: oklch(0.96 0.012 85);
|
||||
--sidebar-foreground: oklch(0.25 0.02 250);
|
||||
--sidebar-primary: oklch(0.35 0.08 250);
|
||||
--sidebar-primary-foreground: oklch(0.98 0.008 85);
|
||||
--sidebar-accent: oklch(0.92 0.015 85);
|
||||
--sidebar-accent-foreground: oklch(0.30 0.03 250);
|
||||
--sidebar-border: oklch(0.85 0.04 230);
|
||||
--sidebar-ring: oklch(0.55 0.12 250);
|
||||
}
|
||||
|
||||
.dark {
|
||||
--background: oklch(0.145 0 0);
|
||||
--foreground: oklch(0.985 0 0);
|
||||
--card: oklch(0.205 0 0);
|
||||
--card-foreground: oklch(0.985 0 0);
|
||||
--popover: oklch(0.205 0 0);
|
||||
--popover-foreground: oklch(0.985 0 0);
|
||||
--primary: oklch(0.922 0 0);
|
||||
--primary-foreground: oklch(0.205 0 0);
|
||||
--secondary: oklch(0.269 0 0);
|
||||
--secondary-foreground: oklch(0.985 0 0);
|
||||
--muted: oklch(0.269 0 0);
|
||||
--muted-foreground: oklch(0.708 0 0);
|
||||
--accent: oklch(0.269 0 0);
|
||||
--accent-foreground: oklch(0.985 0 0);
|
||||
--destructive: oklch(0.704 0.191 22.216);
|
||||
--border: oklch(1 0 0 / 10%);
|
||||
--input: oklch(1 0 0 / 15%);
|
||||
--ring: oklch(0.556 0 0);
|
||||
--chart-1: oklch(0.488 0.243 264.376);
|
||||
--chart-2: oklch(0.696 0.17 162.48);
|
||||
--chart-3: oklch(0.769 0.188 70.08);
|
||||
--chart-4: oklch(0.627 0.265 303.9);
|
||||
--chart-5: oklch(0.645 0.246 16.439);
|
||||
--sidebar: oklch(0.205 0 0);
|
||||
--sidebar-foreground: oklch(0.985 0 0);
|
||||
--sidebar-primary: oklch(0.488 0.243 264.376);
|
||||
--sidebar-primary-foreground: oklch(0.985 0 0);
|
||||
--sidebar-accent: oklch(0.269 0 0);
|
||||
--sidebar-accent-foreground: oklch(0.985 0 0);
|
||||
--sidebar-border: oklch(1 0 0 / 10%);
|
||||
--sidebar-ring: oklch(0.556 0 0);
|
||||
}
|
||||
|
||||
@layer base {
|
||||
* {
|
||||
@apply border-border outline-ring/50;
|
||||
}
|
||||
body {
|
||||
@apply bg-background text-foreground;
|
||||
}
|
||||
}
|
||||
|
||||
/* Graph paper background pattern */
|
||||
.graph-paper {
|
||||
background-image:
|
||||
linear-gradient(to right, oklch(0.85 0.03 230 / 0.3) 1px, transparent 1px),
|
||||
linear-gradient(to bottom, oklch(0.85 0.03 230 / 0.3) 1px, transparent 1px);
|
||||
background-size: 20px 20px;
|
||||
}
|
||||
|
||||
.graph-paper-large {
|
||||
background-image:
|
||||
linear-gradient(to right, oklch(0.85 0.03 230 / 0.2) 1px, transparent 1px),
|
||||
linear-gradient(to bottom, oklch(0.85 0.03 230 / 0.2) 1px, transparent 1px),
|
||||
linear-gradient(to right, oklch(0.80 0.04 230 / 0.4) 1px, transparent 1px),
|
||||
linear-gradient(to bottom, oklch(0.80 0.04 230 / 0.4) 1px, transparent 1px);
|
||||
background-size: 10px 10px, 10px 10px, 50px 50px, 50px 50px;
|
||||
}
|
||||
|
||||
/* Card hover effect */
|
||||
.book-card {
|
||||
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
||||
}
|
||||
.book-card:hover {
|
||||
transform: translateY(-4px);
|
||||
box-shadow: 0 12px 24px -8px oklch(0.3 0.02 250 / 0.15);
|
||||
}
|
||||
|
||||
/* Cover image styling */
|
||||
.book-cover {
|
||||
aspect-ratio: 3/4;
|
||||
object-fit: cover;
|
||||
border: 1px solid oklch(0.85 0.04 230);
|
||||
}
|
||||
|
||||
/* Nostalgic text styling */
|
||||
.title-accent {
|
||||
font-family: Georgia, 'Times New Roman', serif;
|
||||
letter-spacing: -0.02em;
|
||||
}
|
||||
|
||||
/* Circuit trace decoration */
|
||||
.circuit-border {
|
||||
border: 2px solid oklch(0.55 0.15 145);
|
||||
border-radius: 4px;
|
||||
position: relative;
|
||||
}
|
||||
.circuit-border::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: -6px;
|
||||
left: 20px;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
background: oklch(0.55 0.15 145);
|
||||
border-radius: 50%;
|
||||
}
|
||||
.circuit-border::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: -6px;
|
||||
right: 20px;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
background: oklch(0.55 0.15 145);
|
||||
border-radius: 50%;
|
||||
}
|
||||
18
site/tsconfig.json
Normal file
@ -0,0 +1,18 @@
|
||||
{
|
||||
"extends": "astro/tsconfigs/strict",
|
||||
"include": [
|
||||
".astro/types.d.ts",
|
||||
"**/*"
|
||||
],
|
||||
"exclude": [
|
||||
"dist"
|
||||
],
|
||||
"compilerOptions": {
|
||||
"jsx": "react-jsx",
|
||||
"jsxImportSource": "react",
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
}
|
||||
}
|
||||
}
|
||||