# Gauge.S Editor and Agent Workflow

> Browser editor and WebAssembly renderer for Gauge.S V6/V6.1 color gauges.
> Hosted at https://editor.sorek.uk and locally on the device. Check the target's
> version/capabilities; hosted and device releases can differ. Accounts, licensing,
> downloads and publishing are handled by https://portal.sorek.uk.

## Pick a workflow

- Create a reusable Lua module (global warnings/timers/overlays): start with [Module authoring](modules/authoring.md), then the [Module API and complete example](modules/llms.txt). Device URLs are `/wifi/modules/authoring.md` and `/wifi/modules/llms.txt`. These offline guides cover manifest, sandbox, transparent views, executable production-WASM checks and explicit installation; use a Module API 1 editor. Repository entry point: `deploy/wifi/modules/llms.txt`.
- Create or revise a gauge by hand: open [/sim/editor.html](https://editor.sorek.uk/sim/editor.html), use the widget/JSON/customs editors, preview it in WASM, then **Export .ogp**. Keep the complete pack, including `.dat`, `.fnt`, customs and auxiliary files.
- Author JSON/assets without the editor UI: read the [Gauge authoring guide](https://editor.sorek.uk/gauge-guide.md). It covers the schema, asset paths, customs/presets, rendering and memory limits, complete `.ogp` format, device install and troubleshooting.
- Test JSON in a headless browser: use [agent-render.cjs](https://editor.sorek.uk/sim/agent-render.cjs) with JSON plus asset files. It uses the editor renderer and `GaugeAgent`; JSON-only bundles do not contain referenced assets unless you provide them.
- Work with an existing Portal design: sign in to the portal and use **Open in editor** for the human workflow. For a local agent, use a scoped Portal API key to fetch an authorized source pack, then load it into `GaugeAgent` as described below.
- Work offline on a device: browse `http://gauge.local/wifi/sim/editor.html` or the device AP address. Local editing, import/export, preview and explicit device Save/Load work without internet. Portal publishing requires internet.
- Edit an older V5 mono gauge: use [/v5](https://editor.sorek.uk/v5). V5 uses a different monochrome format and is not accepted by the V6 Portal pack API.

## Element fields, defaults and good design

The complete, always-current list of element fields, defaults and `?` tooltips is machine-readable at [/sim/ogs_editor_schema.js](https://editor.sorek.uk/sim/ogs_editor_schema.js) (generated from the renderer's own schema). Prefer it over copying fields from examples; the [gauge authoring guide](https://editor.sorek.uk/gauge-guide.md) explains the concepts (coordinates, layers, binding, customs, memory budget).

Design practices that make gauges fast and easy to use:

- Ship named **presets** in `customs.json` for every custom set, so users pick a look from a list instead of editing individual values.
- Put backgrounds and static art on **layer 0 with `drawOnce: true`** — baked once at init, free per frame, and the source images are freed from RAM.
- Keep sprite layers few (~4 max; a full-screen one costs ~850 KB). Size `w×h` to the content and reuse one layer for elements that move together.
- **Group elements that share a parameter**: children inherit the group's `defaultParam`/`minValue`/`maxValue`, so one binding drives the whole cluster and the dashboard binding list stays short.
- Keep the redraw defaults (needles/arcs 50 Hz, text 7 Hz) and lower `updateHz` for slow values; `smoothing` defaults to 5 (0 = instant).

## Browser automation API

After `GaugeAgent.ready()` returns `true`, the editor page exposes:

- `loadDesign(jsonText, [{name,b64|bytes}])` for JSON plus individual assets.
- `await loadOgpB64(b64, name, sourceInfo?)` for a complete OGP. It validates/imports the pack while retaining its sidecar presets, asset paths and auxiliary files. Module manifests route to lazy-loaded module source tools, preserving the current gauge; use `exportModuleOgpB64()` for that pack. `sourceInfo` may be the decoded Portal `X-Gauge-Source` response header; this is display/attribution metadata, not authorization.
- `getDesignJson()` / `setDesignJson(jsonText)` to inspect and revise the gauge JSON while preserving the imported pack's sidecar presets, assets and source attribution.
- `setParam(name,value)`, `listParams()`, `listFonts()`, `screenshotPng()` and `exportOgpB64()` for preview and complete-pack export. For WebGL, prefer an automation screenshot of `#canvas-wrap`; `screenshotPng()` may be blank.

Example Playwright/Puppeteer flow: wait for `GaugeAgent.ready()`, call `loadOgpB64`, parse and modify `getDesignJson()`, call `setDesignJson`, render, then call `exportOgpB64()`. The exported pack preserves assets and customs; the Portal still rechecks all ownership, license, source-version and publication rules.

## Portal authoring API

The Editor has no persistent account/API credentials of its own. Portal agent keys are managed at [/my/api-keys](https://portal.sorek.uk/my/api-keys) after sign-in and current-password confirmation. Keys are shown once, stored as hashes, optionally expire, can be revoked, and are invalidated by account security/session-epoch changes. Keep a key in a local secret manager or process environment; never put it in a URL, browser storage, source code, prompt transcript or log.

Only two key scopes exist:

- `design:read`: read a complete currently downloadable pack. The Portal enforces the owner's normal author/free/purchase entitlement, records/counts the download and applies its daily cap.
- `design:submit`: create a private draft or submit a new/linked-fork pack. Key issuance requires explicit consent to the current submission Terms. Public submissions must send `terms:true`; reissue/renew consent if Terms change.

Fetch an eligible source pack with `GET https://portal.sorek.uk/api/designs/{slug}/source.ogp` and `Authorization: Bearer $GAUGE_S_API_KEY`. The body is the original `.ogp`; decode the base64 `X-Gauge-Source` header to obtain its pinned `version_id`, license and hash. CORS permits only the configured `https://editor.sorek.uk` origin; a server-side agent can omit `Origin`. Do not send cookies or put keys in browser URLs.

Example for a Node-based agent; the credential stays in the process and only the pack/public attribution is passed into the editor page:

```js
const response = await fetch(`https://portal.sorek.uk/api/designs/${encodeURIComponent(slug)}/source.ogp`, {
  headers: { Authorization: `Bearer ${process.env.GAUGE_S_API_KEY}` }
});
if (!response.ok) throw new Error(`Portal source read: HTTP ${response.status}`);
const packB64 = Buffer.from(await response.arrayBuffer()).toString('base64');
const sourceInfo = JSON.parse(Buffer.from(response.headers.get('X-Gauge-Source'), 'base64').toString('utf8'));
const loaded = await page.evaluate(({b64, source}) => GaugeAgent.loadOgpB64(b64, 'portal-source.ogp', source), {b64: packB64, source: sourceInfo});
if (!loaded.ok) throw new Error(loaded.error);
```

Use `GaugeAgent.getDesignJson()` / `setDesignJson()` to change gauge JSON and `exportOgpB64()` to produce the complete edited pack. The original asset bytes, sidecar presets and `portal-source.json` attribution are retained.

After editing/exporting, submit the whole OGP to `POST https://portal.sorek.uk/api/designs` as JSON with `title`, optional `description`/`car_tags`, `license`, `terms`, `pack_b64`, and—when forking—`fork_source_version` from `X-Gauge-Source` plus `fork_changes`. `pack_b64` is limited to a 20 MiB OGP; the JSON body is capped at 32 MiB. The response is `201` with `{slug,url,status}`. Duplicate packs return `409`; there is no idempotency key, so check My designs after an ambiguous timeout before retrying.

Approved public free sources can become linked forks; the Portal rechecks the source version, inherits its license, and keeps the fork free. A paid source must already be purchased to read and can only be saved privately; API keys cannot buy it or publish/price a paid-source copy. Every submission still runs pack validation, rendering and the Portal's moderation/trusted-creator policy. Keys cannot purchase designs, spend points, update an existing listing, change accounts/settings, price listings, follow/comment, or moderate.

For ordinary browser publishing, choose **Publish to portal** in the editor. A data-only, exact-origin/window/nonce bridge attaches the complete pack to a portal form; it never auto-submits, transfers cookies, or sends a reusable key. The one-use advanced token at `/my/link-editor` remains an alternative: it is issued in an authenticated portal session, displayed once, expires after 10 minutes and authorizes one submission.

## Device security and references

The physical device HTTP API is unauthenticated on its local LAN/AP. It supports file upload, list, parameters, screenshot, activation, refresh and restart. **Do not expose the device API to the public internet.** Use it only on a trusted local network; verify uploads before restart. See the [device HTTP guide](https://editor.sorek.uk/gauge-guide.md#12-deploying-to-a-device-over-http).

- [Gauge.S Portal agent guide](https://portal.sorek.uk/llms.txt)
- [Gauge authoring guide](https://editor.sorek.uk/gauge-guide.md)
- [ECU definition files](https://github.com/handmade0octopus/gauge.s-sorek.uk)
- [Firmware/editor source](https://github.com/handmade0octopus/Gauge.S3)
## Reusable modules (V6 / V6.1)

Offline user guide: [modules/guide.html](modules/guide.html)
From-scratch authoring and verification: [modules/authoring.md](modules/authoring.md)
Compact API and complete Lua + manifest + view example: [modules/llms.txt](modules/llms.txt)
Open Modules in the device sidebar, or File > Modules in the editor. Settings,
appearance previews and source authoring are explicit; staging/setup/start are separate.
