authorgravatar for git@paperclover.netclover caruso <git@paperclover.net> 2026-10-02 11:02:35-07:00
committergravatar for git@paperclover.netclover caruso <git@paperclover.net> 2026-10-02 11:12:41-07:00
log9b22f115f0b0570cfc78ce223c6a325b1714939b
tree9dbd7c26881cd372db6e4dea673d51201cb1764d
parente2e4ea381f12668cf85d93e6afeaf73b824a56e9
signature Signed by SSH key SHA256:52mNGHRsVFBDED9IAX5pe+LRWUefqTbxEReunq21QvU

feat: Live Share uses relay.snowbound.paperclover.net by default

Desktop builds include live presence (still off unless SNOWBOUND_LIVE asks); the web build leaves it out, as its sockets do not build for wasm. Assisted-by: claude-opus-5.5

4 files changed, 13 insertions(+), 5 deletions(-)

crates/snowbound/Cargo.toml+2-2
...@@ -5,11 +5,11 @@ edition = "2024"...@@ -5,11 +5,11 @@ edition = "2024"
5publish = false5publish = false
66
7[features]7[features]
8default = ["wgpu"]8default = ["wgpu", "live"]
9# Paints through wgpu; without it through OpenGL 2.1, for Mac OS X 10.6.9# Paints through wgpu; without it through OpenGL 2.1, for Mac OS X 10.6.
10wgpu = ["draw/wgpu", "dep:wgpu"]10wgpu = ["draw/wgpu", "dep:wgpu"]
11# Live presence: the others with a notebook open, their pages and carets, met over the11# Live presence: the others with a notebook open, their pages and carets, met over the
12# network (resources/live-share.md). Runs only where SNOWBOUND_LIVE asks.12# network (resources/live-share.md). Built in by default; runs only where SNOWBOUND_LIVE asks.
13live = ["notebook/live"]13live = ["notebook/live"]
1414
15[dependencies]15[dependencies]
crates/snowbound/src/live.rs+9-1
...@@ -1,5 +1,5 @@...@@ -1,5 +1,5 @@
1//! Live presence in the window (feature `live`, on where `SNOWBOUND_LIVE` is `network` or1//! Live presence in the window (feature `live`, on where `SNOWBOUND_LIVE` is `network` or
2//! `loopback`, or `SNOWBOUND_LIVE_RELAY` names a relay, `wss://live.example.net`): the others2//! `loopback`, through the default relay unless `SNOWBOUND_LIVE_RELAY` names another or `off`): the others
3//! with the notebook open as avatars beside the search box, a dot on the tab of the page each3//! with the notebook open as avatars beside the search box, a dot on the tab of the page each
4//! has open, and their carets on the page, each in a colour of their own. They meet through4//! has open, and their carets on the page, each in a colour of their own. They meet through
5//! the notebook's identity, or through `SNOWBOUND_LIVE_CODE` where it is set; through a relay,5//! the notebook's identity, or through `SNOWBOUND_LIVE_CODE` where it is set; through a relay,
...@@ -27,6 +27,9 @@ pub(crate) struct Peers {...@@ -27,6 +27,9 @@ pub(crate) struct Peers {
27 pictures: HashMap<[u8; 16], Option<draw::RasterImage>>,27 pictures: HashMap<[u8; 16], Option<draw::RasterImage>>,
28}28}
2929
30/// The relay used when `SNOWBOUND_LIVE_RELAY` names none; `off` keeps peers to the LAN.
31const DEFAULT_RELAY: &str = "wss://relay.snowbound.paperclover.net";
32
30fn reach() -> Option<Reach> {33fn reach() -> Option<Reach> {
31 match std::env::var("SNOWBOUND_LIVE").ok()?.as_str() {34 match std::env::var("SNOWBOUND_LIVE").ok()?.as_str() {
32 "network" => Some(Reach::Network),35 "network" => Some(Reach::Network),
...@@ -42,6 +45,11 @@ impl State {...@@ -42,6 +45,11 @@ impl State {
42 if reach.is_none() && relay.is_none() {45 if reach.is_none() && relay.is_none() {
43 return;46 return;
44 }47 }
48 let relay = match relay.as_deref() {
49 Some("off") => None,
50 Some(_) => relay,
51 None => Some(DEFAULT_RELAY.to_owned()),
52 };
45 let room = match std::env::var("SNOWBOUND_LIVE_CODE") {53 let room = match std::env::var("SNOWBOUND_LIVE_CODE") {
46 Ok(code) => Some(Room::Code(code)),54 Ok(code) => Some(Room::Code(code)),
47 Err(_) => self55 Err(_) => self
tools/ci.py+1-1
...@@ -80,7 +80,7 @@ def lanes():...@@ -80,7 +80,7 @@ def lanes():
80 result.append(dict(80 result.append(dict(
81 name='web', minutes=20, packages=['snowbound'], paths=('crates/snowbound/web/', 'tools/release_web.py'),81 name='web', minutes=20, packages=['snowbound'], paths=('crates/snowbound/web/', 'tools/release_web.py'),
82 # Clippy builds it; the module itself is linked by release_web.py.82 # Clippy builds it; the module itself is linked by release_web.py.
83 commands=[cargo('clippy', '-p', 'snowbound', '--target', 'wasm32-unknown-unknown', '--', '-D', 'warnings')],83 commands=[cargo('clippy', '-p', 'snowbound', '--target', 'wasm32-unknown-unknown', '--no-default-features', '--features', 'wgpu', '--', '-D', 'warnings')],
84 environment={**(wasm or {}), 'CARGO_TARGET_DIR': str(TARGET / 'wasm')},84 environment={**(wasm or {}), 'CARGO_TARGET_DIR': str(TARGET / 'wasm')},
85 missing='needs the wasm32-unknown-unknown target' if 'wasm32-unknown-unknown' not in targets.split()85 missing='needs the wasm32-unknown-unknown target' if 'wasm32-unknown-unknown' not in targets.split()
86 else None if wasm else 'needs nix for a clang that builds for wasm32'))86 else None if wasm else 'needs nix for a clang that builds for wasm32'))
tools/release_web.py+1-1
...@@ -57,7 +57,7 @@ def environment():...@@ -57,7 +57,7 @@ def environment():
5757
58def build(out):58def build(out):
59 """Writes index.html, the module, its JavaScript and the fonts to `out`."""59 """Writes index.html, the module, its JavaScript and the fonts to `out`."""
60 run(['cargo', 'build', '--locked', '-p', 'snowbound', '--release', '--target', 'wasm32-unknown-unknown',60 run(['cargo', 'build', '--locked', '-p', 'snowbound', '--release', '--target', 'wasm32-unknown-unknown', '--no-default-features', '--features', 'wgpu',
61 *PROFILE], env=environment())61 *PROFILE], env=environment())
62 if out.exists():62 if out.exists():
63 shutil.rmtree(out)63 shutil.rmtree(out)