Build optimization: pre-built PM3 binaries, ARM64 CI, base image caching

Replace PM3 compile-from-source in pi-gen with pre-built tarball extraction
(saves 43-58 min). Merge stagePM3 into stageDangerousPi as 02-pm3-install
substage, renumber all subsequent substages. Switch CI PM3 build to native
ARM64 runner (ubuntu-24.04-arm64) eliminating QEMU overhead. Add weekly
base-image workflow for pre-baking stages 0-2. Support PM3_TARBALL,
BASE_IMAGE, and APT_PROXY env vars in build-image.sh.

Also includes prior Phase 5 work: theme system, design system integration,
component update system, OS updates, CI build pipeline, and test results.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
michael
2026-03-04 12:01:01 -08:00
parent 2ec89041ef
commit a9acdb85ce
163 changed files with 8124 additions and 921 deletions

View File

@@ -0,0 +1,68 @@
"""VivoKey Supra Theme plugin for Dangerous Pi.
Registers the Supra theme (Material Design 3 with VivoKey blue, rounded
corners, elevation shadows) via the theme_register hook so it appears in
Settings > Appearance when this plugin is enabled.
"""
import shutil
import sys
from pathlib import Path
_plugin_manager_module = (
sys.modules.get('app.backend.managers.plugin_manager') or
sys.modules.get('backend.managers.plugin_manager')
)
if _plugin_manager_module:
PluginBase = _plugin_manager_module.PluginBase
PluginMetadata = _plugin_manager_module.PluginMetadata
else:
from pathlib import Path as _P
app_path = _P(__file__).parent.parent.parent
if str(app_path) not in sys.path:
sys.path.insert(0, str(app_path))
from backend.managers.plugin_manager import PluginBase, PluginMetadata
# Paths
_PLUGIN_DIR = Path(__file__).parent
_STATIC_CSS = _PLUGIN_DIR / "static" / "tokens.css"
# Where the frontend serves themes from
_THEMES_DIR = _PLUGIN_DIR.parent.parent / "frontend" / "themes" / "supra"
class SupraThemePlugin(PluginBase):
"""Provides the VivoKey Supra theme to Dangerous Pi."""
async def on_load(self):
pass
async def on_enable(self):
# Copy CSS to the themes directory so it's served by the /themes mount
_THEMES_DIR.mkdir(parents=True, exist_ok=True)
shutil.copy2(_STATIC_CSS, _THEMES_DIR / "tokens.css")
# Register the theme via the theme_register hook.
# MUST be synchronous — the theme scanner skips async callbacks.
def supra_theme_register():
return {
"id": "supra",
"name": "VivoKey Supra",
"description": "Material Design 3 with VivoKey blue, rounded corners, elevation shadows",
"supportsModes": ["dark", "light", "auto"],
"defaultMode": "dark",
"author": "Dangerous Things",
"css_url": "/themes/supra/tokens.css",
}
self.register_hook("theme_register", supra_theme_register)
async def on_disable(self):
# Clean up the copied theme files
if _THEMES_DIR.exists():
shutil.rmtree(_THEMES_DIR, ignore_errors=True)
async def on_unload(self):
pass
def get_metadata(self) -> PluginMetadata:
return self.metadata

View File

@@ -0,0 +1,10 @@
{
"id": "supra_theme",
"name": "VivoKey Supra Theme",
"version": "1.0.0",
"description": "Material Design 3 theme with VivoKey blue, rounded corners, and elevation shadows",
"author": "Dangerous Things",
"homepage": "https://github.com/dangerous-tac0s/dt-design-system",
"dependencies": [],
"permissions": []
}

View File

@@ -0,0 +1,232 @@
/* VivoKey Supra Theme — CSS Custom Properties + Component Overrides */
/* Distributed as a Dangerous-Pi theme plugin */
/* Source: @dangerousthings/tokens + @dangerousthings/web */
/* ============================================================================
Dark Mode (Default)
============================================================================ */
:root[data-brand="supra"] {
/* Backgrounds */
--color-bg: #000000;
--color-bg-rgb: 0, 0, 0;
--color-surface: #28292E;
--color-surface-rgb: 40, 41, 46;
--color-surface-hover: #303136;
--color-border: #42667E;
/* Text */
--color-text-primary: #ffffff;
--color-text-secondary: #e2e8f0;
--color-text-muted: #94a3b8;
/* Brand Palette */
--color-primary: #42667E;
--color-primary-rgb: 66, 102, 126;
--color-primary-dim: #385872;
--color-secondary: #8BAEC7;
--color-secondary-rgb: 139, 174, 199;
--color-accent: #8BAEC7;
--color-accent-rgb: 139, 174, 199;
--color-other: #F67448;
--color-other-rgb: 246, 116, 72;
/* Status */
--color-success: #8BB174;
--color-success-rgb: 139, 177, 116;
--color-warning: #F67448;
--color-warning-rgb: 246, 116, 72;
--color-error: #ED4337;
--color-error-rgb: 237, 67, 55;
--color-info: #42667E;
--color-info-rgb: 66, 102, 126;
/* Typography */
--font-heading: var(--font-sans);
--font-body: var(--font-sans);
/* Shape */
--bevel-sm: 0px;
--bevel-md: 0px;
--bevel-lg: 0px;
--radius-sm: 0.5rem;
--radius: 0.625rem;
--radius-lg: 0.75rem;
color-scheme: dark;
}
/* ============================================================================
Light Mode
============================================================================ */
:root[data-brand="supra"][data-theme="light"] {
/* Backgrounds */
--color-bg: #ffffff;
--color-bg-rgb: 255, 255, 255;
--color-surface: #f0f2f4;
--color-surface-rgb: 240, 242, 244;
--color-surface-hover: #e5e8eb;
--color-border: #000000;
/* Text */
--color-text-primary: #000000;
--color-text-secondary: #374151;
--color-text-muted: #6b7280;
/* Brand Palette */
--color-primary: #42667E;
--color-primary-rgb: 66, 102, 126;
--color-primary-dim: #385872;
--color-secondary: #F67448;
--color-secondary-rgb: 246, 116, 72;
--color-accent: #b45309;
--color-accent-rgb: 180, 83, 9;
--color-other: #F67448;
--color-other-rgb: 246, 116, 72;
/* Status */
--color-success: #4d7c0f;
--color-success-rgb: 77, 124, 15;
--color-warning: #F67448;
--color-warning-rgb: 246, 116, 72;
--color-error: #ED4337;
--color-error-rgb: 237, 67, 55;
--color-info: #42667E;
--color-info-rgb: 66, 102, 126;
color-scheme: light;
}
/* ============================================================================
Auto Mode (follows system preference)
============================================================================ */
@media (prefers-color-scheme: light) {
:root[data-brand="supra"][data-theme="auto"] {
/* Backgrounds */
--color-bg: #ffffff;
--color-bg-rgb: 255, 255, 255;
--color-surface: #f0f2f4;
--color-surface-rgb: 240, 242, 244;
--color-surface-hover: #e5e8eb;
--color-border: #000000;
/* Text */
--color-text-primary: #000000;
--color-text-secondary: #374151;
--color-text-muted: #6b7280;
/* Brand Palette */
--color-primary: #42667E;
--color-primary-rgb: 66, 102, 126;
--color-primary-dim: #385872;
--color-secondary: #F67448;
--color-secondary-rgb: 246, 116, 72;
--color-accent: #b45309;
--color-accent-rgb: 180, 83, 9;
--color-other: #F67448;
--color-other-rgb: 246, 116, 72;
/* Status */
--color-success: #4d7c0f;
--color-success-rgb: 77, 124, 15;
--color-warning: #F67448;
--color-warning-rgb: 246, 116, 72;
--color-error: #ED4337;
--color-error-rgb: 237, 67, 55;
--color-info: #42667E;
--color-info-rgb: 66, 102, 126;
color-scheme: light;
}
}
/* ============================================================================
Supra Component Overrides — MD3 Elevation & Rounded Corners
============================================================================ */
/* Card Elevation */
[data-brand="supra"] .card {
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.18);
border-radius: var(--radius-lg);
}
[data-brand="supra"] .card:hover {
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.22);
}
/* Button — Dual-layer inset shadow (hardware button feel) */
[data-brand="supra"] .btn-primary {
box-shadow:
inset -2px -2px 4px rgba(0, 0, 0, 0.18),
inset 2px 2px 4px rgba(255, 255, 255, 0.25);
border-radius: var(--radius);
}
[data-brand="supra"] .btn-primary:hover {
box-shadow:
inset -2px -2px 6px rgba(0, 0, 0, 0.22),
inset 2px 2px 6px rgba(255, 255, 255, 0.3);
transform: translateY(-1px);
}
[data-brand="supra"] .btn-secondary {
border-radius: var(--radius);
}
[data-brand="supra"] .btn-danger {
border-radius: var(--radius);
}
/* Badge — Rounded pill (MD3 chip style) */
[data-brand="supra"] .badge {
border-radius: 8px;
}
/* Badge text — readable on translucent backgrounds */
[data-brand="supra"] .badge-success,
[data-brand="supra"] .badge-error,
[data-brand="supra"] .badge-warning,
[data-brand="supra"] .badge-info {
color: var(--color-text-primary);
}
/* Input — Rounded with standard focus ring */
[data-brand="supra"] .input,
[data-brand="supra"] input,
[data-brand="supra"] textarea,
[data-brand="supra"] select {
border-radius: var(--radius);
}
[data-brand="supra"] .input:focus,
[data-brand="supra"] input:focus,
[data-brand="supra"] textarea:focus,
[data-brand="supra"] select:focus {
box-shadow: 0 0 0 3px rgba(var(--color-primary-rgb), 0.2);
}
/* Terminal — Rounded with subtle elevation */
[data-brand="supra"] .terminal {
border-radius: var(--radius);
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.18);
}
/* Elevation Utility Classes */
.supra-elevation-1 {
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.12);
}
.supra-elevation-2 {
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.18);
}
.supra-elevation-3 {
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.22);
}
.supra-elevation-4 {
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.25);
}
.supra-elevation-5 {
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.3);
}