Rebuild the editor as a Vue app: sidebar, pages side by side, tile settings in a drawer (0.2.74)

The page is now web/ (Vue 3 + Vite + TypeScript), built into screen_manager/app/static
with hashed file names; server.py serves index.html as built and /assets/. The Python
API, the data and the firmware are unchanged. Your screens sit in a sidebar, the pages
of a screen stand next to each other at the screen's own shape, the library sits on the
right, and a tile's settings slide in as a drawer while the mockup stays in view. New
screen, Firmware & USB, Alerts, Override YAML and Settings are pages of their own.
Fonts moved to web/src/assets; the tests read the source through tests/editor_sources.py.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
Max Gramser
2026-09-18 07:48:45 +02:00
parent 9f5694b9eb
commit 510ebdf885
72 changed files with 5486 additions and 5409 deletions
+7 -7
View File
@@ -1,7 +1,6 @@
"""HA Ingress app. HA writes: text.set_value on discovered inboxes (or the screen_message action on firmware 0.2.33+), each screen's alert actions when an alert event for every screen fires, and one persistent notification when a nightly update stops."""
import asyncio
import contextlib
import hashlib
import json
import logging
import os
@@ -1495,12 +1494,13 @@ def create_app(manager, development=False):
app = web.Application(middlewares=[guard], client_max_size=16*1024)
static = Path(__file__).parent / 'static'
# The page asks for its script and styles with a stamp of their content, so a browser that keeps old copies
# anyway (Safari did after an update) never runs an old script against a new page.
stamp = hashlib.sha1(b''.join((static / name).read_bytes() for name in ('app.js', 'style.css'))).hexdigest()[:12]
page = (static / 'index.html').read_text().replace('"static/app.js"', f'"static/app.js?v={stamp}"').replace('"static/style.css"', f'"static/style.css?v={stamp}"')
# The page is the Vite build of web/ (npm run build writes it here): index.html names its script and styles
# by a hash of their content, so a browser that keeps old copies anyway (Safari did after an update) never
# runs an old script against a new page.
page = (static / 'index.html').read_text()
async def index(request):
return web.Response(text=page, content_type='text/html')
# SCREEN_DEV reads the file every time, so a fresh `npm run build` shows up without a restart.
return web.Response(text=(static / 'index.html').read_text() if development else page, content_type='text/html')
def light_payload(screens=None):
"""Screens and update status: everything that changes while the page is open."""
if screens is None:
@@ -1674,7 +1674,7 @@ def create_app(manager, development=False):
app.router.add_get('/api/events', events)
app.router.add_put('/api/screens/{inbox}', save)
app.router.add_put('/api/screens/{inbox}/settings', change_settings)
app.router.add_static('/static/', static)
app.router.add_static('/assets/', static / 'assets')
return app
async def main():