From 16615e443acbe643a610abf31105910243025cce Mon Sep 17 00:00:00 2001 From: prompt.ac/@jeffrey Date: Sat, 11 Apr 2026 16:00:15 +0000 Subject: [PATCH] feat(native): add wasm prompt preview route --- fedac/native/wasm/README.md | 34 ++++++++++++++++++++++++++++++++++ fedac/native/wasm/build-offline-page.mjs | 902 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ fedac/native/wasm/package.json | 11 +++++++++++ system/public/ac-native-wasm/index.html | 630 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 file(s) changed, 1577 insertion(s)(+), 0 deletion(s)(-) diff --git a/fedac/native/wasm/README.md b/fedac/native/wasm/README.md new file mode 100644 --- /dev/null +++ b/fedac/native/wasm/README.md @@ -0,0 +1,34 @@ +# AC Native WASM Prototype + +This is a small browser-hosted prototype for running a slice of `ac-native` +offline in a single HTML file. + +Current shape: + +- Boots the real native [`prompt.mjs`](/workspaces/aesthetic-computer/fedac/native/pieces/prompt.mjs) piece +- Uses a tiny WebAssembly raster core for framebuffer clears and boxes +- Uses browser shims for keyboard input, config storage, theme, wifi, and piece jumps +- Generates a single offline HTML artifact + +Build: + +```bash +cd /workspaces/aesthetic-computer/fedac/native/wasm +npm install +npm run build +``` + +Output: + +- `/workspaces/aesthetic-computer/fedac/native/build/ac-native-prompt-offline.html` +- `/workspaces/aesthetic-computer/system/public/ac-native-wasm/index.html` + +Production route: + +- `https://aesthetic.computer/ac-native-wasm/` + +Notes: + +- This is intentionally not full `ac-native` parity yet. +- Text rendering is still done by the browser canvas host. +- Native-only features like raw WiFi control, PTY, power management, and Linux device access are stubbed. diff --git a/fedac/native/wasm/build-offline-page.mjs b/fedac/native/wasm/build-offline-page.mjs new file mode 100644 --- /dev/null +++ b/fedac/native/wasm/build-offline-page.mjs @@ -0,0 +1,902 @@ +import fs from "node:fs/promises"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; +import wabtFactory from "wabt"; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const nativeRoot = path.resolve(__dirname, ".."); +const promptPath = path.join(nativeRoot, "pieces", "prompt.mjs"); +const outputDir = path.join(nativeRoot, "build"); +const outputPath = path.join(outputDir, "ac-native-prompt-offline.html"); +const publicOutputDir = path.resolve(nativeRoot, "..", "..", "system", "public", "ac-native-wasm"); +const publicOutputPath = path.join(publicOutputDir, "index.html"); + +const promptSource = await fs.readFile(promptPath, "utf8"); +const wabt = await wabtFactory(); + +const watSource = String.raw`(module + (memory (export "memory") 80) + (global $width (mut i32) (i32.const 0)) + (global $height (mut i32) (i32.const 0)) + (global $ink_r (mut i32) (i32.const 255)) + (global $ink_g (mut i32) (i32.const 255)) + (global $ink_b (mut i32) (i32.const 255)) + (global $ink_a (mut i32) (i32.const 255)) + + (func (export "init") (param $w i32) (param $h i32) (result i32) + local.get $w + global.set $width + local.get $h + global.set $height + i32.const 0 + ) + + (func (export "set_ink") (param $r i32) (param $g i32) (param $b i32) (param $a i32) + local.get $r + global.set $ink_r + local.get $g + global.set $ink_g + local.get $b + global.set $ink_b + local.get $a + global.set $ink_a + ) + + (func $pixel_offset (param $x i32) (param $y i32) (result i32) + local.get $y + global.get $width + i32.mul + local.get $x + i32.add + i32.const 4 + i32.mul + ) + + (func $blend (param $dst i32) (param $src i32) (param $a i32) (result i32) + local.get $src + local.get $a + i32.mul + local.get $dst + i32.const 255 + local.get $a + i32.sub + i32.mul + i32.add + i32.const 127 + i32.add + i32.const 255 + i32.div_u + ) + + (func (export "clear") + (local $i i32) + (local $total i32) + (local $addr i32) + + global.get $width + global.get $height + i32.mul + local.set $total + + block $done + loop $loop + local.get $i + local.get $total + i32.ge_u + br_if $done + + local.get $i + i32.const 4 + i32.mul + local.set $addr + + local.get $addr + global.get $ink_r + i32.store8 + + local.get $addr + i32.const 1 + i32.add + global.get $ink_g + i32.store8 + + local.get $addr + i32.const 2 + i32.add + global.get $ink_b + i32.store8 + + local.get $addr + i32.const 3 + i32.add + i32.const 255 + i32.store8 + + local.get $i + i32.const 1 + i32.add + local.set $i + br $loop + end + end + ) + + (func (export "fill_rect") (param $x i32) (param $y i32) (param $w i32) (param $h i32) + (local $yy i32) + (local $xx i32) + (local $px i32) + (local $py i32) + (local $addr i32) + (local $dr i32) + (local $dg i32) + (local $db i32) + + block $outer_done + loop $outer + local.get $yy + local.get $h + i32.ge_u + br_if $outer_done + + i32.const 0 + local.set $xx + + block $inner_done + loop $inner + local.get $xx + local.get $w + i32.ge_u + br_if $inner_done + + local.get $x + local.get $xx + i32.add + local.set $px + + local.get $y + local.get $yy + i32.add + local.set $py + + local.get $px + local.get $py + call $pixel_offset + local.set $addr + + global.get $ink_a + i32.const 255 + i32.eq + if + local.get $addr + global.get $ink_r + i32.store8 + + local.get $addr + i32.const 1 + i32.add + global.get $ink_g + i32.store8 + + local.get $addr + i32.const 2 + i32.add + global.get $ink_b + i32.store8 + + local.get $addr + i32.const 3 + i32.add + i32.const 255 + i32.store8 + else + local.get $addr + i32.load8_u + local.set $dr + + local.get $addr + i32.const 1 + i32.add + i32.load8_u + local.set $dg + + local.get $addr + i32.const 2 + i32.add + i32.load8_u + local.set $db + + local.get $addr + local.get $dr + global.get $ink_r + global.get $ink_a + call $blend + i32.store8 + + local.get $addr + i32.const 1 + i32.add + local.get $dg + global.get $ink_g + global.get $ink_a + call $blend + i32.store8 + + local.get $addr + i32.const 2 + i32.add + local.get $db + global.get $ink_b + global.get $ink_a + call $blend + i32.store8 + + local.get $addr + i32.const 3 + i32.add + i32.const 255 + i32.store8 + end + + local.get $xx + i32.const 1 + i32.add + local.set $xx + br $inner + end + end + + local.get $yy + i32.const 1 + i32.add + local.set $yy + br $outer + end + end + ) +)`; + +const wasmModule = wabt.parseWat("raster.wat", watSource); +const { buffer } = wasmModule.toBinary({ log: false, write_debug_names: true }); +const wasmBase64 = Buffer.from(buffer).toString("base64"); + +const html = ` + + + + + AC Native Prompt WASM Prototype + + + +
+
+
AC Native Prompt • WASM Offline Prototype
+
Type in the canvas. Esc clears. Tab completes. Backspace returns from stub pieces.
+
+
+ +
+
+
booting...
+
single-file offline html
+
+
+ + + +`; + +await fs.mkdir(outputDir, { recursive: true }); +await fs.writeFile(outputPath, html, "utf8"); +await fs.mkdir(publicOutputDir, { recursive: true }); +await fs.writeFile(publicOutputPath, html, "utf8"); + +console.log("Built offline artifact:"); +console.log(outputPath); +console.log("Built production route:"); +console.log(publicOutputPath); diff --git a/fedac/native/wasm/package.json b/fedac/native/wasm/package.json new file mode 100644 --- /dev/null +++ b/fedac/native/wasm/package.json @@ -0,0 +1,11 @@ +{ + "name": "ac-native-wasm-prototype", + "private": true, + "type": "module", + "scripts": { + "build": "node build-offline-page.mjs" + }, + "dependencies": { + "wabt": "^1.0.37" + } +} diff --git a/system/public/ac-native-wasm/index.html b/system/public/ac-native-wasm/index.html new file mode 100644 --- /dev/null +++ b/system/public/ac-native-wasm/index.html @@ -0,0 +1,630 @@ + + + + + + AC Native Prompt WASM Prototype + + + +
+
+
AC Native Prompt • WASM Offline Prototype
+
Type in the canvas. Esc clears. Tab completes. Backspace returns from stub pieces.
+
+
+ +
+
+
booting...
+
single-file offline html
+
+
+ + + -- tangled.sh