opencharm 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/main.mjs +111 -26
- package/package.json +2 -1
- package/sim/charm_sim.js +1 -1
- package/sim/charm_sim.wasm +0 -0
- package/sim/index.html +2 -0
- package/sim/sim.js +10 -0
package/README.md
CHANGED
|
@@ -18,7 +18,7 @@ npx opencharm
|
|
|
18
18
|
| `opencharm sim` | The charm on screen, in your browser, talking to charmd (`--url`, `--port`, `--no-open`) |
|
|
19
19
|
| `sudo opencharm setup --domain <d>` | Install charmd as a hardened systemd service on a Linux server (`--dry-run` prints the plan; see `docs/deploy.md`) |
|
|
20
20
|
| `opencharm pair <code>` | Pair the charm showing that code; you choose its PIN (`--name <name>`) |
|
|
21
|
-
| `opencharm status` |
|
|
21
|
+
| `opencharm status` | What's running (charmd, each connected charm's build, the starter commit of the workspace; spec 015), then the paired charms and whether they are connected, locked or blocked |
|
|
22
22
|
| `opencharm lock` / `unlock` / `revoke <charm>` | Lock it now; clear a block and unlock; forget it |
|
|
23
23
|
| `opencharm look` | Show the charm's look, or change it on every unlocked charm until charmd restarts (`--colour <colour>`, `--greeting <text>`, `--motion full\|calm`); to keep it, set `charm` in `opencharm.json` |
|
|
24
24
|
| `opencharm dev face <charm> <state> [text]` / `dev say <charm> <text>` / `dev ask <charm> <question>` | Show a face, speak, or ask a question (hold = yes, press = no) on a connected charm (testing) |
|
|
@@ -30,6 +30,6 @@ Node 24 or newer. Runtime dependencies: `ws`, `zod`.
|
|
|
30
30
|
|
|
31
31
|
Source and hardware: <https://github.com/opencharm-labs/opencharm>
|
|
32
32
|
|
|
33
|
-
Development: this package lives in the OpenCharm monorepo (`packages/cli`). Run it from the repo root with `npm run cli -- <command>`; `npm run build -w packages/cli` bundles `dist/main.mjs`.
|
|
33
|
+
Development: this package lives in the OpenCharm monorepo (`packages/cli`). Run it from the repo root with `npm run cli -- <command>`; `npm run build -w packages/cli` bundles `dist/main.mjs`. `opencharm --version` prints `cli@<version> (<commit>)` (spec 015); from source it says `cli@0.0.0`: released versions come from their `cli@` tag, and each merged `fix`/`feat`/`perf` that touches the CLI publishes one (CONTRIBUTING, "Releasing").
|
|
34
34
|
|
|
35
35
|
Not affiliated with Meta, Nous Research, OpenClaw, Waveshare or Espressif.
|
package/dist/main.mjs
CHANGED
|
@@ -5,7 +5,7 @@ import { dirname, extname, isAbsolute, join, normalize, resolve, sep } from "nod
|
|
|
5
5
|
import { z } from "zod";
|
|
6
6
|
import { createConnection, createServer } from "node:net";
|
|
7
7
|
import { createInterface } from "node:readline";
|
|
8
|
-
import { execFile, spawn, spawnSync } from "node:child_process";
|
|
8
|
+
import { execFile, execFileSync, spawn, spawnSync } from "node:child_process";
|
|
9
9
|
import { createHash, randomBytes, randomInt, randomUUID, scrypt, timingSafeEqual } from "node:crypto";
|
|
10
10
|
import { createServer as createServer$1 } from "node:http";
|
|
11
11
|
import { WebSocketServer } from "ws";
|
|
@@ -455,12 +455,20 @@ const audioParams = z.object({
|
|
|
455
455
|
channels: z.number().int().min(1).max(2),
|
|
456
456
|
frame_duration: z.number().int().positive()
|
|
457
457
|
});
|
|
458
|
+
const plain = (max) => z.string().min(1).max(max).regex(/^[\w.@+-]+$/);
|
|
459
|
+
const charmBuild = z.object({
|
|
460
|
+
kind: plain(16),
|
|
461
|
+
version: plain(64),
|
|
462
|
+
commit: plain(40)
|
|
463
|
+
});
|
|
464
|
+
const build = charmBuild.optional().catch(void 0);
|
|
458
465
|
const clientHello = z.object({
|
|
459
466
|
type: z.literal("hello"),
|
|
460
467
|
version: z.number().int().positive(),
|
|
461
468
|
features: z.record(z.string(), z.boolean()).optional(),
|
|
462
469
|
transport: z.literal("websocket"),
|
|
463
|
-
audio_params: audioParams
|
|
470
|
+
audio_params: audioParams,
|
|
471
|
+
build
|
|
464
472
|
});
|
|
465
473
|
const listen = z.object({
|
|
466
474
|
session_id: sessionId,
|
|
@@ -858,7 +866,7 @@ function sendAdmin(socketPath, request) {
|
|
|
858
866
|
}
|
|
859
867
|
//#endregion
|
|
860
868
|
//#region package.json
|
|
861
|
-
var version = "0.
|
|
869
|
+
var version = "0.2.0";
|
|
862
870
|
function channelToCube(value) {
|
|
863
871
|
if (value < 48) return 0;
|
|
864
872
|
if (value < 115) return 1;
|
|
@@ -999,6 +1007,17 @@ function promptHidden(question) {
|
|
|
999
1007
|
}
|
|
1000
1008
|
//#endregion
|
|
1001
1009
|
//#region src/commands/admin.ts
|
|
1010
|
+
const statusSchema = z.object({
|
|
1011
|
+
charmd: z.string().regex(/^[\w.@+() -]{1,96}$/).optional().catch(void 0),
|
|
1012
|
+
starter: z.string().regex(/^[0-9a-f]{4,40}$/).optional().catch(void 0),
|
|
1013
|
+
charms: z.array(z.object({
|
|
1014
|
+
name: z.string(),
|
|
1015
|
+
state: z.string(),
|
|
1016
|
+
blocked: z.boolean(),
|
|
1017
|
+
failedTries: z.number(),
|
|
1018
|
+
build: charmBuild.optional().catch(void 0)
|
|
1019
|
+
}))
|
|
1020
|
+
});
|
|
1002
1021
|
const DONE = {
|
|
1003
1022
|
lock: "Locked",
|
|
1004
1023
|
unlock: "Unblocked and unlocked",
|
|
@@ -1083,14 +1102,16 @@ async function look(ctx, args, deps) {
|
|
|
1083
1102
|
if (Object.keys(update).length === 0) return;
|
|
1084
1103
|
ctx.out.write(`${result.connected === 0 ? "No charm is unlocked right now: it gets the look when it unlocks." : `Sent to ${result.connected} charm${result.connected === 1 ? "" : "s"}.`} It lasts until charmd restarts; to keep it, set "charm" in opencharm.json.\n`);
|
|
1085
1104
|
}
|
|
1086
|
-
function printStatus(ctx, charms) {
|
|
1105
|
+
function printStatus(ctx, charms, charmd, starter) {
|
|
1106
|
+
if (charmd) ctx.out.write(`charmd ${charmd}\n`);
|
|
1107
|
+
if (starter) ctx.out.write(`workspace from starter ${starter}\n`);
|
|
1087
1108
|
if (charms.length === 0) {
|
|
1088
1109
|
ctx.out.write("No charms paired yet. Start one and run: opencharm pair <code>\n");
|
|
1089
1110
|
return;
|
|
1090
1111
|
}
|
|
1091
1112
|
for (const charm of charms) {
|
|
1092
|
-
const
|
|
1093
|
-
ctx.out.write(`${charm.name.padEnd(16)} ${charm.state.padEnd(10)} ${
|
|
1113
|
+
const details = [charm.blocked ? "blocked (opencharm unlock to clear)" : charm.failedTries > 0 ? `${charm.failedTries} wrong PIN(s)` : "", charm.build ? `${charm.build.kind} ${charm.build.version} (${charm.build.commit})` : ""].filter(Boolean).join(" ");
|
|
1114
|
+
ctx.out.write(`${charm.name.padEnd(16)} ${charm.state.padEnd(10)} ${details}`.trimEnd() + "\n");
|
|
1094
1115
|
}
|
|
1095
1116
|
}
|
|
1096
1117
|
async function runAdminCommand(ctx, command, args, deps) {
|
|
@@ -1108,8 +1129,13 @@ async function runAdminCommand(ctx, command, args, deps) {
|
|
|
1108
1129
|
return;
|
|
1109
1130
|
}
|
|
1110
1131
|
if (command === "status") {
|
|
1111
|
-
const
|
|
1112
|
-
|
|
1132
|
+
const answer = statusSchema.safeParse(await deps.send(deps.socket, { cmd: "status" }));
|
|
1133
|
+
if (!answer.success) {
|
|
1134
|
+
fail$2(ctx, "Unexpected answer from charmd: is it the same version as this CLI?");
|
|
1135
|
+
return;
|
|
1136
|
+
}
|
|
1137
|
+
const { charms, charmd, starter } = answer.data;
|
|
1138
|
+
printStatus(ctx, charms, charmd, starter);
|
|
1113
1139
|
return;
|
|
1114
1140
|
}
|
|
1115
1141
|
const charm = firstPositional(args);
|
|
@@ -1705,7 +1731,7 @@ function parseInput(schema, input) {
|
|
|
1705
1731
|
if (!result.success) throw new Error(result.error.issues.map((i) => i.message).join("; "));
|
|
1706
1732
|
return result.data;
|
|
1707
1733
|
}
|
|
1708
|
-
function createAdminActions({ store, pairing, sessions, look, agentCanChangeLook }) {
|
|
1734
|
+
function createAdminActions({ store, pairing, sessions, look, agentCanChangeLook, identity, starter }) {
|
|
1709
1735
|
function sessionsOf(charmId) {
|
|
1710
1736
|
return [...sessions()].filter((s) => s.charmId === charmId && s.state !== "closed");
|
|
1711
1737
|
}
|
|
@@ -1811,17 +1837,22 @@ function createAdminActions({ store, pairing, sessions, look, agentCanChangeLook
|
|
|
1811
1837
|
});
|
|
1812
1838
|
},
|
|
1813
1839
|
status() {
|
|
1814
|
-
return {
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1840
|
+
return {
|
|
1841
|
+
charmd: identity ?? "unknown",
|
|
1842
|
+
...starter ? { starter } : {},
|
|
1843
|
+
charms: store.list().map((charm) => {
|
|
1844
|
+
const live = sessionsOf(charm.id).at(-1);
|
|
1845
|
+
return {
|
|
1846
|
+
id: charm.id,
|
|
1847
|
+
name: charm.name,
|
|
1848
|
+
blocked: charm.blocked,
|
|
1849
|
+
failedTries: charm.failedTries,
|
|
1850
|
+
connected: live !== void 0,
|
|
1851
|
+
state: live?.state ?? "offline",
|
|
1852
|
+
build: live?.build
|
|
1853
|
+
};
|
|
1854
|
+
})
|
|
1855
|
+
};
|
|
1825
1856
|
}
|
|
1826
1857
|
};
|
|
1827
1858
|
}
|
|
@@ -2085,6 +2116,7 @@ var Session = class {
|
|
|
2085
2116
|
sessionId = randomBytes(8).toString("hex");
|
|
2086
2117
|
#state = "awaiting_hello";
|
|
2087
2118
|
#charmId;
|
|
2119
|
+
#build;
|
|
2088
2120
|
#queue = Promise.resolve();
|
|
2089
2121
|
#turn;
|
|
2090
2122
|
#asks = Promise.resolve();
|
|
@@ -2097,6 +2129,9 @@ var Session = class {
|
|
|
2097
2129
|
get state() {
|
|
2098
2130
|
return this.#state;
|
|
2099
2131
|
}
|
|
2132
|
+
get build() {
|
|
2133
|
+
return this.#build;
|
|
2134
|
+
}
|
|
2100
2135
|
get charmId() {
|
|
2101
2136
|
return this.#charmId;
|
|
2102
2137
|
}
|
|
@@ -2181,6 +2216,7 @@ var Session = class {
|
|
|
2181
2216
|
this.#close(CLOSE_POLICY$1);
|
|
2182
2217
|
return;
|
|
2183
2218
|
}
|
|
2219
|
+
this.#build = message.build;
|
|
2184
2220
|
this.#onHello();
|
|
2185
2221
|
return;
|
|
2186
2222
|
}
|
|
@@ -3612,7 +3648,9 @@ async function startDaemon(config, options = {}) {
|
|
|
3612
3648
|
pairing,
|
|
3613
3649
|
sessions: () => sessions.values(),
|
|
3614
3650
|
look,
|
|
3615
|
-
agentCanChangeLook
|
|
3651
|
+
agentCanChangeLook,
|
|
3652
|
+
identity: options.identity,
|
|
3653
|
+
starter: options.starter
|
|
3616
3654
|
});
|
|
3617
3655
|
const adminServer = await startAdminServer(config.adminSocket, admin);
|
|
3618
3656
|
let websocketUrl = config.publicUrl ?? "";
|
|
@@ -3699,7 +3737,12 @@ async function startDaemon(config, options = {}) {
|
|
|
3699
3737
|
const { port } = http.address();
|
|
3700
3738
|
const url = `ws://${config.listen.host.includes(":") ? `[${config.listen.host}]` : config.listen.host}:${port}/charm`;
|
|
3701
3739
|
websocketUrl = config.publicUrl ?? url;
|
|
3702
|
-
if (!options.quiet) console.log(
|
|
3740
|
+
if (!options.quiet) console.log([
|
|
3741
|
+
"charmd",
|
|
3742
|
+
options.identity,
|
|
3743
|
+
"listening on",
|
|
3744
|
+
url
|
|
3745
|
+
].filter(Boolean).join(" "));
|
|
3703
3746
|
return {
|
|
3704
3747
|
url,
|
|
3705
3748
|
admin,
|
|
@@ -3714,14 +3757,53 @@ async function startDaemon(config, options = {}) {
|
|
|
3714
3757
|
};
|
|
3715
3758
|
}
|
|
3716
3759
|
//#endregion
|
|
3760
|
+
//#region src/identity.ts
|
|
3761
|
+
function git(cwd, ...args) {
|
|
3762
|
+
return execFileSync("git", args, {
|
|
3763
|
+
cwd,
|
|
3764
|
+
encoding: "utf8",
|
|
3765
|
+
stdio: [
|
|
3766
|
+
"ignore",
|
|
3767
|
+
"pipe",
|
|
3768
|
+
"ignore"
|
|
3769
|
+
]
|
|
3770
|
+
}).trim();
|
|
3771
|
+
}
|
|
3772
|
+
function starterCommit(dir) {
|
|
3773
|
+
try {
|
|
3774
|
+
if (!existsSync(join(git(dir, "rev-parse", "--show-toplevel"), "opencharm.json"))) return void 0;
|
|
3775
|
+
return git(dir, "rev-parse", "--short", git(dir, "merge-base", "HEAD", "upstream/HEAD"));
|
|
3776
|
+
} catch {
|
|
3777
|
+
return;
|
|
3778
|
+
}
|
|
3779
|
+
}
|
|
3780
|
+
function formatIdentity(unit, version, commit) {
|
|
3781
|
+
return `${unit}@${version} (${commit})`;
|
|
3782
|
+
}
|
|
3783
|
+
let cached;
|
|
3784
|
+
function identity() {
|
|
3785
|
+
if (cached) return cached;
|
|
3786
|
+
const commit = "f4edfa0";
|
|
3787
|
+
cached = {
|
|
3788
|
+
version: `cli@${version}`,
|
|
3789
|
+
commit,
|
|
3790
|
+
text: formatIdentity("cli", version, commit)
|
|
3791
|
+
};
|
|
3792
|
+
return cached;
|
|
3793
|
+
}
|
|
3794
|
+
//#endregion
|
|
3717
3795
|
//#region src/commands/serve.ts
|
|
3718
3796
|
async function runServe(ctx, args) {
|
|
3719
3797
|
let config;
|
|
3720
3798
|
let daemon;
|
|
3721
3799
|
try {
|
|
3722
|
-
|
|
3800
|
+
const configPath = flagValue(args, "--config");
|
|
3801
|
+
config = loadConfig(configPath);
|
|
3802
|
+
const workspace = configPath ? dirname(resolve(configPath)) : existsSync("opencharm.json") ? process.cwd() : void 0;
|
|
3723
3803
|
daemon = await startDaemon(config, {
|
|
3724
3804
|
quiet: true,
|
|
3805
|
+
identity: identity().text,
|
|
3806
|
+
starter: workspace ? starterCommit(workspace) : void 0,
|
|
3725
3807
|
charmTools: {
|
|
3726
3808
|
command: process.execPath,
|
|
3727
3809
|
args: [
|
|
@@ -3740,7 +3822,7 @@ async function runServe(ctx, args) {
|
|
|
3740
3822
|
process.exitCode = 1;
|
|
3741
3823
|
return;
|
|
3742
3824
|
}
|
|
3743
|
-
ctx.out.write(`charmd (the charm daemon) is listening on ${daemon.url}\n`);
|
|
3825
|
+
ctx.out.write(`charmd (the charm daemon) ${identity().text} is listening on ${daemon.url}\n`);
|
|
3744
3826
|
ctx.out.write(`Admin socket: ${config.adminSocket}\n`);
|
|
3745
3827
|
ctx.out.write("Pair a charm: opencharm pair <code> Stop: Ctrl-C\n");
|
|
3746
3828
|
const stop = () => {
|
|
@@ -3993,7 +4075,10 @@ function createSimServer(dir, charmdUrl) {
|
|
|
3993
4075
|
return;
|
|
3994
4076
|
}
|
|
3995
4077
|
if (path === "/config.json") {
|
|
3996
|
-
res.writeHead(200, { "content-type": TYPES[".json"] }).end(JSON.stringify({
|
|
4078
|
+
res.writeHead(200, { "content-type": TYPES[".json"] }).end(JSON.stringify({
|
|
4079
|
+
url: charmdUrl,
|
|
4080
|
+
version: identity().version
|
|
4081
|
+
}));
|
|
3997
4082
|
return;
|
|
3998
4083
|
}
|
|
3999
4084
|
const file = resolve(root, normalize(`.${path === "/" ? "/index.html" : path}`));
|
|
@@ -4086,7 +4171,7 @@ async function main(argv) {
|
|
|
4086
4171
|
const ctx = buildContext(argv);
|
|
4087
4172
|
const { command, rest, help, version } = parseArgs(argv);
|
|
4088
4173
|
if (version) {
|
|
4089
|
-
ctx.out.write(`${
|
|
4174
|
+
ctx.out.write(`${identity().text}\n`);
|
|
4090
4175
|
return;
|
|
4091
4176
|
}
|
|
4092
4177
|
if (help || command === "help") {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencharm",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "An open-source body for the AI agent you already run: a face, a voice, one key. Claude Code, Codex, Gemini CLI, Hermes, OpenClaw or any ACP or OpenAI-compatible agent; on a small board, your Mac's notch or the browser.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
"devDependencies": {
|
|
25
25
|
"@opencharm-labs/charmd": "*",
|
|
26
26
|
"@opencharm-labs/design": "*",
|
|
27
|
+
"@opencharm-labs/protocol": "*",
|
|
27
28
|
"playwright": "^1.63.0",
|
|
28
29
|
"tsdown": "^0.23.0"
|
|
29
30
|
},
|
package/sim/charm_sim.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
async function createCharm(moduleArg={}){var Module=moduleArg;var ENVIRONMENT_IS_WEB=true;var ENVIRONMENT_IS_WORKER=false;var programArgs=[];var thisProgram="./this.program";var _scriptName=import.meta.url;var scriptDirectory="";function locateFile(path){if(Module["locateFile"]){return Module["locateFile"](path,scriptDirectory)}return scriptDirectory+path}var readAsync,readBinary;if(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER){try{scriptDirectory=new URL(".",_scriptName).href}catch{}{readAsync=async url=>{var response=await fetch(url,{credentials:"same-origin"});if(response.ok){return response.arrayBuffer()}throw new Error(response.status+" : "+response.url)}}}else{}var out=console.log.bind(console);var err=console.error.bind(console);var wasmBinary;var ABORT=false;class EmscriptenEH{}class EmscriptenSjLj extends EmscriptenEH{}var runtimeInitialized=false;function getMemoryBuffer(){return wasmMemory.buffer}function updateMemoryViews(){if(HEAP8?.buffer?.resizable)return;var b=getMemoryBuffer();HEAP8=new Int8Array(b);Module["HEAP16"]=HEAP16=new Int16Array(b);Module["HEAPU8"]=HEAPU8=new Uint8Array(b);Module["HEAP32"]=HEAP32=new Int32Array(b);HEAPU32=new Uint32Array(b);Module["HEAPF32"]=HEAPF32=new Float32Array(b)}function preRun(){var preRun=Module["preRun"];if(preRun){if(typeof preRun=="function")preRun=[preRun];onPreRuns.push(...preRun)}callRuntimeCallbacks(onPreRuns)}function initRuntime(){runtimeInitialized=true;wasmExports["r"]()}function postRun(){var postRun=Module["postRun"];if(postRun){if(typeof postRun=="function")postRun=[postRun];onPostRuns.push(...postRun)}callRuntimeCallbacks(onPostRuns)}function abort(what){Module["onAbort"]?.(what);what=`Aborted(${what})`;err(what);ABORT=true;what+=". Build with -sASSERTIONS for more info.";var e=new WebAssembly.RuntimeError(what);throw e}var wasmBinaryFile;function findWasmBinary(){if(Module["locateFile"]){return locateFile("charm_sim.wasm")}return new URL("charm_sim.wasm",import.meta.url).href}function getBinarySync(file){if(readBinary){return readBinary(file)}throw"both async and sync fetching of the wasm failed"}async function getWasmBinary(binaryFile){if(!wasmBinary){try{var response=await readAsync(binaryFile);return new Uint8Array(response)}catch{}}return getBinarySync(binaryFile)}async function instantiateArrayBuffer(binaryFile,imports){try{var binary=await getWasmBinary(binaryFile);var instance=await WebAssembly.instantiate(binary,imports);return instance}catch(reason){err(`failed to asynchronously prepare wasm: ${reason}`);abort(reason)}}async function instantiateAsync(binary,binaryFile,imports){if(!binary){try{var response=fetch(binaryFile,{credentials:"same-origin"});var instantiationResult=await WebAssembly.instantiateStreaming(response,imports);return instantiationResult}catch(reason){err(`wasm streaming compile failed: ${reason}`);err("falling back to ArrayBuffer instantiation")}}return instantiateArrayBuffer(binaryFile,imports)}function getWasmImports(){var imports={a:wasmImports};return imports}async function createWasm(){function receiveInstance(instance){wasmExports=instance.exports;assignWasmExports(wasmExports);updateMemoryViews();return wasmExports}function receiveInstantiationResult(result){return receiveInstance(result["instance"])}var info=getWasmImports();var instantiateWasm=Module["instantiateWasm"];if(instantiateWasm){return new Promise(resolve=>{instantiateWasm(info,inst=>resolve(receiveInstance(inst)))})}wasmBinaryFile??=findWasmBinary();var result=await instantiateAsync(wasmBinary,wasmBinaryFile,info);var exports=receiveInstantiationResult(result);return exports}class ExitStatus{name="ExitStatus";constructor(status){this.message=`Program terminated with exit(${status})`;this.status=status}}var HEAP8;var callRuntimeCallbacks=callbacks=>{while(callbacks.length>0){callbacks.shift()(Module)}};var onPostRuns=[];var onPreRuns=[];var noExitRuntime=true;var HEAPU32;class ExceptionInfo{constructor(excPtr){this.excPtr=excPtr;this.ptr=excPtr-24}set_type(type){HEAPU32[this.ptr+4>>2]=type}get_type(){return HEAPU32[this.ptr+4>>2]}set_destructor(destructor){HEAPU32[this.ptr+8>>2]=destructor}get_destructor(){return HEAPU32[this.ptr+8>>2]}set_caught(caught){caught=caught?1:0;HEAP8[this.ptr+12]=caught}get_caught(){return HEAP8[this.ptr+12]!=0}set_rethrown(rethrown){rethrown=rethrown?1:0;HEAP8[this.ptr+13]=rethrown}get_rethrown(){return HEAP8[this.ptr+13]!=0}init(type,destructor){this.set_adjusted_ptr(0);this.set_type(type);this.set_destructor(destructor)}set_adjusted_ptr(adjustedPtr){HEAPU32[this.ptr+16>>2]=adjustedPtr}get_adjusted_ptr(){return HEAPU32[this.ptr+16>>2]}}var uncaughtExceptionCount=0;var __Unwind_RaiseException=ex=>{abort()};var ___cxa_throw=(ptr,type,destructor)=>{var info=new ExceptionInfo(ptr);info.init(type,destructor);uncaughtExceptionCount++;__Unwind_RaiseException(ptr)};var __abort_js=()=>abort("");var getHeapMax=()=>2147483648;var alignMemory=(size,alignment)=>Math.ceil(size/alignment)*alignment;var growMemory=size=>{var oldHeapSize=wasmMemory.buffer.byteLength;var pages=(size-oldHeapSize+65535)/65536|0;try{wasmMemory.grow(pages);updateMemoryViews();return 1}catch(e){}};var HEAPU8;var _emscripten_resize_heap=requestedSize=>{var oldSize=HEAPU8.length;requestedSize>>>=0;var maxHeapSize=getHeapMax();if(requestedSize>maxHeapSize){return false}for(var cutDown=1;cutDown<=4;cutDown*=2){var overGrownHeapSize=oldSize*(1+.2/cutDown);overGrownHeapSize=Math.min(overGrownHeapSize,requestedSize+100663296);var newSize=Math.min(maxHeapSize,alignMemory(Math.max(requestedSize,overGrownHeapSize),65536));var replacement=growMemory(newSize);if(replacement){return true}}return false};var _fd_close=fd=>52;var printCharBuffers=[null,[],[]];var UTF8Decoder=globalThis.TextDecoder&&new TextDecoder;var findStringEnd=(heapOrArray,idx,maxBytesToRead,ignoreNul)=>{var maxIdx=idx+maxBytesToRead;if(ignoreNul)return maxIdx;while(heapOrArray[idx]&&!(idx>=maxIdx))++idx;return idx};var UTF8ArrayToString=(heapOrArray,idx=0,maxBytesToRead,ignoreNul)=>{var endPtr=findStringEnd(heapOrArray,idx,maxBytesToRead,ignoreNul);if(endPtr-idx>16&&heapOrArray.buffer&&UTF8Decoder){return UTF8Decoder.decode(heapOrArray.subarray(idx,endPtr))}var str="";while(idx<endPtr){var u0=heapOrArray[idx++];if(!(u0&128)){str+=String.fromCharCode(u0);continue}var u1=heapOrArray[idx++]&63;if((u0&224)==192){str+=String.fromCharCode((u0&31)<<6|u1);continue}var u2=heapOrArray[idx++]&63;if((u0&240)==224){u0=(u0&15)<<12|u1<<6|u2}else{u0=(u0&7)<<18|u1<<12|u2<<6|heapOrArray[idx++]&63}if(u0<65536){str+=String.fromCharCode(u0)}else{var ch=u0-65536;str+=String.fromCharCode(55296|ch>>10,56320|ch&1023)}}return str};var printChar=(stream,curr)=>{var buffer=printCharBuffers[stream];if(!curr||curr===10){(stream===1?out:err)(UTF8ArrayToString(buffer));buffer.length=0}else{buffer.push(curr)}};var UTF8ToString=(ptr,maxBytesToRead,ignoreNul)=>ptr?UTF8ArrayToString(HEAPU8,ptr,maxBytesToRead,ignoreNul):"";var _fd_write=(fd,iov,iovcnt,pnum)=>{var num=0;for(var i=0;i<iovcnt;i++){var ptr=HEAPU32[iov>>2];var len=HEAPU32[iov+4>>2];iov+=8;for(var j=0;j<len;j++){printChar(fd,HEAPU8[ptr+j])}num+=len}HEAPU32[pnum>>2]=num;return 0};var HEAPF32;var HEAP16;var HEAP32;var stringToUTF8Array=(str,heap,outIdx,maxBytesToWrite)=>{if(!(maxBytesToWrite>0))return 0;var startIdx=outIdx;var endIdx=outIdx+maxBytesToWrite-1;for(var i=0;i<str.length;++i){var u=str.codePointAt(i);if(u<=127){if(outIdx>=endIdx)break;heap[outIdx++]=u}else if(u<=2047){if(outIdx+1>=endIdx)break;heap[outIdx++]=192|u>>6;heap[outIdx++]=128|u&63}else if(u<=65535){if(outIdx+2>=endIdx)break;heap[outIdx++]=224|u>>12;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63}else{if(outIdx+3>=endIdx)break;heap[outIdx++]=240|u>>18;heap[outIdx++]=128|u>>12&63;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63;i++}}heap[outIdx]=0;return outIdx-startIdx};var stringToUTF8=(str,outPtr,maxBytesToWrite)=>stringToUTF8Array(str,HEAPU8,outPtr,maxBytesToWrite);var lengthBytesUTF8=str=>{var len=0;for(var i=0;i<str.length;++i){var c=str.charCodeAt(i);if(c<=127){len++}else if(c<=2047){len+=2}else if(c>=55296&&c<=57343){len+=4;++i}else{len+=3}}return len};{if(Module["noExitRuntime"])noExitRuntime=Module["noExitRuntime"];if(Module["print"])out=Module["print"];if(Module["printErr"])err=Module["printErr"];if(Module["arguments"])programArgs=Module["arguments"];if(Module["thisProgram"])thisProgram=Module["thisProgram"];var preInit=Module["preInit"];if(preInit){if(typeof preInit=="function")Module["preInit"]=preInit=[preInit];while(preInit.length>0){preInit.shift()()}}}Module["UTF8ToString"]=UTF8ToString;Module["stringToUTF8"]=stringToUTF8;Module["lengthBytesUTF8"]=lengthBytesUTF8;function js_panel(open){charmSim.panel(!!open)}function js_send_text(json){charmSim.sendText(UTF8ToString(json))}function js_send_binary(data,size){charmSim.sendBinary(HEAPU8.slice(data,data+size))}function js_mic(on){charmSim.mic(!!on)}function js_play_pcm(pcm,samples){charmSim.playPcm(HEAPF32.slice(pcm>>2,(pcm>>2)+samples))}function js_stop_audio(){charmSim.stopAudio()}function js_brightness(percent){charmSim.brightness(percent)}function js_store_get(key){var value=localStorage.getItem("opencharm."+UTF8ToString(key));if(value===null)return 0;var size=lengthBytesUTF8(value)+1;var ptr=_malloc(size);stringToUTF8(value,ptr,size);return ptr}function js_store_set(key,value){localStorage.setItem("opencharm."+UTF8ToString(key),UTF8ToString(value))}function js_store_erase(key){localStorage.removeItem("opencharm."+UTF8ToString(key))}function js_reconnect(){charmSim.reconnect()}var _sim_init,_sim_frame,_sim_pin_key,_sim_frame_box,_sim_frame_dirty,_sim_tick,_sim_pointer,_sim_key,_sim_connected,_sim_disconnected,_sim_text,_sim_audio,_sim_mic_pcm,_sim_token,_sim_forget,_free,_malloc,memory,__indirect_function_table,wasmMemory;function assignWasmExports(wasmExports){_sim_init=Module["_sim_init"]=wasmExports["s"];_sim_frame=Module["_sim_frame"]=wasmExports["t"];_sim_pin_key=Module["_sim_pin_key"]=wasmExports["u"];_sim_frame_box=Module["_sim_frame_box"]=wasmExports["v"];_sim_frame_dirty=Module["_sim_frame_dirty"]=wasmExports["w"];_sim_tick=Module["_sim_tick"]=wasmExports["x"];_sim_pointer=Module["_sim_pointer"]=wasmExports["y"];_sim_key=Module["_sim_key"]=wasmExports["z"];_sim_connected=Module["_sim_connected"]=wasmExports["A"];_sim_disconnected=Module["_sim_disconnected"]=wasmExports["B"];_sim_text=Module["_sim_text"]=wasmExports["C"];_sim_audio=Module["_sim_audio"]=wasmExports["D"];_sim_mic_pcm=Module["_sim_mic_pcm"]=wasmExports["E"];_sim_token=Module["_sim_token"]=wasmExports["F"];_sim_forget=Module["_sim_forget"]=wasmExports["G"];_free=Module["_free"]=wasmExports["H"];_malloc=Module["_malloc"]=wasmExports["I"];memory=wasmMemory=wasmExports["q"];__indirect_function_table=wasmExports["__indirect_function_table"]}var wasmImports={o:___cxa_throw,l:__abort_js,j:_emscripten_resize_heap,k:_fd_close,b:_fd_write,e:js_brightness,a:js_mic,p:js_panel,g:js_play_pcm,m:js_reconnect,h:js_send_binary,i:js_send_text,f:js_stop_audio,n:js_store_erase,d:js_store_get,c:js_store_set};async function run(){preRun();var setStatus=Module["setStatus"];if(setStatus){setStatus("Running...");await new Promise(resolve=>setTimeout(resolve,1));setTimeout(setStatus,1,"")}if(ABORT)return;initRuntime();Module["onRuntimeInitialized"]?.();postRun()}var wasmExports;wasmExports=await createWasm();await run();
|
|
1
|
+
async function createCharm(moduleArg={}){var Module=moduleArg;var ENVIRONMENT_IS_WEB=true;var ENVIRONMENT_IS_WORKER=false;var programArgs=[];var thisProgram="./this.program";var _scriptName=import.meta.url;var scriptDirectory="";function locateFile(path){if(Module["locateFile"]){return Module["locateFile"](path,scriptDirectory)}return scriptDirectory+path}var readAsync,readBinary;if(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER){try{scriptDirectory=new URL(".",_scriptName).href}catch{}{readAsync=async url=>{var response=await fetch(url,{credentials:"same-origin"});if(response.ok){return response.arrayBuffer()}throw new Error(response.status+" : "+response.url)}}}else{}var out=console.log.bind(console);var err=console.error.bind(console);var wasmBinary;var ABORT=false;class EmscriptenEH{}class EmscriptenSjLj extends EmscriptenEH{}var runtimeInitialized=false;function getMemoryBuffer(){return wasmMemory.buffer}function updateMemoryViews(){if(HEAP8?.buffer?.resizable)return;var b=getMemoryBuffer();HEAP8=new Int8Array(b);Module["HEAP16"]=HEAP16=new Int16Array(b);Module["HEAPU8"]=HEAPU8=new Uint8Array(b);Module["HEAP32"]=HEAP32=new Int32Array(b);HEAPU32=new Uint32Array(b);Module["HEAPF32"]=HEAPF32=new Float32Array(b)}function preRun(){var preRun=Module["preRun"];if(preRun){if(typeof preRun=="function")preRun=[preRun];onPreRuns.push(...preRun)}callRuntimeCallbacks(onPreRuns)}function initRuntime(){runtimeInitialized=true;wasmExports["r"]()}function postRun(){var postRun=Module["postRun"];if(postRun){if(typeof postRun=="function")postRun=[postRun];onPostRuns.push(...postRun)}callRuntimeCallbacks(onPostRuns)}function abort(what){Module["onAbort"]?.(what);what=`Aborted(${what})`;err(what);ABORT=true;what+=". Build with -sASSERTIONS for more info.";var e=new WebAssembly.RuntimeError(what);throw e}var wasmBinaryFile;function findWasmBinary(){if(Module["locateFile"]){return locateFile("charm_sim.wasm")}return new URL("charm_sim.wasm",import.meta.url).href}function getBinarySync(file){if(readBinary){return readBinary(file)}throw"both async and sync fetching of the wasm failed"}async function getWasmBinary(binaryFile){if(!wasmBinary){try{var response=await readAsync(binaryFile);return new Uint8Array(response)}catch{}}return getBinarySync(binaryFile)}async function instantiateArrayBuffer(binaryFile,imports){try{var binary=await getWasmBinary(binaryFile);var instance=await WebAssembly.instantiate(binary,imports);return instance}catch(reason){err(`failed to asynchronously prepare wasm: ${reason}`);abort(reason)}}async function instantiateAsync(binary,binaryFile,imports){if(!binary){try{var response=fetch(binaryFile,{credentials:"same-origin"});var instantiationResult=await WebAssembly.instantiateStreaming(response,imports);return instantiationResult}catch(reason){err(`wasm streaming compile failed: ${reason}`);err("falling back to ArrayBuffer instantiation")}}return instantiateArrayBuffer(binaryFile,imports)}function getWasmImports(){var imports={a:wasmImports};return imports}async function createWasm(){function receiveInstance(instance){wasmExports=instance.exports;assignWasmExports(wasmExports);updateMemoryViews();return wasmExports}function receiveInstantiationResult(result){return receiveInstance(result["instance"])}var info=getWasmImports();var instantiateWasm=Module["instantiateWasm"];if(instantiateWasm){return new Promise(resolve=>{instantiateWasm(info,inst=>resolve(receiveInstance(inst)))})}wasmBinaryFile??=findWasmBinary();var result=await instantiateAsync(wasmBinary,wasmBinaryFile,info);var exports=receiveInstantiationResult(result);return exports}class ExitStatus{name="ExitStatus";constructor(status){this.message=`Program terminated with exit(${status})`;this.status=status}}var HEAP8;var callRuntimeCallbacks=callbacks=>{while(callbacks.length>0){callbacks.shift()(Module)}};var onPostRuns=[];var onPreRuns=[];var noExitRuntime=true;var HEAPU32;class ExceptionInfo{constructor(excPtr){this.excPtr=excPtr;this.ptr=excPtr-24}set_type(type){HEAPU32[this.ptr+4>>2]=type}get_type(){return HEAPU32[this.ptr+4>>2]}set_destructor(destructor){HEAPU32[this.ptr+8>>2]=destructor}get_destructor(){return HEAPU32[this.ptr+8>>2]}set_caught(caught){caught=caught?1:0;HEAP8[this.ptr+12]=caught}get_caught(){return HEAP8[this.ptr+12]!=0}set_rethrown(rethrown){rethrown=rethrown?1:0;HEAP8[this.ptr+13]=rethrown}get_rethrown(){return HEAP8[this.ptr+13]!=0}init(type,destructor){this.set_adjusted_ptr(0);this.set_type(type);this.set_destructor(destructor)}set_adjusted_ptr(adjustedPtr){HEAPU32[this.ptr+16>>2]=adjustedPtr}get_adjusted_ptr(){return HEAPU32[this.ptr+16>>2]}}var uncaughtExceptionCount=0;var __Unwind_RaiseException=ex=>{abort()};var ___cxa_throw=(ptr,type,destructor)=>{var info=new ExceptionInfo(ptr);info.init(type,destructor);uncaughtExceptionCount++;__Unwind_RaiseException(ptr)};var __abort_js=()=>abort("");var getHeapMax=()=>2147483648;var alignMemory=(size,alignment)=>Math.ceil(size/alignment)*alignment;var growMemory=size=>{var oldHeapSize=wasmMemory.buffer.byteLength;var pages=(size-oldHeapSize+65535)/65536|0;try{wasmMemory.grow(pages);updateMemoryViews();return 1}catch(e){}};var HEAPU8;var _emscripten_resize_heap=requestedSize=>{var oldSize=HEAPU8.length;requestedSize>>>=0;var maxHeapSize=getHeapMax();if(requestedSize>maxHeapSize){return false}for(var cutDown=1;cutDown<=4;cutDown*=2){var overGrownHeapSize=oldSize*(1+.2/cutDown);overGrownHeapSize=Math.min(overGrownHeapSize,requestedSize+100663296);var newSize=Math.min(maxHeapSize,alignMemory(Math.max(requestedSize,overGrownHeapSize),65536));var replacement=growMemory(newSize);if(replacement){return true}}return false};var _fd_close=fd=>52;var printCharBuffers=[null,[],[]];var UTF8Decoder=globalThis.TextDecoder&&new TextDecoder;var findStringEnd=(heapOrArray,idx,maxBytesToRead,ignoreNul)=>{var maxIdx=idx+maxBytesToRead;if(ignoreNul)return maxIdx;while(heapOrArray[idx]&&!(idx>=maxIdx))++idx;return idx};var UTF8ArrayToString=(heapOrArray,idx=0,maxBytesToRead,ignoreNul)=>{var endPtr=findStringEnd(heapOrArray,idx,maxBytesToRead,ignoreNul);if(endPtr-idx>16&&heapOrArray.buffer&&UTF8Decoder){return UTF8Decoder.decode(heapOrArray.subarray(idx,endPtr))}var str="";while(idx<endPtr){var u0=heapOrArray[idx++];if(!(u0&128)){str+=String.fromCharCode(u0);continue}var u1=heapOrArray[idx++]&63;if((u0&224)==192){str+=String.fromCharCode((u0&31)<<6|u1);continue}var u2=heapOrArray[idx++]&63;if((u0&240)==224){u0=(u0&15)<<12|u1<<6|u2}else{u0=(u0&7)<<18|u1<<12|u2<<6|heapOrArray[idx++]&63}if(u0<65536){str+=String.fromCharCode(u0)}else{var ch=u0-65536;str+=String.fromCharCode(55296|ch>>10,56320|ch&1023)}}return str};var printChar=(stream,curr)=>{var buffer=printCharBuffers[stream];if(!curr||curr===10){(stream===1?out:err)(UTF8ArrayToString(buffer));buffer.length=0}else{buffer.push(curr)}};var UTF8ToString=(ptr,maxBytesToRead,ignoreNul)=>ptr?UTF8ArrayToString(HEAPU8,ptr,maxBytesToRead,ignoreNul):"";var _fd_write=(fd,iov,iovcnt,pnum)=>{var num=0;for(var i=0;i<iovcnt;i++){var ptr=HEAPU32[iov>>2];var len=HEAPU32[iov+4>>2];iov+=8;for(var j=0;j<len;j++){printChar(fd,HEAPU8[ptr+j])}num+=len}HEAPU32[pnum>>2]=num;return 0};var HEAPF32;var HEAP16;var HEAP32;var stringToUTF8Array=(str,heap,outIdx,maxBytesToWrite)=>{if(!(maxBytesToWrite>0))return 0;var startIdx=outIdx;var endIdx=outIdx+maxBytesToWrite-1;for(var i=0;i<str.length;++i){var u=str.codePointAt(i);if(u<=127){if(outIdx>=endIdx)break;heap[outIdx++]=u}else if(u<=2047){if(outIdx+1>=endIdx)break;heap[outIdx++]=192|u>>6;heap[outIdx++]=128|u&63}else if(u<=65535){if(outIdx+2>=endIdx)break;heap[outIdx++]=224|u>>12;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63}else{if(outIdx+3>=endIdx)break;heap[outIdx++]=240|u>>18;heap[outIdx++]=128|u>>12&63;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63;i++}}heap[outIdx]=0;return outIdx-startIdx};var stringToUTF8=(str,outPtr,maxBytesToWrite)=>stringToUTF8Array(str,HEAPU8,outPtr,maxBytesToWrite);var lengthBytesUTF8=str=>{var len=0;for(var i=0;i<str.length;++i){var c=str.charCodeAt(i);if(c<=127){len++}else if(c<=2047){len+=2}else if(c>=55296&&c<=57343){len+=4;++i}else{len+=3}}return len};{if(Module["noExitRuntime"])noExitRuntime=Module["noExitRuntime"];if(Module["print"])out=Module["print"];if(Module["printErr"])err=Module["printErr"];if(Module["arguments"])programArgs=Module["arguments"];if(Module["thisProgram"])thisProgram=Module["thisProgram"];var preInit=Module["preInit"];if(preInit){if(typeof preInit=="function")Module["preInit"]=preInit=[preInit];while(preInit.length>0){preInit.shift()()}}}Module["UTF8ToString"]=UTF8ToString;Module["stringToUTF8"]=stringToUTF8;Module["lengthBytesUTF8"]=lengthBytesUTF8;function js_panel(open){charmSim.panel(!!open)}function js_send_text(json){charmSim.sendText(UTF8ToString(json))}function js_send_binary(data,size){charmSim.sendBinary(HEAPU8.slice(data,data+size))}function js_mic(on){charmSim.mic(!!on)}function js_play_pcm(pcm,samples){charmSim.playPcm(HEAPF32.slice(pcm>>2,(pcm>>2)+samples))}function js_stop_audio(){charmSim.stopAudio()}function js_brightness(percent){charmSim.brightness(percent)}function js_store_get(key){var value=localStorage.getItem("opencharm."+UTF8ToString(key));if(value===null)return 0;var size=lengthBytesUTF8(value)+1;var ptr=_malloc(size);stringToUTF8(value,ptr,size);return ptr}function js_store_set(key,value){localStorage.setItem("opencharm."+UTF8ToString(key),UTF8ToString(value))}function js_store_erase(key){localStorage.removeItem("opencharm."+UTF8ToString(key))}function js_reconnect(){charmSim.reconnect()}var _sim_set_build,_sim_commit,_sim_init,_sim_frame,_sim_pin_key,_sim_frame_box,_sim_frame_dirty,_sim_tick,_sim_pointer,_sim_key,_sim_connected,_sim_disconnected,_sim_text,_sim_audio,_sim_mic_pcm,_sim_token,_sim_forget,_free,_malloc,memory,__indirect_function_table,wasmMemory;function assignWasmExports(wasmExports){_sim_set_build=Module["_sim_set_build"]=wasmExports["s"];_sim_commit=Module["_sim_commit"]=wasmExports["t"];_sim_init=Module["_sim_init"]=wasmExports["u"];_sim_frame=Module["_sim_frame"]=wasmExports["v"];_sim_pin_key=Module["_sim_pin_key"]=wasmExports["w"];_sim_frame_box=Module["_sim_frame_box"]=wasmExports["x"];_sim_frame_dirty=Module["_sim_frame_dirty"]=wasmExports["y"];_sim_tick=Module["_sim_tick"]=wasmExports["z"];_sim_pointer=Module["_sim_pointer"]=wasmExports["A"];_sim_key=Module["_sim_key"]=wasmExports["B"];_sim_connected=Module["_sim_connected"]=wasmExports["C"];_sim_disconnected=Module["_sim_disconnected"]=wasmExports["D"];_sim_text=Module["_sim_text"]=wasmExports["E"];_sim_audio=Module["_sim_audio"]=wasmExports["F"];_sim_mic_pcm=Module["_sim_mic_pcm"]=wasmExports["G"];_sim_token=Module["_sim_token"]=wasmExports["H"];_sim_forget=Module["_sim_forget"]=wasmExports["I"];_free=Module["_free"]=wasmExports["J"];_malloc=Module["_malloc"]=wasmExports["K"];memory=wasmMemory=wasmExports["q"];__indirect_function_table=wasmExports["__indirect_function_table"]}var wasmImports={o:___cxa_throw,l:__abort_js,j:_emscripten_resize_heap,k:_fd_close,b:_fd_write,e:js_brightness,a:js_mic,p:js_panel,g:js_play_pcm,m:js_reconnect,h:js_send_binary,i:js_send_text,f:js_stop_audio,n:js_store_erase,d:js_store_get,c:js_store_set};async function run(){preRun();var setStatus=Module["setStatus"];if(setStatus){setStatus("Running...");await new Promise(resolve=>setTimeout(resolve,1));setTimeout(setStatus,1,"")}if(ABORT)return;initRuntime();Module["onRuntimeInitialized"]?.();postRun()}var wasmExports;wasmExports=await createWasm();await run();
|
|
2
2
|
;return Module}export default createCharm;
|
package/sim/charm_sim.wasm
CHANGED
|
Binary file
|
package/sim/index.html
CHANGED
|
@@ -44,6 +44,8 @@
|
|
|
44
44
|
</p>
|
|
45
45
|
<p class="mono dim">CHARMD</p>
|
|
46
46
|
<p class="mono small" id="url"></p>
|
|
47
|
+
<p class="mono dim">BUILD</p>
|
|
48
|
+
<p class="mono small" id="build"></p>
|
|
47
49
|
<div class="buttons">
|
|
48
50
|
<button class="pill" id="wifi">Drop Wi-Fi</button>
|
|
49
51
|
<button class="pill" id="shape">Round screen</button>
|
package/sim/sim.js
CHANGED
|
@@ -122,6 +122,16 @@ window.charmSim = {
|
|
|
122
122
|
};
|
|
123
123
|
|
|
124
124
|
const M = await createCharm();
|
|
125
|
+
// What this charm runs (spec 015): the desktop app passes its identity in charmParams, `opencharm sim`
|
|
126
|
+
// in config.json. Shown in the side panel and sent to charmd in hello, never on the charm's screen.
|
|
127
|
+
const version = params.get("version") || config.version || "unknown";
|
|
128
|
+
withString(window.charmParams ? "desktop" : "emulator", (kind) =>
|
|
129
|
+
withString(version, (v) => M._sim_set_build(kind, v))
|
|
130
|
+
);
|
|
131
|
+
// The emulator's page has a BUILD line; the desktop app's page doesn't (its Settings show it).
|
|
132
|
+
const buildLine = document.getElementById("build");
|
|
133
|
+
if (buildLine)
|
|
134
|
+
buildLine.textContent = `${version} (${M.UTF8ToString(M._sim_commit())})`;
|
|
125
135
|
const glyph = 0;
|
|
126
136
|
M._sim_init(
|
|
127
137
|
W,
|