workers.

Orual a2ceb29f 9e5ec080

+212 -126
+24 -24
Cargo.lock
··· 330 330 331 331 [[package]] 332 332 name = "async-compression" 333 - version = "0.4.34" 333 + version = "0.4.35" 334 334 source = "registry+https://github.com/rust-lang/crates.io-index" 335 - checksum = "0e86f6d3dc9dc4352edeea6b8e499e13e3f5dc3b964d7ca5fd411415a3498473" 335 + checksum = "07a926debf178f2d355197f9caddb08e54a9329d44748034bba349c5848cb519" 336 336 dependencies = [ 337 337 "compression-codecs", 338 338 "compression-core", ··· 654 654 655 655 [[package]] 656 656 name = "base64ct" 657 - version = "1.8.0" 657 + version = "1.8.1" 658 658 source = "registry+https://github.com/rust-lang/crates.io-index" 659 - checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba" 659 + checksum = "0e050f626429857a27ddccb31e0aca21356bfa709c04041aefddac081a8f068a" 660 660 661 661 [[package]] 662 662 name = "basic-toml" ··· 958 958 959 959 [[package]] 960 960 name = "cc" 961 - version = "1.2.48" 961 + version = "1.2.49" 962 962 source = "registry+https://github.com/rust-lang/crates.io-index" 963 - checksum = "c481bdbf0ed3b892f6f806287d72acd515b352a4ec27a208489b8c1bc839633a" 963 + checksum = "90583009037521a116abf44494efecd645ba48b6622457080f080b85544e2215" 964 964 dependencies = [ 965 965 "find-msvc-tools", 966 966 "jobserver", ··· 1215 1215 1216 1216 [[package]] 1217 1217 name = "compression-codecs" 1218 - version = "0.4.33" 1218 + version = "0.4.34" 1219 1219 source = "registry+https://github.com/rust-lang/crates.io-index" 1220 - checksum = "302266479cb963552d11bd042013a58ef1adc56768016c8b82b4199488f2d4ad" 1220 + checksum = "34a3cbbb8b6eca96f3a5c4bf6938d5b27ced3675d69f95bb51948722870bc323" 1221 1221 dependencies = [ 1222 1222 "compression-core", 1223 1223 "flate2", ··· 3190 3190 3191 3191 [[package]] 3192 3192 name = "dlopen2" 3193 - version = "0.8.1" 3193 + version = "0.8.2" 3194 3194 source = "registry+https://github.com/rust-lang/crates.io-index" 3195 - checksum = "8d65cde5fb0c42a3d5882d99807698b459f5928de035fa7f547c784fb7b34219" 3195 + checksum = "5e2c5bd4158e66d1e215c49b837e11d62f3267b30c92f1d171c4d3105e3dc4d4" 3196 3196 dependencies = [ 3197 3197 "dlopen2_derive", 3198 3198 "libc", ··· 3202 3202 3203 3203 [[package]] 3204 3204 name = "dlopen2_derive" 3205 - version = "0.4.2" 3205 + version = "0.4.3" 3206 3206 source = "registry+https://github.com/rust-lang/crates.io-index" 3207 - checksum = "95f4a04e1bfbfa4835a6073177aafb95ead4de0722dbb339195fdc7e0a09599b" 3207 + checksum = "0fbbb781877580993a8707ec48672673ec7b81eeba04cfd2310bd28c08e47c8f" 3208 3208 dependencies = [ 3209 3209 "proc-macro2", 3210 3210 "quote", ··· 3626 3626 3627 3627 [[package]] 3628 3628 name = "flate2" 3629 - version = "1.1.7" 3629 + version = "1.1.5" 3630 3630 source = "registry+https://github.com/rust-lang/crates.io-index" 3631 - checksum = "a2152dbcb980c05735e2a651d96011320a949eb31a0c8b38b72645ce97dec676" 3631 + checksum = "bfe33edd8e85a12a67454e37f8c75e730830d83e313556ab9ebf9ee7fbeb3bfb" 3632 3632 dependencies = [ 3633 3633 "crc32fast", 3634 3634 "miniz_oxide", ··· 8106 8106 source = "registry+https://github.com/rust-lang/crates.io-index" 8107 8107 checksum = "219cb19e96be00ab2e37d6e299658a0cfa83e52429179969b0f0121b4ac46983" 8108 8108 dependencies = [ 8109 - "toml_edit 0.23.8", 8109 + "toml_edit 0.23.9", 8110 8110 ] 8111 8111 8112 8112 [[package]] ··· 9464 9464 9465 9465 [[package]] 9466 9466 name = "simd-adler32" 9467 - version = "0.3.7" 9467 + version = "0.3.8" 9468 9468 source = "registry+https://github.com/rust-lang/crates.io-index" 9469 - checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" 9469 + checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2" 9470 9470 9471 9471 [[package]] 9472 9472 name = "simdutf8" ··· 9557 9557 9558 9558 [[package]] 9559 9559 name = "slotmap" 9560 - version = "1.1.0" 9560 + version = "1.1.1" 9561 9561 source = "registry+https://github.com/rust-lang/crates.io-index" 9562 - checksum = "811fec94324ff439b2d5e4a1cf88cc5b6c09d92358715d600b2fe91c0489b5cb" 9562 + checksum = "bdd58c3c93c3d278ca835519292445cb4b0d4dc59ccfdf7ceadaab3f8aeb4038" 9563 9563 dependencies = [ 9564 9564 "serde", 9565 9565 "version_check", ··· 10176 10176 "core-graphics", 10177 10177 "crossbeam-channel", 10178 10178 "dispatch", 10179 - "dlopen2 0.8.1", 10179 + "dlopen2 0.8.2", 10180 10180 "dpi", 10181 10181 "gdkwayland-sys", 10182 10182 "gdkx11-sys", ··· 10651 10651 10652 10652 [[package]] 10653 10653 name = "toml_edit" 10654 - version = "0.23.8" 10654 + version = "0.23.9" 10655 10655 source = "registry+https://github.com/rust-lang/crates.io-index" 10656 - checksum = "5a9b7ac41d92f2d2803f233e297127bac397df7b337e0460a1cc39d6c006dee4" 10656 + checksum = "5d7cbc3b4b49633d57a0509303158ca50de80ae32c265093b24c414705807832" 10657 10657 dependencies = [ 10658 10658 "indexmap 2.12.1", 10659 10659 "toml_datetime 0.7.3", ··· 10710 10710 10711 10711 [[package]] 10712 10712 name = "tower-http" 10713 - version = "0.6.7" 10713 + version = "0.6.8" 10714 10714 source = "registry+https://github.com/rust-lang/crates.io-index" 10715 - checksum = "9cf146f99d442e8e68e585f5d798ccd3cad9a7835b917e09728880a862706456" 10715 + checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" 10716 10716 dependencies = [ 10717 10717 "bitflags 2.10.0", 10718 10718 "bytes",
+21
build-workers.sh
··· 1 + #!/usr/bin/env bash 2 + set -euo pipefail 3 + 4 + echo "==> Building worker WASMs" 5 + export RUSTFLAGS='--cfg getrandom_backend="wasm_js"' 6 + cargo build -p weaver-app --bin editor_worker --bin embed_worker \ 7 + --target wasm32-unknown-unknown --release \ 8 + --no-default-features --features "web" 9 + 10 + echo "==> Running wasm-bindgen" 11 + wasm-bindgen target/wasm32-unknown-unknown/release/editor_worker.wasm \ 12 + --out-dir crates/weaver-app/public \ 13 + --target no-modules \ 14 + --no-typescript 15 + wasm-bindgen target/wasm32-unknown-unknown/release/embed_worker.wasm \ 16 + --out-dir crates/weaver-app/public \ 17 + --target no-modules \ 18 + --no-typescript 19 + 20 + echo "==> Done" 21 + ls -lh crates/weaver-app/public/*worker*
+2
crates/weaver-app/Cargo.toml
··· 15 15 [[bin]] 16 16 name = "editor_worker" 17 17 path = "src/bin/editor_worker.rs" 18 + required-features = ["web"] 18 19 19 20 [[bin]] 20 21 name = "embed_worker" 21 22 path = "src/bin/embed_worker.rs" 23 + required-features = ["web"] 22 24 23 25 [features] 24 26 default = ["web", "fullstack-server", "no-app-index"]
+7 -7
crates/weaver-app/public/editor_worker.js
··· 171 171 wasm.wasm_bindgen_1add006a0ed82fd3___convert__closures_____invoke___wasm_bindgen_1add006a0ed82fd3___JsValue_____(arg0, arg1, arg2); 172 172 } 173 173 174 - function wasm_bindgen_1add006a0ed82fd3___convert__closures_____invoke___web_sys_81daa69175395078___features__gen_MessageEvent__MessageEvent_____(arg0, arg1, arg2) { 175 - wasm.wasm_bindgen_1add006a0ed82fd3___convert__closures_____invoke___web_sys_81daa69175395078___features__gen_MessageEvent__MessageEvent_____(arg0, arg1, arg2); 174 + function wasm_bindgen_1add006a0ed82fd3___convert__closures_____invoke___web_sys_1984c39bba2ffe3a___features__gen_MessageEvent__MessageEvent_____(arg0, arg1, arg2) { 175 + wasm.wasm_bindgen_1add006a0ed82fd3___convert__closures_____invoke___web_sys_1984c39bba2ffe3a___features__gen_MessageEvent__MessageEvent_____(arg0, arg1, arg2); 176 176 } 177 177 178 178 function wasm_bindgen_1add006a0ed82fd3___convert__closures_____invoke___wasm_bindgen_1add006a0ed82fd3___JsValue__wasm_bindgen_1add006a0ed82fd3___JsValue_____(arg0, arg1, arg2, arg3) { ··· 604 604 const ret = getStringFromWasm0(arg0, arg1); 605 605 return ret; 606 606 }; 607 + imports.wbg.__wbindgen_cast_7486151126cca30f = function(arg0, arg1) { 608 + // Cast intrinsic for `Closure(Closure { dtor_idx: 66, function: Function { arguments: [NamedExternref("MessageEvent")], shim_idx: 67, ret: Unit, inner_ret: Some(Unit) }, mutable: false }) -> Externref`. 609 + const ret = makeClosure(arg0, arg1, wasm.wasm_bindgen_1add006a0ed82fd3___closure__destroy___dyn_core_b125d98f3949a913___ops__function__Fn__web_sys_1984c39bba2ffe3a___features__gen_MessageEvent__MessageEvent____Output_______, wasm_bindgen_1add006a0ed82fd3___convert__closures_____invoke___web_sys_1984c39bba2ffe3a___features__gen_MessageEvent__MessageEvent_____); 610 + return ret; 611 + }; 607 612 imports.wbg.__wbindgen_cast_cb9088102bce6b30 = function(arg0, arg1) { 608 613 // Cast intrinsic for `Ref(Slice(U8)) -> NamedExternref("Uint8Array")`. 609 614 const ret = getArrayU8FromWasm0(arg0, arg1); 610 - return ret; 611 - }; 612 - imports.wbg.__wbindgen_cast_df669a3c3b68a548 = function(arg0, arg1) { 613 - // Cast intrinsic for `Closure(Closure { dtor_idx: 65, function: Function { arguments: [NamedExternref("MessageEvent")], shim_idx: 66, ret: Unit, inner_ret: Some(Unit) }, mutable: false }) -> Externref`. 614 - const ret = makeClosure(arg0, arg1, wasm.wasm_bindgen_1add006a0ed82fd3___closure__destroy___dyn_core_b125d98f3949a913___ops__function__Fn__web_sys_81daa69175395078___features__gen_MessageEvent__MessageEvent____Output_______, wasm_bindgen_1add006a0ed82fd3___convert__closures_____invoke___web_sys_81daa69175395078___features__gen_MessageEvent__MessageEvent_____); 615 615 return ret; 616 616 }; 617 617 imports.wbg.__wbindgen_init_externref_table = function() {
+14 -42
crates/weaver-app/public/embed_worker.js
··· 236 236 wasm.wasm_bindgen_1add006a0ed82fd3___convert__closures_____invoke______(arg0, arg1); 237 237 } 238 238 239 - function wasm_bindgen_1add006a0ed82fd3___convert__closures_____invoke___web_sys_81daa69175395078___features__gen_MessageEvent__MessageEvent_____(arg0, arg1, arg2) { 240 - wasm.wasm_bindgen_1add006a0ed82fd3___convert__closures_____invoke___web_sys_81daa69175395078___features__gen_MessageEvent__MessageEvent_____(arg0, arg1, arg2); 239 + function wasm_bindgen_1add006a0ed82fd3___convert__closures_____invoke___web_sys_1984c39bba2ffe3a___features__gen_MessageEvent__MessageEvent_____(arg0, arg1, arg2) { 240 + wasm.wasm_bindgen_1add006a0ed82fd3___convert__closures_____invoke___web_sys_1984c39bba2ffe3a___features__gen_MessageEvent__MessageEvent_____(arg0, arg1, arg2); 241 241 } 242 242 243 243 function wasm_bindgen_1add006a0ed82fd3___convert__closures_____invoke_______1_(arg0, arg1) { ··· 480 480 imports.wbg.__wbg_abort_399ecbcfd6ef3c8e = function(arg0, arg1) { 481 481 arg0.abort(arg1); 482 482 }; 483 - imports.wbg.__wbg_append_0a085a855907169f = function() { return handleError(function (arg0, arg1, arg2, arg3) { 484 - arg0.append(getStringFromWasm0(arg1, arg2), arg3); 485 - }, arguments) }; 486 - imports.wbg.__wbg_append_90fbf76fbc23e35f = function() { return handleError(function (arg0, arg1, arg2, arg3, arg4) { 487 - arg0.append(getStringFromWasm0(arg1, arg2), getStringFromWasm0(arg3, arg4)); 488 - }, arguments) }; 489 483 imports.wbg.__wbg_append_c5cbdf46455cc776 = function() { return handleError(function (arg0, arg1, arg2, arg3, arg4) { 490 484 arg0.append(getStringFromWasm0(arg1, arg2), getStringFromWasm0(arg3, arg4)); 491 - }, arguments) }; 492 - imports.wbg.__wbg_append_ef47c723098c3768 = function() { return handleError(function (arg0, arg1, arg2, arg3, arg4, arg5) { 493 - arg0.append(getStringFromWasm0(arg1, arg2), arg3, getStringFromWasm0(arg4, arg5)); 494 485 }, arguments) }; 495 486 imports.wbg.__wbg_arrayBuffer_c04af4fce566092d = function() { return handleError(function (arg0) { 496 487 const ret = arg0.arrayBuffer(); ··· 618 609 const ret = new Object(); 619 610 return ret; 620 611 }; 621 - imports.wbg.__wbg_new_25f239778d6112b9 = function() { 622 - const ret = new Array(); 623 - return ret; 624 - }; 625 612 imports.wbg.__wbg_new_3c79b3bb1b32b7d3 = function() { return handleError(function () { 626 613 const ret = new Headers(); 627 614 return ret; ··· 642 629 const ret = new Error(getStringFromWasm0(arg0, arg1)); 643 630 return ret; 644 631 }; 645 - imports.wbg.__wbg_new_e66247b901edc931 = function() { return handleError(function () { 646 - const ret = new FormData(); 647 - return ret; 648 - }, arguments) }; 649 632 imports.wbg.__wbg_new_ff12d2b041fb48f1 = function(arg0, arg1) { 650 633 try { 651 634 var state0 = {a: arg0, b: arg1}; ··· 680 663 const ret = new Request(getStringFromWasm0(arg0, arg1), arg2); 681 664 return ret; 682 665 }, arguments) }; 683 - imports.wbg.__wbg_new_with_u8_array_sequence_and_options_d4def9ec0588c7ec = function() { return handleError(function (arg0, arg1) { 684 - const ret = new Blob(arg0, arg1); 685 - return ret; 686 - }, arguments) }; 687 666 imports.wbg.__wbg_next_138a17bbf04e926c = function(arg0) { 688 667 const ret = arg0.next; 689 668 return ret; ··· 714 693 imports.wbg.__wbg_prototypesetcall_dfe9b766cdc1f1fd = function(arg0, arg1, arg2) { 715 694 Uint8Array.prototype.set.call(getArrayU8FromWasm0(arg0, arg1), arg2); 716 695 }; 717 - imports.wbg.__wbg_push_7d9be8f38fc13975 = function(arg0, arg1) { 718 - const ret = arg0.push(arg1); 719 - return ret; 720 - }; 721 696 imports.wbg.__wbg_queueMicrotask_9b549dfce8865860 = function(arg0) { 722 697 const ret = arg0.queueMicrotask; 723 698 return ret; ··· 766 741 }; 767 742 imports.wbg.__wbg_set_signal_e89be862d0091009 = function(arg0, arg1) { 768 743 arg0.signal = arg1; 769 - }; 770 - imports.wbg.__wbg_set_type_7ce650670a34c68f = function(arg0, arg1, arg2) { 771 - arg0.type = getStringFromWasm0(arg1, arg2); 772 744 }; 773 745 imports.wbg.__wbg_signal_3c14fbdc89694b39 = function(arg0) { 774 746 const ret = arg0.signal; ··· 837 809 const ret = getStringFromWasm0(arg0, arg1); 838 810 return ret; 839 811 }; 840 - imports.wbg.__wbindgen_cast_600f2adf1b415b72 = function(arg0, arg1) { 841 - // Cast intrinsic for `Closure(Closure { dtor_idx: 364, function: Function { arguments: [NamedExternref("MessageEvent")], shim_idx: 365, ret: Unit, inner_ret: Some(Unit) }, mutable: false }) -> Externref`. 842 - const ret = makeClosure(arg0, arg1, wasm.wasm_bindgen_1add006a0ed82fd3___closure__destroy___dyn_core_b125d98f3949a913___ops__function__Fn__web_sys_81daa69175395078___features__gen_MessageEvent__MessageEvent____Output_______, wasm_bindgen_1add006a0ed82fd3___convert__closures_____invoke___web_sys_81daa69175395078___features__gen_MessageEvent__MessageEvent_____); 812 + imports.wbg.__wbindgen_cast_58535cde6944cfce = function(arg0, arg1) { 813 + // Cast intrinsic for `Closure(Closure { dtor_idx: 2267, function: Function { arguments: [Externref], shim_idx: 2268, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`. 814 + const ret = makeMutClosure(arg0, arg1, wasm.wasm_bindgen_1add006a0ed82fd3___closure__destroy___dyn_core_b125d98f3949a913___ops__function__FnMut__wasm_bindgen_1add006a0ed82fd3___JsValue____Output_______, wasm_bindgen_1add006a0ed82fd3___convert__closures_____invoke___wasm_bindgen_1add006a0ed82fd3___JsValue_____); 843 815 return ret; 844 816 }; 845 - imports.wbg.__wbindgen_cast_89cd6f7b6be057aa = function(arg0, arg1) { 846 - // Cast intrinsic for `Closure(Closure { dtor_idx: 2024, function: Function { arguments: [], shim_idx: 2025, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`. 847 - const ret = makeMutClosure(arg0, arg1, wasm.wasm_bindgen_1add006a0ed82fd3___closure__destroy___dyn_core_b125d98f3949a913___ops__function__FnMut_____Output________1_, wasm_bindgen_1add006a0ed82fd3___convert__closures_____invoke_______1_); 817 + imports.wbg.__wbindgen_cast_bd3d7d0f0ebc68be = function(arg0, arg1) { 818 + // Cast intrinsic for `Closure(Closure { dtor_idx: 1185, function: Function { arguments: [], shim_idx: 1186, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`. 819 + const ret = makeMutClosure(arg0, arg1, wasm.wasm_bindgen_1add006a0ed82fd3___closure__destroy___dyn_core_b125d98f3949a913___ops__function__FnMut_____Output_______, wasm_bindgen_1add006a0ed82fd3___convert__closures_____invoke______); 848 820 return ret; 849 821 }; 850 - imports.wbg.__wbindgen_cast_9216bf6c1753368f = function(arg0, arg1) { 851 - // Cast intrinsic for `Closure(Closure { dtor_idx: 1144, function: Function { arguments: [], shim_idx: 1145, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`. 852 - const ret = makeMutClosure(arg0, arg1, wasm.wasm_bindgen_1add006a0ed82fd3___closure__destroy___dyn_core_b125d98f3949a913___ops__function__FnMut_____Output_______, wasm_bindgen_1add006a0ed82fd3___convert__closures_____invoke______); 822 + imports.wbg.__wbindgen_cast_c5ee62b4aaae9530 = function(arg0, arg1) { 823 + // Cast intrinsic for `Closure(Closure { dtor_idx: 1445, function: Function { arguments: [], shim_idx: 1446, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`. 824 + const ret = makeMutClosure(arg0, arg1, wasm.wasm_bindgen_1add006a0ed82fd3___closure__destroy___dyn_core_b125d98f3949a913___ops__function__FnMut_____Output________1_, wasm_bindgen_1add006a0ed82fd3___convert__closures_____invoke_______1_); 853 825 return ret; 854 826 }; 855 - imports.wbg.__wbindgen_cast_eee509cff0277222 = function(arg0, arg1) { 856 - // Cast intrinsic for `Closure(Closure { dtor_idx: 2221, function: Function { arguments: [Externref], shim_idx: 2222, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`. 857 - const ret = makeMutClosure(arg0, arg1, wasm.wasm_bindgen_1add006a0ed82fd3___closure__destroy___dyn_core_b125d98f3949a913___ops__function__FnMut__wasm_bindgen_1add006a0ed82fd3___JsValue____Output_______, wasm_bindgen_1add006a0ed82fd3___convert__closures_____invoke___wasm_bindgen_1add006a0ed82fd3___JsValue_____); 827 + imports.wbg.__wbindgen_cast_cf4ca89562bb2a76 = function(arg0, arg1) { 828 + // Cast intrinsic for `Closure(Closure { dtor_idx: 419, function: Function { arguments: [NamedExternref("MessageEvent")], shim_idx: 420, ret: Unit, inner_ret: Some(Unit) }, mutable: false }) -> Externref`. 829 + const ret = makeClosure(arg0, arg1, wasm.wasm_bindgen_1add006a0ed82fd3___closure__destroy___dyn_core_b125d98f3949a913___ops__function__Fn__web_sys_1984c39bba2ffe3a___features__gen_MessageEvent__MessageEvent____Output_______, wasm_bindgen_1add006a0ed82fd3___convert__closures_____invoke___web_sys_1984c39bba2ffe3a___features__gen_MessageEvent__MessageEvent_____); 858 830 return ret; 859 831 }; 860 832 imports.wbg.__wbindgen_init_externref_table = function() {
+127 -38
crates/weaver-app/src/components/editor/component.rs
··· 184 184 doc.commit(); 185 185 186 186 // Pre-warm blob cache for images 187 + #[cfg(feature = "fullstack-server")] 187 188 if let Some(ref embeds) = loaded.entry.embeds { 188 189 if let Some(ref images) = embeds.images { 189 190 let ident: &str = match uri.authority() { ··· 491 492 paras 492 493 }); 493 494 494 - // Background fetch for AT embeds 495 - let mut resolved_content_for_fetch = resolved_content.clone(); 496 - let doc_for_embeds = document.clone(); 497 - let fetcher_for_embeds = fetcher.clone(); 498 - use_effect(move || { 499 - let refs = doc_for_embeds.collected_refs.read(); 500 - let current_resolved = resolved_content_for_fetch.peek(); 501 - let fetcher = fetcher_for_embeds.clone(); 495 + // Background fetch for AT embeds via worker 496 + #[cfg(all(target_arch = "wasm32", target_os = "unknown"))] 497 + { 498 + use super::worker::{EmbedWorker, EmbedWorkerInput, EmbedWorkerOutput}; 499 + use dioxus::prelude::Writable; 500 + use gloo_worker::Spawnable; 501 + 502 + let resolved_content_for_fetch = resolved_content; 503 + let mut embed_worker_bridge: Signal<Option<gloo_worker::WorkerBridge<EmbedWorker>>> = 504 + use_signal(|| None); 502 505 503 - // Find AT embeds that need fetching 504 - let to_fetch: Vec<String> = refs 505 - .iter() 506 - .filter_map(|r| match r { 507 - weaver_common::ExtractedRef::AtEmbed { uri, .. } => { 508 - // Skip if already resolved 509 - if let Ok(at_uri) = jacquard::types::string::AtUri::new(uri) { 510 - if current_resolved.get_embed_content(&at_uri).is_none() { 511 - return Some(uri.clone()); 506 + // Spawn embed worker on mount 507 + let doc_for_embeds = document.clone(); 508 + use_effect(move || { 509 + // Callback for worker responses - uses write_unchecked since we're in a Fn closure 510 + let on_output = move |output: EmbedWorkerOutput| match output { 511 + EmbedWorkerOutput::Embeds { 512 + results, 513 + errors, 514 + fetch_ms, 515 + } => { 516 + if !results.is_empty() { 517 + let mut rc = resolved_content_for_fetch.write_unchecked(); 518 + for (uri_str, html) in results { 519 + if let Ok(at_uri) = 520 + jacquard::types::string::AtUri::new_owned(uri_str) 521 + { 522 + rc.add_embed(at_uri, html, None); 523 + } 512 524 } 525 + tracing::debug!( 526 + count = rc.embed_content.len(), 527 + fetch_ms, 528 + "embed worker fetched embeds" 529 + ); 513 530 } 514 - None 531 + for (uri, err) in errors { 532 + tracing::warn!("embed worker failed to fetch {}: {}", uri, err); 533 + } 515 534 } 516 - _ => None, 517 - }) 518 - .collect(); 535 + EmbedWorkerOutput::CacheCleared => { 536 + tracing::debug!("embed worker cache cleared"); 537 + } 538 + }; 519 539 520 - if to_fetch.is_empty() { 521 - return; 522 - } 540 + let bridge = EmbedWorker::spawner() 541 + .callback(on_output) 542 + .spawn("/embed_worker.js"); 543 + embed_worker_bridge.set(Some(bridge)); 544 + tracing::info!("Embed worker spawned"); 545 + }); 523 546 524 - // Spawn background fetches 525 - dioxus::prelude::spawn(async move { 526 - for uri_str in to_fetch { 527 - let Ok(at_uri) = jacquard::types::string::AtUri::new(&uri_str) else { 528 - continue; 529 - }; 547 + // Send embeds to worker when collected_refs changes 548 + use_effect(move || { 549 + let refs = doc_for_embeds.collected_refs.read(); 550 + let current_resolved = resolved_content_for_fetch.peek(); 530 551 531 - match weaver_renderer::atproto::fetch_and_render(&at_uri, &fetcher).await { 532 - Ok(html) => { 533 - let mut rc = resolved_content_for_fetch.write(); 534 - rc.add_embed(at_uri.into_static(), html, None); 552 + // Find AT embeds that need fetching 553 + let to_fetch: Vec<String> = refs 554 + .iter() 555 + .filter_map(|r| match r { 556 + weaver_common::ExtractedRef::AtEmbed { uri, .. } => { 557 + // Skip if already resolved 558 + if let Ok(at_uri) = jacquard::types::string::AtUri::new(uri) { 559 + if current_resolved.get_embed_content(&at_uri).is_none() { 560 + return Some(uri.clone()); 561 + } 562 + } 563 + None 535 564 } 536 - Err(e) => { 537 - tracing::warn!("failed to fetch embed {}: {}", uri_str, e); 565 + _ => None, 566 + }) 567 + .collect(); 568 + 569 + if to_fetch.is_empty() { 570 + return; 571 + } 572 + 573 + // Send to worker 574 + if let Some(ref bridge) = *embed_worker_bridge.peek() { 575 + bridge.send(EmbedWorkerInput::FetchEmbeds { uris: to_fetch }); 576 + } 577 + }); 578 + } 579 + 580 + // Fallback for non-WASM (server-side rendering) 581 + #[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))] 582 + { 583 + let mut resolved_content_for_fetch = resolved_content.clone(); 584 + let doc_for_embeds = document.clone(); 585 + let fetcher_for_embeds = fetcher.clone(); 586 + use_effect(move || { 587 + let refs = doc_for_embeds.collected_refs.read(); 588 + let current_resolved = resolved_content_for_fetch.peek(); 589 + let fetcher = fetcher_for_embeds.clone(); 590 + 591 + // Find AT embeds that need fetching 592 + let to_fetch: Vec<String> = refs 593 + .iter() 594 + .filter_map(|r| match r { 595 + weaver_common::ExtractedRef::AtEmbed { uri, .. } => { 596 + // Skip if already resolved 597 + if let Ok(at_uri) = jacquard::types::string::AtUri::new(uri) { 598 + if current_resolved.get_embed_content(&at_uri).is_none() { 599 + return Some(uri.clone()); 600 + } 601 + } 602 + None 603 + } 604 + _ => None, 605 + }) 606 + .collect(); 607 + 608 + if to_fetch.is_empty() { 609 + return; 610 + } 611 + 612 + // Spawn background fetches (main thread fallback) 613 + dioxus::prelude::spawn(async move { 614 + for uri_str in to_fetch { 615 + let Ok(at_uri) = jacquard::types::string::AtUri::new(&uri_str) else { 616 + continue; 617 + }; 618 + 619 + match weaver_renderer::atproto::fetch_and_render(&at_uri, &fetcher).await { 620 + Ok(html) => { 621 + let mut rc = resolved_content_for_fetch.write(); 622 + rc.add_embed(at_uri.into_static(), html, None); 623 + } 624 + Err(e) => { 625 + tracing::warn!("failed to fetch embed {}: {}", uri_str, e); 626 + } 538 627 } 539 628 } 540 - } 629 + }); 541 630 }); 542 - }); 631 + } 543 632 544 633 let mut new_tag = use_signal(String::new); 545 634
+3
crates/weaver-app/src/components/editor/sync.rs
··· 67 67 let embeds_map = doc.get_map("embeds"); 68 68 69 69 // Pre-warm blob cache for images 70 + #[cfg(feature = "fullstack-server")] 70 71 if let Some(ident) = owner_ident { 71 72 if let Ok(images_container) = 72 73 embeds_map.get_or_create_container("images", loro::LoroList::new()) ··· 97 98 } 98 99 } 99 100 } 101 + #[cfg(not(feature = "fullstack-server"))] 102 + let _ = owner_ident; 100 103 101 104 // Strategy 1: Get embeds from Loro embeds map -> records list 102 105
+1 -1
crates/weaver-app/src/components/editor/worker.rs
··· 312 312 results.insert(uri_str, html); 313 313 } 314 314 Err(e) => { 315 - errors.insert(uri_str, e.to_string()); 315 + errors.insert(uri_str, format!("{:?}", e)); 316 316 } 317 317 } 318 318 }
+1 -3
crates/weaver-app/src/components/editor/writer.rs
··· 6 6 //! Uses Parser::into_offset_iter() to track gaps between events, which 7 7 //! represent consumed formatting characters. 8 8 9 - use crate::data::cache_blob; 10 - 11 9 use super::offset_map::{OffsetMapping, RenderResult}; 12 10 use jacquard::types::{ident::AtIdentifier, string::Rkey}; 13 11 use loro::LoroText; ··· 948 946 self.offset_maps.push(mapping); 949 947 self.current_node_char_offset += utf16_len; 950 948 } else { 951 - tracing::warn!("[RECORD_MAPPING] SKIPPED - current_node_id is None!"); 949 + tracing::debug!("[RECORD_MAPPING] SKIPPED - current_node_id is None!"); 952 950 } 953 951 } 954 952
+1 -1
crates/weaver-app/src/data.rs
··· 138 138 } 139 139 } 140 140 } 141 - Some(entry) 141 + Some((entry.0.clone(), entry.1.clone())) 142 142 } else { 143 143 None 144 144 }
+11 -10
crates/weaver-renderer/src/atproto/embed_renderer.rs
··· 46 46 let did = match ident { 47 47 AtIdentifier::Did(d) => d.clone(), 48 48 AtIdentifier::Handle(h) => { 49 - let did_str = agent 50 - .resolve_handle(h) 51 - .await 52 - .map_err(|e| AtProtoPreprocessError::FetchFailed(e.to_string()))?; 49 + let did_str = agent.resolve_handle(h).await.map_err(|e| { 50 + AtProtoPreprocessError::FetchFailed(format!("resolving handle {:?}", e)) 51 + })?; 53 52 Did::new(&did_str) 54 - .map_err(|e| AtProtoPreprocessError::InvalidUri(e.to_string()))? 53 + .map_err(|e| AtProtoPreprocessError::InvalidUri(format!("{:?}", e)))? 55 54 .into_static() 56 55 } 57 56 }; ··· 60 59 let (_uri, profile_view) = agent 61 60 .hydrate_profile_view(&did) 62 61 .await 63 - .map_err(|e| AtProtoPreprocessError::FetchFailed(e.to_string()))?; 62 + .map_err(|e| AtProtoPreprocessError::FetchFailed(format!("{:?}", e)))?; 64 63 65 64 // Render based on which profile type we got 66 65 render_profile_data_view(&profile_view.inner) ··· 77 76 // Use GetPosts for richer data (author info, engagement counts) 78 77 let request = GetPosts::new().uris(vec![uri.clone()]).build(); 79 78 let response = agent.send(request).await; 80 - let response = response.map_err(|e| AtProtoPreprocessError::FetchFailed(e.to_string()))?; 79 + let response = response.map_err(|e| { 80 + AtProtoPreprocessError::FetchFailed(format!("getting post from appview {:?}", e)) 81 + })?; 81 82 82 83 let output = response 83 84 .into_output() 84 - .map_err(|e| AtProtoPreprocessError::FetchFailed(e.to_string()))?; 85 + .map_err(|e| AtProtoPreprocessError::FetchFailed(format!("{:?}", e)))?; 85 86 86 87 let post_view = output 87 88 .posts ··· 106 107 let output = agent 107 108 .fetch_record_slingshot(uri) 108 109 .await 109 - .map_err(|e| AtProtoPreprocessError::FetchFailed(e.to_string()))?; 110 + .map_err(|e| AtProtoPreprocessError::FetchFailed(format!("{:?}", e)))?; 110 111 111 112 // Probe for meaningful fields 112 113 render_generic_record(&output.value, uri) ··· 144 145 ClientWriter::<_, _, ()>::new(parser, &mut content_html) 145 146 .run() 146 147 .map_err(|e| { 147 - AtProtoPreprocessError::FetchFailed(format!("Markdown render failed: {}", e)) 148 + AtProtoPreprocessError::FetchFailed(format!("Markdown render failed: {:?}", e)) 148 149 })?; 149 150 150 151 // Generate unique ID for the toggle checkbox