Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions .impeccable/design.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"schemaVersion": 2,
"generatedAt": "2026-09-27T23:44:35.712408Z",
"generatedAt": "2026-10-08T00:06:48.945959Z",
"title": "Design System: OpenAthan",
"extensions": {
"colorMeta": {
Expand Down Expand Up @@ -261,7 +261,7 @@
"name": "Primary action",
"kind": "button",
"description": "The main action in the current task.",
"html": "<button class=\"ds-primary\" type=\"button\">Open recovery</button>",
"html": "<button class=\"ds-primary\" type=\"button\">Choose USB device</button>",
"css": ".ds-primary { font-family: 'Mukta Malar', sans-serif; display: inline-flex; align-items: center; justify-content: center; min-height: 48px; padding: 10px 30px; border: 1px solid var(--accent, #355e83); border-radius: 4px; background: var(--accent, #355e83); color: var(--paper, #ffffff); font-size: 1.35rem; font-weight: 700; line-height: 1.35; text-decoration: none; text-align: center; cursor: pointer; } .ds-primary:hover:not(:disabled) { background: var(--accent-hover, #254764); border-color: var(--accent-hover, #254764); color: var(--paper, #ffffff); } .ds-primary:focus-visible { outline: 3px solid var(--accent, #355e83); outline-offset: 4px; } @media (max-width: 600px) { .ds-primary { font-size: 1.15rem; padding-inline: 22px; } }",
"refersTo": "button-primary"
},
Expand Down Expand Up @@ -308,7 +308,7 @@
"name": "Availability notice",
"kind": "custom",
"description": "An amber notice that explains availability or a caution.",
"html": "<aside class=\"ds-notice\"><strong>Installation release not available yet.</strong><p>First-time installation will open when a tested firmware release and approved recordings are ready.</p></aside>",
"html": "<aside class=\"ds-notice\"><strong>New installation is unavailable.</strong><p>You can still connect an existing OpenAthan to open its settings or fix Wi-Fi and its device password.</p></aside>",
"css": ".ds-notice { font-family: 'Mukta Malar', sans-serif; margin: 20px 0; padding: 16px 20px; border-radius: 4px; background: var(--warning-bg, #fff5e6); color: var(--warning-text, #805719); font-size: 20px; line-height: 1.45; } .ds-notice strong { font-weight: 700; } .ds-notice p { margin: 4px 0 0; color: var(--text, #38434f); } @media (max-width: 600px) { .ds-notice { font-size: 18px; } }",
"refersTo": "notice"
},
Expand All @@ -323,7 +323,7 @@
"name": "Uncertain feedback",
"kind": "custom",
"description": "A polite status message explaining that the result needs checking.",
"html": "<div class=\"ds-uncertain\" role=\"status\" aria-live=\"polite\" aria-atomic=\"true\">The device\u2019s current status could not be confirmed. Reconnect through recovery to check it before making another change.</div>",
"html": "<div class=\"ds-uncertain\" role=\"status\" aria-live=\"polite\" aria-atomic=\"true\">The device\u2019s current status could not be confirmed. Reconnect through USB setup to check it before making another change.</div>",
"css": ".ds-uncertain { font-family: 'Mukta Malar', sans-serif; margin: 20px 0; padding: 14px 18px; border-radius: 4px; background: var(--warning-bg, #fff5e6); color: var(--warning-text, #805719); font-size: 20px; line-height: 1.45; overflow-wrap: anywhere; } @media (max-width: 600px) { .ds-uncertain { font-size: 18px; } }",
"refersTo": "feedback-uncertain"
},
Expand Down
3 changes: 1 addition & 2 deletions DESIGN.md
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,6 @@ Spacing centers on the frontmatter scale, with 24px section padding, 16px paragr
| ------------------- | ------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Homepage | Main information and supporting panels use a 1.9:1 grid with a 24px column gap. | Grid ratio becomes 1.6:1 with a 20px gap and smaller section type. | Single column: features, hardware guidance, then contributions. Hardware guidance returns to white; contributions retain a pale panel. The primary action fills the width. |
| Documentation index | Reading column plus a 225px contents panel, separated by 36px. | Single reading column; the duplicate contents navigation is hidden. Section anchors remain. | Smaller headings and introductions; paired cable diagrams become one column. |
| USB choices | Task choices plus a 320px help column, separated by 36px. | Help flows below the choices. | Shared narrow-screen gutters and control typography apply. |
| USB flow | A single task panel capped at 880px; forms are capped at 760px. | Width follows the available container. | Actions wrap; fields remain full width and long addresses break safely. |

Documentation articles use a single reading column capped at 76ch. There is no sticky header, floating navigation or animated layout transition. Reduced-motion preferences explicitly disable transitions and smooth scrolling.
Expand Down Expand Up @@ -248,7 +247,7 @@ Help and contribution panels use pale blue with 24px padding. The documentation

Each page has one visible h1. Documentation uses h2 sections and h3 subsections; its link-row titles remain link text rather than false section headings. Section anchors retain a 24px scroll margin.

The USB entry screen keeps the page heading as its visible title. A visually hidden h2 names the choices region and serves as a focus target. Inside a flow, a visible h2 identifies USB recovery or new installation and gives its preservation/setup context; the changing step title is an h3. Unsupported-browser guidance uses an h2 beneath the page title. Preserve these relationships when adding installer states.
The USB page has a Set up OpenAthan h1 and a visible Connect speaker h2 beside its cable instructions and single connection action. After discovery, a visible h2 identifies Connected speaker or New installation and gives its preservation/setup context; the changing step title is an h3. Unrecognized firmware opens installation confirmation with retry, cancel and troubleshooting. Recognized firmware with unreadable status cannot install. Unsupported-browser guidance uses an h2 beneath the page title. Preserve these relationships when adding installer states.

After the initial render, installer navigation focuses the new step heading. The initial page load does not move focus. Focusable step headings have `tabindex="-1"`; keyboard-visible heading focus uses a 3px slate outline with a 5px offset. Other keyboard-focusable elements use a 3px outline with a 4px offset. The skip link becomes visible on focus and leads to the main content. Keep native links, buttons, labels and heading semantics.

Expand Down
2 changes: 1 addition & 1 deletion PRODUCT.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ The public website explains the project, provides documentation and performs sup

- Public fresh installation is available for the selected approved stable reference release. Preserve artifact verification, publication approval and explicit erase confirmation.
- The website adopts stable latest after validation, with manual pin/disable policies for rollback. This never installs updates on existing speakers; owners use the authenticated device page or a preserving USB transition.
- Recovery is separate from new installation and preserves prayer settings and history.
- USB setup begins with one connection action, then checks the speaker. Existing-device controls and recovery preserve prayer settings and history. Unrecognized firmware requires explicit hardware/full-erasure confirmation for installation; recognized firmware with unreadable status cannot install.
- Keep hardware model numbers and connection diagrams in setup documentation rather than homepage focal areas.
- Preserve existing routes, anchors, protocol behavior and simulator isolation.
- Firmware and the device-local UI belong in the firmware repository. The public website consumes published release artifacts and does not compile firmware.
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ The website and USB installer for OpenAthan, a standalone speaker that automatic

Visit [openathan.com](https://openathan.com) for supported hardware, installation, setup, and recovery instructions.

- [Install OpenAthan](https://openathan.com/install/)
- [Connect your speaker](https://openathan.com/install/)
- [Getting started and supported hardware](https://openathan.com/docs/getting-started/)
- [Troubleshooting and recovery](https://openathan.com/docs/troubleshooting/)

## Installation and updates

The browser installer supports fresh installation on **AtomS3R C126 + Pyramid A167** and Wi-Fi/password recovery for existing devices. **Fresh installation erases saved data.** Existing owners should [update from the device page or follow the preserving USB transition](https://openathan.com/docs/getting-started/#firmware-updates).
Connect once through USB setup: the website checks the speaker and shows existing-device controls or installation confirmation. The browser installer supports fresh installation on **AtomS3R C126 + Pyramid A167** and Wi-Fi/password recovery for existing devices. **Fresh installation erases saved data.** Existing owners should [update from the device page or follow the preserving USB transition](https://openathan.com/docs/getting-started/#firmware-updates).

The installer uses the latest approved stable OpenAthan release. The website is scheduled to check for releases twice hourly and deploys only after artifact verification and website tests pass. See the [deployment guide](deployment/README.md) for timing limitations, release selection, and rollback.

Expand Down
10 changes: 6 additions & 4 deletions installer/README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# OpenAthan browser installer

The `/install/` wizard supports two explicit paths: **Install a new device** and **Fix Wi-Fi or password**. Supported reference hardware is M5Stack AtomS3R C126 + Pyramid A167, with an unlocked ESP32-S3 and 8 MiB flash. USB setup targets desktop Chrome and Edge with Web Serial in a secure context. The documentation at `/docs/` remains available in other browsers.
The `/install/` wizard has one **Connect speaker** entry with a **Choose USB device** action. Read-only discovery then opens existing-speaker controls or new-installation confirmation. Installation and credential recovery remain separate operations. Supported reference hardware is M5Stack AtomS3R C126 + Pyramid A167, with an unlocked ESP32-S3 and 8 MiB flash. USB setup targets desktop Chrome and Edge with Web Serial in a secure context. The documentation at `/docs/` remains available in other browsers.

The website adopts the firmware repository’s latest approved stable release after verification and website tests. The committed pin is a local-preview default and a manual rollback option, not a version label that needs changing for every release. Recovery remains a separate command-only path.
The website adopts the firmware repository’s latest approved stable release after verification and website tests. The committed pin is a local-preview default and a manual rollback option, not a version label that needs changing for every release. Credential recovery remains a command-only operation after discovery.

Review `/preview/installer/` on the development server to exercise the complete wizard without hardware. Its injected simulator transport cannot reach Web Serial or the flasher; its route and code are excluded from the static build. Both example addresses are clickable and open `/preview/device/` in a new tab, a clearly labelled local handoff preview. They never navigate to the example hostname or LAN IP. Actual device addresses open the reported local device URL. Synthetic firmware and recording fixtures are never served.

Expand All @@ -21,15 +21,15 @@ The implementation follows the firmware [provisioning handoff](https://github.co

Credentials are held only in temporary browser memory, removed from forms after submission and never logged, stored, added to URLs or sent to analytics. JavaScript strings cannot be guaranteed to be physically zeroed; transmitted byte buffers are cleared after writing. Avoid browser tracing with real credentials. The site opens only validated device-reported local links chosen by the user; it does not call the device's HTTP API across origins.

Recovery never imports or invokes the flashing adapter, erase operations or settings/history commands. Unrecognized firmware receives troubleshooting guidance. Recognized OpenAthan firmware on the installation path is directed to recovery, including when its status is unreadable. A failed recognition probe is not permission to erase: new installation requires a separate explicit hardware/full-erasure confirmation. Existing-device upgrades happen on the authenticated device page or through a preserving USB transition, not this browser installer. See the [owner guidance](https://openathan.com/docs/getting-started/#firmware-updates).
Recovery never imports or invokes the flashing adapter, erase operations or settings/history commands. Unrecognized firmware receives troubleshooting guidance. Recognized OpenAthan firmware opens the connected-speaker menu. If identity succeeds but status cannot be read, installation is blocked and the user receives reconnect/troubleshooting guidance. An unrecognized result goes directly to installation confirmation, with retry, cancel and troubleshooting available. It does not identify a blank device or authorize erasure: new installation requires a separate explicit hardware/full-erasure confirmation. Discovery disconnects cannot become installation candidates. Existing-device upgrades happen on the authenticated device page or through a preserving USB transition, not this browser installer. See the [owner guidance](https://openathan.com/docs/getting-started/#firmware-updates).

## Approved release discovery and import

The website consumes stable published releases only from `OpenAthan-Project/openathan`. It never builds firmware, accepts local binary uploads or fetches user-provided manifest URLs.

`catalog.json` retains schema 1 with an optional `automatic` boolean (omitted means `false`). With automatic mode enabled, publishing a qualified release as GitHub’s **latest** authorizes website adoption. Maintainers must finish media rights/content review, appropriate physical qualification and publication approval first. Generated `mediaReviewed` and `hardwareQualified` fields record that publication policy; software checks cannot establish those human approvals or new physical results.

The committed release remains a verified manual pin. Set `automatic` to `false` to hold or roll back to it through a reviewed PR. Set `release` to `null` to disable fresh installation before the USB picker; null takes precedence over automatic discovery and keeps recovery available. Automatic mode must remain off for a persistent manual rollback.
The committed release remains a verified manual pin. Set `automatic` to `false` to hold or roll back to it through a reviewed PR. Set `release` to `null` to disable fresh installation; null takes precedence over automatic release discovery. The USB picker remains available for existing-device controls and recovery, while unrecognized firmware receives an unavailable-installation screen without erase controls. Automatic mode must remain off for a persistent manual rollback.

`npm run release:import` discovers a selection once, verifies the published identity, exact asset names/URLs, source commit, bounded sizes, hardware/layout and hashes, then imports only its manifest and two binaries under ignored `public/releases/<tag>/`. Old generated imports are removed only after the candidate verifies; null removes all generated firmware imports without querying the firmware service. CI first runs `release:select`, then `release:import -- --snapshot` to retain its frozen selection.

Expand Down Expand Up @@ -93,6 +93,8 @@ The flashing transport releases its output lock even when a USB write rejects. I

## Validation boundaries and public-launch prerequisites

Development preview scenarios select new, existing, unrecognized or unreadable device state independently of the connection action. Simulator routes stay excluded from production.

Unit tests exercise Python-compatible framing, fragmentation, checksums, malformed data, timeouts, command serialization, uncertain writes and release rejection. Fake-programmer tests require confirmation, compatible hardware and successful flash readback before completion. Recovery tests assert that the flasher is never called. Browser tests cover complete simulated installation/recovery, denied/busy ports, disconnects, wrong Wi-Fi credentials, password validation, storage faults, uncertain acknowledgements, unsupported browsers, focus and narrow layouts.

These simulations establish software behavior only. They do not establish USB reliability, bootloader entry, physical writes, power-cycle behavior, audible playback or durable preservation. Release maintainers must retain source-bound physical qualification evidence for the applicable behaviors below. Reuse accepted evidence where applicable, state untested changes, and coordinate new hardware testing when required; automatic website adoption does not repeat or establish these results:
Expand Down
Loading
Loading