Two root causes kept the wasm web client from syncing with native peers,
both fixed here:
1. Gossip island — a node that registered with the rendezvous first joined
its gossip topic alone and never pulled later-joining peers into its
active view (its single subscribe_and_join was done, so a browser that
appeared afterwards had a live magicsock path but no gossip neighbor).
The re-register loop now seeds peer relay addrs into the MemoryLookup and
calls GossipSender::join_peers each round, so neighbors form both ways.
2. Engine::send panicked on wasm — directly_covered() calls Instant::now(),
which traps ("time not implemented") on wasm32-unknown-unknown, so send
threw before ever reaching the gossip broadcast (0 Broadcast commands).
web-time backs Instant with Performance.now() on wasm; native keeps
std::time and is unaffected (web-time isn't linked there).
Supporting connectivity: the rendezvous now stores + returns each peer's
iroh relay URL (PeerInfo{id,relay}) so a browser — relay-only, can't resolve
id→addr via discovery — can dial natives via a seeded MemoryLookup; CORS
added so the browser's fetch to the rendezvous is allowed.
Verified live, cross-relay: text typed in a browser tab lands on the Mac
clipboard and vice-versa, with the wasm engine running as a relay-only iroh
peer. Browser-console tracing capped at INFO; demo send wrapped in try/catch.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
81 lines
2.9 KiB
HTML
81 lines
2.9 KiB
HTML
<!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);
|
|
window.engine = engine; // expose for debugging
|
|
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) {
|
|
try {
|
|
engine.send(text);
|
|
$("feed").value += "→ " + text + "\n"; $("msg").value = "";
|
|
} catch (e) {
|
|
console.error("send failed:", e);
|
|
}
|
|
}
|
|
};
|
|
$("msg").addEventListener("keydown", (e) => { if (e.key === "Enter") $("send").click(); });
|
|
</script>
|
|
</body>
|
|
</html>
|