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:
68
app/plugins/supra_theme/main.py
Normal file
68
app/plugins/supra_theme/main.py
Normal 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
|
||||
10
app/plugins/supra_theme/plugin.json
Normal file
10
app/plugins/supra_theme/plugin.json
Normal 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": []
|
||||
}
|
||||
232
app/plugins/supra_theme/static/tokens.css
Normal file
232
app/plugins/supra_theme/static/tokens.css
Normal 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);
|
||||
}
|
||||
Reference in New Issue
Block a user