Even G2 dev workspace: docs snapshot (28 pages), README, toolchain notes

This commit is contained in:
drjones
2026-09-02 19:33:07 -07:00
commit a51178e83e
89 changed files with 4828 additions and 0 deletions

35
README.md Normal file
View File

@@ -0,0 +1,35 @@
# Even Realities G2 — App Lab
Dev workspace for Even G2 smart glasses apps. Naming theme: constellations/stars.
## Toolchain (all installed globally)
| Tool | Command | Version |
|---|---|---|
| Even Hub CLI | `evenhub` / `eh` | 0.1.13 |
| Simulator | `evenhub-simulator` | 0.8.0 |
| SDK (per-app dep) | `@evenrealities/even_hub_sdk` | 0.0.14 |
| Node | v26.8.1 | — |
## Layout
- `apps/` — one folder per app (web apps: Vite + TypeScript + SDK)
- `docs/` — full Even Realities developer docs snapshot
- `docs/pages/` — per-page markdown
- `docs/site-data.json` / `docs/links.txt` — doc index
## Quick loop
```bash
cd apps/<name>
npm run dev # Vite on :5173
evenhub-simulator http://localhost:5173/ --automation-port 9898
curl http://127.0.0.1:9898/api/ping # -> pong
curl -X POST http://127.0.0.1:9898/api/input -H 'Content-Type: application/json' -d '{"action":"click"}'
curl -s http://127.0.0.1:9898/api/screenshot/glasses -o frame.png
```
## Critical platform notes
- Simulator sends clicks as `sysEvent`, hardware as `textEvent` — handlers must accept BOTH.
- `min_sdk_version` in app.json must match the installed SDK version.
- HMR reloads break the bridge — restart simulator after code changes.
- `waitForEvenAppBridge()` before any SDK call.
- Network whitelist ≠ CORS bypass — remote API needs real CORS headers too.
- Full reference: docs/pages/

File diff suppressed because one or more lines are too long

26
docs/app-submission.html Normal file

File diff suppressed because one or more lines are too long

29
docs/architecture.html Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

26
docs/beta-testing.html Normal file

File diff suppressed because one or more lines are too long

26
docs/build.html Normal file

File diff suppressed because one or more lines are too long

26
docs/changelog.html Normal file

File diff suppressed because one or more lines are too long

26
docs/claude-code.html Normal file

File diff suppressed because one or more lines are too long

26
docs/cli.html Normal file

File diff suppressed because one or more lines are too long

26
docs/contextual-menu.html Normal file

File diff suppressed because one or more lines are too long

26
docs/device-apis.html Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

26
docs/faq.html Normal file

File diff suppressed because one or more lines are too long

26
docs/glossary.html Normal file

File diff suppressed because one or more lines are too long

26
docs/hardware.html Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

28
docs/links.txt Normal file
View File

@@ -0,0 +1,28 @@
/AI-tooling/claude-code
/build/background-lifecycle
/build/contextual-menu
/build/design-guidelines
/build/device-apis
/build/display
/build/networking
/build/page-lifecycle
/get-started/architecture
/get-started/overview
/get-started/quickstart/first-app
/get-started/quickstart/hardware
/get-started/quickstart/index.md
/get-started/quickstart/install-node
/get-started/quickstart/install-tools
/get-started/quickstart/sign-in
/get-started/quickstart/templates
/reference/changelog
/reference/cli
/reference/faq
/reference/glossary
/reference/versioning
/ship/app-submission
/ship/packaging
/test/beta-testing
/test/local-testing
/test/private-testing
/test/simulator

26
docs/local-testing.html Normal file

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,23 @@
Even Realities Developer Docs | Documentation
-
-
-
-
-
-
Skip to contentDocumentationSearch⌘CtrlKThemeMenuReturn to top Redirecting to Get Started…

23
docs/md/app-submission.md Normal file
View File

@@ -0,0 +1,23 @@
Even Realities Developer Docs | Documentation
-
-
-
-
-
-
Skip to contentDocumentationSearch⌘CtrlKThemeMenuReturn to top Redirecting to Get Started…

45
docs/md/architecture.md Normal file
View File

@@ -0,0 +1,45 @@
Architecture | Documentation
-
-
-
-
-
-
Skip to contentDocumentationSearch⌘CtrlKThemeMenuReturn to topLast updated: 2026-06-11Even Hub apps are web apps - HTML, CSS, and JavaScript - glued to the glasses by the Even Hub SDK. You build them on your laptop, package them up, and submit them through the dev portal for users to install.
## Connection model ​
```
┌──────────────────┐ HTTPS ┌────────────────────┐ Bluetooth ┌─────────────────────┐
│ Even Hub Cloud │ ◄──────────► │ Phone │ ◄────────────► │ Even G2 Glasses │
│ (distribution │ │ (Even Realities │ │ (display + input) │
│ & hosting) │ │ App + WebView) │ │ │
└──────────────────┘ └────────────────────┘ └─────────────────────┘
```
- The phone runs the Even Realities App (Flutter), which hosts your plugin in a WebView - Chromium on Android, WKWebView on iOS. Your app logic runs inside that WebView. The Even Realities App relays everything to and from the glasses over Bluetooth.
- The glasses render UI containers and emit input events - presses, scrolls, swipes. Apart from native scroll handling, no app logic runs on them.Network whitelist is not a CORS bypassThe app.json network whitelist is an Even-side permission check - it controls which domains your plugin is allowed to call from the WebView. It does not bypass CORS.In production, fetch() requires both:
- The remote domain listed in your app.json network whitelist, and
- Correct CORS headers (Access-Control-Allow-Origin, etc.) returned by that remote API.APIs that work on localhost but fail inside the WebView are almost always CORS misconfigurations on the remote side, not Even bugs. See Networking for the full request flow and debugging tips.
## Testing your app ​
Three ways to run your app during development:
- QR sideload - the CLI prints a QR pointing at your local dev server; scan it from the Even Realities App and your app loads on the glasses with hot reload.
- Private build - evenhub pack produces an .ehpk; upload it through the dev portal to install on your own devices.
- Simulator - preview layouts and exercise logic entirely on your laptop, no hardware needed.
## PWA as an alternative ​
If you'd rather stay outside the Even Hub distribution flow, build a Progressive Web App and point users at your hosted URL. You keep full control over distribution and hosting; you also skip the dev portal entirely - no packaging, no review.
## The SDK bridge ​
The SDK injects a JavaScript bridge (EvenAppBridge) into the WebView. Your frontend calls into it to drive the display and receive input.Web → Glasses: your JS calls bridge.callEvenApp(method, params) → WebView bridge → Even Realities App → Bluetooth → glasses.Glasses → Web: input events travel Bluetooth → Even Realities App → window._listenEvenAppMessage(...) → your callback.For the project layout and a working scaffold, see Your First App § Project structure.

View File

@@ -0,0 +1,23 @@
Even Realities Developer Docs | Documentation
-
-
-
-
-
-
Skip to contentDocumentationSearch⌘CtrlKThemeMenuReturn to top Redirecting to Get Started…

23
docs/md/beta-testing.md Normal file
View File

@@ -0,0 +1,23 @@
Even Realities Developer Docs | Documentation
-
-
-
-
-
-
Skip to contentDocumentationSearch⌘CtrlKThemeMenuReturn to top Redirecting to Get Started…

23
docs/md/build.md Normal file
View File

@@ -0,0 +1,23 @@
Even Realities Developer Docs | Documentation
-
-
-
-
-
-
Skip to contentDocumentationSearch⌘CtrlKThemeMenuReturn to top Redirecting to Get Started…

23
docs/md/changelog.md Normal file
View File

@@ -0,0 +1,23 @@
Even Realities Developer Docs | Documentation
-
-
-
-
-
-
Skip to contentDocumentationSearch⌘CtrlKThemeMenuReturn to top Redirecting to Get Started…

23
docs/md/claude-code.md Normal file
View File

@@ -0,0 +1,23 @@
Even Realities Developer Docs | Documentation
-
-
-
-
-
-
Skip to contentDocumentationSearch⌘CtrlKThemeMenuReturn to top Redirecting to Get Started…

23
docs/md/cli.md Normal file
View File

@@ -0,0 +1,23 @@
Even Realities Developer Docs | Documentation
-
-
-
-
-
-
Skip to contentDocumentationSearch⌘CtrlKThemeMenuReturn to top Redirecting to Get Started…

View File

@@ -0,0 +1,23 @@
Even Realities Developer Docs | Documentation
-
-
-
-
-
-
Skip to contentDocumentationSearch⌘CtrlKThemeMenuReturn to top Redirecting to Get Started…

23
docs/md/device-apis.md Normal file
View File

@@ -0,0 +1,23 @@
Even Realities Developer Docs | Documentation
-
-
-
-
-
-
Skip to contentDocumentationSearch⌘CtrlKThemeMenuReturn to top Redirecting to Get Started…

View File

@@ -0,0 +1,23 @@
Even Realities Developer Docs | Documentation
-
-
-
-
-
-
Skip to contentDocumentationSearch⌘CtrlKThemeMenuReturn to top Redirecting to Get Started…

23
docs/md/faq.md Normal file
View File

@@ -0,0 +1,23 @@
Even Realities Developer Docs | Documentation
-
-
-
-
-
-
Skip to contentDocumentationSearch⌘CtrlKThemeMenuReturn to top Redirecting to Get Started…

23
docs/md/glossary.md Normal file
View File

@@ -0,0 +1,23 @@
Even Realities Developer Docs | Documentation
-
-
-
-
-
-
Skip to contentDocumentationSearch⌘CtrlKThemeMenuReturn to top Redirecting to Get Started…

23
docs/md/hardware.md Normal file
View File

@@ -0,0 +1,23 @@
Even Realities Developer Docs | Documentation
-
-
-
-
-
-
Skip to contentDocumentationSearch⌘CtrlKThemeMenuReturn to top Redirecting to Get Started…

View File

@@ -0,0 +1,23 @@
Even Realities Developer Docs | Documentation
-
-
-
-
-
-
Skip to contentDocumentationSearch⌘CtrlKThemeMenuReturn to top Redirecting to Get Started…

View File

@@ -0,0 +1,23 @@
Even Realities Developer Docs | Documentation
-
-
-
-
-
-
Skip to contentDocumentationSearch⌘CtrlKThemeMenuReturn to top Redirecting to Get Started…

23
docs/md/local-testing.md Normal file
View File

@@ -0,0 +1,23 @@
Even Realities Developer Docs | Documentation
-
-
-
-
-
-
Skip to contentDocumentationSearch⌘CtrlKThemeMenuReturn to top Redirecting to Get Started…

23
docs/md/networking.md Normal file
View File

@@ -0,0 +1,23 @@
Even Realities Developer Docs | Documentation
-
-
-
-
-
-
Skip to contentDocumentationSearch⌘CtrlKThemeMenuReturn to top Redirecting to Get Started…

38
docs/md/overview.md Normal file
View File

@@ -0,0 +1,38 @@
Overview | Documentation
-
-
-
-
-
-
Skip to contentDocumentationSearch⌘CtrlKThemeMenuReturn to topLast updated: 2026-08-07The Even G2 is a pair of smart glasses with a dual micro-LED display - one per lens - a four-mic array, touchpads on both temples, and an optional R1 ring for extra input. It pairs with your phone over Bluetooth LE 5.2. Everything you build runs there; the glasses are the screen.
## Key hardware specs ​
SpecValueDisplay576 x 288 px per eyeColor depthMonochrome green, 16 levelsConnectivityBluetooth Low Energy 5.2Audio input4-mic array, single stream, 16 kHz PCMEven G2 touchpadsPress, double press, swipe up, swipe down, tap then long press and releaseR1 touchpadsSame gestures as the Even G2 (optional accessory)Camera / SpeakerNoneNo camera, no speaker on the glasses - the omission is the point. Your code lives on the phone; the glasses render and capture input. (Plugins can still reach the phone camera and album through the SDK when the user grants permission.)
## What you can build ​
Today, one surface is live: plugins. They are web apps, written in HTML, CSS, and JavaScript or TypeScript, glued to the glasses by the Even Hub SDK. Bring any stack - Vite, React, plain JS - the SDK takes it from there.Three more surfaces are coming: dashboard widgets, dashboard layouts, and AI skills.
## What development looks like ​
```
1. Write code Standard web app (Vite + SDK)
2. Preview locally evenhub-simulator http://localhost:5173
3. Test on device QR sideload, or private build in the dev portal
4. Package evenhub pack app.json dist -o myapp.ehpk
5. Submit Upload the .ehpk through the dev portal
```
## Quick reference ​
ResourceLinkSDKnpm: @evenrealities/even_hub_sdkSimulatornpm: @evenrealities/evenhub-simulatorCLInpm: @evenrealities/evenhub-cliDesign GuidelinesFigma: Software Design GuidelinesCommunity notesGitHub: even-g2-notesCommunity toolkitGitHub: even-toolkitCommunity IDEGitHub: ER Studio - editor, simulator mirror, and one-click pack in one window (macOS Apple Silicon)Discorddiscord.gg/Y4jHMCU4sv

View File

@@ -0,0 +1,23 @@
Even Realities Developer Docs | Documentation
-
-
-
-
-
-
Skip to contentDocumentationSearch⌘CtrlKThemeMenuReturn to top Redirecting to Get Started…

23
docs/md/page-lifecycle.md Normal file
View File

@@ -0,0 +1,23 @@
Even Realities Developer Docs | Documentation
-
-
-
-
-
-
Skip to contentDocumentationSearch⌘CtrlKThemeMenuReturn to top Redirecting to Get Started…

View File

@@ -0,0 +1,23 @@
Even Realities Developer Docs | Documentation
-
-
-
-
-
-
Skip to contentDocumentationSearch⌘CtrlKThemeMenuReturn to top Redirecting to Get Started…

51
docs/md/quickstart.md Normal file
View File

@@ -0,0 +1,51 @@
Quickstart | Documentation
-
-
-
-
-
-
Skip to contentDocumentationSearch⌘CtrlKThemeMenuReturn to topLast updated: 2026-06-04The one-page path from zero to a running app. Read it top to bottom the first time; come back to it as a checklist after that.By the end you'll have an Even Realities account, a paired and updated pair of Even G2s, Developer Mode on, Node installed, and Hello from G2! rendering on the glasses via QR sideload.
## 1. Install the Even Realities App and register ​
On your phone, type this URL into your browser to download the app:
```
evenapp.evenrealities.com
```
Install the app, then register your Even Realities account inside it. The account you create here is the same one you'll use everywhere else (web hub, Developer Mode).
## 2. Log in to the web hub ​
Open hub.evenrealities.com/login and sign in with the same account.→ Detail: Sign in
## 3. Wake the glasses out of shipping mode ​
New Even G2s arrive in shipping mode so the battery doesn't drain in transit. To wake them, drop both arms into the charging case with the case plugged in. The case light comes on; the glasses are ready to pair.→ Detail: Set up your Even G2 & R1
## 4. Pair over Bluetooth and update firmware ​
Open the Even Realities App and walk through the in-app pairing tutorial. The app then offers a firmware update - take it before doing anything else. Most "it won't connect" reports trace back to stale firmware.→ Detail: Set up your Even G2 & R1
## 5. Enable Developer Mode ​
Developer Mode is what unlocks QR sideload and local testing.
- Sign in to hub.evenrealities.com/login with the same account as the phone app.
- Force-quit and reopen the Even Realities App.
- The Even Hub tab now shows a developer section in the top right.→ Detail: Enable Developer Mode
## 6. Install Node.js & npm ​
You need Node 20 LTS or 22+. Coming from outside the JS world? The detail page walks through nvm, fnm, Homebrew, winget, and the PATH gotchas you'll otherwise hit.→ Detail: Install Node.js & npm
## 7. Install Even Hub tooling ​
bash
```
npm install -g @evenrealities/evenhub-cli @evenrealities/evenhub-simulator
```
→ Detail: Installation
## 8. Run "Hello from G2!" ​
Scaffold the minimal app, start the dev server, and sideload it via QR. If the phone scans the QR but the app never loads, it's almost always a firewall or Wi-Fi AP-isolation issue.Your First App shows both routes: the hand-built Vite route installs the SDK after creating the project directory, while the template route already includes the SDK and only needs npm install for the template dependencies.In a hurry? Templates lists ready-to-run starters from the official evenhub-templates repo.→ Detail: Your First App · Network & Firewall Setup
## If you get stuck ​
SymptomMost likely causeWhere to lookNo way to find the Scan QR entry in the appDeveloper Mode not enabled, or app not restarted after web loginEnable Developer ModeQR scans but app never loadsFirewall / AP isolation blocks phone-to-laptopNetwork & Firewall Setupnode / npm "command not found"PATH not set after installInstall Node.js & npmGlasses won't connect at allStill in shipment mode, or stale firmwareSet up your Even G2 & R1

23
docs/md/simulator.md Normal file
View File

@@ -0,0 +1,23 @@
Even Realities Developer Docs | Documentation
-
-
-
-
-
-
Skip to contentDocumentationSearch⌘CtrlKThemeMenuReturn to top Redirecting to Get Started…

23
docs/md/templates.md Normal file
View File

@@ -0,0 +1,23 @@
Even Realities Developer Docs | Documentation
-
-
-
-
-
-
Skip to contentDocumentationSearch⌘CtrlKThemeMenuReturn to top Redirecting to Get Started…

View File

@@ -0,0 +1,23 @@
Even Realities Developer Docs | Documentation
-
-
-
-
-
-
Skip to contentDocumentationSearch⌘CtrlKThemeMenuReturn to top Redirecting to Get Started…

View File

@@ -0,0 +1,23 @@
Even Realities Developer Docs | Documentation
-
-
-
-
-
-
Skip to contentDocumentationSearch⌘CtrlKThemeMenuReturn to top Redirecting to Get Started…

23
docs/md/your-first-app.md Normal file
View File

@@ -0,0 +1,23 @@
Even Realities Developer Docs | Documentation
-
-
-
-
-
-
Skip to contentDocumentationSearch⌘CtrlKThemeMenuReturn to top Redirecting to Get Started…

26
docs/networking.html Normal file

File diff suppressed because one or more lines are too long

29
docs/overview.html Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

26
docs/page-lifecycle.html Normal file

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,73 @@
Claude Code | Documentation
-
-
-
-
-
-
Skip to contentDocumentationSearch⌘CtrlK Main Navigation PortalThemeMenuReturn to top Sidebar Navigation
## Get Started
Overview
### Quickstart
Sign inHardwareInstall Node.js & npmInstall Even Hub toolingYour First AppTemplatesArchitecture
## Build
Page LifecycleDisplay & UI SystemUI/UX Design GuidelinesDevice APIsContextual MenuNetworkingBackground & Lifecycle
## Test
SimulatorLocal TestingPrivate TestingBeta Testing
## Ship
Packaging & DeploymentApp Submission & QA Guidelines
## Reference
GlossaryCLIVersioning PolicyChangelogFAQ
## AI Tooling
Claude CodeOn this pageLast updated: 2026-06-11everything-evenhub is an open-source Claude Code plugin that teaches Claude how to build for the Even G2 - the SDK, the display constraints, the simulator, packaging, all of it. Install once; describe what you want.Already using Claude Code? Jump to Install ↓
## What is this? ​
Claude Code is Anthropic's command-line AI coding tool. Instead of pasting prompts into a chat window, Claude runs in your terminal, reads your files, writes code, and runs commands. Free to try, works with the same Claude account you may already use. More at claude.com/claude-code.Out of the box, Claude Code knows nothing specific about the Even G2 platform. The plugin adds 12 skills covering the full development lifecycle: |
| | Tier | Skills | Purpose
| | Tier 1 - One-click | quickstart, template, build-and-deploy | Scaffold a new app; package and publish
| | Tier 2 - Core dev | glasses-ui, handle-input, device-features, test-with-simulator, simulator-automation, font-measurement | Day-to-day coding tasks
| | Tier 3 - Reference | sdk-reference, cli-reference, design-guidelines | Look-up / deep-dive
## Install ​
- Install Claude Code - see claude.com/claude-code
- In your terminal, add the marketplace:bash
```
/plugin marketplace add even-realities/everything-evenhub
```
- Install the plugin:bash
```
/plugin install everything-evenhub
```
That's it - Claude picks up the skills on its own whenever your task mentions the Even G2.
## Try it ​
In Claude Code:Build me a hello-world app for the Even G2 glasses that shows "Hello, Even!" on the display.Claude recognizes the request, invokes the quickstart skill, scaffolds the project, and walks you through running it in the simulator.
## How it works ​
You don't have to teach Claude about the skills or memorize their names. Each skill is a markdown file with a short description; Claude reads those descriptions and picks the right one for what you asked. Behind the scenes, the same SDK docs and design guidelines you'll find elsewhere on this site - routed through Claude's context.
## Skill catalog ​
Lookup table for every skill - what it does, sample prompts, and how the skills chain. Definitions live in the open-source even-realities/everything-evenhub repo (skills/). The accordions below mirror that layout - click any skill to expand.
### Tier 1 - one-click ​
quickstart - Scaffold a blank Even G2 app from scratchPurpose: Scaffold a blank Even G2 app from scratch (Vite + TypeScript + @evenrealities/even_hub_sdk).Trigger example: /quickstart my-weather-app or "Build me a new Even G2 app called stopwatch."What it does: Creates a fresh Vite project and wires the SDK - not a curated example app.Related: → template (when you want a starter instead of blank), build-and-deploy, glasses-uitemplate - Scaffold from a curated starter via degitPurpose: Scaffold from a curated starter in even-realities/evenhub-templates via degit - wiring included.Trigger example: /template my-reader --text-heavy, /template --asr my-transcription-app, /template --image photo-frame, /template --minimal hello-glassesWhat it does: Pulls minimal, asr, image, or text-heavy template; normalizes flags (--withasr, --reader, etc.); renames package.json / app.json; runs npm install.Related: → quickstart (blank slate), build-and-deploy, font-measurement (for text-heavy pagination), Templates (manual flow without Claude)build-and-deploy - Package and publish to Even HubPurpose: Package and publish your app to Even Hub.Trigger example: /build-and-deploy or "Package my app and upload it to the dev portal."What it does: Uses the Even Hub CLI to build the .ehpk and complete the deployment flow.Related: → quickstart, template, cli-reference
### Tier 2 - core development ​
glasses-ui - Build display UI (containers, text, images, lists)Purpose: Build glasses display UI - containers, text, images, lists - for the Even G2 screen.Trigger example: /glasses-ui "show a 3-item menu with a title bar"What it does: Layout and components tuned to Even G2 display constraints.Related: → handle-input, font-measurement, design-guidelineshandle-input - Touchpad gestures, ring input, contextual menuPurpose: Handle touchpad gestures, ring input, tap then long press, and contextual-menu selections.Trigger example: /handle-input "single press cycles screens, double press exits, tap then long press charges a throw"What it does: Wires listeners and handlers for supported inputs.Related: → glasses-uidevice-features - Audio, IMU, device info, local storagePurpose: Use hardware-facing capabilities - audio capture, IMU, device info, local storage, etc.Trigger example: /device-features "toggle microphone recording on click"What it does: Interfaces with the SDK for sensors, audio, and related APIs.Related: → sdk-referencetest-with-simulator - Run and debug in the simulatorPurpose: Run and debug the app in the Even Hub Simulator.Trigger example: /test-with-simulator "debug my app with glow effect"What it does: Launches the local simulator workflow for the current project.Related: → simulator-automationsimulator-automation - Drive the simulator over HTTPPurpose: Drive the simulator over its HTTP API - screenshots, input injection, console logs.Trigger example: /simulator-automation "take a screenshot and verify text is displayed"What it does: Automates simulator actions for repeatable checks.Related: → test-with-simulatorfont-measurement - Pixel-accurate text and list measurementPurpose: Pixel-accurate text and list measurement aligned with LVGL firmware rendering.Trigger example: /font-measurement "size a text container for a long paragraph with 8px padding"What it does: Measurement utilities for layout that matches on-device text metrics.Related: → glasses-ui
### Tier 3 - reference ​
sdk-reference - Look up SDK APIs, types, patternsPurpose: Look up Even Hub SDK APIs, types, and patterns.Trigger example: /sdk-reference createStartUpPageContainerWhat it does: Surfaces SDK documentation for the symbol or topic you name.Related: → device-featurescli-reference - Look up Even Hub CLI commands and flagsPurpose: Look up Even Hub CLI commands and flags.Trigger example: /cli-reference evenhub qrWhat it does: Usage and examples for CLI tooling (evenhub, packaging, QR, etc.).Related: → build-and-deploydesign-guidelines - Display constraints and UX best practicesPurpose: Even G2 display design constraints and UX best practices.Trigger example: /design-guidelines settings screen with 5 optionsWhat it does: Design specs and guidance for readable, consistent glasses UI.Related: → glasses-ui
### Harness testing ​
The plugin repo ships a harness runner that regression-tests skills with an AI agent. Example:bash
```
/harness quickstart
```
See harness/README.md for how to add tests for new skills.PagerPrevious pageAI Tooling

View File

@@ -0,0 +1,51 @@
Background & Lifecycle | Documentation
-
-
-
-
-
-
Skip to contentDocumentationSearch⌘CtrlK Main Navigation PortalThemeMenuReturn to top Sidebar Navigation
## Get Started
Overview
### Quickstart
Sign inHardwareInstall Node.js & npmInstall Even Hub toolingYour First AppTemplatesArchitecture
## Build
Page LifecycleDisplay & UI SystemUI/UX Design GuidelinesDevice APIsContextual MenuNetworkingBackground & Lifecycle
## Test
SimulatorLocal TestingPrivate TestingBeta Testing
## Ship
Packaging & DeploymentApp Submission & QA Guidelines
## Reference
GlossaryCLIVersioning PolicyChangelogFAQ
## AI Tooling
Claude CodeOn this pageLast updated: 2026-06-22Your app is a WebView hosted by the Even Realities App. When the phone backgrounds that app or the screen locks, iOS and Android behave very differently. Read this before you sit down for Beta Testing.
## What the OS does to a backgrounded WebView ​
|
| | Platform | Engine | When the Even app backgrounds
| | iOS | WKWebView | Clean. The WebView keeps running. In-memory JS state survives. No special handling needed.
| | Android | Chromium WebView | May be suspended under memory pressure. Below the OS threshold the app keeps running; above it, the process is reclaimed and in-memory state is gone.The practical takeaway is Android-specific: if a piece of state matters, persist it eagerly so you can rebuild on relaunch.
## What survives, what doesn't ​
|
| | Resource | On background / lock | Notes
| | localStorage | Always survives (persisted to disk) | Safe for state you must not lose.
| | In-memory JS state | iOS: survives. Android: may be lost if the WebView is suspended. | Re-derive from localStorage on relaunch.
| | Open WebSocket | iOS: typically holds. Android: typically dropped if suspended. | Handle the close event; reconnect on relaunch.
| | audioControl(true, ...) capture | Stops if the WebView is suspended | Re-enable on foreground (with the same AudioInputSource you used before); don't assume the stream is still live.
| | startAppLocationUpdates(...) stream | Stops if the WebView is suspended | Re-arm on foreground; cached fixes from localStorage are fine to render while you wait.WARNINGTreat Android suspend as "the app starts cold." Your code should survive both cases - persist anything important to localStorage, and rebuild in-memory state on relaunch.
## Why this matters for Beta Testing ​
The Beta Testing lock-screen check - install as beta, lock the phone for 5 minutes, relaunch - exists to surface exactly these Android suspension bugs. If your app loses state, leaks a dead socket, or never re-enables capture on relaunch, it fails the check. Validate against this page first.→ Testing Modes · Page Lifecycle · App Submission & QA GuidelinesPagerPrevious pageNetworkingNext pageTest

View File

@@ -0,0 +1,149 @@
Contextual Menu | Documentation
-
-
-
-
-
-
Skip to contentDocumentationSearch⌘CtrlK Main Navigation PortalThemeMenuReturn to top Sidebar Navigation
## Get Started
Overview
### Quickstart
Sign inHardwareInstall Node.js & npmInstall Even Hub toolingYour First AppTemplatesArchitecture
## Build
Page LifecycleDisplay & UI SystemUI/UX Design GuidelinesDevice APIsContextual MenuNetworkingBackground & Lifecycle
## Test
SimulatorLocal TestingPrivate TestingBeta Testing
## Ship
Packaging & DeploymentApp Submission & QA Guidelines
## Reference
GlossaryCLIVersioning PolicyChangelogFAQ
## AI Tooling
Claude CodeOn this pageLast updated: 2026-08-25An app can add its own items to the glasses contextual menu - the overlay the OS raises on tap then long press (SDK 0.0.14+, Even App 2.2.9+). Each item you declare is an action: the user selects it, your app gets one event, and the menu closes.The menu is declared with the page, not fetched on demand. You attach a menuObject to createStartUpPageContainer or rebuildPageContainer; the OS holds it and renders it without waking your WebView.
## Slots ​
The glasses OS owns the menu frame. Your items land between permanent system slots: |
| | Slot | Owner | Notes
| | Display off (top) | System | Always present, not reachable from the SDK
| | Brightness | System | Always present. Handled end to end by the OS - your app is never notified. Global display brightness, unrelated to the per-container textColor levels
| | Your action items | Your app | Declared via menuObject, up to 10
| | Close [app name] (bottom) | System | Always present, not reachable from the SDK. Renders your app's name, so the row reads Close Timer rather than a bare CloseDeclare nothing and the user still gets the system items - the same default every unadapted app shows. Your items exist only for the life of the page that declared them.Don't count screen rows. The system set can grow between firmware releases and none of it is visible to the SDK, so treat the menu as your items wrapped in system items you don't control. You hand over a list; you never address a slot.
## Declaring a menu ​
typescript
```
import { waitForEvenAppBridge } from '@evenrealities/even_hub_sdk'
const bridge = await waitForEvenAppBridge()
await bridge.createStartUpPageContainer({
containerTotalNum: 1,
textObject: [{
xPosition: 0,
yPosition: 0,
width: 576,
height: 288,
containerID: 1,
containerName: 'main',
content: 'Timer running',
isEventCapture: 1,
}],
menuObject: {
menuItems: [
{ itemName: 'Restart', itemID: 1 },
{ itemName: 'Recenter', itemID: 2 },
],
},
})
```
### menuItems[] fields ​
|
| | Field | Type | Required | Notes
| | itemName | string | Yes | Label the OS renders. Max 32 UTF-8 bytes - not 32 characters
| | itemID | number | Yes | Non-zero uint32, unique across the menu. Comes back on the click eventitemID cannot be 0. Zero is reserved by the protocol, so start your IDs at 1.There is no ordering field. Items render in payload order - MenuItemProperty carries itemName and itemID and nothing else, and its toJson() drops any other property before the payload leaves the SDK. Reorder the array to reorder the menu.
## Handling a selection ​
The selected item arrives on onEvenHubEvent as menuItemClickEvent, carrying only the itemID you assigned:typescript
```
const handlers: Record<number, () => void> = {
1: () => restartTimer(),
2: () => recenterView(),
}
const unsubscribe = bridge.onEvenHubEvent(event => {
const itemID = event.menuItemClickEvent?.itemID
if (itemID === undefined) return
handlers[itemID]?.()
})
// unsubscribe()
```
Menu clicks do not route through isEventCapture. menuItemClickEvent is its own top-level field on the event, independent of the list/text container routing, so it arrives whichever container is capturing. Handle it on the same onEvenHubEvent subscription you already use for list, text, and system events.
## The menu is a foreground overlay ​
The OS draws the menu on top of your page, and it tells your app about it through the ordinary foreground events. Opening the menu delivers FOREGROUND_ENTER_EVENT (4); dismissing it delivers FOREGROUND_EXIT_EVENT (5). Selecting an item is the whole sequence:
```
FOREGROUND_ENTER_EVENT -> menuItemClickEvent -> FOREGROUND_EXIT_EVENT
```
FOREGROUND_EXIT_EVENT here means the overlay went away. It does not mean your app was torn down - your page stays mounted and owns the screen underneath the whole time.That distinction decides how you write the handler. If foreground exit is where you stop timers, drop subscriptions, or clear in-progress state, that work now runs every time the user opens the menu. Make the handler idempotent, or separate "the overlay closed" from "the user left" before doing anything destructive.
## Fire-and-forget only ​
Every item is an action. Selecting one sends a single event and closes the menu - the glasses do not hold or re-render item state, and there is no acknowledgement path back from your app to the menu.So an item that reads Status: high will still read Status: high the next time the user opens the menu, even if your handler changed the value. If the label has to track state, re-declare the menu:typescript
```
async function setQuality(next: 'high' | 'low') {
quality = next
await bridge.rebuildPageContainer({
containerTotalNum: 1,
textObject: [/* ... */],
menuObject: {
menuItems: [
{ itemName: `Quality: ${quality}`, itemID: 1 },
],
},
})
}
```
Design the labels as verbs (Restart, Skip, Mute) rather than as state readouts, and this stops mattering.
## Updating and clearing ​
menuObject is replaced wholesale, never merged: |
| | Call | Effect on the menu
| | rebuildPageContainer with menuObject | Replaces the whole menu with what you sent
| | rebuildPageContainer without menuObject | Clears your items; the system items remainOmitting menuObject on a rebuild is significant, not neutral. A rebuild that drops it because the layout changed will also drop the menu - carry it forward explicitly on every rebuild that should keep it.typescript
```
// Clears the custom menu, restores default registration
await bridge.rebuildPageContainer({
containerTotalNum: 1,
textObject: [/* ... */],
})
```
## Validation ​
The SDK checks the menu before it reaches the glasses. On a violation it logs an EvenHubPageContainerValidationErrorCode and the call fails locally - createStartUpPageContainer returns StartUpPageCreateResult.invalid, rebuildPageContainer returns false. Nothing partial reaches the firmware. |
| | Code | Cause
| | TOO_MANY_MENU_ITEMS | More than 10 items in menuItems
| | INVALID_MENU_ITEM_ID | itemID is 0, negative, non-integral, or outside uint32
| | DUPLICATE_MENU_ITEM_ID | The same itemID used twice
| | INVALID_MENU_ITEM_NAME | itemName exceeds 32 UTF-8 bytesValidation runs client-side, so a bad menu fails in development rather than shipping as a silently missing menu.
## Version gate ​
The contextual menu needs SDK 0.0.14 and Even App 2.2.9. On an older app the declaration is a silent no-op - the page still renders, the user still gets the system items, your items just never appear. Building against SDK 0.0.14 makes the CLI stamp the 2.2.9 floor at pack time, so those users are blocked at open instead. See Auto-deriving min_app_version.
## Testing it ​
Simulator 0.9.0+ draws the menu, so hardware is not needed to see whether yours is right. In the window, tap then long press raises it; over the automation API, POST /api/input with {"action":"context_menu"} toggles it, up / down move focus, and click fires your menuItemClickEvent.The simulator honours the rebuild rules above - carry menuObject forward and the menu comes back identical, omit it and your items are gone on the next open. One wrinkle it does not share with the contract: a rebuild underneath an already open menu does not dismiss the overlay, so what you see may outlive the declaration behind it. What the simulator can't confirm is which system slots the OS puts alongside your items on a given firmware build.
## Design notes ​
- 32 UTF-8 bytes, not characters. ASCII gets 32; CJK is 3 bytes per glyph, so a Chinese label is capped near 10.
- Keep labels short. The OS renders one line per slot and doesn't wrap. Under ~16 ASCII characters reads cleanly.
- 10 is the ceiling, not the target. A menu is a shortcut list. Past five or six items, users scroll further than they would have tapped.
- Don't duplicate the page. The menu is for what the current screen can't reach, not a second copy of its controls.
- Closing lives in the system slot. Don't add your own exit item - the root-page double-tap contract in App Submission is unchanged and still required.PagerPrevious pageDevice APIsNext pageNetworking

View File

@@ -0,0 +1,78 @@
UI/UX Design Guidelines | Documentation
-
-
-
-
-
-
Skip to contentDocumentationSearch⌘CtrlK Main Navigation PortalThemeMenuReturn to top Sidebar Navigation
## Get Started
Overview
### Quickstart
Sign inHardwareInstall Node.js & npmInstall Even Hub toolingYour First AppTemplatesArchitecture
## Build
Page LifecycleDisplay & UI SystemUI/UX Design GuidelinesDevice APIsContextual MenuNetworkingBackground & Lifecycle
## Test
SimulatorLocal TestingPrivate TestingBeta Testing
## Ship
Packaging & DeploymentApp Submission & QA Guidelines
## Reference
GlossaryCLIVersioning PolicyChangelogFAQ
## AI Tooling
Claude CodeOn this pageLast updated: 2026-07-10Even Realities publishes the canonical software design guidelines - layout, components, interaction patterns, and visual standards - for both the glasses display and the companion app.Open the Design Guidelines in Figma →
## Display constraints ​
Designing for the Even G2 display:
- 576 x 288 px canvas. The whole canvas is renderable. Coordinate origin is top-left; X increases rightward, Y downward.
- 4-bit greyscale. Design in shades of grey; the hardware renders them as shades of green.
- No background fill. Borders and text or image content are the only structure available.
- Max 4 image containers, 8 other containers. Plan the layout inside that ceiling.
- One event-capturing container. Design around a single active input target.
## Designing icons ​
Icons on the Even G2 are pixel art. You don't have to draw them pixel by pixel — design at full size in whatever tool you like, then bring the artwork down to the pixel grid deliberately.
### The workflow ​
- Draw big, in any tool. Figma, Illustrator, Inkscape, an AI image generator — anything that exports SVG or a high-resolution PNG.
- Fill the canvas border to border. No padding, no margins. At 24 x 24 there are no pixels to spare; empty space in the source becomes wasted pixels on the glasses.
- Downscale to native resolution, then clean up by hand. Scale to the actual render size (24 x 24 is the norm), threshold to pure on/off pixels — no anti-aliasing — and nudge strokes onto the grid. The last 10% is always manual.
### What survives 24 x 24 — and what doesn't ​
|
| | Do | Don't
| | One solid color on a plain background | Gradients, shadows, textures
| | Flat filled shapes | Hairline strokes, outline-style icons
| | A single subject, edge to edge | Busy scenes, multiple elements, decorative borders
| | Strokes at least 2 px wide at final size | Thinner lines — they vanish or clump when downscaled
| | A silhouette recognizable from shape alone | Detail that only reads large: faces, text, fine patternsIf you use an AI generator for source art, prompt for flat solid-black icon, white background, thick strokes, no gradients, no shading, centered, fills the frame — then downscale and clean up as above.
### The store icon ​
The icon on your store listing is stricter than in-app artwork. It is drawn in the Developer Portal's 24 x 24 pixel editor and validated on upload:
- 1-bit monochrome. Every pixel is fully on or fully off. No greys, no anti-aliasing.
- Built from 2 x 2 blocks. Every lit pixel must be part of a 2 x 2 block of lit pixels. Blocks can sit at any position and chain into bars, corners, and irregular shapes, but the thinnest possible stroke is 2 px — single pixels and 1-px lines don't validate. Plan on an effective 12 x 12 detail budget.Reviewers also reject illegible icons outright — see Store listing & visual assets.
## Common UI patterns ​
|
| | Pattern | How
| | Fake buttons | Prefix text with > as a cursor indicator
| | Selection highlight | Toggle borderWidth on individual text containers
| | Multi-row layout | Stack multiple text containers vertically (e.g., 3 containers at 96px height)
| | Progress bars | Use Unicode block characters: ━ and ─
| | Page flipping | Pre-paginate text at ~400–500 character boundaries, rebuild on scroll events
## Useful characters for building UIs ​
The glasses render a single LVGL font with no monospaced variant and no sizing controls (see Font and Unicode support). The characters below render reliably and stand in for common UI affordances: |
| | Use case | Characters
| | Progress bars | ━ ─ █▇▆▅▄▃▂▁
| | Navigation | ▲△▶▷▼▽◀◁
| | Selection | ●○ ■□ ★☆
| | Borders | ╭╮╯╰ │─ box-drawing set
| | Card suits | ♠♣♥♦Full glyph tables live in the community Even G2 notes.PagerPrevious pageDisplay & UI SystemNext pageDevice APIs

View File

@@ -0,0 +1,308 @@
Device APIs | Documentation
-
-
-
-
-
-
Skip to contentDocumentationSearch⌘CtrlK Main Navigation PortalThemeMenuReturn to top Sidebar Navigation
## Get Started
Overview
### Quickstart
Sign inHardwareInstall Node.js & npmInstall Even Hub toolingYour First AppTemplatesArchitecture
## Build
Page LifecycleDisplay & UI SystemUI/UX Design GuidelinesDevice APIsContextual MenuNetworkingBackground & Lifecycle
## Test
SimulatorLocal TestingPrivate TestingBeta Testing
## Ship
Packaging & DeploymentApp Submission & QA Guidelines
## Reference
GlossaryCLIVersioning PolicyChangelogFAQ
## AI Tooling
Claude CodeOn this pageLast updated: 2026-08-25The bridge exposes inputs (touchpads, ring, IMU), microphone capture, location, photo picker / camera capture, device and user info, and local storage.
## Inputs ​
The Even G2 touchpads, the optional R1 ring, and the IMU each provide a distinct input stream: |
| | Source | Gestures / Data | Notes
| | Even G2 touchpads (temple) | Press, double press, swipe up, swipe down, tap then long press and release | Primary input on the glasses frame
| | Even R1 touchpads (ring) | Press, double press, swipe up, swipe down, tap then long press and release | Same gesture set as Even G2, distinguishable by source
| | IMU (accelerometer / gyroscope) | Head orientation, motion data | Available for motion-aware apps - see IMU belowEven G2 and R1 touchpad events share the same event types but carry distinct sources, so you can route glasses-vs-ring input to different handlers.
### Event types ​
|
| | Event | Value | Description
| | CLICK_EVENT | 0 | Single press (Even G2 or Even R1)
| | SCROLL_TOP_EVENT | 1 | Swipe up / scroll reaches top boundary
| | SCROLL_BOTTOM_EVENT | 2 | Swipe down / scroll reaches bottom boundary
| | DOUBLE_CLICK_EVENT | 3 | Double press (Even G2 or Even R1)
| | LONG_PRESS_EVENT | 9 | Tap then long press begins (SDK 0.0.14+, Even App 2.2.9+)
| | LONG_PRESS_RELEASE_EVENT | 10 | That press is released (SDK 0.0.14+, Even App 2.2.9+)
### Tap then long press ​
Tap then long press (SDK 0.0.14+, Even App 2.2.9+) fires as two events - one when the press starts, one when the finger lifts:typescript
```
let pressStartedAt = 0
bridge.onEvenHubEvent(event => {
// Both events arrive on sysEvent, whichever container is capturing.
const type = event.sysEvent?.eventType
if (type === OsEventTypeList.LONG_PRESS_EVENT) {
pressStartedAt = Date.now()
showChargingIndicator()
}
if (type === OsEventTypeList.LONG_PRESS_RELEASE_EVENT) {
fire(Date.now() - pressStartedAt)
}
})
```
Both events always arrive on event.sysEvent, whichever container holds isEventCapture, and eventSource is absent on them. The routing table governs scrolling and selection, not this pair. There is no separate subscription - they reach the same onEvenHubEvent callback as everything else.Two things to hold onto:
- A rebuild mid-press keeps the pair intact. Rebuilding the page while the finger is still down still delivers LONG_PRESS_RELEASE_EVENT.
- The OS uses tap then long press for its own menu. Whether the gesture reaches your app depends on which layer owns the press. Treat it as an enhancement, not the only path to a feature - see Contextual Menu.Simulator 0.9.0+ simulates the gesture in its window - hold the control, or use its keyboard shortcut; 0.9.1+ delivers both events over the automation API, so long-press handling is scriptable in CI.
### Handling events ​
typescript
```
bridge.onEvenHubEvent(event => {
const textEvent = event.textEvent
if (textEvent) {
const eventType = textEvent.eventType
switch (eventType) {
case OsEventTypeList.CLICK_EVENT:
case undefined: // SDK normalizes 0 to undefined in some cases
// Handle press
break
case OsEventTypeList.DOUBLE_CLICK_EVENT:
// Handle double press
break
case OsEventTypeList.SCROLL_TOP_EVENT:
// Handle swipe up / scroll up
break
case OsEventTypeList.SCROLL_BOTTOM_EVENT:
// Handle swipe down / scroll down
break
}
}
// Tap then long press never reaches the capturing container.
switch (event.sysEvent?.eventType) {
case OsEventTypeList.LONG_PRESS_EVENT:
// Tap then long press started (SDK 0.0.14+)
break
case OsEventTypeList.LONG_PRESS_RELEASE_EVENT:
// Tap then long press released (SDK 0.0.14+)
break
}
})
```
### Event routing ​
Which container has isEventCapture: 1 decides where events land: |
| | Capture container | Events arrive as
| | Text container | event.textEvent
| | List container | event.listEventOnly one container per page captures events. Design around a single active target.Two things bypass that table entirely, arriving on the same subscription no matter which container is capturing:
- Contextual-menu selections arrive as event.menuItemClickEvent. See Contextual Menu.
- Tap then long press arrives as event.sysEvent, with eventSource absent. See Tap then long press.
## Audio ​
Capture audio from the glasses four-mic array or the phone microphone. Pick the source on every audioControl(true, ...) call - default is glasses if you omit the second arg.typescript
```
import { AudioInputSource, waitForEvenAppBridge } from '@evenrealities/even_hub_sdk'
const bridge = await waitForEvenAppBridge()
await bridge.audioControl(true, AudioInputSource.Glasses) // start - G2 mics
// await bridge.audioControl(true, AudioInputSource.Phone) // start - phone mic
await bridge.audioControl(false) // stop
```
### AudioInputSource ​
|
| | Value | Source | Notes
| | AudioInputSource.Glasses | Even G2 four-mic array | Default. Requires createStartUpPageContainer to have run first.
| | AudioInputSource.Phone | Phone microphone | No startup-page requirement; routes through the phone the WebView lives on.Audio data arrives via audioEvent in the event callback:typescript
```
bridge.onEvenHubEvent(event => {
const audio = event.audioEvent
if (!audio) return
// audio.source: AudioInputSource.Glasses | AudioInputSource.Phone
// audio.audioPcm: Uint8Array - PCM 16 kHz, signed 16-bit little-endian, mono
})
```
Format on both sources: PCM 16 kHz, signed 16-bit little-endian, mono. The source field tells you which mic the buffer came from - useful if you audioControl(true, ...) between sources at runtime.Permissions: g2-microphone for Glasses, phone-microphone for Phone. See Packaging § Permissions.
## Location ​
Two modes - one-shot (getAppLocation) and continuous (startAppLocationUpdates + onAppLocationChanged). Both read from the phone's location services; declare location in app.json permissions before calling.
### One-shot ​
typescript
```
import { AppLocationAccuracy, waitForEvenAppBridge } from '@evenrealities/even_hub_sdk'
const bridge = await waitForEvenAppBridge()
const fix = await bridge.getAppLocation({
accuracy: AppLocationAccuracy.High,
timeoutMs: 5000,
})
if (fix) {
console.log(fix.latitude, fix.longitude)
}
```
Returns null when the host has no fix in time, the user denied permission, or the coordinates are invalid - always null-check.
### Continuous ​
typescript
```
await bridge.startAppLocationUpdates({
accuracy: AppLocationAccuracy.Medium,
intervalMs: 1000,
distanceFilter: 5, // meters - host skips pushes smaller than this
})
const unsubscribe = bridge.onAppLocationChanged(loc => {
console.log(loc.latitude, loc.longitude, loc.speed)
})
// Later:
await bridge.stopAppLocationUpdates()
unsubscribe()
```
AppLocationOptions fields - accuracy, timeoutMs (one-shot only), intervalMs and distanceFilter (continuous only). All optional; the host picks sensible defaults.
### AppLocationAccuracy ​
|
| | Value | Use for
| | AppLocationAccuracy.Low | City-level - cheapest, kindest to battery
| | AppLocationAccuracy.Medium | Block-level - balanced default
| | AppLocationAccuracy.High | Best available fix - most battery
### AppLocation shape ​
|
| | Field | Type | Notes
| | latitude | number | Degrees
| | longitude | number | Degrees
| | accuracy | number? | Horizontal accuracy in meters
| | altitude | number? | Meters above sea level (when available)
| | speed | number? | Meters per second (when available)
| | heading | number? | Degrees from true north (when available)
| | timestamp | number? | Unix milliseconds (when available)
## Photos ​
Two ways to bring an image into your app from the phone - pick from the photo album or capture from the phone camera. Both are single-shot, return one AppImageAsset, and use phone hardware (the Even G2 has no camera).typescript
```
const fromAlbum = await bridge.pickImageFromAlbum() // requires `album` permission
const fromCamera = await bridge.captureImageFromCamera() // requires `camera` permission
if (fromAlbum) {
// fromAlbum.base64 is ready to drop into an <img src="data:...">
}
```
Both calls return null when the user cancels the picker / camera, or denies permission. The album picker is single-select only - no multi-import.
### AppImageAsset shape ​
|
| | Field | Type | Notes
| | path | string | Host-side path; opaque to the WebView
| | name | string | Original filename
| | mimeType | string | e.g. image/jpeg, image/png
| | size | number | Bytes
| | base64 | string | Inline data, ready for <img> or further processingThe image arrives in the WebView as base64 - there's no fetch-from-disk step. Watch size: a 12-megapixel JPEG is several MB of base64, which is fine to display but expensive to pump into a glasses container via updateImageRawData. Downscale before sending pixels to the glasses.
## IMU ​
The Even G2 has an IMU (inertial measurement unit). imuControl starts and stops the motion data stream.typescript
```
import { waitForEvenAppBridge, ImuReportPace, OsEventTypeList } from '@evenrealities/even_hub_sdk'
const bridge = await waitForEvenAppBridge()
// Start IMU reporting
await bridge.imuControl(true, ImuReportPace.P500)
// Listen for IMU data
const unsubscribe = bridge.onEvenHubEvent(event => {
const sys = event.sysEvent
if (!sys?.imuData) return
if (sys.eventType !== OsEventTypeList.IMU_DATA_REPORT) return
const { x, y, z } = sys.imuData
console.log('IMU:', x, y, z)
})
// Stop IMU reporting
await bridge.imuControl(false)
unsubscribe()
```
### imuControl(isOpen, reportFrq) ​
|
| | Parameter | Type | Description
| | isOpen | boolean | true to start, false to stop
| | reportFrq | ImuReportPace | Pacing code for report frequency (optional when stopping - defaults to P100)
### ImuReportPace ​
The reportFrq parameter accepts one of the following pacing codes: |
| | Value | Constant
| | 100 | ImuReportPace.P100
| | 200 | ImuReportPace.P200
| | 300 | ImuReportPace.P300
| | 400 | ImuReportPace.P400
| | 500 | ImuReportPace.P500
| | 600 | ImuReportPace.P600
| | 700 | ImuReportPace.P700
| | 800 | ImuReportPace.P800
| | 900 | ImuReportPace.P900
| | 1000 | ImuReportPace.P1000These are protocol pacing codes, not literal Hz values.
### IMU data shape ​
IMU samples arrive as Sys_ItemEvent through event.sysEvent in onEvenHubEvent. Each sample: |
| | Field | Type | Description
| | eventType | OsEventTypeList | IMU_DATA_REPORT for IMU samples
| | imuData.x | float | X-axis value
| | imuData.y | float | Y-axis value
| | imuData.z | float | Z-axis valueimuData is an IMU_Report_Data protobuf. Once imuControl(true, ...) fires, samples push continuously until imuControl(false) stops them.
## Device info ​
typescript
```
const info = await bridge.getDeviceInfo()
// Returns: model (G1/G2/Ring1), serial number, battery, wearing status, charging, in-case
```
Real-time monitoring:typescript
```
bridge.onDeviceStatusChanged(status => {
// Battery, wearing, charging updates
})
```
## User info ​
typescript
```
const user = await bridge.getUserInfo()
// Returns: uid, name, avatar, country
```
## Local storage ​
typescript
```
await bridge.setLocalStorage('key', 'value')
const value = await bridge.getLocalStorage('key')
```
## OS event models ​
Models the SDK exposes for OS-to-app events: |
| | Model | Description
| | Text_ItemEvent | Text container event
| | List_ItemEvent | List container event
| | Sys_ItemEvent | System event - carries eventType, eventSource, imuData
| | IMU_Report_Data | IMU sample payload (x, y, z floats) inside Sys_ItemEvent.imuData
| | AudioEventPayload | Microphone payload on event.audioEvent - source (AudioInputSource.Glasses | AudioInputSource.Phone) and audioPcm (Uint8Array)
| | MenuItemClickEvent | Contextual-menu selection on event.menuItemClickEvent - carries the itemID you declared (SDK 0.0.14+)
| | OsEventTypeList | Event type enum - includes CLICK_EVENT, DOUBLE_CLICK_EVENT, SCROLL_TOP_EVENT, SCROLL_BOTTOM_EVENT, IMU_DATA_REPORT, LONG_PRESS_EVENT, LONG_PRESS_RELEASE_EVENT
| | ImuCtrlCmd / ImuCtrlCmdResponse | Protobuf command/response maps used internally by imuControl
## SDK reference ​
Method signatures, parameter types, return values, and event payloads all live in the SDK package's TypeScript definitions - the *.d.ts files are the authoritative source.npm: @evenrealities/even_hub_sdk
## What the SDK doesn't expose ​
No direct Bluetooth access, no arbitrary pixel drawing, no audio output, no text alignment, no font control, no background colors, no per-item list styling, no programmatic scroll position, no animations, no glasses-side camera (the Even G2 has none - the phone camera is reachable via captureImageFromCamera), and images are greyscale-only.For the consumer-app version of these same questions - "can I render emoji?", "can I open a WebSocket while backgrounded?", etc. - see the FAQ.
### Need more than the public SDK exposes? ​
The public SDK is tuned for consumer plugin distribution - a stable, sandboxed surface that has to hold up across thousands of third-party apps. If you're building an enterprise (2B) or government (2G) deployment with requirements outside that envelope - deeper hardware access, custom firmware behavior, white-labeled distribution, dedicated SLAs, or PaaS-style integration into your own platform - reach out. We work with partners directly on those.Contact: software@evenrealities.comPagerPrevious pageUI/UX Design GuidelinesNext pageContextual Menu

172
docs/pages/build_display.md Normal file
View File

@@ -0,0 +1,172 @@
Display & UI System | Documentation
-
-
-
-
-
-
Skip to contentDocumentationSearch⌘CtrlK Main Navigation PortalThemeMenuReturn to top Sidebar Navigation
## Get Started
Overview
### Quickstart
Sign inHardwareInstall Node.js & npmInstall Even Hub toolingYour First AppTemplatesArchitecture
## Build
Page LifecycleDisplay & UI SystemUI/UX Design GuidelinesDevice APIsContextual MenuNetworkingBackground & Lifecycle
## Test
SimulatorLocal TestingPrivate TestingBeta Testing
## Ship
Packaging & DeploymentApp Submission & QA Guidelines
## Reference
GlossaryCLIVersioning PolicyChangelogFAQ
## AI Tooling
Claude CodeOn this pageLast updated: 2026-08-25The glasses don't render arbitrary HTML. They composite a fixed canvas from a small set of SDK container objects, each placed by absolute pixel coordinates.
## Canvas ​
Each eye displays a 576 x 288 pixel canvas. The coordinate origin is the top-left corner. X increases to the right; Y increases downward.Color is 4-bit greyscale - 16 levels of green. White pixels show as bright green; black pixels are off (transparent).
## Containers ​
The UI is built from containers - rectangular regions placed by absolute pixel coordinates. No CSS, no flexbox, no DOM.Rules:
- At most 4 image containers and 8 other containers per page (mix freely).
- Exactly one container has isEventCapture: 1. It receives all input events.
- Containers can overlap. zOrderIndex controls stacking explicitly (SDK 0.0.12+) - see Stacking order. Pages that omit it keep the old rule: later declarations draw on top.
### Shared properties ​
|
| | Property | Type | Range | Notes
| | xPosition | number | 0–576 | Left edge (px)
| | yPosition | number | 0–288 | Top edge (px)
| | width | number | 0–576 | Container width (px)
| | height | number | 0–288 | Container height (px)
| | containerID | number | - | Unique per page
| | containerName | string | max 16 chars | Unique per page
| | isEventCapture | number | 0 or 1 | Exactly one must be 1
| | zOrderIndex | number | unique per page | Stacking - larger renders in front (SDK 0.0.12+). All-or-nothing per page - see Stacking order
### Border properties ​
Available on text and list containers only: |
| | Property | Type | Range | Notes
| | borderWidth | number | 0–5 | 0 = no border
| | borderColor | number | 0–15 / 0–16 | Greyscale level
| | borderRadius | number | 0–10 | Rounded corners (note: typo preserved from SDK protobuf)
| | paddingLength | number | 0–32 | Uniform padding on all sidesThere is no background or fill property. The border is the only visual decoration.
### Stacking order (zOrderIndex) ​
List, text, and image containers take an optional zOrderIndex (SDK 0.0.12+) in both createStartUpPageContainer and rebuildPageContainer. Larger values render closer to the front.Rules:
- All-or-nothing. If any container on a page sets zOrderIndex, every list, text, and image container on that page must set it. Omit it on all of them and the page keeps declaration-order stacking.
- Unique per page. No two containers on the same page may share a value. There is no same-value tie-break.
- Rendering only. zOrderIndex doesn't touch input routing - the isEventCapture rule (exactly one per page) applies unchanged.Violations never reach the glasses: the SDK validates before calling native, logs an EvenHubPageContainerValidationErrorCode error, and createStartUpPageContainer returns StartUpPageCreateResult.invalid (rebuildPageContainer returns false).The layout this unlocks - an image as the backdrop, text floating in front, stable across rebuilds:typescript
```
await bridge.createStartUpPageContainer({
containerTotalNum: 2,
imageObject: [{
xPosition: 144,
yPosition: 72,
width: 288,
height: 144,
containerID: 1,
containerName: 'backdrop',
zOrderIndex: 1, // back
}],
textObject: [{
xPosition: 168,
yPosition: 112,
width: 240,
height: 64,
containerID: 2,
containerName: 'caption',
content: 'Now playing',
isEventCapture: 1,
zOrderIndex: 2, // front
}],
})
```
The image container starts empty - push pixels with updateImageRawData after the page is created.
## Text containers ​
The primary container type. Plain text, left-aligned, top-aligned. No alignment options, no font-size control, no bold or italic.typescript
```
new TextContainerProperty({
xPosition: 0,
yPosition: 0,
width: 576,
height: 288,
borderWidth: 0,
borderColor: 5,
paddingLength: 4,
containerID: 1,
containerName: 'main',
content: 'Your text here',
textColor: 4,
isEventCapture: 1,
})
```
### Content limits ​
|
| | Method | Max Characters
| | createStartUpPageContainer | 1,000
| | textContainerUpgrade | 2,000
| | rebuildPageContainer | 1,000
### Behavior ​
- Text wraps at the container width.
- If content overflows and the container has isEventCapture: 1, the firmware scrolls it.
- \n is a line break.
- Unicode works as long as the glyph is in the firmware's font set.
- A full-screen text container holds roughly 400-500 characters.
- "Centering" means padding with spaces.
### Text brightness (textColor) ​
Text containers take an optional textColor (SDK 0.0.14+) - five brightness levels, 0 to 4. Despite the field name there is no color here: the display is monochrome green, and textColor sets how bright the glyphs burn.This is per-container text brightness. It is not the global display brightness the user sets from the system contextual menu - that one is OS-owned and never reaches your app. |
| | Context | Omit it and you get
| | createStartUpPageContainer / rebuildPageContainer | Device default, level 4 (brightest)
| | textContainerUpgrade | The container's current brightness, unchangedtypescript
```
// Dim secondary text, full-brightness heading
await bridge.textContainerUpgrade(new TextContainerUpgrade({
containerID: 2,
containerName: 'caption',
content: 'Updated 3 min ago',
textColor: 2,
}))
```
Two things to keep straight:
- textColor is 0-4. borderColor is 0-15. Different scales on the same container - the border keeps the 16-level greyscale range, text brightness does not.
- Level 0 is the dimmest level, not "unset". The SDK accepts it, so a textColor: 0 container may render effectively invisible. Verify on hardware before shipping a design that relies on 0.Values outside 0..4 never reach the glasses: the SDK logs INVALID_TEXT_BRIGHTNESS and the call fails locally, the same way z-order violations do. textContainerUpgrade rejects out-of-range values before calling the host too.
### In-place updates ​
Reach for textContainerUpgrade - it's faster than a full rebuild and flicker-free on hardware. Pass a TextContainerUpgrade instance. containerID, containerName, and content are the only required fields for a basic update; contentOffset and contentLength are for partial-string updates, and textColor re-brightens the text (SDK 0.0.14+, see Text brightness).typescript
```
import { TextContainerUpgrade } from '@evenrealities/even_hub_sdk'
await bridge.textContainerUpgrade(new TextContainerUpgrade({
containerID: 1,
containerName: 'main',
content: 'Updated text',
}))
```
## List containers ​
Native scrollable lists, with scroll highlighting handled in firmware.
- Up to 20 items per list.
- Up to 64 characters per item.
- No per-item styling, no row-height control, no separators.
- No in-place updates - changing a list means rebuilding the whole page.
## Image containers ​
Render greyscale images.
- Up to 288 x 144 px per container (width x height).
- 4-bit greyscale.
- Accepts number[], Uint8Array, ArrayBuffer, or base64.
- Cannot send during createStartUpPageContainer - create a placeholder, then update via updateImageRawData.
- No concurrent image sends.
- Raw data is LZ4-compressed in transit automatically (SDK 0.0.12+) - zero code change. Transfers shrink and image updates land faster.
### Image sends are paced at 100ms ​
Each updateImageRawData holds the image path for 100ms (SDK 0.0.14+). A call that arrives inside that window is held and flushed on the next one - nothing is dropped, and nothing arrives early.Treat 100ms as a floor, not a frame budget. A single frame still takes far longer than that to cross BLE, so the pacing is rarely your bottleneck - it's a guard against bursts, not a throughput target.Keep awaiting each send. updateImageRawData resolves to an ImageRawDataUpdateResult and you should still check it, but read it as a call result rather than a delivery receipt from the glasses - a success means the update was accepted, not that the pixels are lit.Image-first apps: put a full-screen text container with content: ' ' and isEventCapture: 1 behind the image container - lower zOrderIndex, or declared first if the page omits zOrderIndex. The text container collects input; the image draws on top.
## Font and Unicode support ​
The glasses ship a single LVGL font baked into firmware. No font selection, no size control, not monospaced. Characters outside the font set are silently dropped.The Useful Characters for Building UIs section of the design guidelines has a lookup table of glyphs that ship in the set.PagerPrevious pagePage LifecycleNext pageUI/UX Design Guidelines

View File

@@ -0,0 +1,85 @@
Networking | Documentation
-
-
-
-
-
-
Skip to contentDocumentationSearch⌘CtrlK Main Navigation PortalThemeMenuReturn to top Sidebar Navigation
## Get Started
Overview
### Quickstart
Sign inHardwareInstall Node.js & npmInstall Even Hub toolingYour First AppTemplatesArchitecture
## Build
Page LifecycleDisplay & UI SystemUI/UX Design GuidelinesDevice APIsContextual MenuNetworkingBackground & Lifecycle
## Test
SimulatorLocal TestingPrivate TestingBeta Testing
## Ship
Packaging & DeploymentApp Submission & QA Guidelines
## Reference
GlossaryCLIVersioning PolicyChangelogFAQ
## AI Tooling
Claude CodeOn this pageLast updated: 2026-06-11Even Hub plugins talk to the network the way any web app does - fetch(), XMLHttpRequest, WebSockets - from inside the WebView the Even Realities App hosts on the phone. Two independent gates sit in front of every outbound request, and most "works locally, fails on device" bugs trace back to confusing one for the other.
## The two gates ​
Every outgoing request has to clear both checks:
- Even-side permission check. The destination domain must be in your app.json network permission whitelist. The Even Realities App enforces it before the request ever leaves the WebView. Anything not in the whitelist is blocked - no traffic generated at all.
- Browser CORS check. The WebView's browser engine (Chromium on Android, WKWebView on iOS) enforces standard CORS. The remote server has to return the right Access-Control-Allow-Origin (and friends), or the response is dropped before your fetch() resolves.The whitelist is not a CORS bypassAdding a domain to app.json does not override CORS. It only tells the Even Realities App that your plugin is allowed to reach that domain at all. If the server's CORS headers are wrong, the browser still drops the response - same as in any other web page.
## Declaring the whitelist ​
In your app.json, request the network permission and list every domain your plugin will hit:json
```
"permissions": [
{
"name": "network",
"desc": "Fetches weather data and stores user preferences in the cloud.",
"whitelist": [
"https://api.weather.com",
"https://prefs.example.com"
]
}
]
```
Notes:
- One whitelist entry per origin. Use the full origin (https://api.example.com) - bare hostnames and wildcards aren't supported.
- HTTPS in production. Plain http:// is only useful for local dev against a LAN dev server.
- Full schema in Packaging & Deployment → Permissions Format.
## Required server-side CORS headers ​
For a third-party API to be reachable from the WebView, the server must return at minimum:http
```
Access-Control-Allow-Origin: *
# or specifically the WebView origin if you can identify it
```
For requests with custom headers, JSON bodies, or non-GET/POST methods, also handle the preflight:http
```
# Response to the OPTIONS preflight
HTTP/1.1 204 No Content
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Max-Age: 86400
```
If the API is third-party and you can't touch its CORS, proxy through a server you control that sets the right headers - then put that server's domain in the app.json whitelist.
## Debugging checklist ​
When a request fails on device, work this list in order:
- Domain is in app.json network.whitelist? Repack and reupload if you changed it.
- Response has Access-Control-Allow-Origin? If it's missing or doesn't match your origin, fix it on the server.
- Preflight (OPTIONS) returns 2xx with the right Allow-Methods / Allow-Headers? Only matters for JSON bodies and custom headers.
- Same request from curl succeed? If curl works and the WebView doesn't, it's almost always CORS.
## Related ​
- Packaging & Deployment → Permissions Format
- App Submission & QA Guidelines → First-run experience
- ArchitecturePagerPrevious pageContextual MenuNext pageBackground & Lifecycle

View File

@@ -0,0 +1,60 @@
Page Lifecycle | Documentation
-
-
-
-
-
-
Skip to contentDocumentationSearch⌘CtrlK Main Navigation PortalThemeMenuReturn to top Sidebar Navigation
## Get Started
Overview
### Quickstart
Sign inHardwareInstall Node.js & npmInstall Even Hub toolingYour First AppTemplatesArchitecture
## Build
Page LifecycleDisplay & UI SystemUI/UX Design GuidelinesDevice APIsContextual MenuNetworkingBackground & Lifecycle
## Test
SimulatorLocal TestingPrivate TestingBeta Testing
## Ship
Packaging & DeploymentApp Submission & QA Guidelines
## Reference
GlossaryCLIVersioning PolicyChangelogFAQ
## AI Tooling
Claude CodeOn this pageLast updated: 2026-08-25Every glasses screen flows through a small set of SDK calls - one for creation, two for updating, one for shutting down.
## Methods ​
|
| | Method | Purpose | Notes
| | createStartUpPageContainer | Create the initial page | Called exactly once at startup. Returns result code. Takes an optional menuObject - see Contextual Menu.
| | rebuildPageContainer | Replace the entire page | Full redraw - all state is lost, brief flicker on hardware. Omitting menuObject clears the custom menu.
| | textContainerUpgrade | Update text in-place | Faster, flicker-free on hardware. Requires matching containerID + containerName. Optional textColor re-brightens the text (SDK 0.0.14+).
| | updateImageRawData | Update an image container | No concurrent sends allowed. LZ4-compressed in transit (SDK 0.0.12+) - no code change.
| | shutDownPageContainer | Exit the app | Pass 1 for the system exit-confirmation dialog (required on the root page); pass 0 for immediate exit (internal pages only).
| | callEvenApp | Generic method call | Escape hatch - all typed methods are wrappers around this.
## Result codes ​
For createStartUpPageContainer: |
| | Code | Meaning
| | 0 | Success
| | 1 | Invalid parameters
| | 2 | Oversize
| | 3 | Out of memoryrebuildPageContainer, textContainerUpgrade, and shutDownPageContainer return boolean.updateImageRawData returns a status string: success, imageException, imageSizeInvalid, imageToGray4Failed, or sendFailed.
## Best practices ​
- Always call shutDownPageContainer(1) from the root page. Mode 1 shows the system exit-confirmation dialog. QA reviewers explicitly check for it. Apps that exit silently with mode 0 from the root - or use a custom in-app exit UI in its place - get rejected. Mode 0 is only OK on internal pages where the user already confirmed.
- Use textContainerUpgrade for frequent text updates (counters, status, live data). It skips the flicker of a full rebuild.
- Use rebuildPageContainer only when the layout itself changes - adding or removing containers, switching between text and list.
- Match containerID and containerName exactly when calling textContainerUpgrade. Mismatches silently no-op.
- Don't call updateImageRawData concurrently. Wait for one to resolve before sending the next.
- Re-send menuObject on every rebuild that should keep the menu. rebuildPageContainer replaces the menu wholesale; leaving menuObject out clears your items rather than preserving them.PagerPrevious pageBuildNext pageDisplay & UI System

View File

@@ -0,0 +1,59 @@
Architecture | Documentation
-
-
-
-
-
-
Skip to contentDocumentationSearch⌘CtrlK Main Navigation PortalThemeMenuReturn to top Sidebar Navigation
## Get Started
Overview
### Quickstart
Sign inHardwareInstall Node.js & npmInstall Even Hub toolingYour First AppTemplatesArchitecture
## Build
Page LifecycleDisplay & UI SystemUI/UX Design GuidelinesDevice APIsContextual MenuNetworkingBackground & Lifecycle
## Test
SimulatorLocal TestingPrivate TestingBeta Testing
## Ship
Packaging & DeploymentApp Submission & QA Guidelines
## Reference
GlossaryCLIVersioning PolicyChangelogFAQ
## AI Tooling
Claude CodeOn this pageLast updated: 2026-06-11Even Hub apps are web apps - HTML, CSS, and JavaScript - glued to the glasses by the Even Hub SDK. You build them on your laptop, package them up, and submit them through the dev portal for users to install.
## Connection model ​
```
┌──────────────────┐ HTTPS ┌────────────────────┐ Bluetooth ┌─────────────────────┐
│ Even Hub Cloud │ ◄──────────► │ Phone │ ◄────────────► │ Even G2 Glasses │
│ (distribution │ │ (Even Realities │ │ (display + input) │
│ & hosting) │ │ App + WebView) │ │ │
└──────────────────┘ └────────────────────┘ └─────────────────────┘
```
- The phone runs the Even Realities App (Flutter), which hosts your plugin in a WebView - Chromium on Android, WKWebView on iOS. Your app logic runs inside that WebView. The Even Realities App relays everything to and from the glasses over Bluetooth.
- The glasses render UI containers and emit input events - presses, scrolls, swipes. Apart from native scroll handling, no app logic runs on them.Network whitelist is not a CORS bypassThe app.json network whitelist is an Even-side permission check - it controls which domains your plugin is allowed to call from the WebView. It does not bypass CORS.In production, fetch() requires both:
- The remote domain listed in your app.json network whitelist, and
- Correct CORS headers (Access-Control-Allow-Origin, etc.) returned by that remote API.APIs that work on localhost but fail inside the WebView are almost always CORS misconfigurations on the remote side, not Even bugs. See Networking for the full request flow and debugging tips.
## Testing your app ​
Three ways to run your app during development:
- QR sideload - the CLI prints a QR pointing at your local dev server; scan it from the Even Realities App and your app loads on the glasses with hot reload.
- Private build - evenhub pack produces an .ehpk; upload it through the dev portal to install on your own devices.
- Simulator - preview layouts and exercise logic entirely on your laptop, no hardware needed.
## PWA as an alternative ​
If you'd rather stay outside the Even Hub distribution flow, build a Progressive Web App and point users at your hosted URL. You keep full control over distribution and hosting; you also skip the dev portal entirely - no packaging, no review.
## The SDK bridge ​
The SDK injects a JavaScript bridge (EvenAppBridge) into the WebView. Your frontend calls into it to drive the display and receive input.Web → Glasses: your JS calls bridge.callEvenApp(method, params) → WebView bridge → Even Realities App → Bluetooth → glasses.Glasses → Web: input events travel Bluetooth → Even Realities App → window._listenEvenAppMessage(...) → your callback.For the project layout and a working scaffold, see Your First App § Project structure.PagerPrevious pageTemplatesNext pageBuild

View File

@@ -0,0 +1,69 @@
Overview | Documentation
-
-
-
-
-
-
Skip to contentDocumentationSearch⌘CtrlK Main Navigation PortalThemeMenuReturn to top Sidebar Navigation
## Get Started
Overview
### Quickstart
Sign inHardwareInstall Node.js & npmInstall Even Hub toolingYour First AppTemplatesArchitecture
## Build
Page LifecycleDisplay & UI SystemUI/UX Design GuidelinesDevice APIsContextual MenuNetworkingBackground & Lifecycle
## Test
SimulatorLocal TestingPrivate TestingBeta Testing
## Ship
Packaging & DeploymentApp Submission & QA Guidelines
## Reference
GlossaryCLIVersioning PolicyChangelogFAQ
## AI Tooling
Claude CodeOn this pageLast updated: 2026-08-07The Even G2 is a pair of smart glasses with a dual micro-LED display - one per lens - a four-mic array, touchpads on both temples, and an optional R1 ring for extra input. It pairs with your phone over Bluetooth LE 5.2. Everything you build runs there; the glasses are the screen.
## Key hardware specs ​
|
| | Spec | Value
| | Display | 576 x 288 px per eye
| | Color depth | Monochrome green, 16 levels
| | Connectivity | Bluetooth Low Energy 5.2
| | Audio input | 4-mic array, single stream, 16 kHz PCM
| | Even G2 touchpads | Press, double press, swipe up, swipe down, tap then long press and release
| | R1 touchpads | Same gestures as the Even G2 (optional accessory)
| | Camera / Speaker | NoneNo camera, no speaker on the glasses - the omission is the point. Your code lives on the phone; the glasses render and capture input. (Plugins can still reach the phone camera and album through the SDK when the user grants permission.)
## What you can build ​
Today, one surface is live: plugins. They are web apps, written in HTML, CSS, and JavaScript or TypeScript, glued to the glasses by the Even Hub SDK. Bring any stack - Vite, React, plain JS - the SDK takes it from there.Three more surfaces are coming: dashboard widgets, dashboard layouts, and AI skills.
## What development looks like ​
```
1. Write code Standard web app (Vite + SDK)
2. Preview locally evenhub-simulator http://localhost:5173
3. Test on device QR sideload, or private build in the dev portal
4. Package evenhub pack app.json dist -o myapp.ehpk
5. Submit Upload the .ehpk through the dev portal
```
## Quick reference ​
|
| | Resource | Link
| | SDK | npm: @evenrealities/even_hub_sdk
| | Simulator | npm: @evenrealities/evenhub-simulator
| | CLI | npm: @evenrealities/evenhub-cli
| | Design Guidelines | Figma: Software Design Guidelines
| | Community notes | GitHub: even-g2-notes
| | Community toolkit | GitHub: even-toolkit
| | Community IDE | GitHub: ER Studio - editor, simulator mirror, and one-click pack in one window (macOS Apple Silicon)
| | Discord | discord.gg/Y4jHMCU4svPagerPrevious pageGet StartedNext pageQuickstart

View File

@@ -0,0 +1,236 @@
Your First App | Documentation
-
-
-
-
-
-
Skip to contentDocumentationSearch⌘CtrlK Main Navigation PortalThemeMenuReturn to top Sidebar Navigation
## Get Started
Overview
### Quickstart
Sign inHardwareInstall Node.js & npmInstall Even Hub toolingYour First AppTemplatesArchitecture
## Build
Page LifecycleDisplay & UI SystemUI/UX Design GuidelinesDevice APIsContextual MenuNetworkingBackground & Lifecycle
## Test
SimulatorLocal TestingPrivate TestingBeta Testing
## Ship
Packaging & DeploymentApp Submission & QA Guidelines
## Reference
GlossaryCLIVersioning PolicyChangelogFAQ
## AI Tooling
Claude CodeOn this pageLast updated: 2026-07-04The end state of this page:
- A Vite + TypeScript project with the SDK wired in
- A page rendering on the simulator, with a click handler
- An app.json manifest ready for packaging
- The same app running on real Even G2 glasses via QR sideloadPrerequisites: Install Node.js & npm, then install only the global CLI and simulator from Install Even Hub tooling. Install the SDK after creating the project in Route A, or let the template dependencies install it in Route B. For the real-hardware half of step 6, also finish Hardware.
## 1. Scaffold the project ​
Two routes - pick one.
### Route A - Create a Vite app by hand ​
Create a Vite + TypeScript project, install the SDK, then generate the Even Hub manifest:bash
```
npm create vite@latest my-first-app -- --template vanilla-ts
cd my-first-app
npm install
npm install @evenrealities/even_hub_sdk@latest
evenhub init
```
evenhub init creates app.json; it does not scaffold the Vite project.
### Route B - Template repo or AI tooling ​
Clone a ready-to-run starter from the evenhub-templates repo:bash
```
npx degit even-realities/evenhub-templates/minimal my-first-app
cd my-first-app
npm install
```
The templates already include the SDK; npm install installs the template's dependencies.Templates has the full list - minimal, text-heavy, asr, image. If you live in Claude Code, the template skill picks and scaffolds one for you.
## 2. Project structure ​
A minimal Even Hub app looks like this:
```
my-first-app/
├── src/
│ └── main.ts ← your app's entry point
├── public/
│ └── icon.png ← greyscale app icon (24×24)
├── index.html ← Vite HTML entry - keep as-is
├── package.json
├── vite.config.ts ← Vite config - keep as-is for now
├── tsconfig.json
└── app.json ← Even Hub manifest (required for packaging)
```
The only Even-specific file is app.json (the manifest). Everything else is standard Vite.
## 3. Write main.ts ​
Open src/main.ts and replace its contents with:typescript
```
import {
waitForEvenAppBridge,
TextContainerProperty,
TextContainerUpgrade,
CreateStartUpPageContainer,
OsEventTypeList,
} from '@evenrealities/even_hub_sdk'
// Wait for the bridge to be ready before doing anything else.
// In the simulator this resolves immediately; on hardware it waits
// for the WebView to initialize the SDK bridge.
const bridge = await waitForEvenAppBridge()
// Build a single text container that fills the visible canvas (576×288).
const mainText = new TextContainerProperty({
xPosition: 0,
yPosition: 0,
width: 576,
height: 288,
borderWidth: 0,
borderColor: 5,
paddingLength: 4,
containerID: 1,
containerName: 'main',
content: 'Hello from G2!\n\nTap to count: 0\nDouble-tap to exit',
isEventCapture: 1, // ← receive click events on this container
})
// Render the page. `result` is 0 on success.
const result = await bridge.createStartUpPageContainer(
new CreateStartUpPageContainer({
containerTotalNum: 1,
textObject: [mainText],
}),
)
if (result !== 0) {
console.error('createStartUpPageContainer failed:', result)
// 1 = invalid params, 2 = oversize, 3 = out of memory
}
// Single event subscription - all OS events arrive through onEvenHubEvent.
// Inspect event.textEvent / event.listEvent / event.sysEvent to route by source.
let count = 0
bridge.onEvenHubEvent((event) => {
const textEvent = event.textEvent
if (!textEvent || textEvent.containerID !== 1) return
switch (textEvent.eventType) {
case OsEventTypeList.CLICK_EVENT:
case undefined: // SDK normalizes 0 to undefined in some cases
count += 1
bridge.textContainerUpgrade(new TextContainerUpgrade({
containerID: 1,
containerName: 'main',
content: `Hello from G2!\n\nTap to count: ${count}\nDouble-tap to exit`,
}))
break
case OsEventTypeList.DOUBLE_CLICK_EVENT:
// Mode 1 shows the system exit-confirmation dialog -
// required on the root page; silent exit (mode 0) is rejected in QA.
bridge.shutDownPageContainer(1)
break
}
})
```
Worth knowing as you read it:
- waitForEvenAppBridge() - always await this before calling any other SDK method. Calling SDK methods before the bridge is ready silently no-ops.
- isEventCapture: 1 - required if you want a container to receive input events. The default is 0 (display-only).
- onEvenHubEvent - the single subscription point for OS-to-app events. The payload's textEvent / listEvent / sysEvent fields tell you which kind fired; switch on eventType to route. See Device APIs § Handling Events.
- textContainerUpgrade - the flicker-free way to update text. Pass a new TextContainerUpgrade({...}). Reserve rebuildPageContainer for layout changes (adding/removing containers).
- shutDownPageContainer(1) - mode 1 shows the system exit-confirmation dialog and is required on the root page. Apps that exit silently with mode 0 (or a custom exit UI) get rejected in QA. See Page Lifecycle.
- Result codes - createStartUpPageContainer returns an int; everything else returns a boolean or a status string. See Page Lifecycle.
## 4. Write the manifest (app.json) ​
The manifest lives at the project root and is required for packaging. The minimum:json
```
{
"package_id": "com.exampleco.exampleapp",
"name": "My First App",
"version": "0.1.0",
"edition": "202601",
"min_sdk_version": "0.0.12",
"entrypoint": "index.html",
"permissions": [],
"supported_languages": ["en"]
}
```
Field notes:
- package_id - globally unique. Convention: reverse-DNS of your handle.
- edition - the platform contract version. Use "202601" until told otherwise.
- min_sdk_version - match the SDK you installed. Check with npm list @evenrealities/even_hub_sdk.
- min_app_version - omitted here on purpose. The CLI derives it from your SDK version at pack time; you only declare it to pin a stricter floor. See Auto-deriving min_app_version.
- permissions - empty for this app. To make external fetch() calls, add a network permission with a whitelist: {"name": "network", "desc": "...", "whitelist": ["https://api.example.com"]}. Microphone access uses {"name": "g2-microphone", "desc": "..."}. See Packaging § Permissions Format.
## 5. Run in the simulator ​
bash
```
# Terminal 1 - Vite dev server
npm run dev
# Terminal 2 - Simulator (point it at the dev URL)
evenhub-simulator http://localhost:5173
```
The simulator window opens with a 576×288 green canvas. You should see:
```
Hello from G2!
Tap to count: 0
Double-tap to exit
```
Click anywhere in the canvas - the counter increments. Double-click - the system exit-confirmation dialog appears. Edit main.ts; Vite hot-reloads; the simulator refreshes.
### Common simulator errors ​
|
| | Symptom | Cause | Fix
| | Blank green canvas, no text | waitForEvenAppBridge() not awaited | Check that the await is present and main.ts runs at module top-level
| | Text shows but click does nothing | Container has isEventCapture: 0, or click event type is normalized | Set isEventCapture: 1 and handle both OsEventTypeList.CLICK_EVENT and undefined inside onEvenHubEvent; don't listen for a DOM click
| | Cannot find module @evenrealities/even_hub_sdk | SDK dependency missing | Route A: run npm install @evenrealities/even_hub_sdk@latest; templates: run npm install
| | Counter increments but text disappears | textContainerUpgrade was called with a wrong containerID / containerName | Both must match the values you used in createStartUpPageContainer
## 6. Run on real hardware ​
Prerequisites: the phone app has the Scan QR button visible (see Enable Developer Mode), and the phone can reach your laptop's LAN IP (see Network & Firewall Setup).Find your LAN IP, then generate a QR pointing at the dev server:bash
```
# macOS / Linux
ipconfig getifaddr en0 # macOS Wi-Fi (use en1 if blank)
hostname -I | awk '{print $1}' # Linux
# Windows
ipconfig | findstr /i "IPv4"
```
Then:bash
```
evenhub qr --url "http://<YOUR-LAN-IP>:5173"
```
A QR code prints in the terminal. Tap Scan QR in the phone app and aim it at the terminal. The glasses render your app within a second. Hot-reload still works - edits to main.ts reflow on the glasses without re-scanning.Tap a temple to fire the click handler - the counter increments on the glasses.Known issue: manually triggered Link jumps in Dev Preview mode may currently fail. Fall back to re-scanning the QR. Tracked at Enable Developer Mode → Known issue.
### Common hardware errors ​
|
| | Symptom | Likely cause | Fix
| | Phone says "Couldn't connect" after scan | Firewall / AP isolation | See Network & Firewall Setup
| | Glasses show last app, not yours | Cached app container | Re-scan the QR (forces reload)
| | App loads but click doesn't fire | Container without isEventCapture: 1 | Same as the simulator section
| | Works in sim, blank on glasses | App tried to render before bridge ready | Confirm await waitForEvenAppBridge() is there
## 7. What you have now ​
A working repo with:
- A Vite + TypeScript project
- A single page rendering on the glasses
- A click handler that mutates state and updates the display
- An app.json manifest ready for packagingThat's the minimum surface area of every Even Hub app. Multi-page navigation, audio capture, IMU, networking - all of it composes from these same primitives.
## Where to go next ​
Branch into whichever direction you need: |
| | You want to… | Read
| | Build a multi-page UI with lists and detail views | Display & UI System
| | Handle the R1 ring, IMU, swipes, double-taps | Device APIs
| | Use the microphone or local storage | Device APIs
| | Hit an external API from your app | Networking
| | Understand what happens when the phone locks | Background & Lifecycle
| | Lay out content for the green canvas | Design Guidelines
| | Package, version, and ship | Packaging & Deployment → App Submission & QA
| | Pick the right testing mode for what you're about to do | Testing ModesPagerPrevious pageInstall Even Hub toolingNext pageTemplates

View File

@@ -0,0 +1,116 @@
Hardware | Documentation
-
-
-
-
-
-
Skip to contentDocumentationSearch⌘CtrlK Main Navigation PortalThemeMenuReturn to top Sidebar Navigation
## Get Started
Overview
### Quickstart
Sign inHardwareInstall Node.js & npmInstall Even Hub toolingYour First AppTemplatesArchitecture
## Build
Page LifecycleDisplay & UI SystemUI/UX Design GuidelinesDevice APIsContextual MenuNetworkingBackground & Lifecycle
## Test
SimulatorLocal TestingPrivate TestingBeta Testing
## Ship
Packaging & DeploymentApp Submission & QA Guidelines
## Reference
GlossaryCLIVersioning PolicyChangelogFAQ
## AI Tooling
Claude CodeOn this pageLast updated: 2026-06-04You can build a lot in the simulator alone, but the rest of the docs assume your glasses are paired and on current firmware. This page gets you there - hardware setup, Developer Mode, and the firewall / Wi-Fi gotchas that QR sideloading runs into.
## Set up your Even G2 & R1 ​
If you only have the simulator for now, jump to Your First App and come back once the glasses arrive.
### Naming convention ​
Throughout the docs and the phone app:
- Even G2 - the glasses.
- Even R1 - the optional input ring.Your devices appear under these names in the Bluetooth pairing list.
### Exit shipping mode ​
New glasses arrive in shipping mode - battery isolated so they don't drain in transit. They look dead out of the box. Wake them:
- Drop both arms into the charging case and close it.
- Watch the case light:
- White - charged and ready.
- Orange (≈3 s) - seating feedback when you insert the glasses.
- Open the case. The glasses are now powered and discoverable.
### Pairing ​
- Open the Even Realities App and sign in.
- Go to Devices → Add device and select Even G2.
- Confirm the pairing prompt. The glasses display a brief confirmation.
### Firmware update ​
The app checks for firmware right after pairing. Take the update before doing anything else - most early connection failures trace back to stale firmware.
- The app prompts when an update is available, or check Devices → Firmware manually.
- Keep the glasses in the case and the phone nearby until the flash finishes.
- Don't interrupt it.
### Pair the R1 ring (optional) ​
The R1 adds a touchpad you can use without raising a hand to your temple.
- In the app, go to Devices → Add device → Even R1.
- Follow the same confirm-prompt flow as the glasses.The R1 gesture and event model lives in Device APIs → Inputs.
## Enable Developer Mode ​
There is no toggle. Signing in to the web hub flips your account to developer; the next restart of the phone app surfaces the developer section.
### 1. Sign in to the web hub ​
Log in at hub.evenrealities.com/login with the same account you registered in the phone app. See Sign in for the full flow.
### 2. Restart the Even Realities App ​
Force-quit the app - swipe it away from recents, not just background - and reopen it.
### 3. Check the Even Hub tab ​
A developer section appears in the top-right of the Even Hub tab. That's where the Scan QR button lives, alongside the rest of the dev features Your First App uses.
### Known issue: manual Link jump in Dev Preview ​
In Dev Preview, a manually triggered Link jump may fail - no response, the wrong page, or a bounce to the home screen. The mobile team is on it. Workaround: re-scan the QR instead of relying on in-app Link navigation. This page updates once the fix ships.
## Network & Firewall Setup ​
If QR sideloading already works, skip this section. What follows is for the case where the QR scans but the app never loads.QR sideloading points the phone at a dev server on your laptop (http://192.168.1.100:5173 or similar). For that to work, the phone and laptop have to be on the same network with nothing silently dropping the connection between them - the single most common reason the QR scans and nothing happens.
### 1. Find your LAN IP ​
You need your laptop's address on the local network - not localhost.bash
```
# macOS
ipconfig getifaddr en0 # Wi-Fi; try en1 if blank
# Linux
hostname -I | awk '{print $1}'
# Windows (PowerShell)
ipconfig | findstr /i "IPv4"
```
Use that address in the QR URL:bash
```
evenhub qr --url "http://<your-lan-ip>:5173"
```
### 2. Allow Node through the firewall ​
macOS - Application Firewall ​macOS silently drops the first inbound phone-to-laptop connection. Allow Node explicitly:
- System Settings → Network → Firewall → Options…
- Add your node binary and set it to Allow incoming connections.
- To confirm the firewall is the culprit, toggle it off, retry, then re-enable with the allow rule in place.TIPWith a version manager, the node binary lives under ~/.fnm/... or ~/.nvm/.... Add that path - not a stale /usr/local/bin/node.Windows Defender ​Defender often blocks node.exe inbound without prompting, especially the shimmed node.exe from fnm/nvm-windows. Add an inbound rule on the dev port:
- Windows Security → Firewall & network protection → Advanced settings.
- Inbound Rules → New Rule → Port → TCP → 5173 (or your dev port) → Allow.
- Apply to the Private profile at minimum.
### 3. Wi-Fi AP isolation ​
Corporate Wi-Fi and a lot of home routers ship with AP / client isolation on. It blocks device-to-device traffic even on the same SSID. The symptom: ping and connection silently fail with the firewall already open.Fallbacks, in order of convenience: |
| | Fallback | How
| | Phone hotspot | Tether the laptop to the phone's hotspot; both are then on the phone's network with no isolation.
| | Different network | Use a home/guest network without client isolation.
| | Tailscale | Put phone and laptop on the same Tailscale tailnet and use the Tailscale IP in the QR URL.
### Quick diagnosis ​
If the QR scans but the app never loads, work this list top-down. |
| | Step | Check | If it fails
| | 1 | Open http://<your-lan-ip>:5173 in your phone's browser | Phone can't reach the laptop - go to step 2
| | 2 | Confirm the dev server is actually running (npm run dev shows Local: line) | Restart the dev server, re-generate the QR
| | 3 | Confirm the IP in the QR matches the laptop's current LAN IP (Wi-Fi networks reassign on reconnect) | Re-run evenhub qr --url "http://<current-ip>:5173"
| | 4 | macOS / Windows firewall allowing Node / port 5173? (see step 2 above) | Add the allow rule, retry step 1
| | 5 | Router has AP / client isolation enabled? (see step 3 above) | Fall back to phone hotspot or Tailscale
## Next ​
Your First App - scaffold, run, and sideload via QR.PagerPrevious pageSign inNext pageInstall Node.js & npm

View File

@@ -0,0 +1,70 @@
Quickstart | Documentation
-
-
-
-
-
-
Skip to contentDocumentationSearch⌘CtrlK Main Navigation PortalThemeMenuReturn to top Sidebar Navigation
## Get Started
Overview
### Quickstart
Sign inHardwareInstall Node.js & npmInstall Even Hub toolingYour First AppTemplatesArchitecture
## Build
Page LifecycleDisplay & UI SystemUI/UX Design GuidelinesDevice APIsContextual MenuNetworkingBackground & Lifecycle
## Test
SimulatorLocal TestingPrivate TestingBeta Testing
## Ship
Packaging & DeploymentApp Submission & QA Guidelines
## Reference
GlossaryCLIVersioning PolicyChangelogFAQ
## AI Tooling
Claude CodeOn this pageLast updated: 2026-06-04The one-page path from zero to a running app. Read it top to bottom the first time; come back to it as a checklist after that.By the end you'll have an Even Realities account, a paired and updated pair of Even G2s, Developer Mode on, Node installed, and Hello from G2! rendering on the glasses via QR sideload.
## 1. Install the Even Realities App and register ​
On your phone, type this URL into your browser to download the app:
```
evenapp.evenrealities.com
```
Install the app, then register your Even Realities account inside it. The account you create here is the same one you'll use everywhere else (web hub, Developer Mode).
## 2. Log in to the web hub ​
Open hub.evenrealities.com/login and sign in with the same account.→ Detail: Sign in
## 3. Wake the glasses out of shipping mode ​
New Even G2s arrive in shipping mode so the battery doesn't drain in transit. To wake them, drop both arms into the charging case with the case plugged in. The case light comes on; the glasses are ready to pair.→ Detail: Set up your Even G2 & R1
## 4. Pair over Bluetooth and update firmware ​
Open the Even Realities App and walk through the in-app pairing tutorial. The app then offers a firmware update - take it before doing anything else. Most "it won't connect" reports trace back to stale firmware.→ Detail: Set up your Even G2 & R1
## 5. Enable Developer Mode ​
Developer Mode is what unlocks QR sideload and local testing.
- Sign in to hub.evenrealities.com/login with the same account as the phone app.
- Force-quit and reopen the Even Realities App.
- The Even Hub tab now shows a developer section in the top right.→ Detail: Enable Developer Mode
## 6. Install Node.js & npm ​
You need Node 20 LTS or 22+. Coming from outside the JS world? The detail page walks through nvm, fnm, Homebrew, winget, and the PATH gotchas you'll otherwise hit.→ Detail: Install Node.js & npm
## 7. Install Even Hub tooling ​
bash
```
npm install -g @evenrealities/evenhub-cli @evenrealities/evenhub-simulator
```
→ Detail: Installation
## 8. Run "Hello from G2!" ​
Scaffold the minimal app, start the dev server, and sideload it via QR. If the phone scans the QR but the app never loads, it's almost always a firewall or Wi-Fi AP-isolation issue.Your First App shows both routes: the hand-built Vite route installs the SDK after creating the project directory, while the template route already includes the SDK and only needs npm install for the template dependencies.In a hurry? Templates lists ready-to-run starters from the official evenhub-templates repo.→ Detail: Your First App · Network & Firewall Setup
## If you get stuck ​
|
| | Symptom | Most likely cause | Where to look
| | No way to find the Scan QR entry in the app | Developer Mode not enabled, or app not restarted after web login | Enable Developer Mode
| | QR scans but app never loads | Firewall / AP isolation blocks phone-to-laptop | Network & Firewall Setup
| | node / npm "command not found" | PATH not set after install | Install Node.js & npm
| | Glasses won't connect at all | Still in shipment mode, or stale firmware | Set up your Even G2 & R1PagerPrevious pageOverviewNext pageSign in

View File

@@ -0,0 +1,100 @@
Install Node.js & npm | Documentation
-
-
-
-
-
-
Skip to contentDocumentationSearch⌘CtrlK Main Navigation PortalThemeMenuReturn to top Sidebar Navigation
## Get Started
Overview
### Quickstart
Sign inHardwareInstall Node.js & npmInstall Even Hub toolingYour First AppTemplatesArchitecture
## Build
Page LifecycleDisplay & UI SystemUI/UX Design GuidelinesDevice APIsContextual MenuNetworkingBackground & Lifecycle
## Test
SimulatorLocal TestingPrivate TestingBeta Testing
## Ship
Packaging & DeploymentApp Submission & QA Guidelines
## Reference
GlossaryCLIVersioning PolicyChangelogFAQ
## AI Tooling
Claude CodeOn this pageLast updated: 2026-06-08Even Hub tooling runs on Node.js. Run node -v: if it reports 20.x or 22+, jump to Install Even Hub tooling. Otherwise, start here.Target version: Node 20 LTS or 22+. The SDK declares engines.node = "^20.0.0 || >=22.0.0". Node 18 is not supported.
## macOS - Homebrew ​
First install Homebrew if you don't have it:bash
```
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
```
Then:bash
```
brew install node@22
```
## Windows - official installer ​
Download the LTS installer from nodejs.org and run it. Accept the option to add Node to PATH.
## Linux - NodeSource ​
Distro packages are often outdated. Prefer NodeSource:bash
```
curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -
sudo apt-get install -y nodejs
```
Prefer a version manager? (fnm / nvm / winget)A version manager lets you switch Node versions per project and avoids global-permission headaches.macOS / Linux - fnm or nvmbash
```
# fnm (fast)
curl -fsSL https://fnm.vercel.app/install | bash
# restart your shell, then:
fnm install 22
fnm use 22
# or nvm
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
nvm install 22
nvm use 22
```
Windows - fnm or wingetpowershell
```
# winget (ships with Windows 10/11)
winget install OpenJS.NodeJS.LTS
# or fnm via winget, then:
fnm install 22
fnm use 22
```
## Verify ​
bash
```
node -v # v20.x or v22.x+
npm -v
```
## Common gotchas ​
macOS - PATH after Homebrew. On Apple Silicon, Homebrew installs to /opt/homebrew. If node isn't found after install, ensure your shell profile sources it:bash
```
echo 'eval "$(/opt/homebrew/bin/brew shellenv)"' >> ~/.zprofile
exec zsh
```
Windows - shimmed node.exe. Version managers (fnm/nvm-windows) expose node through a shim. This matters later: Windows Defender may block the shimmed node.exe inbound without prompting - see Hardware → Network & Firewall Setup.Linux/macOS - EACCES on global installs. If npm install -g fails with EACCES, do not sudo npm. Use a version manager (which installs into your home dir), or set a user-owned npm prefix:bash
```
npm config set prefix "$HOME/.npm-global"
export PATH="$HOME/.npm-global/bin:$PATH" # add to your shell profile
```
No global install? Use npx. The CLI runs without a global install:bash
```
npx @evenrealities/evenhub-cli qr --url "http://<your-lan-ip>:5173"
```
Handy on locked-down machines. A global install is faster for repeated use.PagerPrevious pageHardwareNext pageInstall Even Hub tooling

View File

@@ -0,0 +1,50 @@
Install Even Hub tooling | Documentation
-
-
-
-
-
-
Skip to contentDocumentationSearch⌘CtrlK Main Navigation PortalThemeMenuReturn to top Sidebar Navigation
## Get Started
Overview
### Quickstart
Sign inHardwareInstall Node.js & npmInstall Even Hub toolingYour First AppTemplatesArchitecture
## Build
Page LifecycleDisplay & UI SystemUI/UX Design GuidelinesDevice APIsContextual MenuNetworkingBackground & Lifecycle
## Test
SimulatorLocal TestingPrivate TestingBeta Testing
## Ship
Packaging & DeploymentApp Submission & QA Guidelines
## Reference
GlossaryCLIVersioning PolicyChangelogFAQ
## AI Tooling
Claude CodeOn this pageLast updated: 2026-08-25With Node in place, install the two global tools used before you create your first app:bash
```
npm install -g @evenrealities/evenhub-cli @evenrealities/evenhub-simulator
```
The SDK is different: it is a project dependency, not a global tool. Install it only after you have an app project with a package.json.
## Simulator ​
Installed by the global tooling command above.Current version: 0.9.3, tracking SDK 0.0.14. Runs on macOS, Linux, and Windows. Usage and caveats are in Simulator; what each release added is in the Changelog.npm: @evenrealities/evenhub-simulator
## CLI ​
Installed by the global tooling command above. The CLI handles QR sideloading, manifest scaffolding, and .ehpk packaging. It ships an evenhub binary and a shorter eh alias.Current version: 0.1.14. Release history is in the Changelog.npm: @evenrealities/evenhub-cliFull command list in the CLI Reference; the manifest schema and packaging walkthrough is in Packaging & Deployment.
## SDK ​
The SDK is a project dependency - your app imports from it, so it lives in your app's node_modules, not on the system. No -g.Route A in Your First App creates a Vite project first, then runs this command from inside that project directory (the one with package.json):bash
```
npm install @evenrealities/even_hub_sdk@latest
```
Route B uses the official templates, which already include the SDK. For templates, run npm install for the template dependencies; do not install the SDK separately.Current version: 0.0.14, published 2026-08-20, with an Even App floor of 2.2.9. Whatever version you install, match it in app.json's min_sdk_version.It covers display control, input, the contextual menu, tap then long press, audio (glasses or phone mic), IMU, location, photo album and phone camera, device info, and local storage. What each release added is in the Changelog.npm: @evenrealities/even_hub_sdkPagerPrevious pageInstall Node.js & npmNext pageYour First App

View File

@@ -0,0 +1,44 @@
Sign in | Documentation
-
-
-
-
-
-
Skip to contentDocumentationSearch⌘CtrlK Main Navigation PortalThemeMenuReturn to top Sidebar Navigation
## Get Started
Overview
### Quickstart
Sign inHardwareInstall Node.js & npmInstall Even Hub toolingYour First AppTemplatesArchitecture
## Build
Page LifecycleDisplay & UI SystemUI/UX Design GuidelinesDevice APIsContextual MenuNetworkingBackground & Lifecycle
## Test
SimulatorLocal TestingPrivate TestingBeta Testing
## Ship
Packaging & DeploymentApp Submission & QA Guidelines
## Reference
GlossaryCLIVersioning PolicyChangelogFAQ
## AI Tooling
Claude CodeOn this pageLast updated: 2026-06-08Accounts are created in the Even Realities App - the web hub does not have a sign-up flow. Once you have the account from step 1 of the Quickstart, reuse those credentials here:
- Go to hub.evenrealities.com/login.
- Enter the email and password from the app.
- You land on your publisher dashboard - empty for now.
## What this account unlocks ​
|
| | Surface | How the account is used
| | Developer portal | Create, package, submit, and manage apps
| | Developer Mode | The phone app grants local dev access tied to this accountPagerPrevious pageQuickstartNext pageHardware

View File

@@ -0,0 +1,77 @@
Templates | Documentation
-
-
-
-
-
-
Skip to contentDocumentationSearch⌘CtrlK Main Navigation PortalThemeMenuReturn to top Sidebar Navigation
## Get Started
Overview
### Quickstart
Sign inHardwareInstall Node.js & npmInstall Even Hub toolingYour First AppTemplatesArchitecture
## Build
Page LifecycleDisplay & UI SystemUI/UX Design GuidelinesDevice APIsContextual MenuNetworkingBackground & Lifecycle
## Test
SimulatorLocal TestingPrivate TestingBeta Testing
## Ship
Packaging & DeploymentApp Submission & QA Guidelines
## Reference
GlossaryCLIVersioning PolicyChangelogFAQ
## AI Tooling
Claude CodeOn this pageLast updated: 2026-06-04Your First App is the hand-built path. Templates are the opposite - the bridge is wired, the input handlers are in place, and there's a working demo to edit. Reach for one when you want to ship something, not wire one from scratch.
## Where they live ​
All official templates live in one repo:github.com/even-realities/evenhub-templatesThe README has the current list. At time of writing: |
| | Template | What it ships with | Reach for it when
| | minimal | Vite + TypeScript + SDK wired in, single page, click handler | Learning the SDK; one-shot demos
| | text-heavy | Pagination component, large-text rendering, font-measurement helper | Reading apps, long-form content
| | asr | Audio capture wired, transcription scaffolding, mic UI states | Voice notes, transcription, voice control
| | image | Image rendering pipeline, greyscale conversion helper, placeholder swap | Photo viewers, glanceable images
## Use one ​
Pull a template directly with degit:bash
```
npx degit even-realities/evenhub-templates/minimal my-app
cd my-app
npm install
npm run dev
```
Swap minimal for text-heavy, asr, or image. If you live in Claude Code, the /template skill wraps this flow.
## What's the same in every template ​
- Vite + TypeScript project with HMR
- @evenrealities/even_hub_sdk already installed
- app.json manifest pre-filled with sensible defaults (you still need to edit package_id and name)
- src/main.ts boots the bridge with waitForEvenAppBridge() before any other SDK call
- A working index.html and vite.config.ts - keep both as-is
## What you edit ​
- app.json - change package_id (reverse-DNS, globally unique), name, description, and any permissions / network whitelist your app actually needs
- src/ - your app logic. Each template names its own files (pages/, components/, etc.) so consult its README
- public/icon.png - the 24x24 greyscale app icon
## Pulling in template updates later ​
When the template repo ships a fix or a new feature, there is no in-place upgrader. Diff your project against the upstream folder and copy what you need. Migration notes for breaking changes live in the template's README.
## When to skip the template ​
- You're learning the raw SDK. Templates hide bridge mechanics behind helpers - do Your First App at least once first.
- Tight bundle budget. Templates bundle demo code and helpers. Trim before shipping.
- The template's specialty doesn't match yours. minimal is rarely wrong; the others add code you may end up deleting.
## Related ​
- Your First App - manual scaffold walkthrough
- Display & UI System - what containers and primitives templates compose with
- Design Guidelines - styling and layout rules templates already respect
- Background & Lifecycle - what the template lifecycle handlers automatePagerPrevious pageYour First AppNext pageArchitecture

View File

@@ -0,0 +1,127 @@
Changelog | Documentation
-
-
-
-
-
-
Skip to contentDocumentationSearch⌘CtrlK Main Navigation PortalThemeMenuReturn to top Sidebar Navigation
## Get Started
Overview
### Quickstart
Sign inHardwareInstall Node.js & npmInstall Even Hub toolingYour First AppTemplatesArchitecture
## Build
Page LifecycleDisplay & UI SystemUI/UX Design GuidelinesDevice APIsContextual MenuNetworkingBackground & Lifecycle
## Test
SimulatorLocal TestingPrivate TestingBeta Testing
## Ship
Packaging & DeploymentApp Submission & QA Guidelines
## Reference
GlossaryCLIVersioning PolicyChangelogFAQ
## AI Tooling
Claude CodeOn this pageLast updated: 2026-08-25A feature rarely lands in one package. The SDK exposes the API, the simulator learns to render it, the CLI adjusts what it stamps at pack time, and the Even Realities App has to be new enough to carry it. This page groups releases by the area they moved, so one rollout reads as one entry instead of a version number per package.Dates are npm publish dates - the day a version became installable. The raw per-package lists live in each package README on npm: SDK, CLI, simulator.
## Current versions ​
|
| | Package | Version | Shipped
| | SDK @evenrealities/even_hub_sdk | 0.0.14 | 2026-08-20
| | CLI @evenrealities/evenhub-cli | 0.1.14 | 2026-08-20
| | Simulator @evenrealities/evenhub-simulator | 0.9.3 | 2026-08-22Install commands are in Install Even Hub tooling; what to pin and when to bump is in Versioning Policy.
## Release map ​
|
| | Area | Latest move | Packages
| | Contextual menu and tap then long press | 2026-08 | SDK 0.0.14, simulator 0.9.0-0.9.1, CLI 0.1.14
| | Display and images | 2026-08 | SDK 0.0.12 and 0.0.14, simulator 0.8.0 and 0.9.0-0.9.3
| | Phone and sensor access | 2026-08 | SDK 0.0.11 and 0.0.14
| | Version floors and packaging | 2026-08 | SDK 0.0.13, CLI 0.1.14
| | Simulator automation | 2026-04 | Simulator 0.7.0-0.7.3, CLI 0.1.12-0.1.13
| | Earlier releases | 2026-04 | SDK 0.0.1-0.0.10, CLI 0.1.5-0.1.11, simulator 0.1.0-0.6.2
## Contextual menu and tap then long press ​
The overlay the glasses OS raises on tap then long press, plus the gesture itself reaching your app as a pair of events. Guides: Contextual Menu and Device APIs. |
| | Package | Version | What landed | Shipped
| | Simulator | 0.9.1 | long_press and long_press_release on the automation API, so both the gesture and the menu are scriptable in CI | 2026-08-21
| | SDK | 0.0.14 | menuObject on createStartUpPageContainer and rebuildPageContainer - up to 10 action items, with menuItemClickEvent delivered through onEvenHubEvent and SDK-side validation of item count, ID uniqueness, and UTF-8 name length. LONG_PRESS_EVENT (9) and LONG_PRESS_RELEASE_EVENT (10) parsing on list, text, and system events | 2026-08-20
| | Simulator | 0.9.0 | Draws and navigates your menu; simulates the gesture in the window with holdable controls and a keyboard shortcut; context_menu action on the automation API | 2026-08-20
| | CLI | 0.1.14 | Stamps the Even App 2.2.9 floor at pack time | 2026-08-20Needs Even App 2.2.9. On an older app your items silently never appear, so the CLI stamps the floor and the plugin is blocked at open. See the open-time gate.
## Display and images ​
Text brightness, container stacking, and the image transfer path. Guide: Display & UI System. |
| | Package | Version | What landed | Shipped
| | Simulator | 0.9.3 | Grayscale conversion for encoded images matches the glasses; cover resizing and center cropping match when image and container dimensions differ | 2026-08-22
| | Simulator | 0.9.2 | Accepts SDK raw Gray8 and packed Gray4 image data alongside encoded images | 2026-08-22
| | SDK | 0.0.14 | textColor on text containers and on textContainerUpgrade - five brightness levels, 0 to 4. updateImageRawData holds the image path for 100ms and flushes a held call on the next one | 2026-08-20
| | Simulator | 0.9.0 | Renders the five textColor levels | 2026-08-20
| | SDK | 0.0.12 | zOrderIndex on list, text, and image containers - larger renders in front, all-or-nothing per page, values unique. updateImageRawData payloads are LZ4-compressed in transit, with no code change | 2026-07-10
| | Simulator | 0.8.0 | Sorts draw order by zOrderIndex with the same all-or-nothing and uniqueness rules the SDK enforces; caps decoded image pixel dimensions | 2026-07-08Two things the simulator will not settle for you:
- It does not decompress LZ4 - it decodes payload bytes as an ordinary uncompressed image. Validate the compressed transfer path on hardware.
- Its brightness levels are not photometrically matched to the glasses. Use them to check hierarchy; sign off on legibility on hardware.
## Phone and sensor access ​
What a plugin can reach beyond the display. Guide: Device APIs. |
| | Package | Version | What landed | Shipped
| | SDK | 0.0.14 | direction and speakerRole on AudioEvent - direction is the raw glasses direction tag for that PCM frame, speakerRole is an AudioSpeakerRole of self, other, or unknown | 2026-08-20
| | SDK | 0.0.11 | One-shot location and continuous location updates; phone album image picker, single-select; phone camera capture; mic source selection between glasses and phone | 2026-06-22speakerRole comes from an app-side algorithm and carries no firmware identity guarantee. Phone-mic capture and older host apps fall back to direction: null and speakerRole: unknown, so code written before 0.0.14 keeps working unchanged.
## Version floors and packaging ​
You stop hand-writing the phone-app floor; the CLI derives it from your SDK version. Guide: Auto-deriving min_app_version. |
| | Package | Version | What landed | Shipped
| | CLI | 0.1.14 | Derives and stamps min_app_version at pack time; adds --sdk-ver and --enforce-manual-version; adds shell completion for evenhub and eh; returns a failing exit status when packing fails | 2026-08-20
| | SDK | 0.0.13 | First release to publish its floor as minAppVersion in npm metadata - 2.2.6 | 2026-07-31SDK 0.0.12 and older predate the minAppVersion field. npm versions are immutable, so those floors cannot be backfilled - the CLI falls back to a bundled map and prints an info line.
## Simulator automation ​
The simulator gained an HTTP control surface, which is what makes CI runs and agent-driven testing possible. Guide: Headless automation. |
| | Package | Version | What landed | Shipped
| | CLI | 0.1.13 | TERM / COLORTERM detection when printing a QR code in the terminal | 2026-04-20
| | Simulator | 0.7.3 | Native webview capture on each desktop platform, fixing blank /api/screenshot/webview results; list items capped at 63 bytes and 20 items | 2026-04-20
| | CLI | 0.1.12 | qrcode-terminal for terminal QR codes, qr-image for external ones; login command fix | 2026-04-16
| | Simulator | 0.7.2 | Emoji rendering coverage; steadier bounce and spring animations | 2026-04-15
| | Simulator | 0.7.1 | Firmware-matching fixes - no scrollbar past full screen, capped single-container width and height, 999-byte text container limit | 2026-04-09
| | Simulator | 0.7.0 | --automation-port <PORT> starts an HTTP server for screenshots, console logs, and glasses input. Default border_color becomes 0 (invisible) to match the glasses. Unknown-glyph handling matches firmware | Not on npm0.7.0 has upstream notes but no npm release. The first installable build carrying the automation API is 0.7.1, published 2026-04-09.
## Earlier releases ​
Notes below are the upstream package entries, unedited in substance. Coverage is partial in both directions: SDK 0.0.2 through 0.0.7 and simulator 0.1.1, 0.1.2, 0.4.0, and 0.6.0 shipped without upstream notes, while simulator 0.2.0, 0.2.2, and 0.5.1 have notes but were never published to npm. Rows marked Not on npm were never installable.SDK 0.0.1 - 0.0.10 |
| | Version | What landed | Shipped
| | 0.0.10 | Stronger WebView background keep-alive | 2026-04-10
| | 0.0.9 | EventSourceType compatibility, a default source enum fallback, refined event source parsing | 2026-03-25
| | 0.0.8 | Launch source events appMenu and glassesMenu; startup containers raised from 4 to 12; IMU control and IMU data events | 2026-03-25
| | 0.0.2 - 0.0.7 | No upstream notes | 2026-01-22 to 2026-02-11
| | 0.0.1 | Initial bridge, storage, device info, EvenHub protocol, and event APIs | 2026-01-22CLI 0.1.5 - 0.1.11 |
| | Version | What landed | Shipped
| | 0.1.11 | Windows packing fix | 2026-03-25
| | 0.1.10 | app.json restriction adjustments | 2026-03-24
| | 0.1.9 | app.json size limit adjustments | 2026-03-21
| | 0.1.8 | app.json format adjustments | 2026-03-19
| | 0.1.7 | -d option fix on init | 2026-03-13
| | 0.1.6 | app.json format adjustments | 2026-03-12
| | 0.1.5 | First published release, no upstream notes | 2026-01-28Simulator 0.1.0 - 0.6.2 |
| | Version | What landed | Shipped
| | 0.6.2 | Container limits brought closer to firmware | 2026-03-25
| | 0.6.1 | Tracks SDK 0.0.8 - borderRadius typo fixed and unknown property fields now error; Sys_ItemEvent gains fields, with eventSource hardcoded to 1 (TOUCH_EVENT_FROM_GLASSES_R), imuData always null, and systemExitReasonCode ignored | 2026-03-25
| | 0.6.0 | No upstream notes | 2026-03-25
| | 0.5.3 | Logs the original JSON to stdout on payload parse errors under RUST_LOG=debug; x_position / y_position become i32 | 2026-03-03
| | 0.5.2 | 4-bit color rendering; brightness filter removed from the glasses canvas | 2026-02-28
| | 0.5.1 | Rendering mechanism adjustments; better image rendering | Not on npm
| | 0.5.0 | Screenshots | 2026-02-27
| | 0.4.1 | Performance work; a flag to print the default config file location; completion command fix | 2026-02-20
| | 0.4.0 | No upstream notes | 2026-02-20
| | 0.3.2 | Config file location description fix | 2026-02-19
| | 0.3.1 | Audio input device listing format | 2026-02-17
| | 0.3.0 | Shell completion | 2026-02-17
| | 0.2.2 | Audio resampling and input device selection; config file support; more flags to override config options | Not on npm
| | 0.2.0 | lvgl-sys upgraded to v9; lighter CJK font; preliminary audio event support | Not on npm
| | 0.1.2 | No upstream notes | 2026-02-13
| | 0.1.1 | No upstream notes | 2026-02-13
| | 0.1.0 | First published release, no upstream notes | 2026-02-13
## Related ​
- Versioning Policy - semver contract, deprecation windows, pinning, and the min_app_version gate
- Install Even Hub tooling - install commands and the current version of each package
- Packaging & Deployment - what the CLI stamps into your .ehpkPagerPrevious pageVersioning PolicyNext pageFAQ

112
docs/pages/reference_cli.md Normal file
View File

@@ -0,0 +1,112 @@
CLI | Documentation
-
-
-
-
-
-
Skip to contentDocumentationSearch⌘CtrlK Main Navigation PortalThemeMenuReturn to top Sidebar Navigation
## Get Started
Overview
### Quickstart
Sign inHardwareInstall Node.js & npmInstall Even Hub toolingYour First AppTemplatesArchitecture
## Build
Page LifecycleDisplay & UI SystemUI/UX Design GuidelinesDevice APIsContextual MenuNetworkingBackground & Lifecycle
## Test
SimulatorLocal TestingPrivate TestingBeta Testing
## Ship
Packaging & DeploymentApp Submission & QA Guidelines
## Reference
GlossaryCLIVersioning PolicyChangelogFAQ
## AI Tooling
Claude CodeOn this pageLast updated: 2026-08-20The CLI (v0.1.14) handles QR sideloading, manifest scaffolding, and app packaging.
## Installation ​
Install globally so the evenhub binary is on your PATH:bash
```
npm install -g @evenrealities/evenhub-cli
```
Alternatively, pin the version per-repo:bash
```
npm install -D @evenrealities/evenhub-cli
```
npm: @evenrealities/evenhub-cli
### eh shortcut ​
The CLI also installs eh as a shorter alias. The two are interchangeable:bash
```
eh qr --url ... # same as: evenhub qr --url ...
eh pack app.json dist
eh init
```
## Commands ​
### evenhub init ​
Generate a starter app.json manifest in the current or specified directory.bash
```
evenhub init
evenhub init -d ./my-project
evenhub init -o ./config/app.json
```
|
| | Option | Description
| | -d, --directory <dir> | Directory to create the file in (default: ./)
| | -o, --output <path> | Output file path (overrides --directory)
### evenhub qr ​
Generate a QR code for sideloading your app during development.bash
```
# Simplest usage - provide the full URL
evenhub qr --url "http://192.168.1.100:5173"
# Or build the URL from parts
evenhub qr -i 192.168.1.100 -p 5173 --path /my-app
# Output to a file instead of terminal
evenhub qr --url "http://192.168.1.100:5173" -e
```
|
| | Option | Description
| | -u, --url <url> | Full URL (ignores other URL options)
| | -i, --ip <ip> | IP address or hostname
| | -p, --port <port> | Port number
| | --path <path> | URL path
| | --https | Use HTTPS instead of HTTP
| | --http | Use HTTP (default)
| | -e, --external | Open QR in external program instead of terminal
| | -s, --scale <n> | Scale factor for file output (default: 4)
| | --clear | Clear cached scheme, IP, port, and pathScan it with the Even Realities App. Your app loads on the glasses with hot reload live.
### evenhub pack ​
Package your built app into an .ehpk file for distribution.bash
```
evenhub pack app.json dist -o myapp.ehpk
```
|
| | Argument / Option | Description
| | <json> | Path to your app.json manifest
| | <project> | Path to your built output folder (dist, build, etc.)
| | -o, --output <file> | Output filename (default: out.ehpk)
| | --no-ignore | Include hidden files (dotfiles)
| | -c, --check | Check if the package_id is available on Even Hub
| | --sdk-ver <version> | SDK version you built against; the CLI reads its minAppVersion from npm and stamps it as the .ehpk min_app_version floor. Omit to use the latest published SDK.
| | --enforce-manual-version | Stamp min_app_version from app.json as-is, even below the SDK floor. Local testing only; produces a non-submittable build.min_app_version is derived from your SDK version at pack time (CLI 0.1.14+) - pin it with --sdk-ver. See Auto-deriving min_app_version for the resolution rules, warnings, and offline fallback.Since 0.1.14 a failed pack exits non-zero, so a packaging step in CI fails the run instead of passing silently on a missing .ehpk.See Packaging & Deployment for the full app.json schema, validation rules, and troubleshooting guide.
## Shell completions ​
Generate completions for your shell:bash
```
evenhub --completion-bash # Bash
evenhub --completion-zsh # Zsh
evenhub --completion-fish # Fish
```
PagerPrevious pageGlossaryNext pageVersioning Policy

124
docs/pages/reference_faq.md Normal file
View File

@@ -0,0 +1,124 @@
FAQ | Documentation
-
-
-
-
-
-
Skip to contentDocumentationSearch⌘CtrlK Main Navigation PortalThemeMenuReturn to top Sidebar Navigation
## Get Started
Overview
### Quickstart
Sign inHardwareInstall Node.js & npmInstall Even Hub toolingYour First AppTemplatesArchitecture
## Build
Page LifecycleDisplay & UI SystemUI/UX Design GuidelinesDevice APIsContextual MenuNetworkingBackground & Lifecycle
## Test
SimulatorLocal TestingPrivate TestingBeta Testing
## Ship
Packaging & DeploymentApp Submission & QA Guidelines
## Reference
GlossaryCLIVersioning PolicyChangelogFAQ
## AI Tooling
Claude CodeOn this pageLast updated: 2026-08-25Most "can I do X?" questions have a one-line answer plus a pointer. That's what this page is, grouped by surface. If yours isn't here, check the Glossary or open an issue in the dev portal.
## Display & rendering ​
|
| | Q | A
| | Can I draw arbitrary pixels? | No. The display surface is a container-based model - text, images, and lists. See Display & UI System.
| | Can I render full-color content? | No. The Even G2 display is monochrome green, 4-bit (16 shades). Source assets must be greyscale; the hardware tints them.
| | Can I show video / animated GIFs? | No native support. You can simulate animation by sequencing textContainerUpgrade or updateImageRawData calls - BLE bandwidth (~10–30 KB/s) limits practical frame rate.
| | Can I overlay containers (z-order)? | Yes. SDK 0.0.12+ adds explicit zOrderIndex - larger renders in front; all-or-nothing per page and values unique. Pages that omit it everywhere fall back to declaration order (later renders on top). See Display & UI System.
| | Do I need to opt in to image compression? | No. SDK 0.0.12+ LZ4-compresses updateImageRawData payloads in transit automatically - zero code change, smaller transfers, faster image updates.
| | Why LZ4 instead of a higher-ratio codec? | LZ4 has the lowest decompression memory footprint and near-instant encode/decode, which fits the glasses' RTOS memory budget. Compression ratio was a deliberate trade-off, not the priority.
| | Can I change text brightness? | Yes. SDK 0.0.14+ adds textColor on text containers - five levels, 0-4, despite the name it is brightness not color. Omit it for the device default 4. See Display & UI System.
| | Can I use my own font? | No. The glasses render with a fixed system font. Sizing/spacing follows Design Guidelines.
| | What size is the canvas? | 576×288 monochrome green pixels. See Overview.
| | Can I render emoji? | No. Use Unicode geometric / box-drawing characters instead. See Glossary.
## Input & sensors ​
|
| | Q | A
| | Can I detect a tap then long press? | Yes. SDK 0.0.14+ (Even App 2.2.9+) fires LONG_PRESS_EVENT when the press starts and LONG_PRESS_RELEASE_EVENT when it lifts - two separate events, ordinary list/text/sys routing. See Device APIs → Tap then long press.
| | Can I add my own items to the glasses menu? | Yes. SDK 0.0.14+ (Even App 2.2.9+) lets you attach up to 10 action items via menuObject on create/rebuild; selections arrive as menuItemClickEvent. See Contextual Menu.
| | Can a menu item show state, like a toggle? | No. Every item is fire-and-forget - one event, menu closes, the glasses never re-render the label. Re-declare the menu to change what it reads. See Contextual Menu.
| | Can I read raw IMU sensor data? | Yes. IMU_DATA_REPORT events stream at configurable rate. See Device APIs → IMU. A units table is still TBD.
| | Can I pick which microphone to capture? | Yes - pass AudioInputSource.Glasses or AudioInputSource.Phone to audioControl(true, ...). Default is glasses. Each source is mono. Per-mic capture inside the glasses four-mic array + DOA angle reporting is on the roadmap. See Device APIs → Audio.
| | Can I trigger haptics on the glasses or R1? | No. No haptic actuator.
| | Can I detect when the glasses are being worn? | Yes. Wearing-detection events fire on put-on / take-off. See Device APIs.
| | Can I read battery level? | Yes. Device info API exposes battery percentage. See Device APIs.
## Networking ​
|
| | Q | A
| | Can I fetch() arbitrary URLs? | No. The domain must be in your app.json network whitelist. See Networking.
| | Does the network whitelist bypass CORS? | No. Whitelist is necessary but not sufficient - remote API still needs Access-Control-Allow-Origin.
| | Can I open a WebSocket? | Yes - same whitelist rules. Expect drops when the WebView backgrounds. See Background & Lifecycle.
| | Can I open a deep link in the system browser? | TBD. Currently no window.open(url, '_system') equivalent. Track via the dev portal.
| | Can I receive push notifications? | No. Plugins are foreground-only on the glasses. The phone app receives notifications and may surface them in its own UI.
| | Can I make network calls while backgrounded? | No. WebView is suspended on background; in-flight requests stall. Plan for resume. See Background & Lifecycle.
## Storage & state ​
|
| | Q | A
| | Can I use localStorage? | Yes - survives suspension, kill, and update. Cleared on uninstall.
| | Can I use IndexedDB or OPFS? | Yes, but quotas are not yet documented. Treat as "best-effort, persistent." Deep-dive coming.
| | Does state sync between the phone and the glasses? | No - app state lives in the WebView on the phone. The glasses are a render target, not a state store.
| | Can I share state between two of my apps on the same device? | No. Storage is sandboxed per package_id.
| | Can I read/write the user's media library or files? | Read: limited. A user-driven single-image picker via pickImageFromAlbum() (with the album permission) returns one AppImageAsset. No bulk read, no write, no file system, no clipboard. See Device APIs → Photos.
## Packaging & submission ​
|
| | Q | A
| | Can I publish without a developer account? | No. Account at hub.evenrealities.com is required. See Sign in.
| | Can I roll back a released version? | No. Fix-forward only. See Submission Flow.
| | Do I set min_app_version myself? | No. The CLI derives it from your SDK version at pack time and stamps it into the .ehpk. Declare it in app.json only to pin a stricter floor; --enforce-manual-version overrides it for local testing. See Auto-deriving min_app_version.
| | Why won't my plugin open on an older app? | Its min_app_version (the SDK floor it was packed against) is higher than the user's Even Realities App version, so the app blocks it at open with an update prompt - shown on the glasses if launched there. The user updates the app to open it. See the open-time gate.
| | Can I commit my API key to the .ehpk? | No - anyone with the released .ehpk can extract bundled contents. Move keys behind a server-side proxy. See Submission Flow.
| | Can I edit app.json after submission? | Not after Submitted state. Edit metadata in Draft / Test only.
| | Can I publish a hotfix without going through review? | No. Every version goes through the same review path. Hotfix versions are usually approved faster but not bypassed.
| | Can I ship to specific countries / regions? | Not currently. All Released versions are globally visible.
| | Can I price my app? | TBD. No paid distribution yet.
| | What's the maximum .ehpk size? | Practical cap is currently ~10 MB - larger packages still upload but degrade install UX over BLE.
## Testing ​
|
| | Q | A
| | Can I test on the simulator alone? | For UI/logic work, yes. For backgrounding, real permissions, or BLE timing, you need hardware. See Testing Modes.
| | Can I share a build with a teammate? | Yes - via Beta groups in the dev portal. They install from the phone app's Beta tester section.
| | Can I automate sim testing in CI? | Yes - the simulator exposes an HTTP API. See Headless Testing.
| | Can I install two of my own apps side-by-side? | Yes. Each has its own package_id, its own container, its own state.
| | Can I see device logs from real hardware? | Console output appears in the phone app's dev console when in Developer Mode. See Enable Developer Mode.
## Languages & locales ​
|
| | Q | A
| | Which languages are supported in supported_languages? | Currently: en, de, fr, es, it, zh, ja, ko.
| | How are translations selected at runtime? | Device locale → first match in supported_languages → first item if none match.
| | How do I localize my UI strings? | TBD - Internationalisation page coming. For now, switch on bridge.getDeviceInfo().locale.
| | Can I add a locale not in the supported list? | No. Submission validation rejects unknown locales.
## Privacy & permissions ​
|
| | Q | A
| | Does my app see the user's name / email? | Only with the user-info permission, and only what they consented to share.
| | Does my app see other apps' data? | No. Strict per-package_id sandbox.
| | Does my app see ambient audio when not actively capturing? | No. Capture only fires while audioControl(true) is held and the user has granted the mic permission.
| | Can my app access the camera? | Not on the glasses - the Even G2 has no camera (privacy-by-design). It can capture from the phone camera via captureImageFromCamera() with the camera permission. See Device APIs → Photos.
## Misc ​
|
| | Q | A
| | Can I build with React / Vue / Svelte? | Yes - the SDK is framework-agnostic. The boilerplate uses vanilla TS for size; bring whatever framework you prefer.
| | Can I use TypeScript? | Yes - it's the recommended path. The SDK ships full type definitions.
| | Can I use ESM imports? | Yes - the SDK is ESM-first. Vite handles bundling.
| | Can I use AI tooling (Claude Code) to develop? | Yes - there's a dedicated AI Tooling section with the skill catalog.
## Related ​
- Glossary - definitions referenced above
- Versioning Policy - when "TBD" becomes "available"
- Architecture - the model these answers compose againstPagerPrevious pageChangelogNext pageAI Tooling

View File

@@ -0,0 +1,102 @@
Glossary | Documentation
-
-
-
-
-
-
Skip to contentDocumentationSearch⌘CtrlK Main Navigation PortalThemeMenuReturn to top Sidebar Navigation
## Get Started
Overview
### Quickstart
Sign inHardwareInstall Node.js & npmInstall Even Hub toolingYour First AppTemplatesArchitecture
## Build
Page LifecycleDisplay & UI SystemUI/UX Design GuidelinesDevice APIsContextual MenuNetworkingBackground & Lifecycle
## Test
SimulatorLocal TestingPrivate TestingBeta Testing
## Ship
Packaging & DeploymentApp Submission & QA Guidelines
## Reference
GlossaryCLIVersioning PolicyChangelogFAQ
## AI Tooling
Claude CodeOn this pageLast updated: 2026-08-25Quick lookup when a term in the docs doesn't ring a bell. Linked entries point to the deep-dive page.
## Hardware ​
|
| | Term | Definition
| | Even G2 | The smart glasses themselves - dual 576×288 monochrome green micro-LED displays, temple touchpads, four-mic array, Bluetooth 5.2. Canonical name; "G2" alone is acceptable in code/UI but spell it out in docs.
| | Even R1 | Optional input ring - same touchpad gestures as the Even G2 temples (tap, double-tap, swipe up/down, tap then long press). Delivered as ring input events alongside temple events.
| | Temple touchpad | The touch surface on each arm of the Even G2. Source of CLICK_EVENT, DOUBLE_CLICK_EVENT, SCROLL_TOP_EVENT, SCROLL_BOTTOM_EVENT, and LONG_PRESS_EVENT / LONG_PRESS_RELEASE_EVENT (SDK 0.0.14+).
| | Tap then long press | Canonical name for the gesture on the Even G2 temple and Even R1 touchpads. Raises the contextual menu when the OS owns the press; otherwise reaches your app as LONG_PRESS_EVENT / LONG_PRESS_RELEASE_EVENT (SDK 0.0.14+, Even App 2.2.9+). Write it this way in prose - not "long press", "tap and hold", or "press and hold". The SDK event names keep the older LONG_PRESS spelling. See Device APIs → Tap then long press.
| | Charging case | Stores and charges the Even G2. The status light (white when ready, orange briefly on insertion) is the indicator for shipping mode.
| | Shipping mode | Low-power state new Even G2s ship in to prevent battery drain in transit. Exit by dropping both arms into the plugged-in charging case until the case light comes on.
## Software platform ​
|
| | Term | Definition
| | Even Hub | The plugin platform inside the Even Realities phone app. Hosts third-party apps in a WebView and bridges them to the glasses.
| | Even Hub SDK | The npm package @evenrealities/even_hub_sdk - typed methods + event model for talking to the glasses from inside the WebView.
| | Even Realities App | The Flutter phone app that hosts your plugin's WebView and relays SDK calls to the glasses over Bluetooth.
| | Developer portal | hub.evenrealities.com - where you sign up, upload .ehpk builds, manage Beta groups, and submit for review.
| | Developer Mode | The Even Realities App state where developer features (Scan QR, Private/Beta install screens) are visible. There's no toggle - sign in to hub.evenrealities.com/login with the same account, restart the phone app, and the developer section appears in the top-right of the Even Hub tab. See Enable Developer Mode.
| | Bridge | The JavaScript-to-native interface EvenAppBridge injected into the WebView by the SDK. Routes method calls to the phone app and events back.
## Package format ​
|
| | Term | Definition
| | .ehpk | The Even Hub package format - a zip of your built web assets plus the manifest. Produced by evenhub pack. The current and canonical extension; older docs/Figma may show .ehp or .evenpkg (both deprecated).
| | app.json | The manifest at the root of every Even Hub project. Declares package_id, version, permissions, network whitelist, supported_languages, and more. See Packaging.
| | edition | The platform-contract version your app targets. Currently "202601". Bumps when the bridge or manifest schema makes a breaking change.
| | package_id | Globally unique reverse-DNS identifier for your app, e.g. com.exampleco.exampleapp. Lowercase letters and numbers only - no hyphens, no underscores, no uppercase. Permanent - once Released, you cannot change it.
| | min_sdk_version | The minimum SDK version your app supports. Declared in the manifest; required field.
| | min_app_version | The minimum Even Realities App version your build runs on. Optional in the manifest - the CLI derives it from your SDK version at pack time and stamps it into the .ehpk. A plugin whose min_app_version exceeds the user's app version is blocked at open with an update prompt. See Auto-deriving min_app_version.
## Runtime concepts ​
|
| | Term | Definition
| | Page container | A rectangular region on the glasses display that can show text, lists, or images. Apps render content by creating, updating, and destroying containers. See Page Lifecycle.
| | createStartUpPageContainer | The SDK call that produces your app's initial screen. Runs exactly once at boot.
| | textContainerUpgrade | Flicker-free text update for an existing container. Use this for any in-place text refresh.
| | rebuildPageContainer | Full screen redraw - flickers on hardware. Reserve for layout changes (adding/removing containers).
| | shutDownPageContainer(mode) | Exit the app. Mode 0 = immediate; mode 1 = show the system exit-confirmation dialog (required for review).
| | zOrderIndex | Optional stacking index on list/text/image containers (SDK 0.0.12+). Larger renders in front. All-or-nothing per page, values unique. See Display & UI System.
| | textColor | Optional text-container brightness, levels 0-4 (SDK 0.0.14+). Not a color - the display is monochrome green. Omitted on create/rebuild means device default 4; omitted on textContainerUpgrade means unchanged. Distinct from borderColor, which is still 0-15. See Display & UI System.
| | Contextual menu | The overlay the glasses OS raises on tap then long press. Holds permanent system slots - Display off, Brightness, Close - plus up to 10 action items your app declares (SDK 0.0.14+, Even App 2.2.9+). See Contextual Menu.
| | menuObject | The contextual-menu declaration attached to createStartUpPageContainer / rebuildPageContainer. Replaced wholesale, never merged; omitting it on a rebuild clears the menu.
| | menuItemClickEvent | The event fired when the user selects one of your menu items. Carries the itemID you assigned and nothing else. Arrives on onEvenHubEvent regardless of which container has isEventCapture: 1.
## Testing & shipping ​
|
| | Term | Definition
| | Simulator | Desktop window that renders the glasses canvas without hardware. See Simulator.
| | QR sideload | Run your dev server, generate a QR via the CLI, scan it with the phone app. Hot reload, but dies when the WebView backgrounds. See Local Testing.
| | Private build | .ehpk you install to your own glasses for pre-submission smoke. See Private Testing.
| | Beta build | .ehpk distributed to yourself via a Beta group - the only mode that mirrors production lifecycle. Required for clearing review. See Beta Testing.
| | Beta group | A list of testers in the dev portal. Builds pushed to a group become installable by every member from their phone app's Beta tester screen.
| | Submission state | One of Draft, Test, Submitted, Released. See Submission Flow.
| | Fix-forward | The rule that Released versions cannot be edited or rolled back - only superseded by a higher version.
## Networking ​
|
| | Term | Definition
| | Network whitelist | The network array in app.json. An Even-side permission check that allows your app to fetch() listed domains. Does not bypass CORS.
| | CORS | Browser-enforced. Your WebView still needs Access-Control-Allow-Origin headers from any remote API - the whitelist is necessary but not sufficient.
| | AP isolation | A Wi-Fi router feature that blocks device-to-device traffic on the same SSID. Common on corporate / guest networks. Breaks QR sideload silently. See Network & Firewall Setup.
## Tooling ​
|
| | Term | Definition
| | evenhub (CLI) | The npm package @evenrealities/evenhub-cli. Subcommands: init, qr, pack. See CLI Reference.
| | evenhub-simulator | The npm package @evenrealities/evenhub-simulator. Standalone desktop simulator.
| | Template | Pre-wired starter pulled from evenhub-templates - minimal, text-heavy, asr, image. See Templates.
## Related ​
- Versioning Policy - SDK semver rules and migration windows
- FAQ - "Can I do X?" answersPagerPrevious pageReferenceNext pageCLI

View File

@@ -0,0 +1,105 @@
Versioning Policy | Documentation
-
-
-
-
-
-
Skip to contentDocumentationSearch⌘CtrlK Main Navigation PortalThemeMenuReturn to top Sidebar Navigation
## Get Started
Overview
### Quickstart
Sign inHardwareInstall Node.js & npmInstall Even Hub toolingYour First AppTemplatesArchitecture
## Build
Page LifecycleDisplay & UI SystemUI/UX Design GuidelinesDevice APIsContextual MenuNetworkingBackground & Lifecycle
## Test
SimulatorLocal TestingPrivate TestingBeta Testing
## Ship
Packaging & DeploymentApp Submission & QA Guidelines
## Reference
GlossaryCLIVersioning PolicyChangelogFAQ
## AI Tooling
Claude CodeOn this pageLast updated: 2026-08-25The Even Hub SDK follows semantic versioning - MAJOR.MINOR.PATCH - with explicit guarantees about what each bump means for your code.
## Semver contract ​
|
| | Bump | Means | What can break
| | PATCH (0.0.13 → 0.0.14) | Bug fix, internal refactor, or - while the SDK is pre-1.0 - an additive, backward-compatible feature (new optional field, new opt-in behavior). Every feature shipped to date arrived this way; the Changelog lists them. | Nothing for code that doesn't opt in to the new field/behavior. Safe upgrade, but read the release notes - 0.0.x patch bumps can add public surface.
| | MINOR (0.0.x → 0.1.0) | New methods, new event types, new manifest fields | Old code keeps working. Deprecated methods may emit console warnings. Not used yet - every pre-1.0 release to date has shipped as a 0.0.x patch bump instead.
| | MAJOR (0.x → 1.0) | Breaking changes to method signatures, event payloads, or edition | Existing code may need migration. Follow the migration guide for that release.The current SDK is in the 0.x series - the platform is signalling "not yet API-frozen." While it's in 0.0.x, every release - bug fix or new feature alike - bumps the trailing digit; there's no separate MINOR release to watch for. Read the release notes for every 0.0.x release before upgrading production apps.
## Deprecation window ​
When a method or field is deprecated:
- N (current MINOR) - method continues to work; logs a [DEPRECATED] warning in the dev console.
- N+1 - method continues to work; warning becomes a stack-traced error in the dev console (but no runtime failure).
- N+2 - method is removed. Calling it throws.This gives you two MINOR releases to migrate. For example, if oldFoo() is deprecated in 0.2.0, it works through 0.3.x and is removed in 0.4.0.PATCH releases never deprecate anything.
## min_sdk_version migration ​
Your app's app.json declares min_sdk_version. Bumping it is a one-way trip: users on older firmware can no longer install or update to that version. |
| | Situation | What to set min_sdk_version to
| | You use only methods present in your current SDK | Match the SDK you npm install-ed
| | You added a new SDK method introduced in a later version | Match the version where that method first shipped
| | You hit a bug fix that's only in the latest PATCH | Bump to that PATCH version
| | You don't know which method needs which version | Run npm list @evenrealities/even_hub_sdk and use that version - conservative but safeDon't bump preemptively. Higher min_sdk_version strands users on older firmware.
## min_app_version and the open-time gate ​
min_sdk_version (above) is the firmware/SDK floor you set by hand. min_app_version is the phone-app floor - the oldest Even Realities App your build runs on - and you don't set it: the CLI derives it from your SDK version at pack time. When an SDK release adds a bridge API that only a newer app implements, that release publishes the required app version with itself, and evenhub pack reads it and stamps the floor. See Auto-deriving min_app_version. |
| | | min_sdk_version | min_app_version
| | Who sets it | You, in app.json | The CLI, derived from your SDK version at pack time
| | What it gates | Install / update on older firmware | Opening the plugin on an older phone app
### What the floor does at runtime ​
The Even Realities App checks a plugin's min_app_version against its own version when the plugin is opened:
- App at or above the floor - the plugin opens normally.
- App below the floor - the plugin is blocked at open with a prompt to update the Even Realities App. No partial launch, no SDK calls. Opening from the glasses is caught the same way - the phone app is always the host - and the block shows as a short message on the glasses telling the user to update on their phone.The check is local and runs only at open; there's no version block at download or update. Keeping the floor accurate - which auto-derivation does for you - is what stops a working plugin from being needlessly blocked, or a broken one from slipping onto an app too old to run it.
## Reading the changelog ​
The Changelog groups every release by the feature it delivered, across the SDK, the CLI, and the simulator. Read it before upgrading - it is the fastest way to see whether a bump touches anything your app uses.Each npm package also carries its own raw list in a Changelog section of its README, in the usual shape:
- Added - new methods, events, manifest fields (safe to ignore if you don't need them)
- Changed - non-breaking changes to existing behavior (read for caveats)
- Deprecated - see deprecation window above
- Removed - methods removed at the end of their N+2 cycle (MAJOR bumps only)
- Fixed - bug fixes (read; some bug fixes alter semantics in subtle ways)Any edition bump is called out explicitly. An edition change is a platform-contract change - your manifest has to declare the new edition before your app loads under it.
## Pinning vs. floating ​
Recommendations for package.json:json
```
{
"dependencies": {
"@evenrealities/even_hub_sdk": "0.0.14"
}
}
```
|
| | Strategy | When
| | Exact pin ("0.0.14") | Production apps. Repeatable builds. Explicit upgrade decisions.
| | Tilde ("~0.0.14") | Internal demos. Picks up later 0.0.x PATCH releases automatically. Don't use for shipped apps.
| | latest tag | Discovery work only. Never in source-controlled package.json.The CLI accepts npm install @evenrealities/even_hub_sdk@latest to upgrade explicitly.
## Edition changes ​
edition is the platform contract your app targets - currently "202601". An edition bump is a platform-level breaking change: bridge protocols, event names, or fundamental manifest shape have changed.When a new edition ships:
- The release notes call it out explicitly, with a migration guide.
- Existing apps keep working under their old edition.
- To opt in, set "edition": "<new>" in app.json and audit your code against the migration guide.
- There's no auto-migration - opting in is a deliberate, per-app choice.Edition bumps are rare - think years, not months - and well-telegraphed.
## Migration checklist ​
Walk through each step when bumping the SDK in an existing project: |
| | # | Step | Detail
| | 1 | Read the release notes | Cover every version between your current SDK and the target. Pay attention to Deprecated and Removed.
| | 2 | Run your test suite | Headless Testing on the simulator.
| | 3 | Test critical paths via QR sideload | Local Testing on real hardware.
| | 4 | Verify against a beta build | Beta Testing is the only mode that gives production-equivalent behavior. Required for anything heading to release.
| | 5 | Grep your codebase for deprecated / removed APIs | If the release notes flagged a method, search the repo and migrate before bumping the dep.
| | 6 | Allocate appropriately | A MAJOR or edition bump is a sprint, not a side-quest. Don't bundle it with a feature.
## Related ​
- Changelog - what each SDK, CLI, and simulator release delivered
- Installation - current SDK version pin
- Glossary - edition, min_sdk_version, deprecation definitions
- Submission Flow - state machine, reviewer rubric, fix-forward versioning rulesPagerPrevious pageCLINext pageChangelog

View File

@@ -0,0 +1,153 @@
App Submission & QA Guidelines | Documentation
-
-
-
-
-
-
Skip to contentDocumentationSearch⌘CtrlK Main Navigation PortalThemeMenuReturn to top Sidebar Navigation
## Get Started
Overview
### Quickstart
Sign inHardwareInstall Node.js & npmInstall Even Hub toolingYour First AppTemplatesArchitecture
## Build
Page LifecycleDisplay & UI SystemUI/UX Design GuidelinesDevice APIsContextual MenuNetworkingBackground & Lifecycle
## Test
SimulatorLocal TestingPrivate TestingBeta Testing
## Ship
Packaging & DeploymentApp Submission & QA Guidelines
## Reference
GlossaryCLIVersioning PolicyChangelogFAQ
## AI Tooling
Claude CodeOn this pageLast updated: 2026-08-07Shipping an Even Hub app comes down to three things: moving a build through a four-stage state machine, clearing a manual review against a fixed checklist, and accepting that Released versions are immutable. Process first, then the reviewer rubric, then release notes.For the manifest schema and the .ehpk itself, see Packaging & Deployment.Don't commit your API keyNever bundle secrets or API keys into the .ehpk. Once a build is Released, anyone can extract its contents. Move third-party keys behind a server-side proxy you control, and read environment-driven values into the bundle at build time only. This applies to every API key - third-party AI services, analytics, maps, anything.
## The state machine ​
Every build moves through four states. The transition rules are strict - there is no skipping forward and only one path backward.
```
┌──────────┐ upload ┌──────┐ submit ┌───────────┐ approve ┌──────────┐
│ Draft │ ───────────► │ Test │ ───────────► │ Submitted │ ──────────► │ Released │
└──────────┘ └──────┘ └───────────┘ └──────────┘
▲ │ │ │
│ fail review │ │ │
└───────────────────────┴─────────────────────────┘ │
▲ │
│ publish higher version (fix-forward)│
└────────────────────────────────────────┘
```
|
| | State | Triggered by | What it means
| | Draft | You (upload .ehpk to portal) | The build exists in your project. Not visible to anyone else. Editable metadata.
| | Test | You (move from Draft) | The build is installable as a Private build by you, and assignable to a Beta group. Still invisible publicly.
| | Submitted | You (move from Test) | The build is in the reviewer queue. You cannot edit metadata or content. Withdraw requires support.
| | Released | Even Realities reviewer | Publicly listed in the store. No rollback - any change requires publishing a higher version.
## Fix-forward versioning ​
Once a build is Released, that version number is permanent. You cannot:
- Edit the manifest of a released build
- Replace the .ehpk of a released version
- Roll back to an earlier released versionYou can only publish a higher version that supersedes it. Plan accordingly:
- Bug-fix or hotfix? Publish 0.1.1 to supersede 0.1.0.
- Breaking change? Bump min_sdk_version so users on older firmware stay on the previous version.
- Mistake submitted? You can't withdraw a Released version. Publish a higher version that no-ops the broken behavior.
## Reviewer flow ​
When you move a build from Test → Submitted:
- Reviewer assignment - automated.
- Reviewer install - the reviewer installs the build as a beta tester (the same path you use for Beta Testing).
- Reviewer test - the rubric below.
- Reviewer decision - Approve (→ Released) or Reject (→ Draft, with notes).
- Notifications - both decisions email you (see Email notifications) and post to the dev portal inbox.
## Email notifications ​
Every review decision triggers an email to your developer account address:
- Sender: noreply@evenrealities.com - add it to your contacts so decisions don't land in spam. Replies go to shawn.deng@evenrealities.com if you have questions.
- Subject: You have an update on your Even Hub App Submission - the subject is fixed and identical for approvals and rejections.
- Only decisions trigger email. Approved and Rejected each send one; moving a build to Submitted does not.
- The email doesn't carry the outcome. It's a nudge with a link - open the dev portal to see the decision and, for rejections, the reviewer notes.
## What reviewers check ​
The reviewer's job is to confirm your build behaves like a real shipped app. The full rubric:
### Manifest (app.json) ​
- package_id - reverse-domain, lowercase, no hyphens, no underscores, ≥ 2 segments. Every segment must start with a lowercase letter (e.g. com.acme.weather).
- edition - exactly "202601" (current edition as of 2026-04-22).
- name - ≤ 20 characters and must not contain "Even" (case-insensitive). Names like "EvenDoc Reader" or "Even Bible" are auto-rejected as first-party impersonation. Exception: officially affiliated apps with written approval.
- version - three-part semver x.y.z. No v prefix, no pre-release suffix.
- min_sdk_version - required. Current SDK floor: "0.0.14".
- min_app_version - optional in your source app.json since CLI 0.1.14 - the CLI derives it from your SDK version and stamps it into the packed manifest, so the reviewer always sees it filled. Declare it yourself only to pin a stricter floor. See Auto-deriving min_app_version.
- entrypoint - must resolve to a real file inside the build output folder.
- permissions - array of objects with name + desc (1–300 chars). network entries also need whitelist. Not a key-value map.
- Every requested permission must actually be used in app code. Unused permissions are flagged. Common mappings: location for any getAppLocation / startAppLocationUpdates call, album for pickImageFromAlbum, camera for captureImageFromCamera, g2-microphone for audioControl(true, AudioInputSource.Glasses), phone-microphone for audioControl(true, AudioInputSource.Phone).
- New version submissions need a non-empty changelog.See Packaging & Deployment → Field Reference for the underlying schema.
### Store listing & visual assets ​
- Icon is legible - no "black scribble" or noisy patterns.
- Both foreground and background are supplied (neither null nor empty).
- Icon and background image are monochrome / greyscale only. Color assets are rejected.
- Screenshots match what the app actually renders on device - capture them via the simulator's screenshot function.
- Display name matches app.json name and the on-glasses display name (no portal-vs-device mismatch).
- No impersonation of existing apps, no unauthorized brand logos, no keyword stuffing in name/description.
### Privacy ​
- Privacy policy covers every permission the app requests.
- Backend service domains, if any, are documented and traceable to the developer.
### First-run experience (no black screens) ​
- First launch from glasses when setup is needed (city, API key, player name, …) → on-glasses message explains what to do. Never a black screen.
- Setup is remembered across launches (use the localStorage API) - never re-prompt the same setup.
- CORS headers correctly configured on any third-party API the app calls. The app.json network whitelist is an Even-side permission check - it is not a CORS bypass. If a request works locally but fails inside the WebView, the remote API is misconfigured for CORS, not an Even bug.
### Locked-phone operation ​
The Even G2 is designed to be useful with the phone in your pocket. Reviewers specifically test with the phone locked and the Even Realities App backgrounded.Test in the right mode. Reviewers run a beta build via Beta Testing, which survives a locked phone. Local Testing (QR sideload) dies when the WebView backgrounds, so it cannot validate the checks below - reproduce them with a beta build before submitting. The behaviour these checks probe is explained in Background & Lifecycle.
- Phone locked + Even App backgrounded → glasses-launched app renders within reasonable time. No infinite spinner, no black screen.
- Phone locked → the core flow runs end-to-end on glasses + ring input alone. Every gesture has a visible response, every button has feedback, every image loads.
- Long-running single-shot tasks (Timer, etc.) continue and complete correctly while the phone stays locked.
- After 2 minutes idle the app is still alive and responsive. No freeze, no infinite loop, no crash.
- Unlock → use another phone app → re-lock - the glasses session is unaffected.
### Exit & lifecycle ​
- Root-page double-tap calls bridge.shutDownPageContainer(1) - the system exit confirmation dialog. Mode 0 (immediate exit) is not acceptable on the root page. A custom in-app exit confirmation UI is not acceptable on the root page either. Apps that exit silently or do nothing on double-tap are rejected.
- Reference patterns:
- Make15 - root double-tap fires the system dialog directly.
- Chess - root double-tap opens an in-app menu containing an "Exit" item that then calls shutDownPageContainer(1).
- After the user confirms exit on glasses, the phone-side WebView page also closes automatically. Lingering webviews inside the Even Realities App are rejected.
- After exit, glasses can launch other apps and first-party apps (Conversate, Navigate) without restart. Smoke-testing with Conversate alone is sufficient.See Page Lifecycle for page-creation and exit patterns.
### Content & safety ​
- No medical diagnosis, financial advice, or emergency-routing functionality. Flag for legal if unavoidable.
- No offensive, explicit, NSFW, or hateful content.
## Release notes ​
When moving to Submitted you provide release notes for each supported_language. Conventions:
- Length - 1-3 lines per locale. Reviewers and users both skim.
- Tone - what changed for the user, not what changed in the code.
- First version - describe what the app does, not "initial release."
- Hotfixes - name the symptom, not the commit message ("Fix occasional blank screen after returning from lock" not "fix race in onResume").
## Final pre-submission sanity check ​
Before clicking Submit, run through this short loop:
- evenhub pack app.json dist -o myapp.ehpk -c - confirm package_id is available and the manifest validates.
- Install as a beta build via Beta Testing and lock the phone for 5 minutes - does the app stay alive and responsive? (Local Testing / QR sideload dies on background and will give a false failure here.)
- Trigger a root-page double-tap - does the system exit dialog appear and the WebView close?
- Re-launch a first-party app (Conversate) - does it start without restarting glasses?
- Re-read your privacy policy - does it cover every permission in app.json?If all five pass, you are ready to submit.
## Common confusions ​
|
| | Question | Answer
| | "Why is my reviewer install showing old code?" | Reviewers install the version currently in Submitted. If you've made changes since, those sit in Draft - promote to Test and submit a new version.
| | "Can I demote a Released version to Test?" | No. Released is terminal. Publish a higher version.
| | "Submitted → Draft happened. What now?" | The reviewer rejected with notes. Read them, fix, then re-promote through Test → Submitted.
| | "How do I withdraw a Submitted build?" | Contact support. There's no self-serve withdrawal.
| | "Why isn't my user seeing the new version?" | Their firmware likely sits below the new min_sdk_version. They need a firmware update before they're eligible.
## Related ​
- Packaging & Deployment - the manifest schema and .ehpk build
- Beta Testing - pre-submission validation
- Background & Lifecycle - why the 5-min lock test exists
- Versioning Policy - semver rules for the version fieldPagerPrevious pagePackaging & DeploymentNext pageReference

View File

@@ -0,0 +1,197 @@
Packaging & Deployment | Documentation
-
-
-
-
-
-
Skip to contentDocumentationSearch⌘CtrlK Main Navigation PortalThemeMenuReturn to top Sidebar Navigation
## Get Started
Overview
### Quickstart
Sign inHardwareInstall Node.js & npmInstall Even Hub toolingYour First AppTemplatesArchitecture
## Build
Page LifecycleDisplay & UI SystemUI/UX Design GuidelinesDevice APIsContextual MenuNetworkingBackground & Lifecycle
## Test
SimulatorLocal TestingPrivate TestingBeta Testing
## Ship
Packaging & DeploymentApp Submission & QA Guidelines
## Reference
GlossaryCLIVersioning PolicyChangelogFAQ
## AI Tooling
Claude CodeOn this pageLast updated: 2026-08-25Shipping an Even Hub build comes down to three things: validating an app.json manifest, bundling assets into an .ehpk, and uploading it through the developer portal. The manifest schema, the CLI packaging commands, and the validation errors you're most likely to hit are all below.Don't commit your API keyNever bundle secrets or API keys into the .ehpk. Once a build is Released, anyone can extract its contents. Move third-party keys behind a server-side proxy you control, and read environment-driven values into the bundle at build time only. This applies to every API key - third-party AI services, analytics, maps, anything.
## The app.json manifest ​
Every Even Hub app needs an app.json manifest. Generate a starter with:bash
```
evenhub init
```
This creates the following template:json
```
{
"package_id": "com.example.g2demo",
"edition": "202601",
"name": "G2 Demo",
"version": "0.1.0",
"min_app_version": "2.2.6",
"min_sdk_version": "0.0.7",
"entrypoint": "index.html",
"permissions": [
{
"name": "network",
"desc": "This app needs to access the network in order to ...",
"whitelist": ["https://example.com"]
},
{
"name": "location",
"desc": "This app needs to access the location in order to ..."
}
],
"supported_languages": ["en"]
}
```
### Field reference ​
|
| | Field | Type | Required | Rules
| | package_id | string | Yes | Reverse-domain format (e.g., com.yourname.appname). Each segment must start with a lowercase letter and contain only lowercase letters or numbers. Minimum two segments. No hyphens.
| | edition | string | Yes | Must be "202601" (current edition).
| | name | string | Yes | 20 characters or fewer.
| | version | string | Yes | Semver format: x.y.z (e.g., "1.0.0").
| | min_app_version | string | No | Minimum Even Realities App version your build runs on. Derived at pack time from your SDK version - you don't set it by hand. Declare it only to pin a stricter floor; the CLI stamps the higher of your value and the SDK floor. See Auto-deriving min_app_version.
| | min_sdk_version | string | Yes | Minimum SDK version required (e.g., "0.0.14").
| | entrypoint | string | Yes | Path to your HTML entry file relative to the build folder (e.g., "index.html").
| | permissions | array | Yes | Array of permission objects (see below). Can be empty [].
| | supported_languages | array | Yes | Array of language codes. Valid values: en, de, fr, es, it, zh, ja, ko.
### Permissions format ​
Permissions is an array of objects, not a key-value map. Each object: |
| | Key | Type | Required | Notes
| | name | string | Yes | One of: network, location, g2-microphone, phone-microphone, album, camera
| | desc | string | Yes | Human-readable reason, 1–300 characters.
| | whitelist | string[] | network only | List of allowed domains. Optional, defaults to [].Example with every permission name:json
```
"permissions": [
{
"name": "network",
"desc": "Fetches weather data from the API.",
"whitelist": ["https://api.weather.com"]
},
{
"name": "location",
"desc": "Shows nearby points of interest on the display."
},
{
"name": "g2-microphone",
"desc": "Enables voice commands for hands-free control."
},
{
"name": "phone-microphone",
"desc": "Captures a voice note when the user taps record."
},
{
"name": "album",
"desc": "Lets the user pick a photo to send to the glasses."
},
{
"name": "camera",
"desc": "Lets the user capture a photo to send to the glasses."
}
]
```
Declare only the permissions you actually use - unused entries are flagged at review.Common mistakepermissions must be an array of objects, not a key-value map. This shape will fail validation:json
```
"permissions": { "network": ["example.com"] }
```
## Building and packing ​
### Step 1: Build your web app ​
bash
```
npm run build
```
This produces your output directory (typically dist/ or build/).
### Step 2: Pack into .ehpk ​
bash
```
evenhub pack app.json dist -o myapp.ehpk
```
|
| | Argument | Description
| | app.json | Path to your manifest file
| | dist | Path to your built output folder
| | -o myapp.ehpk | Output filename (defaults to out.ehpk)
| | --no-ignore | Include hidden files (dotfiles) - excluded by default
| | -c, --check | Check if your package_id is available on Even Hub
| | --sdk-ver <version> | SDK version you built against; the CLI reads its minAppVersion from npm and stamps it as the .ehpk floor. Omit to use the latest published SDK. See Auto-deriving min_app_version.
| | --enforce-manual-version | Stamp min_app_version from app.json as-is, even below the SDK floor. Local testing only; not submittable.TIPThe entrypoint in your app.json must point to a file that exists inside the build folder. If your manifest says "entrypoint": "index.html" but the build folder doesn't contain index.html, packing will fail with:
```
Entrypoint file not found: dist/index.html
```
## Auto-deriving min_app_version (CLI 0.1.14+) ​
min_app_version is the oldest Even Realities App your build can run on. You don't set it by hand - the CLI derives it from the SDK you built against and stamps it into the .ehpk at pack time. When an SDK release needs a newer app (a new bridge API only that app implements), that requirement ships with the SDK, so the floor stays honest without you tracking it.Every SDK release from 0.0.13 on publishes its floor as minAppVersion in its npm metadata. At pack time the CLI looks it up and stamps it:bash
```
# Pin the SDK you built against (recommended)
evenhub pack app.json dist --sdk-ver 0.0.14
```
```
Stamped myapp.ehpk (30287 bytes)
min_app_version 2.2.9 (SDK 0.0.14, --sdk-ver)
```
SDK 0.0.14 publishes a floor of 2.2.9 because its contextual menu and tap-then-long-press events need bridge APIs that only Even App 2.2.9 implements. Build against it and the CLI raises your floor for you.Omitting --sdk-ver reads the latest SDK, not the one you bundledWithout --sdk-ver, the CLI resolves the floor from whatever npm currently tags latest - which may be newer than the SDK in your package.json. Pass --sdk-ver <version> matching your installed SDK for a reproducible floor. Check yours with npm list @evenrealities/even_hub_sdk.
### If app.json also declares min_app_version ​
The CLI takes the higher of your declared value and the derived SDK floor: |
| | Your app.json value | Result
| | Absent | Stamp the derived floor.
| | Equal to the floor | Stamp it - no change.
| | Higher than the floor | Kept - you're pinning a stricter minimum on purpose.
| | Lower than the floor | The floor is stamped instead, with a warning. The build still packs, but a plugin below its SDK's floor breaks on apps under it.
```
WARNING app.json sets min_app_version 2.0.0, below SDK 0.0.14's floor 2.2.9 (--sdk-ver).
Stamping the higher value, 2.2.9.
To stamp 2.0.0 as-is instead, re-pack with --enforce-manual-version.
Stamped myapp.ehpk (30287 bytes)
```
The CLI never rewrites your source app.json - the derived value is stamped into the .ehpk only.
### --enforce-manual-version (local testing only) ​
To stamp your app.json value as-is even when it's below the floor, pass --enforce-manual-version. This produces a non-submittable local build - it can fail on apps below your value and won't clear review.bash
```
evenhub pack app.json dist --sdk-ver 0.0.14 --enforce-manual-version
```
--enforce-manual-version requires a min_app_version in app.json; running it without one is an error.
### Offline and pre-0.0.13 SDKs ​
The CLI ships a bundled SDK → min_app_version map as a fallback:
- Registry unreachable (network, DNS, timeout, 5xx) - the CLI warns and uses the bundled map. A version the map doesn't know falls back to a default floor. Re-pack online to confirm.
- SDK 0.0.12 and older - these predate the npm minAppVersion field, so the CLI prints an info line and uses the bundled map; npm versions are immutable, so those floors can't be backfilled. A version the bundled map doesn't cover falls back to the default floor.
## Troubleshooting evenhub pack ​
When packing fails, the CLI prints a specific validation error. The common ones and their fixes: |
| | Error you see | Fix
| | Invalid package id | Use lowercase reverse-domain format with at least two segments; no hyphens, no uppercase, no leading numbers. Valid: com.myname.myapp. Invalid: My-App, com.my-app.v2, myapp, com.2fast.app.
| | name: must be 20 characters or fewer | Shorten the app name. Use the tagline or description fields for longer copy.
| | version: must be in x.y.z format | Use three-part semver: "1.0.0", not "1.0" or "v1.0.0".
| | min_sdk_version: expected string, received undefined | min_sdk_version is required - add "min_sdk_version": "0.0.14" (match your installed SDK). min_app_version is no longer required; the CLI derives it - see Auto-deriving min_app_version.
| | SDK version not published on npm: @evenrealities/even_hub_sdk@<version> | Your --sdk-ver points at a version that isn't on npm. Check the value - the CLI won't guess a floor for it. No .ehpk is written.
| | --enforce-manual-version needs a min_app_version in app.json | You passed --enforce-manual-version but app.json declares no min_app_version. Add one, or drop the flag to let the CLI derive the floor.
| | permissions: each permission must be an object with "name" … | Permissions must be an array of objects with name and desc keys. See Permissions Format above.
| | supported_languages: invalid language | Use lowercase ISO codes from the supported set: en, de, fr, es, it, zh, ja, ko.
| | Entrypoint file not found | The file referenced by entrypoint must exist inside your build folder. If your Vite output goes to dist/ and entrypoint is index.html, confirm dist/index.html exists.
| | Project folder not found | The second argument to evenhub pack must be an existing directory of built files. Run npm run build first.
## Distribution ​
Once your build is Released, it surfaces inside the Even Hub catalog. From there:
- Users install it through the Even Realities App.
- They launch it from the glasses menu or from the app's Even Hub tab.PagerPrevious pageShipNext pageApp Submission & QA Guidelines

View File

@@ -0,0 +1,87 @@
Beta Testing | Documentation
-
-
-
-
-
-
Skip to contentDocumentationSearch⌘CtrlK Main Navigation PortalThemeMenuReturn to top Sidebar Navigation
## Get Started
Overview
### Quickstart
Sign inHardwareInstall Node.js & npmInstall Even Hub toolingYour First AppTemplatesArchitecture
## Build
Page LifecycleDisplay & UI SystemUI/UX Design GuidelinesDevice APIsContextual MenuNetworkingBackground & Lifecycle
## Test
SimulatorLocal TestingPrivate TestingBeta Testing
## Ship
Packaging & DeploymentApp Submission & QA Guidelines
## Reference
GlossaryCLIVersioning PolicyChangelogFAQ
## AI Tooling
Claude CodeOn this pageLast updated: 2026-06-04Beta Testing is the only mode that behaves identically to a Released app on a real user's phone. You pack the .ehpk exactly as you would for submission, push it to a Beta group containing yourself (and optionally teammates), and install it from the phone app's Beta tester section. The OS, the lifecycle, the lock-screen behavior, the system exit dialog - all of it matches what an end user (and a QA reviewer) sees.If you're heading into review, this is the gate. Skip it and you'll fail.See Test for how Beta Testing compares to the simulator, local sideload, and private builds.
## The flow ​
bash
```
# 1. Build and pack
npm run build
evenhub pack app.json dist -o myapp.ehpk
```
Then in the dev portal:
- Open hub.evenrealities.com/login, go to your project.
- Beta groups tab - create a group (e.g., self-test) and add your own account email. Add teammates' emails if you want them to test the same build.
- Builds tab - upload myapp.ehpk.
- Push the build to your self-test group.On your phone (and any teammate's phone):
- Open the Even Realities App.
- Me → Beta tester lists the build.
- Tap Install.From here on, the build behaves exactly as if it were Released. Launch from glasses home, full OS lifecycle, real backgrounding.
## What only Beta Testing validates ​
The things you can't test anywhere else:
### The 5-minute locked-phone test ​
Open your app, put it in a steady state (something visible on glasses), lock the phone, wait 5 minutes, unlock. The app should still be where you left it. This is the exact test the QA reviewer runs. Private builds survive briefly but not 5 minutes; Local Testing dies the second the phone locks.
### shutDownPageContainer(1) system exit dialog ​
The system exit-confirmation dialog only renders for a real install. Root-page double-tap must call bridge.shutDownPageContainer(1). Apps using 0 (immediate exit) or a custom in-app exit UI on the root page are auto-rejected at review.
### Real permission denial paths ​
What does your app do when the user denies a permission? Beta Testing is where you actually exercise that branch. Local Testing skips some prompts; Private Testing fires them but the reviewer's denial path is a Beta-equivalent install.
## What still requires care ​
- Console logs are visible in the phone app's Developer Mode console. Handy for debugging, but don't log secrets - that install path is reviewer-visible too.
- Crashes don't auto-report in beta yet. If your app vanishes, the console buffer is your only signal - check it immediately.
- Beta install is a real install. localStorage, IndexedDB, everything persists exactly as it would for an end user. Reset between test rounds when you need a clean state.
## The pre-submission checklist ​
Walk through each of these on the beta build before promoting to Submitted: |
| | # | Check | Pass criteria
| | 1 | 5-minute locked-phone test | Open the app, lock the phone for 5 minutes, unlock - state preserved, no spinner, no black screen.
| | 2 | Root double-tap fires the system exit dialog | Visible confirmation; WebView closes on confirm.
| | 3 | Permission denial paths handled | Deny each declared permission once - app degrades gracefully or surfaces a clear next-step message.
| | 4 | Re-launch a first-party app (Conversate, Navigate) after exit | Launches cleanly without restarting glasses.
| | 5 | No console errors at boot | Console buffer is clean immediately after launch.The full reviewer rubric: App Submission & QA Guidelines.
## Common failure modes ​
|
| | Symptom | Likely cause | Fix
| | Beta tester section doesn't list the build | Account not in the group, or build not pushed to group | Re-check the portal: group membership + build assignment
| | Install succeeds, app vanishes on lock | Backgrounding kills the WebView, no resume handler | Background & Lifecycle - persist eagerly to localStorage and rebuild on relaunch
| | Double-tap exits silently | shutDownPageContainer(0) or no exit handler | Use shutDownPageContainer(1) on the root page
| | Permission prompt copy is wrong | Edit app.json permission desc, repack, re-upload | Old beta still has old copy until reinstalled
| | Build updates don't propagate | Higher min_sdk_version than tester's firmware | Tester needs firmware update first, or you need to lower min_sdk_version
## When you're done ​
The .ehpk that passed every box above is the one you submit. Move the build from Test to Submitted in the portal. The reviewer installs from your beta build's lineage - if your beta passes the 5-minute test, the reviewer almost certainly will too.→ Next: App Submission & QA Guidelines
## Related ​
- Packaging & Deployment - the build path
- App Submission & QA Guidelines - the reviewer rubric
- Background & Lifecycle - what survives the 5-minute lockPagerPrevious pagePrivate TestingNext pageShip

View File

@@ -0,0 +1,91 @@
Local Testing | Documentation
-
-
-
-
-
-
Skip to contentDocumentationSearch⌘CtrlK Main Navigation PortalThemeMenuReturn to top Sidebar Navigation
## Get Started
Overview
### Quickstart
Sign inHardwareInstall Node.js & npmInstall Even Hub toolingYour First AppTemplatesArchitecture
## Build
Page LifecycleDisplay & UI SystemUI/UX Design GuidelinesDevice APIsContextual MenuNetworkingBackground & Lifecycle
## Test
SimulatorLocal TestingPrivate TestingBeta Testing
## Ship
Packaging & DeploymentApp Submission & QA Guidelines
## Reference
GlossaryCLIVersioning PolicyChangelogFAQ
## AI Tooling
Claude CodeOn this pageLast updated: 2026-06-04Local Testing is the daily driver for on-device development. Your Vite dev server runs on your laptop, the CLI prints a QR pointing at it, you scan from the phone app, and the app is rendering on the real Even G2 a second later - with hot module reload live. Edit a file, save, the change reflows on glass.The fastest way to iterate on hardware. Also the most fragile if your network gets in the way.See Test for how Local Testing compares to the simulator, private builds, and beta builds.
## The flow ​
bash
```
# Terminal 1 - your project's dev server
npm run dev
# Terminal 2 - generate a QR pointing at your LAN IP
evenhub qr --url "http://<your-lan-ip>:5173"
```
Open the Even Hub tab in the Even Realities phone app (you enabled this in Hardware → Enable Developer Mode), tap Scan QR, and aim the phone camera at the terminal. Within a second the glasses render your app.Edit src/main.ts, save, and the glasses redraw without re-scanning.→ Detail: Your First App § On Real Hardware · Network & Firewall Setup
## What makes Local Testing fast ​
- HMR works. Vite pushes module updates over WebSocket; the WebView re-evaluates the changed module and your page re-renders.
- Real BLE timing. Unlike the simulator, you see actual phone-to-glasses latency.
- Real input. Temple touches and R1 ring events come through exactly as they would in production.
- Real device APIs. Microphone, IMU, battery readings are live.
## What Local Testing does not cover ​
- Backgrounding. The moment the phone backgrounds the Even Realities App - lock screen, app switcher, Control Center on iOS - the WebView is suspended. The dev WebSocket dies. When the WebView resumes you usually get a blank screen until you re-scan the QR. That's how phone WebViews behave everywhere; it isn't an Even bug.
- Packaging path. Nothing here builds an .ehpk. Manifest errors, packaging validation, icon assets - all skipped.
- Real permissions. Some permission prompts are skipped during dev.
- Reviewer parity. The QA gate explicitly tests with a locked phone. Local Testing dies the moment the phone locks, so it can't validate what reviewers look for. Run Beta Testing before submitting.
## HMR recovery ​
HMR over BLE-indirect WebView is brittle. A few patterns help when it stalls:
- Re-scan the QR. Forces a full page reload - the nuclear option when nothing else clears it.
- Pin the HMR host. In vite.config.ts:typescript
```
export default defineConfig({
server: {
host: true, // accept connections from any interface
hmr: { host: '<your-lan-ip>' }
}
})
```
This stops HMR from trying to talk back to localhost, which the phone can't reach.
- Watch for (disconnected) in the WebView console. Open the dev console from the phone app's Developer Mode screen. If you see WebSocket failures, that's HMR. The page itself usually keeps working until you save the next change.
## Common failure modes ​
|
| | Symptom | Likely cause | Fix
| | Phone says "Couldn't connect" after scan | Firewall blocking inbound on dev port | Network & Firewall Setup
| | Phone scans but nothing happens, no error | Wi-Fi AP isolation blocks phone-to-laptop | Network & Firewall Setup
| | Works at home, fails at the office | Office Wi-Fi has client isolation | Switch to phone hotspot or Tailscale
| | Page loads but blank screen | await waitForEvenAppBridge() missing | Check src/main.ts
| | Page loads, no click response | Container without isEventCapture: 1 | Display & UI System
| | App works once, blank on next change | HMR WebSocket dead - resume after background | Re-scan the QR
## When to graduate ​
Local Testing carries you through almost all UI and input work. Move to:
- Private Testing when you need to test the actual .ehpk build, manifest validation, or real permissions.
- Beta Testing when you're heading into review and need to validate the locked-phone behavior reviewers test.
## Related ​
- Simulator - no-hardware iteration with optional headless API
- Private Testing - first time you build a real .ehpk
- Beta Testing - reviewer-parity, required before submission
- Background & Lifecycle - why Local Testing dies on backgroundPagerPrevious pageSimulatorNext pagePrivate Testing

View File

@@ -0,0 +1,88 @@
Private Testing | Documentation
-
-
-
-
-
-
Skip to contentDocumentationSearch⌘CtrlK Main Navigation PortalThemeMenuReturn to top Sidebar Navigation
## Get Started
Overview
### Quickstart
Sign inHardwareInstall Node.js & npmInstall Even Hub toolingYour First AppTemplatesArchitecture
## Build
Page LifecycleDisplay & UI SystemUI/UX Design GuidelinesDevice APIsContextual MenuNetworkingBackground & Lifecycle
## Test
SimulatorLocal TestingPrivate TestingBeta Testing
## Ship
Packaging & DeploymentApp Submission & QA Guidelines
## Reference
GlossaryCLIVersioning PolicyChangelogFAQ
## AI Tooling
Claude CodeOn this pageLast updated: 2026-06-04Private Testing is the first mode where the real packaging path runs end to end. You build an .ehpk with the CLI, upload it as a private build in the dev portal, and install it on your own glasses through the phone app. The result behaves much closer to a Released app than the dev-server flow ever will - same install path, same manifest enforcement, same icon assets, same permission prompts.There is no scripted install yet. Every iteration goes through the UI - upload, install, look. Budget around ten seconds per cycle.See Test for how Private Testing compares to the simulator, local sideload, and beta builds.
## The flow ​
bash
```
# 1. Build your production bundle
npm run build
# 2. Pack it into .ehpk
evenhub pack app.json dist -o myapp.ehpk
```
Then in the dev portal:
- Open hub.evenrealities.com/login, go to your project, Private builds tab.
- Upload myapp.ehpk.
- On your phone, open the Even Realities App and go to the Even Hub tab (Developer Mode).
- Me → Apps → Private builds lists your upload. Tap Install.Within a few seconds the build is on your glasses. Launch it from the glasses home, the same way a Released app launches.→ Detail: Packaging & Deployment
## What Private Testing covers ​
- Full .ehpk packaging path. Manifest validation, icon checks, file inclusion / exclusion, all of it. If something is going to fail packaging at review time, it fails here first.
- Real permission prompts. The phone app prompts for permissions exactly as it will for end users. Strings, ordering, denial paths - all real.
- Real launch UX. Glasses launch the app from the home menu, not from a dev URL. The first-frame timing and the boot sequence are real.
- Real install / uninstall flow. You see what your users see when they install, including any version-update behavior.
## What Private Testing does not cover ​
- No HMR. Every code change is a full build, re-upload, re-install. Slow loop.
- No headless automation. There's no scripted install + run + assert path for private builds yet. Treat them as manual smoke tests, not regression harness.
- Only partial lifecycle survival. Private builds survive backgrounding briefly but don't pass the 5-minute lock test the QA reviewer runs. For that gate, use Beta Testing.
- No distribution to other testers. Private builds are tied to your account. To get a build onto a colleague's phone, use Beta Testing.
## When to reach for Private Testing ​
- You changed the app.json manifest and want to verify it validates.
- You added or changed a permission and want to see the real prompt.
- You added icon assets or screenshots and want to see how they render in the install list.
- You want a smoke test that the packaged build boots before promoting to a beta.
## Common failure modes ​
|
| | Symptom | Likely cause | Fix
| | Upload rejected: "invalid manifest" | app.json field missing or wrong shape | Check the error detail, see App Submission & QA → Manifest
| | Upload rejected: "package_id taken" | Someone already claimed it | Change package_id to a unique reverse-DNS string you control
| | Install succeeds but glasses show black screen | Bridge call ran before waitForEvenAppBridge() | Check src/main.ts - await the bridge before anything else
| | Permission prompt never appears | Permission declared in app.json but never invoked | Permission prompts fire on first invocation, not at launch. Trigger the API path that needs the permission
| | App vanishes after lock | Backgrounded WebView killed | Expected. Use Beta Testing for lock-survival validation
## When to graduate to Beta Testing ​
Move to Beta Testing when:
- You are within a few iterations of submitting for review.
- You need to validate the 5-minute locked-phone behavior.
- You want a colleague to install the same build for review.
- You want reviewer-parity environment - same install path, same lifecycle, same OS treatment as a Released app.
## Related ​
- Packaging & Deployment - the .ehpk build and manifest schema
- App Submission & QA Guidelines - what reviewers check
- Beta Testing - the next mode up
- Background & Lifecycle - why lock survival is only partial herePagerPrevious pageLocal TestingNext pageBeta Testing

View File

@@ -0,0 +1,290 @@
Simulator | Documentation
-
-
-
-
-
-
Skip to contentDocumentationSearch⌘CtrlK Main Navigation PortalThemeMenuReturn to top Sidebar Navigation
## Get Started
Overview
### Quickstart
Sign inHardwareInstall Node.js & npmInstall Even Hub toolingYour First AppTemplatesArchitecture
## Build
Page LifecycleDisplay & UI SystemUI/UX Design GuidelinesDevice APIsContextual MenuNetworkingBackground & Lifecycle
## Test
SimulatorLocal TestingPrivate TestingBeta Testing
## Ship
Packaging & DeploymentApp Submission & QA Guidelines
## Reference
GlossaryCLIVersioning PolicyChangelogFAQ
## AI Tooling
Claude CodeOn this pageLast updated: 2026-08-25The simulator (v0.9.3) lets you preview layouts and exercise logic without touching hardware. Use it for interactive UI work and for the headless automation flow at the bottom of this page.0.9.3 tracks SDK 0.0.14: five textColor brightness levels, zOrderIndex stacking, tap then long press, and your contextual menu drawn the way firmware draws it - navigation, selection events, enter and exit animations. See Caveats for what still differs from hardware, and the Changelog for what each release added.Upgrade with npm install -g @evenrealities/evenhub-simulator@latest; check yours with evenhub-simulator --version.See Test for how the simulator stacks up against local sideload, private builds, and beta builds.
## Not an emulator ​
The simulator is a Node + LVGL window that mimics how containers, text, and events look on the glasses. It is not a hardware emulator. Performance, frame pacing, BLE timing, and real-device quirks are not reproduced. Use it for layout, copy, and event logic; anything timing- or performance-sensitive has to be confirmed on real glasses.
## Installation ​
bash
```
npm install -g @evenrealities/evenhub-simulator
```
npm: @evenrealities/evenhub-simulator - cross-platform (macOS, Linux, Windows)
## Usage ​
bash
```
evenhub-simulator [OPTIONS] [targetUrl]
```
## Options ​
|
| | Option | Description
| | -c, --config <path> | Path to config file (use --print-config-path to see the default)
| | -g, --glow | Enable glow effect on glasses display
| | --no-glow | Disable glow effect (overrides config)
| | -b, --bounce <type> | Bounce animation type: default or spring
| | --automation-port <port> | Expose the headless control plane on the given port (see below)
| | --list-audio-input-devices | List available audio input devices
| | --aid <device> | Choose a specific audio input device
| | --no-aid | Use default audio device (overrides config)
| | --print-config-path | Print the default config file path and exit
| | --completions <shell> | Generate shell completions: bash, elvish, fish, powershell, zsh
| | -V, --version | Print version
| | -h, --help | Print help
## Default config file paths ​
|
| | Platform | Location
| | Linux | $XDG_CONFIG_HOME or $HOME/.config
| | macOS | $HOME/Library/Application Support
| | Windows | {FOLDERID_RoamingAppData} (e.g., C:\Users\<user>\AppData\Roaming)
## Audio ​
audioEvent payloads match what the device emits:
- Sample rate: 16,000 Hz
- Format: signed 16-bit little-endian PCM
- 100 ms of data per event (3,200 bytes / 1,600 samples)
## Screenshot (v0.5.0+) ​
Clicking the screenshot button exports the glasses display as an RGBA PNG to your current working directory, with a timestamp in the filename. The full path is logged to the simulator's stdout and to the glasses web inspector console.Glow is a post-processing effect only - screenshots are taken from the raw framebuffer, not the glowed render.
## Debugging ​
Two surfaces cover most simulator debugging: the simulator's own log stream on one side, your app's webview on the other.Simulator log stream. Launch with RUST_LOG=debug to see exactly what is being called - each call the simulator receives from your app is logged to stdout as it arrives:bash
```
RUST_LOG=debug evenhub-simulator http://localhost:5173
```
When a container doesn't render or an event doesn't fire, this tells you whether the call ever reached the simulator. Standard env_logger filter syntax applies, so RUST_LOG=trace and per-module filters work too.Inspect the webview. The simulator's webview window - the one hosting your app's HTML - is inspectable. Right-click it and choose Inspect Element for the full web inspector: console, DOM, network, breakpoints. This is where your app-side console.log output and uncaught errors land, same as any web page.Between the two: the debug log shows what the simulator received, the inspector shows what your app did. A call your app makes that never shows up in the debug log never crossed the bridge.
## Caveats ​
- Display rendering isn't pixel-perfect with hardware (font, greyscale levels). Good enough for layout and logic; not for visual QA.
- List scrolling - focused-item positioning can differ from real glasses.
- Image processing is faster than hardware and doesn't enforce on-device size limits.
- Image transfer - the LZ4 compression the SDK applies to updateImageRawData (SDK 0.0.12+) isn't decompressed here; payload bytes are decoded as a normal (uncompressed) image file. Don't rely on the simulator to validate the real hardware's compressed transfer path.
- Events - status events aren't emitted (user and device profiles are hardcoded). Inputs in the window: Up, Down, Click, Double Click, and tap then long press (hold the control, or use its keyboard shortcut). The HTTP API drives a narrower set - see Glasses input.
- Text brightness - textColor 0-4 render as five distinct levels, but they are not photometrically matched to the glasses. Use them to check hierarchy, not to sign off on legibility.
- Contextual menu - your menuObject is drawn, navigable, and returns menuItemClickEvent with the itemID you declared. It honours the rebuild contract - a rebuild that carries menuObject reproduces the menu, one that omits it clears your items and leaves the system's. What it cannot tell you is whether the OS on a given firmware build shows the same system slots alongside your items - check that on hardware.
- Error handling under abnormal conditions can differ from hardware.Always validate on real hardware before deployment. If you spot a discrepancy that affects logic, file it in the Discord.
## Headless automation (v0.7.0+) ​
Simulator 0.7.0+ ships an HTTP control plane. Pass --automation-port at launch and you can drive it from CI, a test harness, or any script that speaks HTTP.bash
```
evenhub-simulator http://localhost:5173 --automation-port 9898
# → control plane on http://127.0.0.1:9898
```
Verify it's up:bash
```
curl http://127.0.0.1:9898/api/ping
# pong
```
This is automation of the Simulator. The simulator runs no real hardware, permissions, or background lifecycle, so headless runs do not replace Beta Testing before submission - see Testing Modes for the full picture.
### When to reach for this ​
- Pre-submission checks - assert the rules from App Submission & QA Guidelines (lit pixels in the framebuffer, system exit dialog on root double-tap, no console errors at boot) before uploading a new .ehpk.
- Genesis Day judging automation - score submissions without manually clicking through every entry.
- Internal QA harness - regression-test SDK upgrades by replaying a journey across many apps.
- CI smoke tests - run a tiny "did the app even boot" check on every PR.
### Endpoints ​
|
| | Endpoint | Purpose
| | GET /api/ping | Health check → returns pong.
| | GET /api/screenshot/glasses | 576×288 RGBA PNG of the LVGL framebuffer. Keep RGBA - converting to RGB fuses background and text (both pure green). Use alpha > 0 as the lit-pixel test.
| | GET /api/screenshot/webview | PNG of the host webview, captured via html2canvas (10 s timeout).
| | GET /api/console[?since_id=N] | Returns { entries, total }. Captures console.*, uncaught exceptions, unhandled rejections, and failed fetch calls. Use since_id for incremental polling.
| | DELETE /api/console | Clears the buffer. Read startup logs before clearing - they are emitted once and lost if you clear too early.
| | POST /api/input body: { "action": "up|down|click|double_click|long_press|long_press_release|context_menu" } | Drives the touchpad. Silently ignored if no event-capturing container is active. Allow ~4 s after launch (SDK init + createStartUpPageContainer) before sending input. See Glasses input for the full action set.
### Glasses input ​
POST /api/input takes exactly seven actions. Anything else is a 400 with the accepted list in the body:
```
invalid action 'bogus_action', expected: up, down, click, double_click, long_press, long_press_release, context_menu
```
|
| | Action | Effect
| | up / down | Move focus through list items or scroll text
| | click | Select the focused item
| | double_click | System double-tap - normally back, dismiss, or the exit dialog on a root page
| | context_menu | Raise the contextual menu (v0.9.0+)
| | long_press | Begin a tap then long press - delivers LONG_PRESS_EVENT (v0.9.1+)
| | long_press_release | End that press - delivers LONG_PRESS_RELEASE_EVENT (v0.9.1+)Driving the contextual menu. context_menu toggles: it opens the menu when closed and closes it when open. up / down move focus inside it, and click selects - your app receives menuItemClickEvent carrying the itemID you declared, and the menu closes. The whole path is scriptable:bash
```
curl -X POST http://127.0.0.1:9898/api/input -H 'Content-Type: application/json' -d '{"action":"context_menu"}'
curl -X POST http://127.0.0.1:9898/api/input -H 'Content-Type: application/json' -d '{"action":"down"}'
curl -X POST http://127.0.0.1:9898/api/input -H 'Content-Type: application/json' -d '{"action":"click"}'
# app receives menuItemClickEvent for the second item
```
Assert on the framebuffer rather than on timing: opening the menu is a large lit-pixel jump, moving focus within it is a small one.Two things that will bite a script. context_menu toggling means a blind second send closes the menu instead of reopening it - track the state or assert the framebuffer between sends. And a rebuildPageContainer underneath an open menu does not dismiss it: the overlay stays up, so a screenshot taken after a rebuild can still be showing the old menu.Driving tap then long press. long_press opens the gesture and long_press_release ends it (simulator 0.9.1+); your app sees LONG_PRESS_EVENT then LONG_PRESS_RELEASE_EVENT the same way it would on hardware. Send them as a pair - a long_press with no release leaves the press open. Without those actions, driving the raw press needs the simulator window (hold the control, or its keyboard shortcut) or real hardware.Note that context_menu is a shortcut to the menu itself, not to the gesture that raises it. If your app handles the raw press, drive it with long_press; if you want the OS menu, use context_menu.
### The end-to-end loop ​
The shape of every test is the same:
- Boot the simulator pointing at your dev server (or an .ehpk URL).
- Wait for ready. The simulator silently drops input until your first event-capturing container exists, so poll GET /api/console for an "app ready" log line (or your own readiness signal). Allow ~4 s minimum after launch.
- Snapshot the state - GET /api/screenshot/glasses for the framebuffer, GET /api/console for log entries.
- Send input - POST /api/input with { "action": "click" | "double_click" | "up" | "down" | "context_menu" | "long_press" | "long_press_release" }.
- Snapshot again and assert.Unverified snippetsThe endpoints and actions above were exercised against simulator 0.9.3. The two snippets below were not run verbatim - treat them as templates and confirm against your own install before relying on them in CI.Python examplepython
```
import time
import io
import sys
from urllib.request import Request, urlopen
import json
from PIL import Image
BASE = "http://127.0.0.1:9898"
READY_MARKER = "[my-app] ready" # whatever your app logs once mounted
TIMEOUT_S = 30
def get_json(path: str):
with urlopen(f"{BASE}{path}") as r:
return json.loads(r.read())
def get_png(path: str) -> Image.Image:
with urlopen(f"{BASE}{path}") as r:
return Image.open(io.BytesIO(r.read()))
def post_json(path: str, body: dict):
req = Request(
f"{BASE}{path}",
data=json.dumps(body).encode("utf-8"),
headers={"Content-Type": "application/json"},
method="POST",
)
with urlopen(req) as r:
return r.read()
def wait_for_ready(timeout: float = TIMEOUT_S):
"""Poll the console buffer until the app prints its ready marker."""
deadline = time.time() + timeout
since_id = 0
while time.time() < deadline:
data = get_json(f"/api/console?since_id={since_id}")
for entry in data.get("entries", []):
since_id = max(since_id, entry["id"])
if READY_MARKER in entry.get("message", ""):
return
time.sleep(0.25)
raise TimeoutError(f"App did not log {READY_MARKER!r} within {timeout}s")
def lit_pixel_count(img: Image.Image) -> int:
"""LVGL framebuffer is RGBA; treat any pixel with alpha > 0 as lit."""
assert img.mode == "RGBA", f"expected RGBA, got {img.mode}"
return sum(1 for px in img.getdata() if px[3] > 0)
def main() -> int:
assert get_json("/api/ping") in ("pong", {"message": "pong"}), "simulator not up"
wait_for_ready()
boot = get_png("/api/screenshot/glasses")
assert lit_pixel_count(boot) > 100, "framebuffer is blank after ready"
post_json("/api/input", {"action": "double_click"})
time.sleep(0.5) # let the dialog render
after = get_png("/api/screenshot/glasses")
delta = abs(lit_pixel_count(after) - lit_pixel_count(boot))
assert delta > 50, "framebuffer did not change after double_click - exit dialog missing?"
print("OK - app booted, rendered, and produced an exit dialog on double-tap")
return 0
if __name__ == "__main__":
sys.exit(main())
```
Node exampletypescript
```
const BASE = 'http://127.0.0.1:9898'
const READY_MARKER = '[my-app] ready'
async function getJson<T>(path: string): Promise<T> {
const res = await fetch(`${BASE}${path}`)
if (!res.ok) throw new Error(`${path} → ${res.status}`)
return res.json() as Promise<T>
}
async function getPng(path: string): Promise<Uint8Array> {
const res = await fetch(`${BASE}${path}`)
if (!res.ok) throw new Error(`${path} → ${res.status}`)
return new Uint8Array(await res.arrayBuffer())
}
async function postJson(path: string, body: unknown): Promise<void> {
const res = await fetch(`${BASE}${path}`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body),
})
if (!res.ok) throw new Error(`${path} → ${res.status}`)
}
interface ConsoleEntry { id: number; message: string }
interface ConsoleResponse { entries: ConsoleEntry[]; total: number }
async function waitForReady(timeoutMs = 30_000): Promise<void> {
const deadline = Date.now() + timeoutMs
let sinceId = 0
while (Date.now() < deadline) {
const data = await getJson<ConsoleResponse>(`/api/console?since_id=${sinceId}`)
for (const entry of data.entries) {
sinceId = Math.max(sinceId, entry.id)
if (entry.message.includes(READY_MARKER)) return
}
await new Promise(r => setTimeout(r, 250))
}
throw new Error(`App did not log "${READY_MARKER}" within ${timeoutMs}ms`)
}
async function main(): Promise<void> {
await getJson('/api/ping')
await waitForReady()
const boot = await getPng('/api/screenshot/glasses')
if (boot.byteLength < 1000) throw new Error('framebuffer is suspiciously small')
await postJson('/api/input', { action: 'double_click' })
await new Promise(r => setTimeout(r, 500))
const after = await getPng('/api/screenshot/glasses')
if (Math.abs(after.byteLength - boot.byteLength) < 100) {
throw new Error('framebuffer did not change after double_click - exit dialog missing?')
}
console.log('OK - app booted, rendered, and produced an exit dialog on double-tap')
}
main().catch(err => {
console.error(err)
process.exit(1)
})
```
TIPThe Node example uses byte-length deltas as a coarse "did anything change" check. For real assertions, decode the PNG (e.g. with sharp) and use the same alpha > 0 lit-pixel rule as the Python example.
### Patterns and pitfalls ​
Read startup logs before clearing. Boot logs - SDK init, manifest load, first createStartUpPageContainer result - are emitted exactly once. Poll for the ready marker first, then clear the buffer.Use since_id for incremental polling. Re-reading the whole console buffer on every tick wastes work and risks double-handling. Track the highest id you've seen and pass it back as ?since_id=N.Keep screenshots in RGBA. /api/screenshot/glasses returns RGBA on purpose. The Even G2 framebuffer renders both background and foreground in pure green; collapsing to RGB fuses them and the lit-pixel check stops working. Test with pixel.alpha > 0, not RGB deltas.Brightness lives in the alpha channel. RGB stays 0,255,0 for every lit pixel - the five textColor levels come back as five distinct alpha values, dimmest to brightest. To assert a container renders at the level you set, sample its region and compare alpha, not colour.Wait for input capture. Posting input before createStartUpPageContainer runs is silently dropped - no error. Wait for your readiness signal first. Roughly 4 s after launch is a reasonable lower bound, but keying off a log line beats sleeping.Cleaning up. The control plane has no shutdown endpoint - kill the simulator process when you're done. In CI, wrap the launch in a child-process supervisor you can SIGTERM from your test runner's afterAll hook.
## Related ​
- Testing Modes - when to use the simulator vs hardware modes
- App Submission & QA Guidelines - what to assert in headless tests
- Page Lifecycle - events you can use as readiness / exit signalsPagerPrevious pageTestNext pageLocal Testing

26
docs/private-testing.html Normal file

File diff suppressed because one or more lines are too long

25
docs/quickstart.html Normal file

File diff suppressed because one or more lines are too long

26
docs/simulator.html Normal file

File diff suppressed because one or more lines are too long

1
docs/site-data.json Normal file
View File

@@ -0,0 +1 @@
{"lang": "en-US", "dir": "ltr", "title": "Documentation", "description": "EvenRealities Developer Portal Documentation", "base": "/docs/", "head": [], "router": {"prefetchLinks": true}, "appearance": "dark", "themeConfig": {"logo": "/imgs/icon.svg", "logoLink": "/docs/get-started/overview", "darkModeSwitchLabel": "Theme", "lightModeSwitchTitle": "Switch to light theme", "darkModeSwitchTitle": "Switch to dark theme", "nav": [{"text": "Portal", "link": "https://evenhub.evenrealities.com", "target": "", "noIcon": true}], "search": {"provider": "local"}, "sidebar": [{"text": "Get Started", "link": "/get-started/index.md", "items": [{"text": "Overview", "link": "/get-started/overview"}, {"text": "Quickstart", "link": "/get-started/quickstart/index.md", "items": [{"text": "Sign in", "link": "/get-started/quickstart/sign-in"}, {"text": "Hardware", "link": "/get-started/quickstart/hardware"}, {"text": "Install Node.js & npm", "link": "/get-started/quickstart/install-node"}, {"text": "Install Even Hub tooling", "link": "/get-started/quickstart/install-tools"}, {"text": "Your First App", "link": "/get-started/quickstart/first-app"}, {"text": "Templates", "link": "/get-started/quickstart/templates"}]}, {"text": "Architecture", "link": "/get-started/architecture"}]}, {"text": "Build", "link": "/build/index.md", "items": [{"text": "Page Lifecycle", "link": "/build/page-lifecycle"}, {"text": "Display & UI System", "link": "/build/display"}, {"text": "UI/UX Design Guidelines", "link": "/build/design-guidelines"}, {"text": "Device APIs", "link": "/build/device-apis"}, {"text": "Contextual Menu", "link": "/build/contextual-menu"}, {"text": "Networking", "link": "/build/networking"}, {"text": "Background & Lifecycle", "link": "/build/background-lifecycle"}]}, {"text": "Test", "link": "/test/index.md", "items": [{"text": "Simulator", "link": "/test/simulator"}, {"text": "Local Testing", "link": "/test/local-testing"}, {"text": "Private Testing", "link": "/test/private-testing"}, {"text": "Beta Testing", "link": "/test/beta-testing"}]}, {"text": "Ship", "link": "/ship/index.md", "items": [{"text": "Packaging & Deployment", "link": "/ship/packaging"}, {"text": "App Submission & QA Guidelines", "link": "/ship/app-submission"}]}, {"text": "Reference", "link": "/reference/index.md", "items": [{"text": "Glossary", "link": "/reference/glossary"}, {"text": "CLI", "link": "/reference/cli"}, {"text": "Versioning Policy", "link": "/reference/versioning"}, {"text": "Changelog", "link": "/reference/changelog"}, {"text": "FAQ", "link": "/reference/faq"}]}, {"text": "AI Tooling", "link": "/AI-tooling/index.md", "items": [{"text": "Claude Code", "link": "/AI-tooling/claude-code"}]}], "socialLinks": []}, "locales": {}, "scrollOffset": 134, "cleanUrls": true, "additionalConfig": {}}

26
docs/templates.html Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

26
docs/your-first-app.html Normal file

File diff suppressed because one or more lines are too long