web: JS bindings step + a minimal browser demo
build-web.sh now generates the browser package with wasm-bindgen (web/pkg/tethercore.js + tethercore_bg.wasm, --target web) after building the cdylib. web/index.html is a tiny demo: import the bindings, derive the room from an account, new WasmEngine(...).start(onMessage)/send — relay-only iroh peer in the browser. Generated web/pkg/ is git-ignored. End to end now proven buildable: Rust core → wasm32 → wasm-bindgen JS (exports WasmEngine + roomForAccount) → importable in a browser. A live in-browser run needs serving web/ + a reachable rendezvous/relay. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -18,7 +18,17 @@ export AR_wasm32_unknown_unknown="$LLVM/bin/llvm-ar"
|
||||
export RUSTFLAGS="${RUSTFLAGS:-} --cfg getrandom_backend=\"wasm_js\""
|
||||
|
||||
rustup target add wasm32-unknown-unknown >/dev/null 2>&1 || true
|
||||
echo "▸ building tethercore for wasm32 (iroh, relay-only in-browser)…"
|
||||
cargo build --target wasm32-unknown-unknown --features iroh-transport --lib "$@"
|
||||
echo "✅ target/wasm32-unknown-unknown/debug/tethercore.wasm"
|
||||
echo " next: wasm-bindgen bindings + a JS shim for the browser app."
|
||||
PROFILE="${PROFILE:-release}"
|
||||
echo "▸ building tethercore for wasm32 ($PROFILE; iroh, relay-only in-browser)…"
|
||||
cargo build --target wasm32-unknown-unknown --features iroh-transport --lib \
|
||||
$([ "$PROFILE" = release ] && echo --release) "$@"
|
||||
|
||||
WASM="target/wasm32-unknown-unknown/$PROFILE/tethercore.wasm"
|
||||
if command -v wasm-bindgen >/dev/null; then
|
||||
echo "▸ generating JS bindings → web/pkg/ (wasm-bindgen)…"
|
||||
wasm-bindgen "$WASM" --out-dir web/pkg --target web
|
||||
echo "✅ web/pkg/tethercore.js + tethercore_bg.wasm — import into the browser app."
|
||||
else
|
||||
echo "⚠ wasm-bindgen CLI not found — built $WASM but skipped JS bindings."
|
||||
echo " install: cargo install wasm-bindgen-cli --version <match Cargo.lock>"
|
||||
fi
|
||||
|
||||
1
core/web/.gitignore
vendored
Normal file
1
core/web/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/pkg/
|
||||
72
core/web/index.html
Normal file
72
core/web/index.html
Normal file
@@ -0,0 +1,72 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>tether — web (wasm)</title>
|
||||
<style>
|
||||
body { font: 15px/1.5 system-ui, sans-serif; max-width: 640px; margin: 2rem auto; padding: 0 1rem; }
|
||||
input, button, textarea { font: inherit; padding: .4rem .5rem; }
|
||||
.row { display: flex; gap: .5rem; margin: .5rem 0; }
|
||||
.row input { flex: 1; }
|
||||
#feed { width: 100%; height: 12rem; }
|
||||
#status { color: #666; font-size: 13px; }
|
||||
.dot { display:inline-block;width:10px;height:10px;border-radius:50%;background:#bbb;vertical-align:middle }
|
||||
.dot.on { background:#2ecc40 }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>tether <small>· web</small> <span id="dot" class="dot"></span></h1>
|
||||
<p>The same Rust core as the native apps, compiled to wasm — a relay-only iroh
|
||||
peer in the browser. Point it at your rendezvous and account.</p>
|
||||
|
||||
<div class="row"><input id="server" value="https://tether.pecord.io" placeholder="rendezvous URL" /></div>
|
||||
<div class="row">
|
||||
<input id="account" value="you@example.com" placeholder="account" />
|
||||
<button id="connect">Connect</button>
|
||||
</div>
|
||||
<div id="status">not connected</div>
|
||||
|
||||
<textarea id="feed" readonly placeholder="inbound clipboards appear here…"></textarea>
|
||||
<div class="row">
|
||||
<input id="msg" placeholder="type and Send to the room…" />
|
||||
<button id="send" disabled>Send</button>
|
||||
</div>
|
||||
|
||||
<script type="module">
|
||||
import init, { WasmEngine, roomForAccount } from "./pkg/tethercore.js";
|
||||
|
||||
const $ = (id) => document.getElementById(id);
|
||||
let engine = null;
|
||||
|
||||
$("connect").onclick = async () => {
|
||||
await init();
|
||||
const server = $("server").value.trim();
|
||||
const account = $("account").value.trim();
|
||||
const room = roomForAccount(account);
|
||||
const from = "web-" + Math.random().toString(36).slice(2, 10);
|
||||
|
||||
engine = new WasmEngine(server, room, from, account);
|
||||
engine.start((text) => {
|
||||
$("feed").value += "← " + text + "\n";
|
||||
$("feed").scrollTop = $("feed").scrollHeight;
|
||||
});
|
||||
|
||||
$("status").textContent = `connecting… room=${room} from=${from}`;
|
||||
$("dot").classList.add("on");
|
||||
$("send").disabled = false;
|
||||
// surface the iroh id once online
|
||||
const tick = setInterval(() => {
|
||||
const id = engine.irohId();
|
||||
if (id) { $("status").textContent = `online · room=${room} · id=${id.slice(0,16)}…`; clearInterval(tick); }
|
||||
}, 500);
|
||||
};
|
||||
|
||||
$("send").onclick = () => {
|
||||
const text = $("msg").value;
|
||||
if (engine && text) { engine.send(text); $("feed").value += "→ " + text + "\n"; $("msg").value = ""; }
|
||||
};
|
||||
$("msg").addEventListener("keydown", (e) => { if (e.key === "Enter") $("send").click(); });
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user