diff --git a/ansible/roles/site/files/pib/Makefile b/ansible/roles/site/files/pib/Makefile new file mode 100644 index 0000000..3e3689b --- /dev/null +++ b/ansible/roles/site/files/pib/Makefile @@ -0,0 +1,54 @@ +VENV := .venv +UV := uv +PYTHON := $(UV) run python +MANAGE := $(PYTHON) manage.py +PORT := 8000 + +APPS := -e pibfpgas -e pibup -e pistat -e snmp_switch +DEPS := 'channels[daphne]' channels_redis + +FIXTURE ?= pibfpgas/fixtures/fpgas.online.json + +.PHONY: serve setup migrate fixtures clean help + +help: ## Show this help + @grep -E '^[a-zA-Z_-]+:.*?## ' $(MAKEFILE_LIST) | \ + awk 'BEGIN {FS = ":.*?## "}; {printf " make %-12s %s\n", $$1, $$2}' + +serve: setup ## Start the local dev server (default: port 8000) + $(MANAGE) runserver 0.0.0.0:$(PORT) + +setup: $(VENV)/pyvenv.cfg pib/local_settings.py | migrate fixtures ## Full setup: venv, deps, settings, migrate, fixtures + +$(VENV)/pyvenv.cfg: + $(UV) venv $(VENV) + $(UV) pip install $(APPS) $(DEPS) + +pib/local_settings.py: + @echo "Creating pib/local_settings.py for local dev..." + @printf '%s\n' \ + '# Local development settings' \ + '# Auto-generated by Makefile. Edit freely; .gitignored.' \ + '' \ + "ALLOWED_HOSTS = ['*']" \ + '' \ + "DOMAIN_NAME = 'localhost:$(PORT)'" \ + "PI_PW = 'dev-password'" \ + '' \ + '# In-memory channel layer (no Redis needed for local dev)' \ + 'CHANNEL_LAYERS = {' \ + ' "default": {' \ + ' "BACKEND": "channels.layers.InMemoryChannelLayer",' \ + ' },' \ + '}' > pib/local_settings.py + +migrate: $(VENV)/pyvenv.cfg ## Run Django migrations + $(MANAGE) migrate + +fixtures: $(VENV)/pyvenv.cfg ## Load fixture data (override: FIXTURE=path/to/file.json) + $(MANAGE) loaddata $(FIXTURE) + +clean: ## Remove venv, database, and local_settings.py + rm -rf $(VENV) + rm -f db.sqlite3 + rm -f pib/local_settings.py diff --git a/ansible/roles/site/files/pib/pibfpgas/fixtures/fpgas.online.json b/ansible/roles/site/files/pib/pibfpgas/fixtures/fpgas.online.json index 5f5bebb..4eb4396 100644 --- a/ansible/roles/site/files/pib/pibfpgas/fixtures/fpgas.online.json +++ b/ansible/roles/site/files/pib/pibfpgas/fixtures/fpgas.online.json @@ -1 +1 @@ -[{"model": "pibfpgas.pi", "pk": 1, "fields": {"port": 34, "mac": "b8:27:eb:6d:27:f6", "cable_color": "white", "location": "", "serial_no": "7a6d27f6"}}, {"model": "pibfpgas.pi", "pk": 2, "fields": {"port": 36, "mac": "b8:27:eb:86:39:63", "cable_color": "blue", "location": "", "serial_no": "80863963"}}, {"model": "pibfpgas.pi", "pk": 3, "fields": {"port": 40, "mac": "e4:5f:01:97:1f:7e", "cable_color": "gray", "location": "", "serial_no": "613a4524"}}, {"model": "pibfpgas.pi", "pk": 4, "fields": {"port": 42, "mac": "e4:5f:01:8d:f7:17", "model": "Raspberry_Pi_4_Model_B_Rev_1", "cable_color": "yellow", "location": "", "serial_no": "f77b8415"}}, {"model": "pibfpgas.pi", "pk": 5, "fields": {"port": 46, "mac": "e4:5f:01:97:32:d2", "cable_color": "gray/white", "location": "", "serial_no": "8483b266"}}, {"model": "pibfpgas.pi", "pk": 6, "fields": {"port": 48, "mac": "e4:5f:01:96:f8:a5", "cable_color": "blue", "location": "", "serial_no": "ce8e3593"}}] \ No newline at end of file +[{"model": "pibfpgas.pi", "pk": 1, "fields": {"port": 34, "mac": "b8:27:eb:6d:27:f6", "cable_color": "white", "location": "", "serial_no": "7a6d27f6", "board_type": "arty_a7"}}, {"model": "pibfpgas.pi", "pk": 2, "fields": {"port": 36, "mac": "b8:27:eb:86:39:63", "cable_color": "blue", "location": "", "serial_no": "80863963", "board_type": "arty_a7"}}, {"model": "pibfpgas.pi", "pk": 3, "fields": {"port": 40, "mac": "e4:5f:01:97:1f:7e", "cable_color": "gray", "location": "", "serial_no": "613a4524", "board_type": "arty_a7"}}, {"model": "pibfpgas.pi", "pk": 4, "fields": {"port": 42, "mac": "e4:5f:01:8d:f7:17", "model": "Raspberry_Pi_4_Model_B_Rev_1", "cable_color": "yellow", "location": "", "serial_no": "f77b8415", "board_type": "arty_a7"}}, {"model": "pibfpgas.pi", "pk": 5, "fields": {"port": 46, "mac": "e4:5f:01:97:32:d2", "cable_color": "gray/white", "location": "", "serial_no": "8483b266", "board_type": "arty_a7"}}, {"model": "pibfpgas.pi", "pk": 6, "fields": {"port": 48, "mac": "e4:5f:01:96:f8:a5", "cable_color": "blue", "location": "", "serial_no": "ce8e3593", "board_type": "arty_a7"}}] diff --git a/ansible/roles/site/files/pib/pibfpgas/fixtures/ps1.fpgas.online.json b/ansible/roles/site/files/pib/pibfpgas/fixtures/ps1.fpgas.online.json index 93341d2..8686565 100644 --- a/ansible/roles/site/files/pib/pibfpgas/fixtures/ps1.fpgas.online.json +++ b/ansible/roles/site/files/pib/pibfpgas/fixtures/ps1.fpgas.online.json @@ -1 +1 @@ -[{"model": "pibfpgas.pi", "pk": 1, "fields": {"port": 2, "mac": "b8:27:eb:2f:5d:08", "model": "Raspberry_Pi_3_Model_B_Rev_1", "cable_color": "grey", "location": "front 1", "serial_no": "042f5d08"}}, {"model": "pibfpgas.pi", "pk": 2, "fields": {"port": 3, "mac": "dc:a6:32:05:32:45", "model": "Raspberry_Pi_4_Model_B_Rev_1", "cable_color": "white", "location": "front 2", "serial_no": "f1b7bb5a"}}, {"model": "pibfpgas.pi", "pk": 3, "fields": {"port": 5, "mac": "b8:27:eb:d4:f1:74", "model": "Raspberry_Pi_3_Model_B_Rev_1", "cable_color": "white", "location": "front 4", "serial_no": "9ed4f174"}}, {"model": "pibfpgas.pi", "pk": 4, "fields": {"port": 7, "mac": "b8:27:eb:33:51:27", "model": "Raspberry_Pi_3_Model_B_Plus_Rev_1", "cable_color": "blue", "location": "front 5", "serial_no": 48335127}}, {"model": "pibfpgas.pi", "pk": 5, "fields": {"port": 9, "mac": "b8:27:eb:a3:51:b4", "model": "Raspberry_Pi_3_Model_B_Plus_Rev_1", "cable_color": "blue", "location": "front 6", "serial_no": "8da351b4"}}, {"model": "pibfpgas.pi", "pk": 6, "fields": {"port": 11, "mac": "b8:27:eb:51:01:df", "model": "Raspberry_Pi_3_Model_B_Rev_1", "cable_color": "blue", "location": "front 7", "serial_no": "265101df"}}, {"model": "pibfpgas.pi", "pk": 7, "fields": {"port": 13, "mac": "b8:27:eb:68:fc:e7", "model": "Raspberry_Pi_3_Model_B_Rev_1", "cable_color": "white", "location": "front 8", "serial_no": "f168fce7"}}, {"model": "pibfpgas.pi", "pk": 8, "fields": {"port": 21, "mac": "b8:27:eb:5f:de:85", "model": "", "cable_color": "blue", "location": "back 7", "serial_no": "7d5fde85"}}, {"model": "pibfpgas.pi", "pk": 9, "fields": {"port": 23, "mac": "b8:27:eb:0c:f8:43", "model": "Raspberry_Pi_3_Model_B_Rev_1", "cable_color": "grey", "location": "back 8", "serial_no": "9b0cf843"}}] \ No newline at end of file +[{"model": "pibfpgas.pi", "pk": 1, "fields": {"port": 2, "mac": "b8:27:eb:2f:5d:08", "model": "Raspberry_Pi_3_Model_B_Rev_1", "cable_color": "grey", "location": "front 1", "serial_no": "042f5d08", "board_type": "arty_a7"}}, {"model": "pibfpgas.pi", "pk": 2, "fields": {"port": 3, "mac": "dc:a6:32:05:32:45", "model": "Raspberry_Pi_4_Model_B_Rev_1", "cable_color": "white", "location": "front 2", "serial_no": "f1b7bb5a", "board_type": "arty_a7"}}, {"model": "pibfpgas.pi", "pk": 3, "fields": {"port": 5, "mac": "b8:27:eb:d4:f1:74", "model": "Raspberry_Pi_3_Model_B_Rev_1", "cable_color": "white", "location": "front 4", "serial_no": "9ed4f174", "board_type": "arty_a7"}}, {"model": "pibfpgas.pi", "pk": 4, "fields": {"port": 7, "mac": "b8:27:eb:33:51:27", "model": "Raspberry_Pi_3_Model_B_Plus_Rev_1", "cable_color": "blue", "location": "front 5", "serial_no": "48335127", "board_type": "arty_a7"}}, {"model": "pibfpgas.pi", "pk": 5, "fields": {"port": 9, "mac": "b8:27:eb:a3:51:b4", "model": "Raspberry_Pi_3_Model_B_Plus_Rev_1", "cable_color": "blue", "location": "front 6", "serial_no": "8da351b4", "board_type": "arty_a7"}}, {"model": "pibfpgas.pi", "pk": 6, "fields": {"port": 11, "mac": "b8:27:eb:51:01:df", "model": "Raspberry_Pi_3_Model_B_Rev_1", "cable_color": "blue", "location": "front 7", "serial_no": "265101df", "board_type": "arty_a7"}}, {"model": "pibfpgas.pi", "pk": 7, "fields": {"port": 13, "mac": "b8:27:eb:68:fc:e7", "model": "Raspberry_Pi_3_Model_B_Rev_1", "cable_color": "white", "location": "front 8", "serial_no": "f168fce7", "board_type": "arty_a7"}}, {"model": "pibfpgas.pi", "pk": 8, "fields": {"port": 21, "mac": "b8:27:eb:5f:de:85", "model": "", "cable_color": "blue", "location": "back 7", "serial_no": "7d5fde85", "board_type": "tt_asic"}}, {"model": "pibfpgas.pi", "pk": 9, "fields": {"port": 23, "mac": "b8:27:eb:0c:f8:43", "model": "Raspberry_Pi_3_Model_B_Rev_1", "cable_color": "grey", "location": "back 8", "serial_no": "9b0cf843", "board_type": "arty_a7"}}] diff --git a/ansible/roles/site/files/pib/pibfpgas/src/pibfpgas/admin.py b/ansible/roles/site/files/pib/pibfpgas/src/pibfpgas/admin.py index e92ff19..bbef7c2 100644 --- a/ansible/roles/site/files/pib/pibfpgas/src/pibfpgas/admin.py +++ b/ansible/roles/site/files/pib/pibfpgas/src/pibfpgas/admin.py @@ -1,7 +1,8 @@ from django.contrib import admin -from .models import * +from .models import Pi class PiAdmin(admin.ModelAdmin): - pass + list_display = ('port', 'board_type', 'location', 'model', 'cable_color') + list_filter = ('board_type',) admin.site.register(Pi, PiAdmin) diff --git a/ansible/roles/site/files/pib/pibfpgas/src/pibfpgas/migrations/0002_pi_board_type.py b/ansible/roles/site/files/pib/pibfpgas/src/pibfpgas/migrations/0002_pi_board_type.py new file mode 100644 index 0000000..dcd7c76 --- /dev/null +++ b/ansible/roles/site/files/pib/pibfpgas/src/pibfpgas/migrations/0002_pi_board_type.py @@ -0,0 +1,30 @@ +# Generated by Django 6.0.2 on 2026-02-28 05:42 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("pibfpgas", "0001_initial"), + ] + + operations = [ + migrations.AddField( + model_name="pi", + name="board_type", + field=models.CharField( + blank=True, + choices=[ + ("arty_a7", "Arty A7"), + ("netv2", "NeTV2"), + ("ulx3s", "ULX3S"), + ("fomu", "Fomu"), + ("tt_asic", "Tiny Tapeout ASIC"), + ("tt_fpga", "Tiny Tapeout FPGA Emulation"), + ], + default="arty_a7", + max_length=20, + ), + ), + ] diff --git a/ansible/roles/site/files/pib/pibfpgas/src/pibfpgas/models.py b/ansible/roles/site/files/pib/pibfpgas/src/pibfpgas/models.py index 5ed49b8..cbe45a5 100644 --- a/ansible/roles/site/files/pib/pibfpgas/src/pibfpgas/models.py +++ b/ansible/roles/site/files/pib/pibfpgas/src/pibfpgas/models.py @@ -2,6 +2,15 @@ from django.db import models +BOARD_TYPES = [ + ('arty_a7', 'Arty A7'), + ('netv2', 'NeTV2'), + ('ulx3s', 'ULX3S'), + ('fomu', 'Fomu'), + ('tt_asic', 'Tiny Tapeout ASIC'), + ('tt_fpga', 'Tiny Tapeout FPGA Emulation'), +] + class Pi(models.Model): port = models.IntegerField() mac = models.CharField(max_length=17, blank=True) @@ -9,3 +18,4 @@ class Pi(models.Model): location = models.CharField(max_length=30, blank=True) model = models.CharField(max_length=30, blank=True) cable_color = models.CharField(max_length=10, blank=True) + board_type = models.CharField(max_length=20, choices=BOARD_TYPES, default='arty_a7', blank=True) diff --git a/ansible/roles/site/files/pib/pibfpgas/src/pibfpgas/static/pib.css b/ansible/roles/site/files/pib/pibfpgas/src/pibfpgas/static/pib.css index 4d68063..f0e4f4a 100644 --- a/ansible/roles/site/files/pib/pibfpgas/src/pibfpgas/static/pib.css +++ b/ansible/roles/site/files/pib/pibfpgas/src/pibfpgas/static/pib.css @@ -1,40 +1,652 @@ - p { - margin: 1px; +/* ============================================ + fpgas.online Design System + Inspired by wafer.space aesthetic + ============================================ */ + +/* --- Custom Properties --- */ +:root { + --color-primary: #5CA7DB; + --color-primary-hover: #4a96ca; + --color-dark: #242930; + --color-dark-alt: #1E2228; + --color-bg: #F9FAFB; + --color-text: #606060; + --color-text-light: #999; + --color-white: #fff; + --color-border: #e5e7eb; + --color-danger: #e74c3c; + --color-success: #27ae60; + + --shadow-card: 0 5px 20px rgba(0, 0, 0, 0.04); + --shadow-card-hover: 0 8px 30px rgba(0, 0, 0, 0.08); + --radius: 4px; + --radius-lg: 8px; + + --font-body: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; + --font-mono: 'SF Mono', 'Fira Code', 'Consolas', monospace; + + --space-xs: 0.25rem; + --space-sm: 0.5rem; + --space-md: 1rem; + --space-lg: 1.5rem; + --space-xl: 2rem; + --space-2xl: 3rem; +} + +/* --- Reset / Base --- */ +*, *::before, *::after { + box-sizing: border-box; +} + +body { + margin: 0; + font-family: var(--font-body); + color: var(--color-text); + background: var(--color-bg); + line-height: 1.6; + -webkit-font-smoothing: antialiased; +} + +a { + color: var(--color-primary); + text-decoration: none; +} + +a:hover { + text-decoration: underline; +} + +img { + max-width: 100%; + height: auto; +} + +/* --- Layout --- */ +.site-container { + display: flex; + flex-direction: column; + min-height: 100vh; +} + +.site-main { + flex: 1; +} + +.container { + max-width: 1200px; + margin: 0 auto; + padding: 0 var(--space-lg); +} + +/* --- Header --- */ +.site-header { + background: var(--color-dark); + padding: var(--space-sm) 0; + position: sticky; + top: 0; + z-index: 100; +} + +.site-header .container { + display: flex; + align-items: center; + justify-content: space-between; +} + +.site-header__logo { + color: var(--color-white); + font-size: 1.25rem; + font-weight: 700; + text-decoration: none; +} + +.site-header__logo:hover { + text-decoration: none; + color: var(--color-primary); +} + +.site-nav { + display: flex; + gap: var(--space-lg); + list-style: none; + margin: 0; + padding: 0; +} + +.site-nav a { + color: #ccc; + font-size: 0.9rem; + transition: color 0.2s; +} + +.site-nav a:hover { + color: var(--color-white); + text-decoration: none; +} + +/* --- Hero --- */ +.hero { + background: var(--color-dark); + color: var(--color-white); + padding: var(--space-2xl) 0; +} + +.hero .container { + display: flex; + align-items: center; + gap: var(--space-2xl); +} + +.hero__content { + flex: 1; +} + +.hero__title { + font-size: 2rem; + font-weight: 700; + margin: 0 0 var(--space-md); + line-height: 1.2; +} + +.hero__text { + color: #b0b8c4; + font-size: 1.05rem; + margin: 0 0 var(--space-lg); + line-height: 1.7; +} + +.hero__actions { + display: flex; + gap: var(--space-md); + flex-wrap: wrap; +} + +.hero__image { + flex: 0 0 auto; +} + +.hero__image img { + max-height: 180px; + border-radius: var(--radius); +} + +/* --- Buttons --- */ +.btn { + display: inline-block; + padding: 0.5rem 1.25rem; + border-radius: var(--radius); + font-family: var(--font-body); + font-size: 0.9rem; + font-weight: 500; + cursor: pointer; + border: 2px solid transparent; + transition: background 0.2s, border-color 0.2s, color 0.2s; + text-align: center; + line-height: 1.4; +} + +.btn:hover { + text-decoration: none; +} + +.btn--primary { + background: var(--color-primary); + color: var(--color-white); +} + +.btn--primary:hover { + background: var(--color-primary-hover); +} + +.btn--outline { + background: transparent; + border-color: rgba(255, 255, 255, 0.3); + color: var(--color-white); +} + +.btn--outline:hover { + border-color: var(--color-white); +} + +.btn--sm { + padding: 0.3rem 0.75rem; + font-size: 0.8rem; +} + +.btn--danger { + background: var(--color-danger); + color: var(--color-white); +} + +.btn--danger:hover { + background: #c0392b; +} + +/* Input buttons styled to match */ +input[type="submit"].btn, +input[type="button"].btn { + appearance: none; + -webkit-appearance: none; +} + +/* --- Section --- */ +.section { + padding: var(--space-2xl) 0; +} + +.section__title { + font-size: 1.5rem; + font-weight: 600; + color: var(--color-dark); + margin: 0 0 var(--space-lg); +} + +/* --- Board Cards (Index Page) --- */ +.board-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); + gap: var(--space-lg); + margin-bottom: var(--space-2xl); +} + +.board-card { + background: var(--color-white); + border-radius: var(--radius); + box-shadow: var(--shadow-card); + overflow: hidden; + transition: box-shadow 0.2s, transform 0.2s; +} + +.board-card:hover { + box-shadow: var(--shadow-card-hover); + transform: translateY(-2px); +} + +.board-card__video { + position: relative; + background: #000; + aspect-ratio: 16 / 10; +} + +.board-card__video .video-js { + width: 100% !important; + height: 100% !important; + position: absolute; + top: 0; + left: 0; +} + +.board-card__body { + padding: var(--space-md); +} + +.board-card__badge { + display: inline-block; + background: var(--color-primary); + color: var(--color-white); + font-size: 0.7rem; + font-weight: 600; + padding: 0.15rem 0.5rem; + border-radius: 2px; + text-transform: uppercase; + letter-spacing: 0.03em; + margin-bottom: var(--space-sm); +} + +.board-card__name { + font-size: 1.1rem; + font-weight: 600; + color: var(--color-dark); + margin: 0 0 var(--space-sm); +} + +.board-card__link { + display: block; + text-align: center; + padding: 0.5rem; + background: var(--color-primary); + color: var(--color-white); + border-radius: var(--radius); + font-weight: 500; + font-size: 0.9rem; + transition: background 0.2s; +} + +.board-card__link:hover { + background: var(--color-primary-hover); + text-decoration: none; +} + +/* --- Detail Page --- */ +.detail-header { + background: var(--color-dark); + color: var(--color-white); + padding: var(--space-lg) 0; +} + +.detail-header__title { + font-size: 1.5rem; + font-weight: 700; + margin: 0; +} + +.detail-header__badge { + display: inline-block; + background: var(--color-primary); + color: var(--color-white); + font-size: 0.75rem; + font-weight: 600; + padding: 0.2rem 0.6rem; + border-radius: 2px; + text-transform: uppercase; + margin-left: var(--space-sm); + vertical-align: middle; +} + +/* --- Toolbar --- */ +.detail-toolbar { + background: var(--color-white); + border-bottom: 1px solid var(--color-border); + padding: var(--space-sm) 0; +} + +.detail-toolbar .container { + display: flex; + align-items: center; + gap: var(--space-lg); + flex-wrap: wrap; +} + +.toolbar-group { + display: flex; + align-items: center; + gap: var(--space-sm); + flex-wrap: wrap; +} + +.toolbar-group__label { + font-size: 0.8rem; + font-weight: 600; + color: var(--color-text-light); + text-transform: uppercase; + letter-spacing: 0.03em; + white-space: nowrap; +} + +.toolbar-btn { + display: inline-block; + padding: 0.25rem 0.6rem; + border-radius: var(--radius); + font-family: var(--font-body); + font-size: 0.8rem; + cursor: pointer; + border: 1px solid var(--color-border); + background: var(--color-white); + color: var(--color-text); + transition: background 0.15s, border-color 0.15s; +} + +.toolbar-btn:hover { + background: var(--color-bg); + border-color: var(--color-primary); +} + +/* --- Detail Panels (Video + Terminal) --- */ +.detail-panels { + display: grid; + grid-template-columns: 1fr 2fr; + gap: var(--space-md); + padding: var(--space-md) 0; +} + +.detail-panels--tt { + grid-template-columns: 1fr 4fr; +} + +.panel { + background: var(--color-white); + border-radius: var(--radius); + box-shadow: var(--shadow-card); + overflow: hidden; + min-height: 400px; +} + +.panel--video .video-js { + width: 100% !important; + height: 100% !important; +} + +.panel--terminal iframe { + width: 100%; + height: 100%; + border: none; + min-height: 400px; +} + +/* --- Status Bar --- */ +.status-bar { + background: var(--color-dark-alt); + padding: var(--space-md) 0; +} + +.status-bar .container { + display: flex; + align-items: flex-start; + gap: var(--space-md); + flex-wrap: wrap; +} + +.status-bar__log { + flex: 1; + min-width: 200px; +} + +.status-bar__log textarea { + width: 100%; + background: var(--color-dark); + color: #a8d8a8; + border: 1px solid #333; + border-radius: var(--radius); + padding: var(--space-sm); + font-family: var(--font-mono); + font-size: 0.8rem; + resize: vertical; +} + +.status-bar__controls { + display: flex; + align-items: center; + gap: var(--space-sm); + flex-wrap: wrap; +} + +.status-bar__controls input[type="text"] { + background: var(--color-dark); + color: var(--color-white); + border: 1px solid #444; + border-radius: var(--radius); + padding: 0.3rem 0.5rem; + font-family: var(--font-mono); + font-size: 0.8rem; +} + +/* --- Code Blocks (Direct Access) --- */ +.code-block { + position: relative; + background: var(--color-dark); + border-radius: var(--radius); + padding: var(--space-md); + margin-bottom: var(--space-md); +} + +.code-block pre { + margin: 0; + color: #a8d8a8; + font-family: var(--font-mono); + font-size: 0.85rem; + white-space: pre-wrap; + word-break: break-all; + padding-right: 4rem; +} + +.code-block__copy { + position: absolute; + top: var(--space-sm); + right: var(--space-sm); + background: rgba(255, 255, 255, 0.1); + color: #ccc; + border: 1px solid rgba(255, 255, 255, 0.15); + border-radius: var(--radius); + padding: 0.2rem 0.5rem; + font-size: 0.75rem; + cursor: pointer; + font-family: var(--font-body); + transition: background 0.15s, color 0.15s; +} + +.code-block__copy:hover { + background: rgba(255, 255, 255, 0.2); + color: var(--color-white); +} + +/* --- Links Grid --- */ +.links-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); + gap: var(--space-lg); +} + +.links-card { + background: var(--color-white); + border-radius: var(--radius); + box-shadow: var(--shadow-card); + padding: var(--space-lg); +} + +.links-card h3 { + font-size: 1rem; + font-weight: 600; + color: var(--color-dark); + margin: 0 0 var(--space-sm); +} + +.links-card ul { + list-style: none; + margin: 0; + padding: 0; +} + +.links-card li { + padding: var(--space-xs) 0; +} + +.links-card li a { + font-size: 0.9rem; +} + +/* --- Upload Form --- */ +.upload-form { + display: flex; + align-items: center; + gap: var(--space-sm); + flex-wrap: wrap; +} + +/* --- Footer --- */ +.site-footer { + background: var(--color-dark); + color: #888; + padding: var(--space-lg) 0; + margin-top: auto; + font-size: 0.85rem; +} + +.site-footer .container { + display: flex; + justify-content: space-between; + align-items: center; + flex-wrap: wrap; + gap: var(--space-md); +} + +.site-footer a { + color: #aaa; +} + +.site-footer a:hover { + color: var(--color-white); +} + +.footer-links { + display: flex; + gap: var(--space-lg); + list-style: none; + margin: 0; + padding: 0; +} + +/* --- Responsive --- */ +@media (max-width: 768px) { + .hero .container { + flex-direction: column; + text-align: center; } - h1 { - padding: 5px; - margin: 0px; + .hero__actions { + justify-content: center; } - h2 { - padding: 1px; - margin: 0px; + + .hero__title { + font-size: 1.5rem; } - ul { - margin: 0px; + .detail-panels, + .detail-panels--tt { + grid-template-columns: 1fr; } - table.buttons { - width: fit-content; - float: left; - height: 100%; + .panel { + min-height: 300px; } - table.buttons td { - width: fit-content; + + .detail-toolbar .container { + flex-direction: column; + align-items: flex-start; } - table td { - padding: 5px; - border: 1px solid black; - text-align: center; + .site-header .container { + flex-direction: column; + gap: var(--space-sm); } - table { - width: 100%; - height: 90%; + + .site-nav { + gap: var(--space-md); + } + + .site-footer .container { + flex-direction: column; text-align: center; } - iframe { - width: 100%; - height: 100%; + + .footer-links { + justify-content: center; + } +} + +@media (max-width: 640px) { + .board-grid { + grid-template-columns: 1fr; + } + + .links-grid { + grid-template-columns: 1fr; + } + + .hero__image { + display: none; + } + + .status-bar .container { + flex-direction: column; } +} diff --git a/ansible/roles/site/files/pib/pibfpgas/src/pibfpgas/templates/base.html b/ansible/roles/site/files/pib/pibfpgas/src/pibfpgas/templates/base.html new file mode 100644 index 0000000..14f0300 --- /dev/null +++ b/ansible/roles/site/files/pib/pibfpgas/src/pibfpgas/templates/base.html @@ -0,0 +1,58 @@ +{% load static %} + + +
+ + +
- Accessing pi{{pi.port}}- |
- -{% include "upload.html" %} - | -||||
-
-
|
- |||||
| - - | - - | -||||
|
-
- - - - +{% extends "base.html" %} +{% load static %} + +{% block title %}pi{{ pi.port }} - fpgas.online{% endblock %} + +{% block extra_head %} + +{% endblock %} + +{% block content %} + +
+
++ pi{{ pi.port }} + {{ pi.get_board_type_display }} ++
+
+
+
-
- Pi patch cable color: {{pi.loc}} {{pi.cable_color}} - - |
- |||||
Accessing directly |
- |||||
|
-
-
-
-
+ vlc https://{{ domain_name }}/live/pi{{pi.port}}.m3u8
-
+
-
- |
- Use your own ssh client. user: pi, host: {{ domain_name }}, port {{o}}22,
- (password is in login banner) - click to copy: -
-
+
+
+
+
+
+
-
+ ssh -p {{o}}22 pi@{{ domain_name }}
-
+
+Direct Access+ +Pi patch cable color: {{ pi.location }} {{ pi.cable_color }} + +
+
+
+ vlc https://{{ domain_name }}/live/pi{{ pi.port }}.m3u8
+
+ Use your own SSH client. User: pi, host: {{ domain_name }}, port {{ o }}22 (password is in login banner): + +
+
+
+ ssh -p {{ o }}22 pi@{{ domain_name }}
+
+
+
+ scp -P {{ o }}22 * pi@{{ domain_name }}:Uploads
+
+
+ Useful Links+
+
+
- Toolchains+The Arty A7 boards have an AMD/Xilinx Artix 7 FPGA supported by multiple toolchains. +
-
-
+ scp -P {{o}}22 * pi@{{ domain_name }}:Uploads
-
+
-
-
- Arty A7 Board+- |
- ||||
The Arty A7 development boards have an AMD/Xilinx Artix 7 FPGA which is supported by multiple different toolchains.
- -Access an FPGA right now! |
- ||||
|
- On this page anyone (including you!) can remotely access and control an FPGA development board! There is even a camera to see the LEDs. -
There are ~10 FPGAs boards connected to this system, so there should always be one free and ready for you to use! - |
-
- |
- |||
FPGA pi{{pi.port}} |
| Use this FPGA |
| - - | -
- Accessing pi{{pi.port}}- |
- ||||||
-
-
|
- ||||||
| - - | - -- - | -|||||
|
-
- +{% extends "base.html" %} +{% load static %} + +{% block title %}pi{{ pi.port }} - Tiny Tapeout - fpgas.online{% endblock %} + +{% block extra_head %} + +{% endblock %} + +{% block content %} + +
+
++ pi{{ pi.port }} + {{ pi.get_board_type_display }} ++
+
+
+
-
-
- |
- ||||||
|
- Use your own ssh client user: pi host: {{ domain_name }} port {{o}}22 - like this (click to copy): - -
-
-
+ ssh -p {{o}}22 pi@{{ domain_name }}
-
+
-
- - |
- ||||||
Pi patch cable color: {{ pi.location }} {{ pi.cable_color }}
+ +Use your own SSH client. User: pi, host: {{ domain_name }}, port {{ o }}22 (password is in login banner):
+ +ssh -p {{ o }}22 pi@{{ domain_name }}
+
+