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:
@@ -0,0 +1,76 @@
|
||||
<script setup lang="ts">
|
||||
// Everything around the screens: firmware updates, alerts, Claude.
|
||||
import { computed, ref } from "vue";
|
||||
import { go, installClaudeSkill, runUpdateAll, setAutoUpdate, state } from "../store";
|
||||
|
||||
const u = computed(() => state.inventory.updates);
|
||||
const outdated = computed(() => state.inventory.screens.filter((s) => s.update?.available).length);
|
||||
const updatesHint = computed(() => !u.value ? "" : u.value.busy
|
||||
? `Updating to firmware ${u.value.target}…`
|
||||
: !state.inventory.screens.length
|
||||
? `No screen paired yet. New screens get firmware ${u.value.target}.`
|
||||
: outdated.value
|
||||
? `Firmware ${u.value.target} is available for ${outdated.value} screen${outdated.value === 1 ? "" : "s"}.`
|
||||
: `All screens have firmware ${u.value.target}.`);
|
||||
const skill = computed(() => state.inventory.claude_skill);
|
||||
const installing = ref(false);
|
||||
async function install() {
|
||||
installing.value = true;
|
||||
try { await installClaudeSkill(); } finally { installing.value = false; }
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="panel" id="settings-view">
|
||||
<div class="panel-head">
|
||||
<div class="tx">
|
||||
<span class="eyebrow">Settings</span>
|
||||
<h1 id="settings-title">Everything around your screens.</h1>
|
||||
</div>
|
||||
<button type="button" class="btn quiet" id="close-settings" @click="go('')">← My screens</button>
|
||||
</div>
|
||||
<div class="card-grid">
|
||||
<section class="card">
|
||||
<h2>Screens</h2>
|
||||
<p>Add a screen, or build and reinstall the firmware of one you already have.</p>
|
||||
<div class="tools">
|
||||
<button type="button" class="tool" @click="go('#new-screen')"><span class="tool-icon">+</span><span class="tx"><b>New screen</b><small>Connect and install</small></span></button>
|
||||
<button type="button" class="tool" @click="go('#firmware')"><span class="tool-icon">⇪</span><span class="tx"><b>Firmware & USB</b><small>Build and install via USB or Wi-Fi</small></span></button>
|
||||
</div>
|
||||
</section>
|
||||
<section v-if="u" class="card updates" id="updates">
|
||||
<h2>Firmware updates</h2>
|
||||
<p id="updates-hint">{{ updatesHint }}</p>
|
||||
<button v-if="u.pending && !u.busy && u.pending >= 2" id="update-all" type="button" class="btn primary" @click="runUpdateAll">Update all {{ u.pending }} screens</button>
|
||||
<label class="check">
|
||||
<input type="checkbox" id="auto-update" :checked="Boolean(u.auto)" @change="setAutoUpdate(($event.target as HTMLInputElement).checked)" />
|
||||
<span>Update automatically every night<small>Between 03:00 and 06:00, one screen at a time. Stops on an error and reports it in Home Assistant.</small></span>
|
||||
</label>
|
||||
</section>
|
||||
<section class="card">
|
||||
<h2>Alerts</h2>
|
||||
<p>A card over the whole screen from an automation, on one screen or on all of them. The cheatsheet has the action names, fields, icons, and colors.</p>
|
||||
<div class="tools">
|
||||
<button type="button" class="tool" @click="go('#alerts')"><span class="tool-icon">!</span><span class="tx"><b>Alerts</b><small>Full-screen notifications, from an automation</small></span></button>
|
||||
</div>
|
||||
</section>
|
||||
<section class="card" id="claude">
|
||||
<h2>Claude</h2>
|
||||
<p>Teach Claude about ESP Screens, then ask: "Show an alert on all my screens when the mailbox is full." Claude knows the alert events, every field, color, and icon.</p>
|
||||
<div class="actions">
|
||||
<button type="button" id="claude-install" class="btn" :class="skill?.installed && skill.current ? 'quiet' : 'primary'" :disabled="!skill || installing" @click="install">
|
||||
{{ !skill || !skill.installed ? "Install for Claude Code" : skill.current ? "Install again" : "Update the skill" }}
|
||||
</button>
|
||||
<span id="claude-status" class="chip" :class="!skill ? '' : !skill.installed ? '' : skill.current ? 'good' : 'update'" role="status">
|
||||
{{ !skill ? "Loading…" : !skill.installed ? "Not installed" : skill.current ? "● Installed" : "Installed · a newer version is ready" }}
|
||||
</span>
|
||||
</div>
|
||||
<small>For Claude Code in Home Assistant. Writes one file to <code id="claude-path">{{ skill?.path || "/homeassistant/.claude/skills/esp-screens" }}</code>; installing again replaces it.</small>
|
||||
<div class="actions">
|
||||
<a class="btn quiet" id="claude-download" href="api/claude-skill.zip" download="esp-screens.zip">Download for claude.ai</a>
|
||||
<small>Upload the zip in Claude under Customize → Skills.</small>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
Reference in New Issue
Block a user