Skip to content

feat(assets): add signage app store to the Add Asset flow - #3114

Merged
vpetersson merged 4 commits into
masterfrom
feat/apps-in-add-asset
Jul 5, 2026
Merged

feat(assets): add signage app store to the Add Asset flow#3114
vpetersson merged 4 commits into
masterfrom
feat/apps-in-add-asset

Conversation

@vpetersson

@vpetersson vpetersson commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

Issues Fixed

Adds a new Apps section to the Add Asset flow: a catalog of pre-defined signage apps from the app store (app-store.srly.io / signage-apps.com), configured from each app's published manifest. Not tied to an existing issue.

Description

Each app publishes a self-describing manifest (/.well-known/signage-app.json) with a settings JSON Schema and a launch URL template. The store index lists them all. This PR consumes those manifests directly:

  • Apps tab in the Add modal fetches the store index + per-app manifests client-side (operator's browser; manifests are CORS-enabled), so the device itself never reaches the store. Renders a catalog grid.
  • Manifest-driven config form, ported from the store's tested expand-template.js + config-manifest.js: text / select / toggle / number / url / timezone widgets, plus a Leaflet + OpenStreetMap location-map (no Google Maps, no API key — bundled locally, tiles fetched in the browser only).
  • Install creates an ordinary webpage asset stamped with metadata.app (id, manifest URL/version, chosen values). Editing an app reopens the same config form seeded from the saved values and rebuilds the launch URL.
  • New assets_create_app endpoint + app-edit path in assets_update, both guarded by a store-host allowlist. playback.refreshIntervalS maps to the viewer's existing per-asset auto-refresh.
  • APP_STORE_INDEX_URL setting defaults to the production store index (signage-apps.com/manifest.json) and stays env-overridable (staging / a self-hosted mirror).

Also: added an ambient alpinejs.d.ts (the package ships no types) and modernized tsconfig (moduleResolution: bundler, dropped dead React-era paths).

Live walkthrough (x86 dev host, headless Chromium against the running server + staging catalog): all 12 apps render with 0 JS errors; install and edit round-trips verified; found and fixed 3 bugs during the audit — spurious location emit (no-default apps now stay unset for IP auto-detect), a blank map (Leaflet collapsed the canvas to 0 height), and stale config fields when switching to a no-settings app.

Known limitation: the generic renderer skips array widgets (World Clock's repeatable city list) — same as the app store, which uses a bespoke form there. World Clock installs with its default cities; other settings are configurable.

Checklist

  • I have performed a self-review of my own code.
  • New and existing unit tests pass locally and on CI with my changes. (12 new Python endpoint tests + 7 bun launch-URL tests; full test_template_views.py green.)
  • I have done an end-to-end test for Raspberry Pi devices. (Pi 4 mvip@…141: built the arm64 server image from this branch, deployed it to the device, installed an app through the on-device UI (12-app catalog loaded, clock.srly.io built), and confirmed the viewer rendered it — logs show Showing asset Digital Clock (webpage) / Current url is https://clock.srly.io/ on eglfs. eglfs has no compositor grab, so no pixel screenshot; verified via viewer logs + the QtWebEngine process + the on-device UI screenshot. Restored afterward.)
  • I have tested my changes for x86 devices. (x86 jdoe@…208: built + deployed the x86 server image from this branch, installed an app on-device, and grim-captured the rendered display showing the clock app live (clock.srly.io). Restored afterward.)
  • I added a documentation for the changes I have made (when necessary).

🤖 Generated with Claude Code

Add an "Apps" tab to the Add Asset modal that installs pre-defined
signage apps from the app store, configured from each app's published
manifest.

- Apps tab fetches the store index + per-app manifests client-side
  (operator's browser; CORS-enabled), renders a catalog grid
- Manifest-driven config form (ported from the store's tested
  expand-template.js + config-manifest.js): text/select/toggle/number/
  url/timezone widgets plus a Leaflet + OSM location-map (no Google
  Maps, no API key, bundled locally)
- Installs as a webpage asset stamped with metadata.app (id, manifest
  URL/version, values) so editing reopens the config form seeded from
  saved values and rebuilds the launch URL
- assets_create_app endpoint + app-edit path in assets_update, guarded
  by a store-host allowlist; playback.refreshIntervalS maps to the
  viewer's per-asset auto-refresh
- APP_STORE_INDEX_URL setting (env-overridable, defaults to staging)

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
@vpetersson
vpetersson requested a review from a team as a code owner July 5, 2026 07:30
@vpetersson vpetersson self-assigned this Jul 5, 2026
@vpetersson
vpetersson requested a review from Copilot July 5, 2026 07:31

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new “Apps” catalog to the Add Asset modal that installs/edit-configures pre-defined signage apps from a remote app store manifest/index, while keeping the device backend constrained to storing a normal webpage asset plus metadata.app and enforcing an origin allowlist.

Changes:

  • UI: New Apps tab in Add Asset modal with manifest-driven settings rendering, launch-URL building, and edit-time reconfiguration.
  • Backend: New assets_create_app endpoint and app-aware assets_update handling, gated by APP_STORE_ALLOWED_HOST_SUFFIXES; plus APP_STORE_INDEX_URL templated into the home page.
  • Tooling/Deps: Adds Leaflet (+ types) and ambient Alpine typings; updates TS config to bundler resolution and excludes Bun test files from tsc.

Reviewed changes

Copilot reviewed 18 out of 21 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
tsconfig.json Switches to moduleResolution: bundler, removes old path aliases, excludes *.test.ts from app tsc pass.
tests/test_template_views.py Adds template integration test for Apps tab wiring plus endpoint tests for app create/update flows.
src/anthias_server/django_project/settings.py Introduces APP_STORE_INDEX_URL and APP_STORE_ALLOWED_HOST_SUFFIXES settings.
src/anthias_server/app/views.py Adds _host_allowed, new assets_create_app endpoint, and app edit handling in assets_update.
src/anthias_server/app/urls.py Routes assets/new-app/ to assets_create_app.
src/anthias_server/app/templates/base.html Exposes app store index URL via <meta name="anthias-app-store-index">.
src/anthias_server/app/templates/_asset_modal.html Adds Apps tab UI, install form, and app settings section for edit mode.
src/anthias_server/app/static/src/home.ts Registers appsTab() / appEdit() Alpine factories on window.
src/anthias_server/app/static/src/apps/types.ts Defines types for store index, manifests, and setting schemas/values.
src/anthias_server/app/static/src/apps/manifest-form.ts Implements generic manifest-driven settings form renderer.
src/anthias_server/app/static/src/apps/location-map.ts Adds Leaflet+OSM map widget for location settings.
src/anthias_server/app/static/src/apps/launch-url.ts Adds RFC6570 {?…} launch URL builder used by add/edit flows.
src/anthias_server/app/static/src/apps/launch-url.test.ts Adds Bun tests for launch URL expansion behavior.
src/anthias_server/app/static/src/apps/catalog.ts Fetches store index + manifests client-side with graceful degradation.
src/anthias_server/app/static/src/apps.ts Alpine glue for Apps tab browsing/config/install + edit-time reconfigure.
src/anthias_server/app/static/src/alpinejs.d.ts Adds minimal ambient Alpine typings for strict TS builds.
src/anthias_server/app/static/sass/anthias.scss Imports Leaflet CSS into the bundled styles.
src/anthias_server/app/static/sass/_styles.scss Adds scoped styling for catalog grid, config form, and map widget.
src/anthias_server/app/helpers.py Threads APP_STORE_INDEX_URL into base template context.
package.json Adds leaflet and @types/leaflet.
bun.lock Locks Leaflet and its type dependencies.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/anthias_server/app/views.py
Comment thread src/anthias_server/app/static/src/apps/location-map.ts
Comment thread src/anthias_server/app/static/src/apps/manifest-form.ts
Comment thread tests/test_template_views.py
Comment thread src/anthias_server/app/static/src/apps/location-map.ts
Comment thread src/anthias_server/app/static/src/apps/location-map.ts
vpetersson and others added 2 commits July 5, 2026 10:33
The integration test test_add_asset_via_url fills input[name="uri"] and
submits form[action*="assets/new"]; the new Apps form reused both, so the
selectors matched two elements (Playwright strict-mode failure).

- Rename the Apps form's hidden uri/values to app_uri/app_values
  (matching the edit path), leaving one name="uri" input (the URL tab)
- Move the install endpoint from /assets/new-app/ to /apps/install/ so
  its submit button no longer matches form[action*="assets/new"]

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
- Default APP_STORE_INDEX_URL to production signage-apps.com (env still
  overridable to staging / a mirror)
- Harden _host_allowed: match on the port/userinfo-free hostname and
  enforce the dotted boundary even for a dot-less configured suffix
- location-map: emit coordinates as numbers (not toFixed strings) so a
  {lat,lng} value compares cleanly against a numeric schema default
- location-map: return a teardown (disconnect ResizeObserver + remove
  map); the form renderer runs it on re-render so repeated open/select
  cycles don't leak detached Leaflet maps (via teardownHost)
- manifest-form: give each timezone <datalist> a unique id to avoid
  duplicate ids across the Add/Edit config hosts
- test: assert the configured APP_STORE_INDEX_URL reached the page
  rather than a hard-coded literal; add _host_allowed + port-in-URL
  coverage

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 18 out of 21 changed files in this pull request and generated 3 comments.

Comment thread tests/test_template_views.py
Comment thread src/anthias_server/app/static/src/apps.ts
Comment thread src/anthias_server/app/static/src/apps.ts
- Reword the app-store fetch errors to reference the network/browser
  connection, not "the device's connection" (the fetch is client-side)
- Fix the appsTab init comment: the pane lazy-loads via x-effect, not
  x-init
- test: pass the uppercase hostname as-is so the case-insensitivity
  assertion actually exercises _host_allowed's internal lower-casing

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
@sonarqubecloud

sonarqubecloud Bot commented Jul 5, 2026

Copy link
Copy Markdown

@vpetersson
vpetersson merged commit 6a7d0c8 into master Jul 5, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants