opencharm 0.5.0 → 0.6.1
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/dist/main.mjs +42 -18
- package/package.json +1 -1
- package/sim/charm_sim.js +1 -1
- package/sim/charm_sim.wasm +0 -0
- package/sim/sim.js +18 -2
package/dist/main.mjs
CHANGED
|
@@ -497,6 +497,11 @@ const unlock = z.object({
|
|
|
497
497
|
op: z.literal("unlock"),
|
|
498
498
|
pin: z.string().regex(/^\d{4,12}$/)
|
|
499
499
|
});
|
|
500
|
+
const typed = z.object({
|
|
501
|
+
type: z.literal("charm"),
|
|
502
|
+
op: z.literal("text"),
|
|
503
|
+
text: z.string().max(2e3).refine((text) => text.trim().length > 0, { message: "empty" })
|
|
504
|
+
});
|
|
500
505
|
const answer = z.object({
|
|
501
506
|
type: z.literal("charm"),
|
|
502
507
|
op: z.literal("answer"),
|
|
@@ -507,7 +512,8 @@ const serverHello = z.object({
|
|
|
507
512
|
type: z.literal("hello"),
|
|
508
513
|
transport: z.literal("websocket"),
|
|
509
514
|
session_id: z.string().min(1).max(128),
|
|
510
|
-
audio_params: audioParams
|
|
515
|
+
audio_params: audioParams,
|
|
516
|
+
features: z.object({ text: z.boolean().optional() }).optional()
|
|
511
517
|
});
|
|
512
518
|
const stt = z.object({
|
|
513
519
|
session_id: sessionId,
|
|
@@ -593,7 +599,8 @@ const CLIENT_SCHEMAS = {
|
|
|
593
599
|
listen,
|
|
594
600
|
abort,
|
|
595
601
|
"charm:unlock": unlock,
|
|
596
|
-
"charm:answer": answer
|
|
602
|
+
"charm:answer": answer,
|
|
603
|
+
"charm:text": typed
|
|
597
604
|
};
|
|
598
605
|
const SERVER_SCHEMAS = {
|
|
599
606
|
hello: serverHello,
|
|
@@ -962,7 +969,7 @@ function sendAdmin(socketPath, request) {
|
|
|
962
969
|
}
|
|
963
970
|
//#endregion
|
|
964
971
|
//#region package.json
|
|
965
|
-
var version = "0.
|
|
972
|
+
var version = "0.6.1";
|
|
966
973
|
function channelToCube(value) {
|
|
967
974
|
if (value < 48) return 0;
|
|
968
975
|
if (value < 115) return 1;
|
|
@@ -2353,7 +2360,8 @@ var Session = class {
|
|
|
2353
2360
|
type: "hello",
|
|
2354
2361
|
transport: "websocket",
|
|
2355
2362
|
session_id: this.sessionId,
|
|
2356
|
-
audio_params: AUDIO_DOWN
|
|
2363
|
+
audio_params: AUDIO_DOWN,
|
|
2364
|
+
...this.#build?.kind === "desktop" ? { features: { text: true } } : {}
|
|
2357
2365
|
});
|
|
2358
2366
|
const { token } = this.#deps;
|
|
2359
2367
|
if (token === void 0) {
|
|
@@ -2398,6 +2406,9 @@ var Session = class {
|
|
|
2398
2406
|
if (message.state === "start") turn.listenStart();
|
|
2399
2407
|
else if (message.state === "stop") turn.listenStop();
|
|
2400
2408
|
} else if (message.type === "abort") turn.abort();
|
|
2409
|
+
else if (message.type === "charm" && message.op === "text") {
|
|
2410
|
+
if (this.#build?.kind === "desktop") turn.typed(message.text);
|
|
2411
|
+
}
|
|
2401
2412
|
}
|
|
2402
2413
|
async #onUnlock(pin) {
|
|
2403
2414
|
if (this.#state === "unlocked") {
|
|
@@ -3289,6 +3300,7 @@ function cleanForSpeech(text) {
|
|
|
3289
3300
|
//#endregion
|
|
3290
3301
|
//#region ../charmd/src/turn/turn.ts
|
|
3291
3302
|
const MAX_FRAMES = 1e3;
|
|
3303
|
+
const LEAD_MS = 250;
|
|
3292
3304
|
const READING_WORDS_PER_SECOND = 3;
|
|
3293
3305
|
const MIN_READING_MS = 2e3;
|
|
3294
3306
|
const LOOKAHEAD = 2;
|
|
@@ -3321,6 +3333,7 @@ var TurnController = class {
|
|
|
3321
3333
|
#generation = 0;
|
|
3322
3334
|
#controller;
|
|
3323
3335
|
#speaking = false;
|
|
3336
|
+
#playedBy = 0;
|
|
3324
3337
|
#asking;
|
|
3325
3338
|
#endFace;
|
|
3326
3339
|
#deps;
|
|
@@ -3392,9 +3405,13 @@ var TurnController = class {
|
|
|
3392
3405
|
this.#face("idle");
|
|
3393
3406
|
return Promise.resolve();
|
|
3394
3407
|
}
|
|
3395
|
-
return this.#run(frames);
|
|
3408
|
+
return this.#run({ frames });
|
|
3396
3409
|
}
|
|
3397
|
-
|
|
3410
|
+
typed(text) {
|
|
3411
|
+
if (this.busy) this.#cancel();
|
|
3412
|
+
return this.#run({ text: text.trim() });
|
|
3413
|
+
}
|
|
3414
|
+
async #run(input) {
|
|
3398
3415
|
const generation = ++this.#generation;
|
|
3399
3416
|
const live = () => this.#generation === generation;
|
|
3400
3417
|
const controller = new AbortController();
|
|
@@ -3439,14 +3456,17 @@ var TurnController = class {
|
|
|
3439
3456
|
this.#state = "thinking";
|
|
3440
3457
|
this.#face("thinking");
|
|
3441
3458
|
try {
|
|
3442
|
-
|
|
3443
|
-
|
|
3444
|
-
|
|
3445
|
-
|
|
3446
|
-
|
|
3447
|
-
|
|
3448
|
-
|
|
3449
|
-
|
|
3459
|
+
if ("text" in input) heard = input.text;
|
|
3460
|
+
else {
|
|
3461
|
+
const ogg = writeOggOpus(input.frames, { inputSampleRate: 16e3 });
|
|
3462
|
+
heard = await Promise.race([inStage("stt", () => this.#deps.voice.transcribe(ogg, signal)), timeoutGuard]);
|
|
3463
|
+
timings.sttMs = now() - started;
|
|
3464
|
+
if (!live()) return;
|
|
3465
|
+
this.#deps.send({
|
|
3466
|
+
type: "stt",
|
|
3467
|
+
text: heard
|
|
3468
|
+
});
|
|
3469
|
+
}
|
|
3450
3470
|
if (!heard) {
|
|
3451
3471
|
outcome = "nothing-heard";
|
|
3452
3472
|
this.#state = "idle";
|
|
@@ -3474,7 +3494,7 @@ var TurnController = class {
|
|
|
3474
3494
|
signal,
|
|
3475
3495
|
...ask ? { ask: (action) => this.ask(`${label} wants to ${action}.`) } : {}
|
|
3476
3496
|
});
|
|
3477
|
-
said = await this.#speak(reply, signal, live, timings, started, timeoutGuard, guessLanguage(heard) ?? this.#deps.voice.language ?? "en", clock, this.#deps.speakAloud?.() ?? true);
|
|
3497
|
+
said = await this.#speak(reply, signal, live, timings, started, timeoutGuard, guessLanguage(heard) ?? this.#deps.voice.language ?? "en", clock, !("text" in input) && (this.#deps.speakAloud?.() ?? true));
|
|
3478
3498
|
if (!live()) return;
|
|
3479
3499
|
if (this.#speaking) this.#deps.send({
|
|
3480
3500
|
type: "tts",
|
|
@@ -3509,6 +3529,7 @@ var TurnController = class {
|
|
|
3509
3529
|
outcome: live() ? outcome : "cancelled",
|
|
3510
3530
|
...timings,
|
|
3511
3531
|
totalMs: now() - started,
|
|
3532
|
+
..."text" in input ? { typed: true } : {},
|
|
3512
3533
|
...this.#deps.logTranscripts ? {
|
|
3513
3534
|
heard,
|
|
3514
3535
|
said: said.trim()
|
|
@@ -3582,6 +3603,7 @@ var TurnController = class {
|
|
|
3582
3603
|
if (!this.#speaking) {
|
|
3583
3604
|
this.#speaking = true;
|
|
3584
3605
|
this.#state = "speaking";
|
|
3606
|
+
this.#playedBy = 0;
|
|
3585
3607
|
timings.firstAudioMs = now() - started;
|
|
3586
3608
|
clock.started();
|
|
3587
3609
|
this.#deps.send({
|
|
@@ -3690,12 +3712,14 @@ var TurnController = class {
|
|
|
3690
3712
|
}
|
|
3691
3713
|
async #sendPaced(packets, live, onPacket) {
|
|
3692
3714
|
const sleep = this.#deps.sleep ?? sleepMs;
|
|
3693
|
-
|
|
3715
|
+
const now = this.#deps.now ?? Date.now;
|
|
3694
3716
|
for await (const packet of packets) {
|
|
3695
3717
|
if (!live()) return;
|
|
3696
3718
|
this.#deps.sendAudio(packet);
|
|
3697
3719
|
onPacket();
|
|
3698
|
-
|
|
3720
|
+
this.#playedBy = Math.max(this.#playedBy, now()) + opusPacketSamples48k(packet) / 48;
|
|
3721
|
+
const wait = this.#playedBy - LEAD_MS - now();
|
|
3722
|
+
if (wait > 0) await sleep(wait);
|
|
3699
3723
|
}
|
|
3700
3724
|
}
|
|
3701
3725
|
dispose() {
|
|
@@ -4917,7 +4941,7 @@ function formatIdentity(unit, version, commit) {
|
|
|
4917
4941
|
let cached;
|
|
4918
4942
|
function identity() {
|
|
4919
4943
|
if (cached) return cached;
|
|
4920
|
-
const commit = "
|
|
4944
|
+
const commit = "e5ffe6e";
|
|
4921
4945
|
cached = {
|
|
4922
4946
|
version: `cli@${version}`,
|
|
4923
4947
|
commit,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencharm",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.1",
|
|
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": {
|
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_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();
|
|
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_type,_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_type=Module["_sim_type"]=wasmExports["F"];_sim_audio=Module["_sim_audio"]=wasmExports["G"];_sim_mic_pcm=Module["_sim_mic_pcm"]=wasmExports["H"];_sim_token=Module["_sim_token"]=wasmExports["I"];_sim_forget=Module["_sim_forget"]=wasmExports["J"];_free=Module["_free"]=wasmExports["K"];_malloc=Module["_malloc"]=wasmExports["L"];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/sim.js
CHANGED
|
@@ -109,6 +109,14 @@ window.charmSim = {
|
|
|
109
109
|
M._sim_forget();
|
|
110
110
|
ws?.close();
|
|
111
111
|
},
|
|
112
|
+
// Typed text from the desktop charm's field (spec 013): "sent", "busy" (a question or a talk), or
|
|
113
|
+
// "unsupported" (an older charmd).
|
|
114
|
+
type: (text) => {
|
|
115
|
+
let result = 0;
|
|
116
|
+
withString(text, (ptr) => (result = M._sim_type(ptr)));
|
|
117
|
+
wake();
|
|
118
|
+
return ["busy", "sent", "unsupported"][result] ?? "busy";
|
|
119
|
+
},
|
|
112
120
|
// A PIN key from a keyboard or the desktop app ("0"…"9", "<", "OK").
|
|
113
121
|
pinKey: (key) => {
|
|
114
122
|
withString(key, (ptr) => M._sim_pin_key(ptr));
|
|
@@ -291,15 +299,22 @@ function key(down) {
|
|
|
291
299
|
wake();
|
|
292
300
|
}
|
|
293
301
|
|
|
302
|
+
// Keys typed into a text field (the desktop charm's typing field) are text, not the charm's key or PIN.
|
|
303
|
+
const typingInto = (e) =>
|
|
304
|
+
e.target instanceof HTMLElement &&
|
|
305
|
+
(e.target.isContentEditable ||
|
|
306
|
+
e.target.tagName === "INPUT" ||
|
|
307
|
+
e.target.tagName === "TEXTAREA");
|
|
308
|
+
|
|
294
309
|
let spaceDown = false;
|
|
295
310
|
addEventListener("keydown", (e) => {
|
|
296
|
-
if (e.code !== "Space" || e.repeat || spaceDown) return;
|
|
311
|
+
if (e.code !== "Space" || e.repeat || spaceDown || typingInto(e)) return;
|
|
297
312
|
e.preventDefault();
|
|
298
313
|
spaceDown = true;
|
|
299
314
|
key(true);
|
|
300
315
|
});
|
|
301
316
|
addEventListener("keyup", (e) => {
|
|
302
|
-
if (e.code !== "Space") return;
|
|
317
|
+
if (e.code !== "Space" || !spaceDown) return;
|
|
303
318
|
e.preventDefault();
|
|
304
319
|
spaceDown = false;
|
|
305
320
|
key(false);
|
|
@@ -349,6 +364,7 @@ document.getElementById("shape").addEventListener("click", () => {
|
|
|
349
364
|
|
|
350
365
|
// The PIN can be typed on the keyboard too (the notch has no room for a pad).
|
|
351
366
|
addEventListener("keydown", (e) => {
|
|
367
|
+
if (typingInto(e)) return;
|
|
352
368
|
const key = /^[0-9]$/.test(e.key)
|
|
353
369
|
? e.key
|
|
354
370
|
: e.key === "Backspace"
|