From 9e5ec0808521b3d16eb52de38b1bfe3dabdfddac Mon Sep 17 00:00:00 2001 From: Orual Date: Mon, 8 Dec 2025 14:04:41 -0500 Subject: [PATCH] worker stuff. --- Cargo.lock | 44 + crates/weaver-app/Cargo.toml | 18 + crates/weaver-app/public/editor_worker.js | 688 +++++++++++++ crates/weaver-app/public/embed_worker.js | 931 ++++++++++++++++++ .../inline0.js | 9 + .../inline0.js | 222 +++++ .../src/js/hydrate.js | 1 + .../src/js/patch_console.js | 1 + .../src/js/set_attribute.js | 1 + .../dioxus-web-fe956e4b19891335/inline0.js | 12 + .../dioxus-web-fe956e4b19891335/inline1.js | 12 + .../src/js/eval.js | 1 + crates/weaver-app/src/bin/editor_worker.rs | 19 + crates/weaver-app/src/bin/embed_worker.rs | 19 + .../src/components/editor/component.rs | 168 +++- .../weaver-app/src/components/editor/mod.rs | 8 + .../src/components/editor/render.rs | 30 +- .../src/components/editor/storage.rs | 20 +- .../weaver-app/src/components/editor/sync.rs | 18 +- .../src/components/editor/worker.rs | 343 +++++++ crates/weaver-app/src/lib.rs | 188 ++++ crates/weaver-app/src/main.rs | 250 +---- crates/weaver-app/src/perf.rs | 58 ++ 23 files changed, 2791 insertions(+), 270 deletions(-) create mode 100644 crates/weaver-app/public/editor_worker.js create mode 100644 crates/weaver-app/public/embed_worker.js create mode 100644 crates/weaver-app/public/snippets/dioxus-cli-config-c2775606adf80e62/inline0.js create mode 100644 crates/weaver-app/public/snippets/dioxus-interpreter-js-ce7056f341163470/inline0.js create mode 100644 crates/weaver-app/public/snippets/dioxus-interpreter-js-ce7056f341163470/src/js/hydrate.js create mode 100644 crates/weaver-app/public/snippets/dioxus-interpreter-js-ce7056f341163470/src/js/patch_console.js create mode 100644 crates/weaver-app/public/snippets/dioxus-interpreter-js-ce7056f341163470/src/js/set_attribute.js create mode 100644 crates/weaver-app/public/snippets/dioxus-web-fe956e4b19891335/inline0.js create mode 100644 crates/weaver-app/public/snippets/dioxus-web-fe956e4b19891335/inline1.js create mode 100644 crates/weaver-app/public/snippets/dioxus-web-fe956e4b19891335/src/js/eval.js create mode 100644 crates/weaver-app/src/bin/editor_worker.rs create mode 100644 crates/weaver-app/src/bin/embed_worker.rs create mode 100644 crates/weaver-app/src/components/editor/worker.rs create mode 100644 crates/weaver-app/src/lib.rs create mode 100644 crates/weaver-app/src/perf.rs diff --git a/Cargo.lock b/Cargo.lock index 02e2b78..db638fe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4289,6 +4289,37 @@ dependencies = [ "web-sys", ] +[[package]] +name = "gloo-worker" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "085f262d7604911c8150162529cefab3782e91adb20202e8658f7275d2aefe5d" +dependencies = [ + "bincode", + "futures", + "gloo-utils", + "gloo-worker-macros", + "js-sys", + "pinned", + "serde", + "thiserror 1.0.69", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "gloo-worker-macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "956caa58d4857bc9941749d55e4bd3000032d8212762586fa5705632967140e7" +dependencies = [ + "proc-macro-crate 1.3.1", + "proc-macro2", + "quote", + "syn 2.0.111", +] + [[package]] name = "gobject-sys" version = "0.18.0" @@ -7809,6 +7840,17 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "pinned" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a829027bd95e54cfe13e3e258a1ae7b645960553fb82b75ff852c29688ee595b" +dependencies = [ + "futures", + "rustversion", + "thiserror 1.0.69", +] + [[package]] name = "pkarr" version = "5.0.0" @@ -11488,6 +11530,7 @@ dependencies = [ "getrandom 0.3.4", "gloo-storage", "gloo-timers", + "gloo-worker", "http", "humansize", "insta", @@ -11503,6 +11546,7 @@ dependencies = [ "mime-sniffer", "mini-moka 0.11.0", "n0-future 0.1.3", + "postcard", "regex", "regex-lite", "reqwest", diff --git a/crates/weaver-app/Cargo.toml b/crates/weaver-app/Cargo.toml index 592a116..b89938e 100644 --- a/crates/weaver-app/Cargo.toml +++ b/crates/weaver-app/Cargo.toml @@ -4,6 +4,22 @@ version = "0.1.0" authors = ["Orual "] edition = "2024" +[lib] +name = "weaver_app" +path = "src/lib.rs" + +[[bin]] +name = "weaver-app" +path = "src/main.rs" + +[[bin]] +name = "editor_worker" +path = "src/bin/editor_worker.rs" + +[[bin]] +name = "embed_worker" +path = "src/bin/embed_worker.rs" + [features] default = ["web", "fullstack-server", "no-app-index"] # Fullstack mode with SSR and server functions @@ -84,6 +100,8 @@ web-sys = { version = "0.3", features = ["ServiceWorkerContainer", "ServiceWorke js-sys = "0.3" gloo-storage = "0.3" gloo-timers = "0.3" +gloo-worker = { version = "0.5", features = ["futures"] } +postcard = "1.1" ring = { version = "0.17", default-features = false, features = ["wasm32_unknown_unknown_js"]} getrandom = { version = "0.3", default-features = false, features = ["wasm_js"] } lol_alloc = "0.4.1" diff --git a/crates/weaver-app/public/editor_worker.js b/crates/weaver-app/public/editor_worker.js new file mode 100644 index 0000000..49edf0e --- /dev/null +++ b/crates/weaver-app/public/editor_worker.js @@ -0,0 +1,688 @@ +let wasm_bindgen; +(function() { + const __exports = {}; + let script_src; + if (typeof document !== 'undefined' && document.currentScript !== null) { + script_src = new URL(document.currentScript.src, location.href).toString(); + } + let wasm = undefined; + + function addToExternrefTable0(obj) { + const idx = wasm.__externref_table_alloc(); + wasm.__wbindgen_externrefs.set(idx, obj); + return idx; + } + + const CLOSURE_DTORS = (typeof FinalizationRegistry === 'undefined') + ? { register: () => {}, unregister: () => {} } + : new FinalizationRegistry(state => state.dtor(state.a, state.b)); + + function getArrayU8FromWasm0(ptr, len) { + ptr = ptr >>> 0; + return getUint8ArrayMemory0().subarray(ptr / 1, ptr / 1 + len); + } + + let cachedDataViewMemory0 = null; + function getDataViewMemory0() { + if (cachedDataViewMemory0 === null || cachedDataViewMemory0.buffer.detached === true || (cachedDataViewMemory0.buffer.detached === undefined && cachedDataViewMemory0.buffer !== wasm.memory.buffer)) { + cachedDataViewMemory0 = new DataView(wasm.memory.buffer); + } + return cachedDataViewMemory0; + } + + function getStringFromWasm0(ptr, len) { + ptr = ptr >>> 0; + return decodeText(ptr, len); + } + + let cachedUint8ArrayMemory0 = null; + function getUint8ArrayMemory0() { + if (cachedUint8ArrayMemory0 === null || cachedUint8ArrayMemory0.byteLength === 0) { + cachedUint8ArrayMemory0 = new Uint8Array(wasm.memory.buffer); + } + return cachedUint8ArrayMemory0; + } + + function handleError(f, args) { + try { + return f.apply(this, args); + } catch (e) { + const idx = addToExternrefTable0(e); + wasm.__wbindgen_exn_store(idx); + } + } + + function isLikeNone(x) { + return x === undefined || x === null; + } + + function makeClosure(arg0, arg1, dtor, f) { + const state = { a: arg0, b: arg1, cnt: 1, dtor }; + const real = (...args) => { + + // First up with a closure we increment the internal reference + // count. This ensures that the Rust closure environment won't + // be deallocated while we're invoking it. + state.cnt++; + try { + return f(state.a, state.b, ...args); + } finally { + real._wbg_cb_unref(); + } + }; + real._wbg_cb_unref = () => { + if (--state.cnt === 0) { + state.dtor(state.a, state.b); + state.a = 0; + CLOSURE_DTORS.unregister(state); + } + }; + CLOSURE_DTORS.register(real, state, state); + return real; + } + + function makeMutClosure(arg0, arg1, dtor, f) { + const state = { a: arg0, b: arg1, cnt: 1, dtor }; + const real = (...args) => { + + // First up with a closure we increment the internal reference + // count. This ensures that the Rust closure environment won't + // be deallocated while we're invoking it. + state.cnt++; + const a = state.a; + state.a = 0; + try { + return f(a, state.b, ...args); + } finally { + state.a = a; + real._wbg_cb_unref(); + } + }; + real._wbg_cb_unref = () => { + if (--state.cnt === 0) { + state.dtor(state.a, state.b); + state.a = 0; + CLOSURE_DTORS.unregister(state); + } + }; + CLOSURE_DTORS.register(real, state, state); + return real; + } + + function passStringToWasm0(arg, malloc, realloc) { + if (realloc === undefined) { + const buf = cachedTextEncoder.encode(arg); + const ptr = malloc(buf.length, 1) >>> 0; + getUint8ArrayMemory0().subarray(ptr, ptr + buf.length).set(buf); + WASM_VECTOR_LEN = buf.length; + return ptr; + } + + let len = arg.length; + let ptr = malloc(len, 1) >>> 0; + + const mem = getUint8ArrayMemory0(); + + let offset = 0; + + for (; offset < len; offset++) { + const code = arg.charCodeAt(offset); + if (code > 0x7F) break; + mem[ptr + offset] = code; + } + if (offset !== len) { + if (offset !== 0) { + arg = arg.slice(offset); + } + ptr = realloc(ptr, len, len = offset + arg.length * 3, 1) >>> 0; + const view = getUint8ArrayMemory0().subarray(ptr + offset, ptr + len); + const ret = cachedTextEncoder.encodeInto(arg, view); + + offset += ret.written; + ptr = realloc(ptr, len, offset, 1) >>> 0; + } + + WASM_VECTOR_LEN = offset; + return ptr; + } + + let cachedTextDecoder = new TextDecoder('utf-8', { ignoreBOM: true, fatal: true }); + cachedTextDecoder.decode(); + function decodeText(ptr, len) { + return cachedTextDecoder.decode(getUint8ArrayMemory0().subarray(ptr, ptr + len)); + } + + const cachedTextEncoder = new TextEncoder(); + + if (!('encodeInto' in cachedTextEncoder)) { + cachedTextEncoder.encodeInto = function (arg, view) { + const buf = cachedTextEncoder.encode(arg); + view.set(buf); + return { + read: arg.length, + written: buf.length + }; + } + } + + let WASM_VECTOR_LEN = 0; + + function wasm_bindgen_1add006a0ed82fd3___convert__closures_____invoke___wasm_bindgen_1add006a0ed82fd3___JsValue_____(arg0, arg1, arg2) { + wasm.wasm_bindgen_1add006a0ed82fd3___convert__closures_____invoke___wasm_bindgen_1add006a0ed82fd3___JsValue_____(arg0, arg1, arg2); + } + + function wasm_bindgen_1add006a0ed82fd3___convert__closures_____invoke___web_sys_81daa69175395078___features__gen_MessageEvent__MessageEvent_____(arg0, arg1, arg2) { + wasm.wasm_bindgen_1add006a0ed82fd3___convert__closures_____invoke___web_sys_81daa69175395078___features__gen_MessageEvent__MessageEvent_____(arg0, arg1, arg2); + } + + function wasm_bindgen_1add006a0ed82fd3___convert__closures_____invoke___wasm_bindgen_1add006a0ed82fd3___JsValue__wasm_bindgen_1add006a0ed82fd3___JsValue_____(arg0, arg1, arg2, arg3) { + wasm.wasm_bindgen_1add006a0ed82fd3___convert__closures_____invoke___wasm_bindgen_1add006a0ed82fd3___JsValue__wasm_bindgen_1add006a0ed82fd3___JsValue_____(arg0, arg1, arg2, arg3); + } + + const __wbindgen_enum_ReadableStreamType = ["bytes"]; + + const IntoUnderlyingByteSourceFinalization = (typeof FinalizationRegistry === 'undefined') + ? { register: () => {}, unregister: () => {} } + : new FinalizationRegistry(ptr => wasm.__wbg_intounderlyingbytesource_free(ptr >>> 0, 1)); + + const IntoUnderlyingSinkFinalization = (typeof FinalizationRegistry === 'undefined') + ? { register: () => {}, unregister: () => {} } + : new FinalizationRegistry(ptr => wasm.__wbg_intounderlyingsink_free(ptr >>> 0, 1)); + + const IntoUnderlyingSourceFinalization = (typeof FinalizationRegistry === 'undefined') + ? { register: () => {}, unregister: () => {} } + : new FinalizationRegistry(ptr => wasm.__wbg_intounderlyingsource_free(ptr >>> 0, 1)); + + const JSOwnerFinalization = (typeof FinalizationRegistry === 'undefined') + ? { register: () => {}, unregister: () => {} } + : new FinalizationRegistry(ptr => wasm.__wbg_jsowner_free(ptr >>> 0, 1)); + + class IntoUnderlyingByteSource { + __destroy_into_raw() { + const ptr = this.__wbg_ptr; + this.__wbg_ptr = 0; + IntoUnderlyingByteSourceFinalization.unregister(this); + return ptr; + } + free() { + const ptr = this.__destroy_into_raw(); + wasm.__wbg_intounderlyingbytesource_free(ptr, 0); + } + /** + * @returns {number} + */ + get autoAllocateChunkSize() { + const ret = wasm.intounderlyingbytesource_autoAllocateChunkSize(this.__wbg_ptr); + return ret >>> 0; + } + /** + * @param {ReadableByteStreamController} controller + * @returns {Promise} + */ + pull(controller) { + const ret = wasm.intounderlyingbytesource_pull(this.__wbg_ptr, controller); + return ret; + } + /** + * @param {ReadableByteStreamController} controller + */ + start(controller) { + wasm.intounderlyingbytesource_start(this.__wbg_ptr, controller); + } + /** + * @returns {ReadableStreamType} + */ + get type() { + const ret = wasm.intounderlyingbytesource_type(this.__wbg_ptr); + return __wbindgen_enum_ReadableStreamType[ret]; + } + cancel() { + const ptr = this.__destroy_into_raw(); + wasm.intounderlyingbytesource_cancel(ptr); + } + } + if (Symbol.dispose) IntoUnderlyingByteSource.prototype[Symbol.dispose] = IntoUnderlyingByteSource.prototype.free; + __exports.IntoUnderlyingByteSource = IntoUnderlyingByteSource; + + class IntoUnderlyingSink { + __destroy_into_raw() { + const ptr = this.__wbg_ptr; + this.__wbg_ptr = 0; + IntoUnderlyingSinkFinalization.unregister(this); + return ptr; + } + free() { + const ptr = this.__destroy_into_raw(); + wasm.__wbg_intounderlyingsink_free(ptr, 0); + } + /** + * @param {any} reason + * @returns {Promise} + */ + abort(reason) { + const ptr = this.__destroy_into_raw(); + const ret = wasm.intounderlyingsink_abort(ptr, reason); + return ret; + } + /** + * @returns {Promise} + */ + close() { + const ptr = this.__destroy_into_raw(); + const ret = wasm.intounderlyingsink_close(ptr); + return ret; + } + /** + * @param {any} chunk + * @returns {Promise} + */ + write(chunk) { + const ret = wasm.intounderlyingsink_write(this.__wbg_ptr, chunk); + return ret; + } + } + if (Symbol.dispose) IntoUnderlyingSink.prototype[Symbol.dispose] = IntoUnderlyingSink.prototype.free; + __exports.IntoUnderlyingSink = IntoUnderlyingSink; + + class IntoUnderlyingSource { + __destroy_into_raw() { + const ptr = this.__wbg_ptr; + this.__wbg_ptr = 0; + IntoUnderlyingSourceFinalization.unregister(this); + return ptr; + } + free() { + const ptr = this.__destroy_into_raw(); + wasm.__wbg_intounderlyingsource_free(ptr, 0); + } + /** + * @param {ReadableStreamDefaultController} controller + * @returns {Promise} + */ + pull(controller) { + const ret = wasm.intounderlyingsource_pull(this.__wbg_ptr, controller); + return ret; + } + cancel() { + const ptr = this.__destroy_into_raw(); + wasm.intounderlyingsource_cancel(ptr); + } + } + if (Symbol.dispose) IntoUnderlyingSource.prototype[Symbol.dispose] = IntoUnderlyingSource.prototype.free; + __exports.IntoUnderlyingSource = IntoUnderlyingSource; + + class JSOwner { + __destroy_into_raw() { + const ptr = this.__wbg_ptr; + this.__wbg_ptr = 0; + JSOwnerFinalization.unregister(this); + return ptr; + } + free() { + const ptr = this.__destroy_into_raw(); + wasm.__wbg_jsowner_free(ptr, 0); + } + } + if (Symbol.dispose) JSOwner.prototype[Symbol.dispose] = JSOwner.prototype.free; + __exports.JSOwner = JSOwner; + + const EXPECTED_RESPONSE_TYPES = new Set(['basic', 'cors', 'default']); + + async function __wbg_load(module, imports) { + if (typeof Response === 'function' && module instanceof Response) { + if (typeof WebAssembly.instantiateStreaming === 'function') { + try { + return await WebAssembly.instantiateStreaming(module, imports); + } catch (e) { + const validResponse = module.ok && EXPECTED_RESPONSE_TYPES.has(module.type); + + if (validResponse && module.headers.get('Content-Type') !== 'application/wasm') { + console.warn("`WebAssembly.instantiateStreaming` failed because your server does not serve Wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:\n", e); + + } else { + throw e; + } + } + } + + const bytes = await module.arrayBuffer(); + return await WebAssembly.instantiate(bytes, imports); + } else { + const instance = await WebAssembly.instantiate(module, imports); + + if (instance instanceof WebAssembly.Instance) { + return { instance, module }; + } else { + return instance; + } + } + } + + function __wbg_get_imports() { + const imports = {}; + imports.wbg = {}; + imports.wbg.__wbg___wbindgen_is_function_8d400b8b1af978cd = function(arg0) { + const ret = typeof(arg0) === 'function'; + return ret; + }; + imports.wbg.__wbg___wbindgen_is_object_ce774f3490692386 = function(arg0) { + const val = arg0; + const ret = typeof(val) === 'object' && val !== null; + return ret; + }; + imports.wbg.__wbg___wbindgen_is_string_704ef9c8fc131030 = function(arg0) { + const ret = typeof(arg0) === 'string'; + return ret; + }; + imports.wbg.__wbg___wbindgen_is_undefined_f6b95eab589e0269 = function(arg0) { + const ret = arg0 === undefined; + return ret; + }; + imports.wbg.__wbg___wbindgen_throw_dd24417ed36fc46e = function(arg0, arg1) { + throw new Error(getStringFromWasm0(arg0, arg1)); + }; + imports.wbg.__wbg__wbg_cb_unref_87dfb5aaa0cbcea7 = function(arg0) { + arg0._wbg_cb_unref(); + }; + imports.wbg.__wbg_buffer_6cb2fecb1f253d71 = function(arg0) { + const ret = arg0.buffer; + return ret; + }; + imports.wbg.__wbg_byobRequest_f8e3517f5f8ad284 = function(arg0) { + const ret = arg0.byobRequest; + return isLikeNone(ret) ? 0 : addToExternrefTable0(ret); + }; + imports.wbg.__wbg_byteLength_faa9938885bdeee6 = function(arg0) { + const ret = arg0.byteLength; + return ret; + }; + imports.wbg.__wbg_byteOffset_3868b6a19ba01dea = function(arg0) { + const ret = arg0.byteOffset; + return ret; + }; + imports.wbg.__wbg_call_3020136f7a2d6e44 = function() { return handleError(function (arg0, arg1, arg2) { + const ret = arg0.call(arg1, arg2); + return ret; + }, arguments) }; + imports.wbg.__wbg_call_abb4ff46ce38be40 = function() { return handleError(function (arg0, arg1) { + const ret = arg0.call(arg1); + return ret; + }, arguments) }; + imports.wbg.__wbg_close_0af5661bf3d335f2 = function() { return handleError(function (arg0) { + arg0.close(); + }, arguments) }; + imports.wbg.__wbg_close_0b472ca2d13f54f7 = function(arg0) { + arg0.close(); + }; + imports.wbg.__wbg_close_3ec111e7b23d94d8 = function() { return handleError(function (arg0) { + arg0.close(); + }, arguments) }; + imports.wbg.__wbg_crypto_574e78ad8b13b65f = function(arg0) { + const ret = arg0.crypto; + return ret; + }; + imports.wbg.__wbg_data_8bf4ae669a78a688 = function(arg0) { + const ret = arg0.data; + return ret; + }; + imports.wbg.__wbg_enqueue_a7e6b1ee87963aad = function() { return handleError(function (arg0, arg1) { + arg0.enqueue(arg1); + }, arguments) }; + imports.wbg.__wbg_error_7534b8e9a36f1ab4 = function(arg0, arg1) { + let deferred0_0; + let deferred0_1; + try { + deferred0_0 = arg0; + deferred0_1 = arg1; + console.error(getStringFromWasm0(arg0, arg1)); + } finally { + wasm.__wbindgen_free(deferred0_0, deferred0_1, 1); + } + }; + imports.wbg.__wbg_getRandomValues_b8f5dbd5f3995a9e = function() { return handleError(function (arg0, arg1) { + arg0.getRandomValues(arg1); + }, arguments) }; + imports.wbg.__wbg_instanceof_Window_b5cf7783caa68180 = function(arg0) { + let result; + try { + result = arg0 instanceof Window; + } catch (_) { + result = false; + } + const ret = result; + return ret; + }; + imports.wbg.__wbg_length_22ac23eaec9d8053 = function(arg0) { + const ret = arg0.length; + return ret; + }; + imports.wbg.__wbg_msCrypto_a61aeb35a24c1329 = function(arg0) { + const ret = arg0.msCrypto; + return ret; + }; + imports.wbg.__wbg_new_8a6f238a6ece86ea = function() { + const ret = new Error(); + return ret; + }; + imports.wbg.__wbg_new_df1173567d5ff028 = function(arg0, arg1) { + const ret = new Error(getStringFromWasm0(arg0, arg1)); + return ret; + }; + imports.wbg.__wbg_new_ff12d2b041fb48f1 = function(arg0, arg1) { + try { + var state0 = {a: arg0, b: arg1}; + var cb0 = (arg0, arg1) => { + const a = state0.a; + state0.a = 0; + try { + return wasm_bindgen_1add006a0ed82fd3___convert__closures_____invoke___wasm_bindgen_1add006a0ed82fd3___JsValue__wasm_bindgen_1add006a0ed82fd3___JsValue_____(a, state0.b, arg0, arg1); + } finally { + state0.a = a; + } + }; + const ret = new Promise(cb0); + return ret; + } finally { + state0.a = state0.b = 0; + } + }; + imports.wbg.__wbg_new_from_slice_f9c22b9153b26992 = function(arg0, arg1) { + const ret = new Uint8Array(getArrayU8FromWasm0(arg0, arg1)); + return ret; + }; + imports.wbg.__wbg_new_no_args_cb138f77cf6151ee = function(arg0, arg1) { + const ret = new Function(getStringFromWasm0(arg0, arg1)); + return ret; + }; + imports.wbg.__wbg_new_with_byte_offset_and_length_d85c3da1fd8df149 = function(arg0, arg1, arg2) { + const ret = new Uint8Array(arg0, arg1 >>> 0, arg2 >>> 0); + return ret; + }; + imports.wbg.__wbg_new_with_length_aa5eaf41d35235e5 = function(arg0) { + const ret = new Uint8Array(arg0 >>> 0); + return ret; + }; + imports.wbg.__wbg_node_905d3e251edff8a2 = function(arg0) { + const ret = arg0.node; + return ret; + }; + imports.wbg.__wbg_now_8a87c5466cc7d560 = function() { + const ret = Date.now(); + return ret; + }; + imports.wbg.__wbg_now_8cf15d6e317793e1 = function(arg0) { + const ret = arg0.now(); + return ret; + }; + imports.wbg.__wbg_performance_c77a440eff2efd9b = function(arg0) { + const ret = arg0.performance; + return isLikeNone(ret) ? 0 : addToExternrefTable0(ret); + }; + imports.wbg.__wbg_postMessage_7243f814e0cfb266 = function() { return handleError(function (arg0, arg1) { + arg0.postMessage(arg1); + }, arguments) }; + imports.wbg.__wbg_process_dc0fbacc7c1c06f7 = function(arg0) { + const ret = arg0.process; + return ret; + }; + imports.wbg.__wbg_prototypesetcall_dfe9b766cdc1f1fd = function(arg0, arg1, arg2) { + Uint8Array.prototype.set.call(getArrayU8FromWasm0(arg0, arg1), arg2); + }; + imports.wbg.__wbg_queueMicrotask_9b549dfce8865860 = function(arg0) { + const ret = arg0.queueMicrotask; + return ret; + }; + imports.wbg.__wbg_queueMicrotask_fca69f5bfad613a5 = function(arg0) { + queueMicrotask(arg0); + }; + imports.wbg.__wbg_randomFillSync_ac0988aba3254290 = function() { return handleError(function (arg0, arg1) { + arg0.randomFillSync(arg1); + }, arguments) }; + imports.wbg.__wbg_require_60cc747a6bc5215a = function() { return handleError(function () { + const ret = module.require; + return ret; + }, arguments) }; + imports.wbg.__wbg_resolve_fd5bfbaa4ce36e1e = function(arg0) { + const ret = Promise.resolve(arg0); + return ret; + }; + imports.wbg.__wbg_respond_9f7fc54636c4a3af = function() { return handleError(function (arg0, arg1) { + arg0.respond(arg1 >>> 0); + }, arguments) }; + imports.wbg.__wbg_set_169e13b608078b7b = function(arg0, arg1, arg2) { + arg0.set(getArrayU8FromWasm0(arg1, arg2)); + }; + imports.wbg.__wbg_set_onmessage_5fe29d0fb54cb575 = function(arg0, arg1) { + arg0.onmessage = arg1; + }; + imports.wbg.__wbg_stack_0ed75d68575b0f3c = function(arg0, arg1) { + const ret = arg1.stack; + const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len1 = WASM_VECTOR_LEN; + getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true); + getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true); + }; + imports.wbg.__wbg_static_accessor_GLOBAL_769e6b65d6557335 = function() { + const ret = typeof global === 'undefined' ? null : global; + return isLikeNone(ret) ? 0 : addToExternrefTable0(ret); + }; + imports.wbg.__wbg_static_accessor_GLOBAL_THIS_60cf02db4de8e1c1 = function() { + const ret = typeof globalThis === 'undefined' ? null : globalThis; + return isLikeNone(ret) ? 0 : addToExternrefTable0(ret); + }; + imports.wbg.__wbg_static_accessor_SELF_08f5a74c69739274 = function() { + const ret = typeof self === 'undefined' ? null : self; + return isLikeNone(ret) ? 0 : addToExternrefTable0(ret); + }; + imports.wbg.__wbg_static_accessor_WINDOW_a8924b26aa92d024 = function() { + const ret = typeof window === 'undefined' ? null : window; + return isLikeNone(ret) ? 0 : addToExternrefTable0(ret); + }; + imports.wbg.__wbg_subarray_845f2f5bce7d061a = function(arg0, arg1, arg2) { + const ret = arg0.subarray(arg1 >>> 0, arg2 >>> 0); + return ret; + }; + imports.wbg.__wbg_then_4f95312d68691235 = function(arg0, arg1) { + const ret = arg0.then(arg1); + return ret; + }; + imports.wbg.__wbg_versions_c01dfd4722a88165 = function(arg0) { + const ret = arg0.versions; + return ret; + }; + imports.wbg.__wbg_view_788aaf149deefd2f = function(arg0) { + const ret = arg0.view; + return isLikeNone(ret) ? 0 : addToExternrefTable0(ret); + }; + imports.wbg.__wbindgen_cast_1201d4f342f74eb8 = function(arg0, arg1) { + // Cast intrinsic for `Closure(Closure { dtor_idx: 909, function: Function { arguments: [Externref], shim_idx: 910, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`. + 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_____); + return ret; + }; + imports.wbg.__wbindgen_cast_2241b6af4c4b2941 = function(arg0, arg1) { + // Cast intrinsic for `Ref(String) -> Externref`. + const ret = getStringFromWasm0(arg0, arg1); + return ret; + }; + imports.wbg.__wbindgen_cast_cb9088102bce6b30 = function(arg0, arg1) { + // Cast intrinsic for `Ref(Slice(U8)) -> NamedExternref("Uint8Array")`. + const ret = getArrayU8FromWasm0(arg0, arg1); + return ret; + }; + imports.wbg.__wbindgen_cast_df669a3c3b68a548 = function(arg0, arg1) { + // 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`. + 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_____); + return ret; + }; + imports.wbg.__wbindgen_init_externref_table = function() { + const table = wasm.__wbindgen_externrefs; + const offset = table.grow(4); + table.set(0, undefined); + table.set(offset + 0, undefined); + table.set(offset + 1, null); + table.set(offset + 2, true); + table.set(offset + 3, false); + }; + + return imports; + } + + function __wbg_finalize_init(instance, module) { + wasm = instance.exports; + __wbg_init.__wbindgen_wasm_module = module; + cachedDataViewMemory0 = null; + cachedUint8ArrayMemory0 = null; + + + wasm.__wbindgen_start(); + return wasm; + } + + function initSync(module) { + if (wasm !== undefined) return wasm; + + + if (typeof module !== 'undefined') { + if (Object.getPrototypeOf(module) === Object.prototype) { + ({module} = module) + } else { + console.warn('using deprecated parameters for `initSync()`; pass a single object instead') + } + } + + const imports = __wbg_get_imports(); + if (!(module instanceof WebAssembly.Module)) { + module = new WebAssembly.Module(module); + } + const instance = new WebAssembly.Instance(module, imports); + return __wbg_finalize_init(instance, module); + } + + async function __wbg_init(module_or_path) { + if (wasm !== undefined) return wasm; + + + if (typeof module_or_path !== 'undefined') { + if (Object.getPrototypeOf(module_or_path) === Object.prototype) { + ({module_or_path} = module_or_path) + } else { + console.warn('using deprecated parameters for the initialization function; pass a single object instead') + } + } + + if (typeof module_or_path === 'undefined' && typeof script_src !== 'undefined') { + module_or_path = script_src.replace(/\.js$/, '_bg.wasm'); + } + const imports = __wbg_get_imports(); + + if (typeof module_or_path === 'string' || (typeof Request === 'function' && module_or_path instanceof Request) || (typeof URL === 'function' && module_or_path instanceof URL)) { + module_or_path = fetch(module_or_path); + } + + const { instance, module } = await __wbg_load(await module_or_path, imports); + + return __wbg_finalize_init(instance, module); + } + + wasm_bindgen = Object.assign(__wbg_init, { initSync }, __exports); +})(); diff --git a/crates/weaver-app/public/embed_worker.js b/crates/weaver-app/public/embed_worker.js new file mode 100644 index 0000000..f58230e --- /dev/null +++ b/crates/weaver-app/public/embed_worker.js @@ -0,0 +1,931 @@ +let wasm_bindgen; +(function() { + const __exports = {}; + let script_src; + if (typeof document !== 'undefined' && document.currentScript !== null) { + script_src = new URL(document.currentScript.src, location.href).toString(); + } + let wasm = undefined; + + function addToExternrefTable0(obj) { + const idx = wasm.__externref_table_alloc(); + wasm.__wbindgen_externrefs.set(idx, obj); + return idx; + } + + const CLOSURE_DTORS = (typeof FinalizationRegistry === 'undefined') + ? { register: () => {}, unregister: () => {} } + : new FinalizationRegistry(state => state.dtor(state.a, state.b)); + + function debugString(val) { + // primitive types + const type = typeof val; + if (type == 'number' || type == 'boolean' || val == null) { + return `${val}`; + } + if (type == 'string') { + return `"${val}"`; + } + if (type == 'symbol') { + const description = val.description; + if (description == null) { + return 'Symbol'; + } else { + return `Symbol(${description})`; + } + } + if (type == 'function') { + const name = val.name; + if (typeof name == 'string' && name.length > 0) { + return `Function(${name})`; + } else { + return 'Function'; + } + } + // objects + if (Array.isArray(val)) { + const length = val.length; + let debug = '['; + if (length > 0) { + debug += debugString(val[0]); + } + for(let i = 1; i < length; i++) { + debug += ', ' + debugString(val[i]); + } + debug += ']'; + return debug; + } + // Test for built-in + const builtInMatches = /\[object ([^\]]+)\]/.exec(toString.call(val)); + let className; + if (builtInMatches && builtInMatches.length > 1) { + className = builtInMatches[1]; + } else { + // Failed to match the standard '[object ClassName]' + return toString.call(val); + } + if (className == 'Object') { + // we're a user defined class or Object + // JSON.stringify avoids problems with cycles, and is generally much + // easier than looping through ownProperties of `val`. + try { + return 'Object(' + JSON.stringify(val) + ')'; + } catch (_) { + return 'Object'; + } + } + // errors + if (val instanceof Error) { + return `${val.name}: ${val.message}\n${val.stack}`; + } + // TODO we could test for more things here, like `Set`s and `Map`s. + return className; + } + + function getArrayU8FromWasm0(ptr, len) { + ptr = ptr >>> 0; + return getUint8ArrayMemory0().subarray(ptr / 1, ptr / 1 + len); + } + + let cachedDataViewMemory0 = null; + function getDataViewMemory0() { + if (cachedDataViewMemory0 === null || cachedDataViewMemory0.buffer.detached === true || (cachedDataViewMemory0.buffer.detached === undefined && cachedDataViewMemory0.buffer !== wasm.memory.buffer)) { + cachedDataViewMemory0 = new DataView(wasm.memory.buffer); + } + return cachedDataViewMemory0; + } + + function getStringFromWasm0(ptr, len) { + ptr = ptr >>> 0; + return decodeText(ptr, len); + } + + let cachedUint8ArrayMemory0 = null; + function getUint8ArrayMemory0() { + if (cachedUint8ArrayMemory0 === null || cachedUint8ArrayMemory0.byteLength === 0) { + cachedUint8ArrayMemory0 = new Uint8Array(wasm.memory.buffer); + } + return cachedUint8ArrayMemory0; + } + + function handleError(f, args) { + try { + return f.apply(this, args); + } catch (e) { + const idx = addToExternrefTable0(e); + wasm.__wbindgen_exn_store(idx); + } + } + + function isLikeNone(x) { + return x === undefined || x === null; + } + + function makeClosure(arg0, arg1, dtor, f) { + const state = { a: arg0, b: arg1, cnt: 1, dtor }; + const real = (...args) => { + + // First up with a closure we increment the internal reference + // count. This ensures that the Rust closure environment won't + // be deallocated while we're invoking it. + state.cnt++; + try { + return f(state.a, state.b, ...args); + } finally { + real._wbg_cb_unref(); + } + }; + real._wbg_cb_unref = () => { + if (--state.cnt === 0) { + state.dtor(state.a, state.b); + state.a = 0; + CLOSURE_DTORS.unregister(state); + } + }; + CLOSURE_DTORS.register(real, state, state); + return real; + } + + function makeMutClosure(arg0, arg1, dtor, f) { + const state = { a: arg0, b: arg1, cnt: 1, dtor }; + const real = (...args) => { + + // First up with a closure we increment the internal reference + // count. This ensures that the Rust closure environment won't + // be deallocated while we're invoking it. + state.cnt++; + const a = state.a; + state.a = 0; + try { + return f(a, state.b, ...args); + } finally { + state.a = a; + real._wbg_cb_unref(); + } + }; + real._wbg_cb_unref = () => { + if (--state.cnt === 0) { + state.dtor(state.a, state.b); + state.a = 0; + CLOSURE_DTORS.unregister(state); + } + }; + CLOSURE_DTORS.register(real, state, state); + return real; + } + + function passStringToWasm0(arg, malloc, realloc) { + if (realloc === undefined) { + const buf = cachedTextEncoder.encode(arg); + const ptr = malloc(buf.length, 1) >>> 0; + getUint8ArrayMemory0().subarray(ptr, ptr + buf.length).set(buf); + WASM_VECTOR_LEN = buf.length; + return ptr; + } + + let len = arg.length; + let ptr = malloc(len, 1) >>> 0; + + const mem = getUint8ArrayMemory0(); + + let offset = 0; + + for (; offset < len; offset++) { + const code = arg.charCodeAt(offset); + if (code > 0x7F) break; + mem[ptr + offset] = code; + } + if (offset !== len) { + if (offset !== 0) { + arg = arg.slice(offset); + } + ptr = realloc(ptr, len, len = offset + arg.length * 3, 1) >>> 0; + const view = getUint8ArrayMemory0().subarray(ptr + offset, ptr + len); + const ret = cachedTextEncoder.encodeInto(arg, view); + + offset += ret.written; + ptr = realloc(ptr, len, offset, 1) >>> 0; + } + + WASM_VECTOR_LEN = offset; + return ptr; + } + + let cachedTextDecoder = new TextDecoder('utf-8', { ignoreBOM: true, fatal: true }); + cachedTextDecoder.decode(); + function decodeText(ptr, len) { + return cachedTextDecoder.decode(getUint8ArrayMemory0().subarray(ptr, ptr + len)); + } + + const cachedTextEncoder = new TextEncoder(); + + if (!('encodeInto' in cachedTextEncoder)) { + cachedTextEncoder.encodeInto = function (arg, view) { + const buf = cachedTextEncoder.encode(arg); + view.set(buf); + return { + read: arg.length, + written: buf.length + }; + } + } + + let WASM_VECTOR_LEN = 0; + + function wasm_bindgen_1add006a0ed82fd3___convert__closures_____invoke______(arg0, arg1) { + wasm.wasm_bindgen_1add006a0ed82fd3___convert__closures_____invoke______(arg0, arg1); + } + + function wasm_bindgen_1add006a0ed82fd3___convert__closures_____invoke___web_sys_81daa69175395078___features__gen_MessageEvent__MessageEvent_____(arg0, arg1, arg2) { + wasm.wasm_bindgen_1add006a0ed82fd3___convert__closures_____invoke___web_sys_81daa69175395078___features__gen_MessageEvent__MessageEvent_____(arg0, arg1, arg2); + } + + function wasm_bindgen_1add006a0ed82fd3___convert__closures_____invoke_______1_(arg0, arg1) { + wasm.wasm_bindgen_1add006a0ed82fd3___convert__closures_____invoke_______1_(arg0, arg1); + } + + function wasm_bindgen_1add006a0ed82fd3___convert__closures_____invoke___wasm_bindgen_1add006a0ed82fd3___JsValue_____(arg0, arg1, arg2) { + wasm.wasm_bindgen_1add006a0ed82fd3___convert__closures_____invoke___wasm_bindgen_1add006a0ed82fd3___JsValue_____(arg0, arg1, arg2); + } + + function wasm_bindgen_1add006a0ed82fd3___convert__closures_____invoke___wasm_bindgen_1add006a0ed82fd3___JsValue__wasm_bindgen_1add006a0ed82fd3___JsValue_____(arg0, arg1, arg2, arg3) { + wasm.wasm_bindgen_1add006a0ed82fd3___convert__closures_____invoke___wasm_bindgen_1add006a0ed82fd3___JsValue__wasm_bindgen_1add006a0ed82fd3___JsValue_____(arg0, arg1, arg2, arg3); + } + + const __wbindgen_enum_ReadableStreamType = ["bytes"]; + + const __wbindgen_enum_RequestCache = ["default", "no-store", "reload", "no-cache", "force-cache", "only-if-cached"]; + + const __wbindgen_enum_RequestCredentials = ["omit", "same-origin", "include"]; + + const __wbindgen_enum_RequestMode = ["same-origin", "no-cors", "cors", "navigate"]; + + const IntoUnderlyingByteSourceFinalization = (typeof FinalizationRegistry === 'undefined') + ? { register: () => {}, unregister: () => {} } + : new FinalizationRegistry(ptr => wasm.__wbg_intounderlyingbytesource_free(ptr >>> 0, 1)); + + const IntoUnderlyingSinkFinalization = (typeof FinalizationRegistry === 'undefined') + ? { register: () => {}, unregister: () => {} } + : new FinalizationRegistry(ptr => wasm.__wbg_intounderlyingsink_free(ptr >>> 0, 1)); + + const IntoUnderlyingSourceFinalization = (typeof FinalizationRegistry === 'undefined') + ? { register: () => {}, unregister: () => {} } + : new FinalizationRegistry(ptr => wasm.__wbg_intounderlyingsource_free(ptr >>> 0, 1)); + + const JSOwnerFinalization = (typeof FinalizationRegistry === 'undefined') + ? { register: () => {}, unregister: () => {} } + : new FinalizationRegistry(ptr => wasm.__wbg_jsowner_free(ptr >>> 0, 1)); + + class IntoUnderlyingByteSource { + __destroy_into_raw() { + const ptr = this.__wbg_ptr; + this.__wbg_ptr = 0; + IntoUnderlyingByteSourceFinalization.unregister(this); + return ptr; + } + free() { + const ptr = this.__destroy_into_raw(); + wasm.__wbg_intounderlyingbytesource_free(ptr, 0); + } + /** + * @returns {number} + */ + get autoAllocateChunkSize() { + const ret = wasm.intounderlyingbytesource_autoAllocateChunkSize(this.__wbg_ptr); + return ret >>> 0; + } + /** + * @param {ReadableByteStreamController} controller + * @returns {Promise} + */ + pull(controller) { + const ret = wasm.intounderlyingbytesource_pull(this.__wbg_ptr, controller); + return ret; + } + /** + * @param {ReadableByteStreamController} controller + */ + start(controller) { + wasm.intounderlyingbytesource_start(this.__wbg_ptr, controller); + } + /** + * @returns {ReadableStreamType} + */ + get type() { + const ret = wasm.intounderlyingbytesource_type(this.__wbg_ptr); + return __wbindgen_enum_ReadableStreamType[ret]; + } + cancel() { + const ptr = this.__destroy_into_raw(); + wasm.intounderlyingbytesource_cancel(ptr); + } + } + if (Symbol.dispose) IntoUnderlyingByteSource.prototype[Symbol.dispose] = IntoUnderlyingByteSource.prototype.free; + __exports.IntoUnderlyingByteSource = IntoUnderlyingByteSource; + + class IntoUnderlyingSink { + __destroy_into_raw() { + const ptr = this.__wbg_ptr; + this.__wbg_ptr = 0; + IntoUnderlyingSinkFinalization.unregister(this); + return ptr; + } + free() { + const ptr = this.__destroy_into_raw(); + wasm.__wbg_intounderlyingsink_free(ptr, 0); + } + /** + * @param {any} reason + * @returns {Promise} + */ + abort(reason) { + const ptr = this.__destroy_into_raw(); + const ret = wasm.intounderlyingsink_abort(ptr, reason); + return ret; + } + /** + * @returns {Promise} + */ + close() { + const ptr = this.__destroy_into_raw(); + const ret = wasm.intounderlyingsink_close(ptr); + return ret; + } + /** + * @param {any} chunk + * @returns {Promise} + */ + write(chunk) { + const ret = wasm.intounderlyingsink_write(this.__wbg_ptr, chunk); + return ret; + } + } + if (Symbol.dispose) IntoUnderlyingSink.prototype[Symbol.dispose] = IntoUnderlyingSink.prototype.free; + __exports.IntoUnderlyingSink = IntoUnderlyingSink; + + class IntoUnderlyingSource { + __destroy_into_raw() { + const ptr = this.__wbg_ptr; + this.__wbg_ptr = 0; + IntoUnderlyingSourceFinalization.unregister(this); + return ptr; + } + free() { + const ptr = this.__destroy_into_raw(); + wasm.__wbg_intounderlyingsource_free(ptr, 0); + } + /** + * @param {ReadableStreamDefaultController} controller + * @returns {Promise} + */ + pull(controller) { + const ret = wasm.intounderlyingsource_pull(this.__wbg_ptr, controller); + return ret; + } + cancel() { + const ptr = this.__destroy_into_raw(); + wasm.intounderlyingsource_cancel(ptr); + } + } + if (Symbol.dispose) IntoUnderlyingSource.prototype[Symbol.dispose] = IntoUnderlyingSource.prototype.free; + __exports.IntoUnderlyingSource = IntoUnderlyingSource; + + class JSOwner { + __destroy_into_raw() { + const ptr = this.__wbg_ptr; + this.__wbg_ptr = 0; + JSOwnerFinalization.unregister(this); + return ptr; + } + free() { + const ptr = this.__destroy_into_raw(); + wasm.__wbg_jsowner_free(ptr, 0); + } + } + if (Symbol.dispose) JSOwner.prototype[Symbol.dispose] = JSOwner.prototype.free; + __exports.JSOwner = JSOwner; + + const EXPECTED_RESPONSE_TYPES = new Set(['basic', 'cors', 'default']); + + async function __wbg_load(module, imports) { + if (typeof Response === 'function' && module instanceof Response) { + if (typeof WebAssembly.instantiateStreaming === 'function') { + try { + return await WebAssembly.instantiateStreaming(module, imports); + } catch (e) { + const validResponse = module.ok && EXPECTED_RESPONSE_TYPES.has(module.type); + + if (validResponse && module.headers.get('Content-Type') !== 'application/wasm') { + console.warn("`WebAssembly.instantiateStreaming` failed because your server does not serve Wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:\n", e); + + } else { + throw e; + } + } + } + + const bytes = await module.arrayBuffer(); + return await WebAssembly.instantiate(bytes, imports); + } else { + const instance = await WebAssembly.instantiate(module, imports); + + if (instance instanceof WebAssembly.Instance) { + return { instance, module }; + } else { + return instance; + } + } + } + + function __wbg_get_imports() { + const imports = {}; + imports.wbg = {}; + imports.wbg.__wbg___wbindgen_debug_string_adfb662ae34724b6 = function(arg0, arg1) { + const ret = debugString(arg1); + const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len1 = WASM_VECTOR_LEN; + getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true); + getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true); + }; + imports.wbg.__wbg___wbindgen_is_function_8d400b8b1af978cd = function(arg0) { + const ret = typeof(arg0) === 'function'; + return ret; + }; + imports.wbg.__wbg___wbindgen_is_object_ce774f3490692386 = function(arg0) { + const val = arg0; + const ret = typeof(val) === 'object' && val !== null; + return ret; + }; + imports.wbg.__wbg___wbindgen_is_undefined_f6b95eab589e0269 = function(arg0) { + const ret = arg0 === undefined; + return ret; + }; + imports.wbg.__wbg___wbindgen_string_get_a2a31e16edf96e42 = function(arg0, arg1) { + const obj = arg1; + const ret = typeof(obj) === 'string' ? obj : undefined; + var ptr1 = isLikeNone(ret) ? 0 : passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + var len1 = WASM_VECTOR_LEN; + getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true); + getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true); + }; + imports.wbg.__wbg___wbindgen_throw_dd24417ed36fc46e = function(arg0, arg1) { + throw new Error(getStringFromWasm0(arg0, arg1)); + }; + imports.wbg.__wbg__wbg_cb_unref_87dfb5aaa0cbcea7 = function(arg0) { + arg0._wbg_cb_unref(); + }; + imports.wbg.__wbg_abort_07646c894ebbf2bd = function(arg0) { + arg0.abort(); + }; + imports.wbg.__wbg_abort_399ecbcfd6ef3c8e = function(arg0, arg1) { + arg0.abort(arg1); + }; + imports.wbg.__wbg_append_0a085a855907169f = function() { return handleError(function (arg0, arg1, arg2, arg3) { + arg0.append(getStringFromWasm0(arg1, arg2), arg3); + }, arguments) }; + imports.wbg.__wbg_append_90fbf76fbc23e35f = function() { return handleError(function (arg0, arg1, arg2, arg3, arg4) { + arg0.append(getStringFromWasm0(arg1, arg2), getStringFromWasm0(arg3, arg4)); + }, arguments) }; + imports.wbg.__wbg_append_c5cbdf46455cc776 = function() { return handleError(function (arg0, arg1, arg2, arg3, arg4) { + arg0.append(getStringFromWasm0(arg1, arg2), getStringFromWasm0(arg3, arg4)); + }, arguments) }; + imports.wbg.__wbg_append_ef47c723098c3768 = function() { return handleError(function (arg0, arg1, arg2, arg3, arg4, arg5) { + arg0.append(getStringFromWasm0(arg1, arg2), arg3, getStringFromWasm0(arg4, arg5)); + }, arguments) }; + imports.wbg.__wbg_arrayBuffer_c04af4fce566092d = function() { return handleError(function (arg0) { + const ret = arg0.arrayBuffer(); + return ret; + }, arguments) }; + imports.wbg.__wbg_buffer_6cb2fecb1f253d71 = function(arg0) { + const ret = arg0.buffer; + return ret; + }; + imports.wbg.__wbg_byobRequest_f8e3517f5f8ad284 = function(arg0) { + const ret = arg0.byobRequest; + return isLikeNone(ret) ? 0 : addToExternrefTable0(ret); + }; + imports.wbg.__wbg_byteLength_faa9938885bdeee6 = function(arg0) { + const ret = arg0.byteLength; + return ret; + }; + imports.wbg.__wbg_byteOffset_3868b6a19ba01dea = function(arg0) { + const ret = arg0.byteOffset; + return ret; + }; + imports.wbg.__wbg_call_3020136f7a2d6e44 = function() { return handleError(function (arg0, arg1, arg2) { + const ret = arg0.call(arg1, arg2); + return ret; + }, arguments) }; + imports.wbg.__wbg_call_abb4ff46ce38be40 = function() { return handleError(function (arg0, arg1) { + const ret = arg0.call(arg1); + return ret; + }, arguments) }; + imports.wbg.__wbg_clearTimeout_15dfc3d1dcb635c6 = function() { return handleError(function (arg0, arg1) { + arg0.clearTimeout(arg1); + }, arguments) }; + imports.wbg.__wbg_clearTimeout_7a42b49784aea641 = function(arg0) { + const ret = clearTimeout(arg0); + return ret; + }; + imports.wbg.__wbg_close_0af5661bf3d335f2 = function() { return handleError(function (arg0) { + arg0.close(); + }, arguments) }; + imports.wbg.__wbg_close_0b472ca2d13f54f7 = function(arg0) { + arg0.close(); + }; + imports.wbg.__wbg_close_3ec111e7b23d94d8 = function() { return handleError(function (arg0) { + arg0.close(); + }, arguments) }; + imports.wbg.__wbg_data_8bf4ae669a78a688 = function(arg0) { + const ret = arg0.data; + return ret; + }; + imports.wbg.__wbg_done_62ea16af4ce34b24 = function(arg0) { + const ret = arg0.done; + return ret; + }; + imports.wbg.__wbg_enqueue_a7e6b1ee87963aad = function() { return handleError(function (arg0, arg1) { + arg0.enqueue(arg1); + }, arguments) }; + imports.wbg.__wbg_error_7534b8e9a36f1ab4 = function(arg0, arg1) { + let deferred0_0; + let deferred0_1; + try { + deferred0_0 = arg0; + deferred0_1 = arg1; + console.error(getStringFromWasm0(arg0, arg1)); + } finally { + wasm.__wbindgen_free(deferred0_0, deferred0_1, 1); + } + }; + imports.wbg.__wbg_fetch_74a3e84ebd2c9a0e = function(arg0) { + const ret = fetch(arg0); + return ret; + }; + imports.wbg.__wbg_fetch_90447c28cc0b095e = function(arg0, arg1) { + const ret = arg0.fetch(arg1); + return ret; + }; + imports.wbg.__wbg_getTime_ad1e9878a735af08 = function(arg0) { + const ret = arg0.getTime(); + return ret; + }; + imports.wbg.__wbg_get_af9dab7e9603ea93 = function() { return handleError(function (arg0, arg1) { + const ret = Reflect.get(arg0, arg1); + return ret; + }, arguments) }; + imports.wbg.__wbg_has_0e670569d65d3a45 = function() { return handleError(function (arg0, arg1) { + const ret = Reflect.has(arg0, arg1); + return ret; + }, arguments) }; + imports.wbg.__wbg_headers_654c30e1bcccc552 = function(arg0) { + const ret = arg0.headers; + return ret; + }; + imports.wbg.__wbg_instanceof_Response_cd74d1c2ac92cb0b = function(arg0) { + let result; + try { + result = arg0 instanceof Response; + } catch (_) { + result = false; + } + const ret = result; + return ret; + }; + imports.wbg.__wbg_instanceof_Window_b5cf7783caa68180 = function(arg0) { + let result; + try { + result = arg0 instanceof Window; + } catch (_) { + result = false; + } + const ret = result; + return ret; + }; + imports.wbg.__wbg_iterator_27b7c8b35ab3e86b = function() { + const ret = Symbol.iterator; + return ret; + }; + imports.wbg.__wbg_length_22ac23eaec9d8053 = function(arg0) { + const ret = arg0.length; + return ret; + }; + imports.wbg.__wbg_new_0_23cedd11d9b40c9d = function() { + const ret = new Date(); + return ret; + }; + imports.wbg.__wbg_new_1ba21ce319a06297 = function() { + const ret = new Object(); + return ret; + }; + imports.wbg.__wbg_new_25f239778d6112b9 = function() { + const ret = new Array(); + return ret; + }; + imports.wbg.__wbg_new_3c79b3bb1b32b7d3 = function() { return handleError(function () { + const ret = new Headers(); + return ret; + }, arguments) }; + imports.wbg.__wbg_new_6421f6084cc5bc5a = function(arg0) { + const ret = new Uint8Array(arg0); + return ret; + }; + imports.wbg.__wbg_new_881a222c65f168fc = function() { return handleError(function () { + const ret = new AbortController(); + return ret; + }, arguments) }; + imports.wbg.__wbg_new_8a6f238a6ece86ea = function() { + const ret = new Error(); + return ret; + }; + imports.wbg.__wbg_new_df1173567d5ff028 = function(arg0, arg1) { + const ret = new Error(getStringFromWasm0(arg0, arg1)); + return ret; + }; + imports.wbg.__wbg_new_e66247b901edc931 = function() { return handleError(function () { + const ret = new FormData(); + return ret; + }, arguments) }; + imports.wbg.__wbg_new_ff12d2b041fb48f1 = function(arg0, arg1) { + try { + var state0 = {a: arg0, b: arg1}; + var cb0 = (arg0, arg1) => { + const a = state0.a; + state0.a = 0; + try { + return wasm_bindgen_1add006a0ed82fd3___convert__closures_____invoke___wasm_bindgen_1add006a0ed82fd3___JsValue__wasm_bindgen_1add006a0ed82fd3___JsValue_____(a, state0.b, arg0, arg1); + } finally { + state0.a = a; + } + }; + const ret = new Promise(cb0); + return ret; + } finally { + state0.a = state0.b = 0; + } + }; + imports.wbg.__wbg_new_from_slice_f9c22b9153b26992 = function(arg0, arg1) { + const ret = new Uint8Array(getArrayU8FromWasm0(arg0, arg1)); + return ret; + }; + imports.wbg.__wbg_new_no_args_cb138f77cf6151ee = function(arg0, arg1) { + const ret = new Function(getStringFromWasm0(arg0, arg1)); + return ret; + }; + imports.wbg.__wbg_new_with_byte_offset_and_length_d85c3da1fd8df149 = function(arg0, arg1, arg2) { + const ret = new Uint8Array(arg0, arg1 >>> 0, arg2 >>> 0); + return ret; + }; + imports.wbg.__wbg_new_with_str_and_init_c5748f76f5108934 = function() { return handleError(function (arg0, arg1, arg2) { + const ret = new Request(getStringFromWasm0(arg0, arg1), arg2); + return ret; + }, arguments) }; + imports.wbg.__wbg_new_with_u8_array_sequence_and_options_d4def9ec0588c7ec = function() { return handleError(function (arg0, arg1) { + const ret = new Blob(arg0, arg1); + return ret; + }, arguments) }; + imports.wbg.__wbg_next_138a17bbf04e926c = function(arg0) { + const ret = arg0.next; + return ret; + }; + imports.wbg.__wbg_next_3cfe5c0fe2a4cc53 = function() { return handleError(function (arg0) { + const ret = arg0.next(); + return ret; + }, arguments) }; + imports.wbg.__wbg_now_2c95c9de01293173 = function(arg0) { + const ret = arg0.now(); + return ret; + }; + imports.wbg.__wbg_now_8cf15d6e317793e1 = function(arg0) { + const ret = arg0.now(); + return ret; + }; + imports.wbg.__wbg_performance_7a3ffd0b17f663ad = function(arg0) { + const ret = arg0.performance; + return ret; + }; + imports.wbg.__wbg_performance_c77a440eff2efd9b = function(arg0) { + const ret = arg0.performance; + return isLikeNone(ret) ? 0 : addToExternrefTable0(ret); + }; + imports.wbg.__wbg_postMessage_7243f814e0cfb266 = function() { return handleError(function (arg0, arg1) { + arg0.postMessage(arg1); + }, arguments) }; + imports.wbg.__wbg_prototypesetcall_dfe9b766cdc1f1fd = function(arg0, arg1, arg2) { + Uint8Array.prototype.set.call(getArrayU8FromWasm0(arg0, arg1), arg2); + }; + imports.wbg.__wbg_push_7d9be8f38fc13975 = function(arg0, arg1) { + const ret = arg0.push(arg1); + return ret; + }; + imports.wbg.__wbg_queueMicrotask_9b549dfce8865860 = function(arg0) { + const ret = arg0.queueMicrotask; + return ret; + }; + imports.wbg.__wbg_queueMicrotask_fca69f5bfad613a5 = function(arg0) { + queueMicrotask(arg0); + }; + imports.wbg.__wbg_resolve_fd5bfbaa4ce36e1e = function(arg0) { + const ret = Promise.resolve(arg0); + return ret; + }; + imports.wbg.__wbg_respond_9f7fc54636c4a3af = function() { return handleError(function (arg0, arg1) { + arg0.respond(arg1 >>> 0); + }, arguments) }; + imports.wbg.__wbg_setTimeout_4eb823e8b72fbe79 = function() { return handleError(function (arg0, arg1, arg2) { + const ret = arg0.setTimeout(arg1, arg2); + return ret; + }, arguments) }; + imports.wbg.__wbg_setTimeout_7bb3429662ab1e70 = function(arg0, arg1) { + const ret = setTimeout(arg0, arg1); + return ret; + }; + imports.wbg.__wbg_set_169e13b608078b7b = function(arg0, arg1, arg2) { + arg0.set(getArrayU8FromWasm0(arg1, arg2)); + }; + imports.wbg.__wbg_set_body_8e743242d6076a4f = function(arg0, arg1) { + arg0.body = arg1; + }; + imports.wbg.__wbg_set_cache_0e437c7c8e838b9b = function(arg0, arg1) { + arg0.cache = __wbindgen_enum_RequestCache[arg1]; + }; + imports.wbg.__wbg_set_credentials_55ae7c3c106fd5be = function(arg0, arg1) { + arg0.credentials = __wbindgen_enum_RequestCredentials[arg1]; + }; + imports.wbg.__wbg_set_headers_5671cf088e114d2b = function(arg0, arg1) { + arg0.headers = arg1; + }; + imports.wbg.__wbg_set_method_76c69e41b3570627 = function(arg0, arg1, arg2) { + arg0.method = getStringFromWasm0(arg1, arg2); + }; + imports.wbg.__wbg_set_mode_611016a6818fc690 = function(arg0, arg1) { + arg0.mode = __wbindgen_enum_RequestMode[arg1]; + }; + imports.wbg.__wbg_set_onmessage_5fe29d0fb54cb575 = function(arg0, arg1) { + arg0.onmessage = arg1; + }; + imports.wbg.__wbg_set_signal_e89be862d0091009 = function(arg0, arg1) { + arg0.signal = arg1; + }; + imports.wbg.__wbg_set_type_7ce650670a34c68f = function(arg0, arg1, arg2) { + arg0.type = getStringFromWasm0(arg1, arg2); + }; + imports.wbg.__wbg_signal_3c14fbdc89694b39 = function(arg0) { + const ret = arg0.signal; + return ret; + }; + imports.wbg.__wbg_stack_0ed75d68575b0f3c = function(arg0, arg1) { + const ret = arg1.stack; + const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len1 = WASM_VECTOR_LEN; + getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true); + getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true); + }; + imports.wbg.__wbg_static_accessor_GLOBAL_769e6b65d6557335 = function() { + const ret = typeof global === 'undefined' ? null : global; + return isLikeNone(ret) ? 0 : addToExternrefTable0(ret); + }; + imports.wbg.__wbg_static_accessor_GLOBAL_THIS_60cf02db4de8e1c1 = function() { + const ret = typeof globalThis === 'undefined' ? null : globalThis; + return isLikeNone(ret) ? 0 : addToExternrefTable0(ret); + }; + imports.wbg.__wbg_static_accessor_SELF_08f5a74c69739274 = function() { + const ret = typeof self === 'undefined' ? null : self; + return isLikeNone(ret) ? 0 : addToExternrefTable0(ret); + }; + imports.wbg.__wbg_static_accessor_WINDOW_a8924b26aa92d024 = function() { + const ret = typeof window === 'undefined' ? null : window; + return isLikeNone(ret) ? 0 : addToExternrefTable0(ret); + }; + imports.wbg.__wbg_status_9bfc680efca4bdfd = function(arg0) { + const ret = arg0.status; + return ret; + }; + imports.wbg.__wbg_stringify_655a6390e1f5eb6b = function() { return handleError(function (arg0) { + const ret = JSON.stringify(arg0); + return ret; + }, arguments) }; + imports.wbg.__wbg_text_51046bb33d257f63 = function() { return handleError(function (arg0) { + const ret = arg0.text(); + return ret; + }, arguments) }; + imports.wbg.__wbg_then_429f7caf1026411d = function(arg0, arg1, arg2) { + const ret = arg0.then(arg1, arg2); + return ret; + }; + imports.wbg.__wbg_then_4f95312d68691235 = function(arg0, arg1) { + const ret = arg0.then(arg1); + return ret; + }; + imports.wbg.__wbg_url_b6d11838a4f95198 = function(arg0, arg1) { + const ret = arg1.url; + const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len1 = WASM_VECTOR_LEN; + getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true); + getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true); + }; + imports.wbg.__wbg_value_57b7b035e117f7ee = function(arg0) { + const ret = arg0.value; + return ret; + }; + imports.wbg.__wbg_view_788aaf149deefd2f = function(arg0) { + const ret = arg0.view; + return isLikeNone(ret) ? 0 : addToExternrefTable0(ret); + }; + imports.wbg.__wbindgen_cast_2241b6af4c4b2941 = function(arg0, arg1) { + // Cast intrinsic for `Ref(String) -> Externref`. + const ret = getStringFromWasm0(arg0, arg1); + return ret; + }; + imports.wbg.__wbindgen_cast_600f2adf1b415b72 = function(arg0, arg1) { + // 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`. + 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_____); + return ret; + }; + imports.wbg.__wbindgen_cast_89cd6f7b6be057aa = function(arg0, arg1) { + // Cast intrinsic for `Closure(Closure { dtor_idx: 2024, function: Function { arguments: [], shim_idx: 2025, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`. + 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_); + return ret; + }; + imports.wbg.__wbindgen_cast_9216bf6c1753368f = function(arg0, arg1) { + // Cast intrinsic for `Closure(Closure { dtor_idx: 1144, function: Function { arguments: [], shim_idx: 1145, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`. + const ret = makeMutClosure(arg0, arg1, wasm.wasm_bindgen_1add006a0ed82fd3___closure__destroy___dyn_core_b125d98f3949a913___ops__function__FnMut_____Output_______, wasm_bindgen_1add006a0ed82fd3___convert__closures_____invoke______); + return ret; + }; + imports.wbg.__wbindgen_cast_eee509cff0277222 = function(arg0, arg1) { + // Cast intrinsic for `Closure(Closure { dtor_idx: 2221, function: Function { arguments: [Externref], shim_idx: 2222, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`. + 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_____); + return ret; + }; + imports.wbg.__wbindgen_init_externref_table = function() { + const table = wasm.__wbindgen_externrefs; + const offset = table.grow(4); + table.set(0, undefined); + table.set(offset + 0, undefined); + table.set(offset + 1, null); + table.set(offset + 2, true); + table.set(offset + 3, false); + }; + + return imports; + } + + function __wbg_finalize_init(instance, module) { + wasm = instance.exports; + __wbg_init.__wbindgen_wasm_module = module; + cachedDataViewMemory0 = null; + cachedUint8ArrayMemory0 = null; + + + wasm.__wbindgen_start(); + return wasm; + } + + function initSync(module) { + if (wasm !== undefined) return wasm; + + + if (typeof module !== 'undefined') { + if (Object.getPrototypeOf(module) === Object.prototype) { + ({module} = module) + } else { + console.warn('using deprecated parameters for `initSync()`; pass a single object instead') + } + } + + const imports = __wbg_get_imports(); + if (!(module instanceof WebAssembly.Module)) { + module = new WebAssembly.Module(module); + } + const instance = new WebAssembly.Instance(module, imports); + return __wbg_finalize_init(instance, module); + } + + async function __wbg_init(module_or_path) { + if (wasm !== undefined) return wasm; + + + if (typeof module_or_path !== 'undefined') { + if (Object.getPrototypeOf(module_or_path) === Object.prototype) { + ({module_or_path} = module_or_path) + } else { + console.warn('using deprecated parameters for the initialization function; pass a single object instead') + } + } + + if (typeof module_or_path === 'undefined' && typeof script_src !== 'undefined') { + module_or_path = script_src.replace(/\.js$/, '_bg.wasm'); + } + const imports = __wbg_get_imports(); + + if (typeof module_or_path === 'string' || (typeof Request === 'function' && module_or_path instanceof Request) || (typeof URL === 'function' && module_or_path instanceof URL)) { + module_or_path = fetch(module_or_path); + } + + const { instance, module } = await __wbg_load(await module_or_path, imports); + + return __wbg_finalize_init(instance, module); + } + + wasm_bindgen = Object.assign(__wbg_init, { initSync }, __exports); +})(); diff --git a/crates/weaver-app/public/snippets/dioxus-cli-config-c2775606adf80e62/inline0.js b/crates/weaver-app/public/snippets/dioxus-cli-config-c2775606adf80e62/inline0.js new file mode 100644 index 0000000..7c972c7 --- /dev/null +++ b/crates/weaver-app/public/snippets/dioxus-cli-config-c2775606adf80e62/inline0.js @@ -0,0 +1,9 @@ + + export function getMetaContents(meta_name) { + const selector = document.querySelector(`meta[name="${meta_name}"]`); + if (!selector) { + return null; + } + return selector.content; + } + \ No newline at end of file diff --git a/crates/weaver-app/public/snippets/dioxus-interpreter-js-ce7056f341163470/inline0.js b/crates/weaver-app/public/snippets/dioxus-interpreter-js-ce7056f341163470/inline0.js new file mode 100644 index 0000000..1878503 --- /dev/null +++ b/crates/weaver-app/public/snippets/dioxus-interpreter-js-ce7056f341163470/inline0.js @@ -0,0 +1,222 @@ + + function setAttributeInner(node,field,value,ns){if(ns==="style"){node.style.setProperty(field,value);return}if(ns){node.setAttributeNS(ns,field,value);return}switch(field){case"value":if(node.tagName==="OPTION")setAttributeDefault(node,field,value);else if(node.value!==value)node.value=value;break;case"initial_value":node.defaultValue=value;break;case"checked":node.checked=truthy(value);break;case"initial_checked":node.defaultChecked=truthy(value);break;case"selected":node.selected=truthy(value);break;case"initial_selected":node.defaultSelected=truthy(value);break;case"dangerous_inner_html":node.innerHTML=value;break;case"style":let existingStyles={};for(let i=0;i{for(let entry of entries)this.handleResizeEvent(entry)});this.resizeObserver.observe(element)}removeResizeObserver(element){if(this.resizeObserver)this.resizeObserver.unobserve(element)}handleIntersectionEvent(entry){let target=entry.target,event=new CustomEvent("visible",{bubbles:!1,detail:entry});target.dispatchEvent(event)}createIntersectionObserver(element){if(!this.intersectionObserver)this.intersectionObserver=new IntersectionObserver((entries)=>{for(let entry of entries)this.handleIntersectionEvent(entry)});this.intersectionObserver.observe(element)}removeIntersectionObserver(element){if(this.intersectionObserver)this.intersectionObserver.unobserve(element)}createListener(event_name,element,bubbles){if(event_name=="resize")this.createResizeObserver(element);else if(event_name=="visible")this.createIntersectionObserver(element);if(bubbles)if(this.global[event_name]===void 0)this.global[event_name]={active:1,callback:this.handler},this.root.addEventListener(event_name,this.handler);else this.global[event_name].active++;else{let id=element.getAttribute("data-dioxus-id");if(!this.local[id])this.local[id]={};element.addEventListener(event_name,this.handler)}}removeListener(element,event_name,bubbles){if(event_name=="resize")this.removeResizeObserver(element);else if(event_name=="visible")this.removeIntersectionObserver(element);else if(bubbles)this.removeBubblingListener(event_name);else this.removeNonBubblingListener(element,event_name)}removeBubblingListener(event_name){if(this.global[event_name].active--,this.global[event_name].active===0)this.root.removeEventListener(event_name,this.global[event_name].callback),delete this.global[event_name]}removeNonBubblingListener(element,event_name){let id=element.getAttribute("data-dioxus-id");if(delete this.local[id][event_name],Object.keys(this.local[id]).length===0)delete this.local[id];element.removeEventListener(event_name,this.handler)}removeAllNonBubblingListeners(element){let id=element.getAttribute("data-dioxus-id");delete this.local[id]}getNode(id){return this.nodes[id]}pushRoot(node){this.stack.push(node)}appendChildren(id,many){let root=this.nodes[id],els=this.stack.splice(this.stack.length-many);for(let k=0;k0;end--)node=node.nextSibling}return node}saveTemplate(nodes,tmpl_id){this.templates[tmpl_id]=nodes}hydrate_node(hydrateNode,ids){let split=hydrateNode.getAttribute("data-node-hydration").split(","),id=ids[parseInt(split[0])];if(this.nodes[id]=hydrateNode,split.length>1){hydrateNode.listening=split.length-1,hydrateNode.setAttribute("data-dioxus-id",id.toString());for(let j=1;j{if(!treeWalker.nextNode())return!1;return treeWalker.currentNode!==nextSibling};while(treeWalker.currentNode){let currentNode=treeWalker.currentNode;if(currentNode.nodeType===Node.COMMENT_NODE){let id=currentNode.textContent,placeholderSplit=id.split("placeholder");if(placeholderSplit.length>1){if(this.nodes[ids[parseInt(placeholderSplit[1])]]=currentNode,!continueToNextNode())break;continue}let textNodeSplit=id.split("node-id");if(textNodeSplit.length>1){let next=currentNode.nextSibling;currentNode.remove();let commentAfterText,textNode;if(next.nodeType===Node.COMMENT_NODE){let newText=next.parentElement.insertBefore(document.createTextNode(""),next);commentAfterText=next,textNode=newText}else textNode=next,commentAfterText=textNode.nextSibling;treeWalker.currentNode=commentAfterText,this.nodes[ids[parseInt(textNodeSplit[1])]]=textNode;let exit=currentNode===under||!continueToNextNode();if(commentAfterText.remove(),exit)break;continue}}if(!continueToNextNode())break}}}setAttributeInner(node,field,value,ns){setAttributeInner(node,field,value,ns)}}export{BaseInterpreter}; + + let bubbles,field,id,many,ns; + export class RawInterpreter extends BaseInterpreter { + constructor(r) { + super(); + this.d=r; + this.m = null; + this.p = null; + this.ls = null; + this.t = null; + this.op = null; + this.e = null; + this.z = null; + this.metaflags = null; + this.u16buf=null;this.u16bufp=null;this.u32buf=null;this.u32bufp=null;this.u8buf=null;this.u8bufp=null;this.attr = []; + this.attr_cache_hit = null; + this.attr_cache_idx; + this.get_attr = function() { + this.attr_cache_idx = this.u8buf[this.u8bufp++]; + if(this.attr_cache_idx & 128){ + this.attr_cache_hit=this.s.substring(this.sp,this.sp+=this.u8buf[this.u8bufp++]); + this.attr[this.attr_cache_idx&4294967167]=this.attr_cache_hit; + return this.attr_cache_hit; + } + else{ + return this.attr[this.attr_cache_idx&4294967167]; + } + };this.el = []; + this.el_cache_hit = null; + this.el_cache_idx; + this.get_el = function() { + this.el_cache_idx = this.u8buf[this.u8bufp++]; + if(this.el_cache_idx & 128){ + this.el_cache_hit=this.s.substring(this.sp,this.sp+=this.u8buf[this.u8bufp++]); + this.el[this.el_cache_idx&4294967167]=this.el_cache_hit; + return this.el_cache_hit; + } + else{ + return this.el[this.el_cache_idx&4294967167]; + } + };this.evt = []; + this.evt_cache_hit = null; + this.evt_cache_idx; + this.get_evt = function() { + this.evt_cache_idx = this.u8buf[this.u8bufp++]; + if(this.evt_cache_idx & 128){ + this.evt_cache_hit=this.s.substring(this.sp,this.sp+=this.u8buf[this.u8bufp++]); + this.evt[this.evt_cache_idx&4294967167]=this.evt_cache_hit; + return this.evt_cache_hit; + } + else{ + return this.evt[this.evt_cache_idx&4294967167]; + } + };this.namespace = []; + this.namespace_cache_hit = null; + this.namespace_cache_idx; + this.get_namespace = function() { + this.namespace_cache_idx = this.u8buf[this.u8bufp++]; + if(this.namespace_cache_idx & 128){ + this.namespace_cache_hit=this.s.substring(this.sp,this.sp+=this.u8buf[this.u8bufp++]); + this.namespace[this.namespace_cache_idx&4294967167]=this.namespace_cache_hit; + return this.namespace_cache_hit; + } + else{ + return this.namespace[this.namespace_cache_idx&4294967167]; + } + };this.ns_cache = []; + this.ns_cache_cache_hit = null; + this.ns_cache_cache_idx; + this.get_ns_cache = function() { + this.ns_cache_cache_idx = this.u8buf[this.u8bufp++]; + if(this.ns_cache_cache_idx & 128){ + this.ns_cache_cache_hit=this.s.substring(this.sp,this.sp+=this.u8buf[this.u8bufp++]); + this.ns_cache[this.ns_cache_cache_idx&4294967167]=this.ns_cache_cache_hit; + return this.ns_cache_cache_hit; + } + else{ + return this.ns_cache[this.ns_cache_cache_idx&4294967167]; + } + };this.s = "";this.lsp = null;this.sp = null;this.sl = null;this.c = new TextDecoder(); + } + + update_memory(b){ + this.m=new DataView(b.buffer) + } + + run(){ + this.metaflags=this.m.getUint32(this.d,true); + if((this.metaflags>>>6)&1){ + this.ls=this.m.getUint32(this.d+6*4,true); + } + this.p=this.ls; + if ((this.metaflags>>>4)&1){ + this.t = this.m.getUint32(this.d+4*4,true); + this.u16buf=new Uint16Array(this.m.buffer,this.t,((this.m.buffer.byteLength-this.t)-(this.m.buffer.byteLength-this.t)%2)/2); + } + this.u16bufp=0;if ((this.metaflags>>>3)&1){ + this.t = this.m.getUint32(this.d+3*4,true); + this.u32buf=new Uint32Array(this.m.buffer,this.t,((this.m.buffer.byteLength-this.t)-(this.m.buffer.byteLength-this.t)%4)/4); + } + this.u32bufp=0;if ((this.metaflags>>>5)&1){ + this.t = this.m.getUint32(this.d+5*4,true); + this.u8buf=new Uint8Array(this.m.buffer,this.t,((this.m.buffer.byteLength-this.t)-(this.m.buffer.byteLength-this.t)%1)/1); + } + this.u8bufp=0;if (this.metaflags&1){ + this.lsp = this.m.getUint32(this.d+1*4,true); + } + if ((this.metaflags>>>2)&1) { + this.sl = this.m.getUint32(this.d+2*4,true); + if ((this.metaflags>>>1)&1) { + this.sp = this.lsp; + this.s = ""; + this.e = this.sp + ((this.sl / 4) | 0) * 4; + while (this.sp < this.e) { + this.t = this.m.getUint32(this.sp, true); + this.s += String.fromCharCode( + this.t & 255, + (this.t & 65280) >> 8, + (this.t & 16711680) >> 16, + this.t >> 24 + ); + this.sp += 4; + } + while (this.sp < this.lsp + this.sl) { + this.s += String.fromCharCode(this.m.getUint8(this.sp++)); + } + } else { + let buffer = new Uint8Array(this.m.buffer, this.lsp, this.sl); + // If the wasm buffer is a shared array buffer, we need to copy the data out before decoding https://github.com/DioxusLabs/dioxus/issues/2589 + // Note: We intentionally don't use instanceof here because SharedArrayBuffer can be created even when SharedArrayBuffer is not defined... + if (this.m.buffer.constructor.name === "SharedArrayBuffer") { + let arrayBuffer = new ArrayBuffer(this.sl); + new Uint8Array(arrayBuffer).set(buffer); + buffer = arrayBuffer; + } + this.s = this.c.decode(buffer); + } + } + this.sp=0; + for(;;){ + this.op=this.m.getUint32(this.p,true); + this.p+=4; + this.z=0; + while(this.z++<4){ + switch(this.op&255){ + case 0:{this.pushRoot(this.nodes[this.u32buf[this.u32bufp++]]);}break;case 1:{this.appendChildren(this.u32buf[this.u32bufp++], this.u16buf[this.u16bufp++]);}break;case 2:{this.stack.pop();}break;case 3:{const root = this.nodes[this.u32buf[this.u32bufp++]]; let els = this.stack.splice(this.stack.length-this.u16buf[this.u16bufp++]); if (root.listening) { this.removeAllNonBubblingListeners(root); } root.replaceWith(...els);}break;case 4:{let node = this.nodes[this.u32buf[this.u32bufp++]];node.after(...this.stack.splice(this.stack.length-this.u16buf[this.u16bufp++]));}break;case 5:{let node = this.nodes[this.u32buf[this.u32bufp++]];node.before(...this.stack.splice(this.stack.length-this.u16buf[this.u16bufp++]));}break;case 6:{let node = this.nodes[this.u32buf[this.u32bufp++]]; if (node !== undefined) { if (node.listening) { this.removeAllNonBubblingListeners(node); } node.remove(); }}break;case 7:{this.stack.push(document.createTextNode(this.s.substring(this.sp,this.sp+=this.u32buf[this.u32bufp++])));}break;case 8:{let node = document.createTextNode(this.s.substring(this.sp,this.sp+=this.u32buf[this.u32bufp++])); this.nodes[this.u32buf[this.u32bufp++]] = node; this.stack.push(node);}break;case 9:{let node = document.createComment('placeholder'); this.stack.push(node); this.nodes[this.u32buf[this.u32bufp++]] = node;}break;case 10:id=this.u32buf[this.u32bufp++]; + const node = this.nodes[id]; + if(node.listening){node.listening += 1;}else{node.listening = 1;} + node.setAttribute('data-dioxus-id', `${id}`); + this.createListener(this.get_evt(), node, this.u8buf[this.u8bufp++]); + break;case 11:{let node = this.nodes[this.u32buf[this.u32bufp++]]; node.listening -= 1; node.removeAttribute('data-dioxus-id'); this.removeListener(node, this.get_evt(), this.u8buf[this.u8bufp++]);}break;case 12:{this.nodes[this.u32buf[this.u32bufp++]].textContent = this.s.substring(this.sp,this.sp+=this.u32buf[this.u32bufp++]);}break;case 13:{let node = this.nodes[this.u32buf[this.u32bufp++]]; this.setAttributeInner(node, this.get_attr(), this.s.substring(this.sp,this.sp+=this.u32buf[this.u32bufp++]), this.get_ns_cache());}break;case 14:field=this.get_attr();ns=this.get_ns_cache();{ + let node = this.nodes[this.u32buf[this.u32bufp++]]; + if (!ns) { + switch (field) { + case "value": + node.value = ""; + node.removeAttribute("value"); + break; + case "checked": + node.checked = false; + break; + case "selected": + node.selected = false; + break; + case "dangerous_inner_html": + node.innerHTML = ""; + break; + default: + node.removeAttribute(field); + break; + } + } else if (ns == "style") { + node.style.removeProperty(field); + } else { + node.removeAttributeNS(ns, field); + } + }break;case 15:{this.nodes[this.u32buf[this.u32bufp++]] = this.loadChild(this.u32buf[this.u32bufp++], this.u8buf[this.u8bufp++]);}break;case 16:{let els = this.stack.splice(this.stack.length - this.u16buf[this.u16bufp++]); let node = this.loadChild(this.u32buf[this.u32bufp++], this.u8buf[this.u8bufp++]); node.replaceWith(...els);}break;case 17:{let node = this.templates[this.u16buf[this.u16bufp++]][this.u16buf[this.u16bufp++]].cloneNode(true); this.nodes[this.u32buf[this.u32bufp++]] = node; this.stack.push(node);}break;case 18:many=this.u16buf[this.u16bufp++];{ + let root = this.stack[this.stack.length-many-1]; + let els = this.stack.splice(this.stack.length-many); + for (let k = 0; k < many; k++) { + root.appendChild(els[k]); + } + }break;case 19:{this.setAttributeInner(this.stack[this.stack.length-1], this.get_attr(), this.s.substring(this.sp,this.sp+=this.u32buf[this.u32bufp++]), this.get_ns_cache());}break;case 20:{let node = document.createComment('placeholder'); this.stack.push(node);}break;case 21:{this.stack.push(document.createElement(this.get_el()))}break;case 22:{this.stack.push(document.createElementNS(this.get_namespace(), this.get_el()))}break;case 23:{this.templates[this.u16buf[this.u16bufp++]] = this.stack.splice(this.stack.length-this.u16buf[this.u16bufp++]);}break;case 24:id=this.u32buf[this.u32bufp++];bubbles=this.u8buf[this.u8bufp++]; + bubbles = bubbles == 1; + let this_node = this.nodes[id]; + if(this_node.listening){ + this_node.listening += 1; + } else { + this_node.listening = 1; + } + this_node.setAttribute('data-dioxus-id', `${id}`); + const event_name = this.get_evt(); + + // if this is a mounted listener, we send the event immediately + if (event_name === "mounted") { + window.ipc.postMessage( + this.sendSerializedEvent({ + name: event_name, + element: id, + data: null, + bubbles, + }) + ); + } else { + this.createListener(event_name, this_node, bubbles, (event) => { + this.handler(event, event_name, bubbles); + }); + }break;case 25:{this.nodes[this.u32buf[this.u32bufp++]] = this.loadChild((()=>{this.e=this.u8bufp+this.u32buf[this.u32bufp++];const final_array = this.u8buf.slice(this.u8bufp,this.e);this.u8bufp=this.e;return final_array;})());}break;case 26:{let els = this.stack.splice(this.stack.length - this.u16buf[this.u16bufp++]); let node = this.loadChild((()=>{this.e=this.u8bufp+this.u32buf[this.u32bufp++];const final_array = this.u8buf.slice(this.u8bufp,this.e);this.u8bufp=this.e;return final_array;})()); node.replaceWith(...els);}break;case 27:return true; + } + this.op>>>=8; + } + } + } + + run_from_bytes(bytes){ + this.d = 0; + this.update_memory(new Uint8Array(bytes)) + this.run() + } + } \ No newline at end of file diff --git a/crates/weaver-app/public/snippets/dioxus-interpreter-js-ce7056f341163470/src/js/hydrate.js b/crates/weaver-app/public/snippets/dioxus-interpreter-js-ce7056f341163470/src/js/hydrate.js new file mode 100644 index 0000000..8da6cdc --- /dev/null +++ b/crates/weaver-app/public/snippets/dioxus-interpreter-js-ce7056f341163470/src/js/hydrate.js @@ -0,0 +1 @@ +function register_rehydrate_chunk_for_streaming(callback){return register_rehydrate_chunk_for_streaming_debug(callback)}function register_rehydrate_chunk_for_streaming_debug(callback){window.hydration_callback=callback;for(let i=0;i c.charCodeAt(0)) + } + export function get_initial_hydration_debug_types() { + return window.initial_dioxus_hydration_debug_types; + } + export function get_initial_hydration_debug_locations() { + return window.initial_dioxus_hydration_debug_locations; + } + \ No newline at end of file diff --git a/crates/weaver-app/public/snippets/dioxus-web-fe956e4b19891335/src/js/eval.js b/crates/weaver-app/public/snippets/dioxus-web-fe956e4b19891335/src/js/eval.js new file mode 100644 index 0000000..0fc6db7 --- /dev/null +++ b/crates/weaver-app/public/snippets/dioxus-web-fe956e4b19891335/src/js/eval.js @@ -0,0 +1 @@ +class Channel{pending;waiting;constructor(){this.pending=[],this.waiting=[]}send(data){if(this.waiting.length>0){this.waiting.shift()(data);return}this.pending.push(data)}async recv(){return new Promise((resolve,_reject)=>{if(this.pending.length>0){resolve(this.pending.shift());return}this.waiting.push(resolve)})}}class WeakDioxusChannel{inner;constructor(channel){this.inner=new WeakRef(channel)}rustSend(data){let channel=this.inner.deref();if(channel)channel.rustSend(data)}async rustRecv(){let channel=this.inner.deref();if(channel)return await channel.rustRecv()}}class DioxusChannel{weak(){return new WeakDioxusChannel(this)}}globalThis.__nextChannelId=0;globalThis.__channels=[];class WebDioxusChannel extends DioxusChannel{js_to_rust;rust_to_js;owner;id;constructor(owner){super();this.owner=owner,this.js_to_rust=new Channel,this.rust_to_js=new Channel,this.id=globalThis.__nextChannelId,globalThis.__channels[this.id]=this,globalThis.__nextChannelId+=1}weak(){return new WeakDioxusChannel(this)}async recv(){return await this.rust_to_js.recv()}send(data){this.js_to_rust.send(data)}rustSend(data){this.rust_to_js.send(data)}async rustRecv(){return await this.js_to_rust.recv()}close(){globalThis.__channels[this.id]=null}}export{WebDioxusChannel,WeakDioxusChannel}; diff --git a/crates/weaver-app/src/bin/editor_worker.rs b/crates/weaver-app/src/bin/editor_worker.rs new file mode 100644 index 0000000..129b240 --- /dev/null +++ b/crates/weaver-app/src/bin/editor_worker.rs @@ -0,0 +1,19 @@ +//! Entry point for the editor web worker. +//! +//! This binary is compiled separately and loaded by the main app +//! to handle CPU-intensive editor operations off the main thread. + +#[cfg(all(target_family = "wasm", target_os = "unknown"))] +fn main() { + console_error_panic_hook::set_once(); + + use gloo_worker::Registrable; + use weaver_app::components::editor::EditorWorker; + + EditorWorker::registrar().register(); +} + +#[cfg(not(all(target_family = "wasm", target_os = "unknown")))] +fn main() { + eprintln!("This binary is only meant to run as a WASM web worker"); +} diff --git a/crates/weaver-app/src/bin/embed_worker.rs b/crates/weaver-app/src/bin/embed_worker.rs new file mode 100644 index 0000000..8c79f7a --- /dev/null +++ b/crates/weaver-app/src/bin/embed_worker.rs @@ -0,0 +1,19 @@ +//! Entry point for the embed web worker. +//! +//! This binary is compiled separately and loaded by the main app +//! to fetch and cache AT Protocol embeds off the main thread. + +#[cfg(all(target_family = "wasm", target_os = "unknown"))] +fn main() { + console_error_panic_hook::set_once(); + + use gloo_worker::Registrable; + use weaver_app::components::editor::EmbedWorker; + + EmbedWorker::registrar().register(); +} + +#[cfg(not(all(target_family = "wasm", target_os = "unknown")))] +fn main() { + eprintln!("This binary is only meant to run as a WASM web worker"); +} diff --git a/crates/weaver-app/src/components/editor/component.rs b/crates/weaver-app/src/components/editor/component.rs index 9f73ac2..34d0635 100644 --- a/crates/weaver-app/src/components/editor/component.rs +++ b/crates/weaver-app/src/components/editor/component.rs @@ -632,38 +632,152 @@ fn MarkdownEditorInner( #[cfg(all(target_arch = "wasm32", target_os = "unknown"))] let mut interval_holder: Signal> = use_signal(|| None); + // Worker-based autosave (offloads export + encode to worker thread) #[cfg(all(target_arch = "wasm32", target_os = "unknown"))] - let doc_for_autosave = document.clone(); - #[cfg(all(target_arch = "wasm32", target_os = "unknown"))] - let draft_key_for_autosave = draft_key.clone(); - #[cfg(all(target_arch = "wasm32", target_os = "unknown"))] - use_effect(move || { - // Check every 500ms if there are unsaved changes - let mut doc = doc_for_autosave.clone(); - let draft_key = draft_key_for_autosave.clone(); - let interval = gloo_timers::callback::Interval::new(500, move || { - let current_frontiers = doc.state_frontiers(); - - // Only save if frontiers changed (document was edited) - let needs_save = { - let last_frontiers = last_saved_frontiers.peek(); - match &*last_frontiers { - None => true, - Some(last) => ¤t_frontiers != last, + { + use super::worker::{EditorWorker, WorkerInput, WorkerOutput}; + use gloo_storage::Storage; + use gloo_worker::Spawnable; + + // Track if worker is available (false = fallback to main thread) + let use_worker: Signal = use_signal(|| true); + // Worker bridge handle + let mut worker_bridge: Signal>> = + use_signal(|| None); + // Track version vector sent to worker (for incremental updates) + let mut last_worker_vv: Signal> = use_signal(|| None); + + // Spawn worker on mount + let doc_for_worker_init = document.clone(); + let draft_key_for_worker = draft_key.clone(); + use_effect(move || { + let doc = doc_for_worker_init.clone(); + let draft_key = draft_key_for_worker.clone(); + + // Callback for worker responses + let on_output = move |output: WorkerOutput| { + match output { + WorkerOutput::Ready => { + tracing::info!("Editor worker ready"); + } + WorkerOutput::Snapshot { + draft_key, + b64_snapshot, + content, + title, + cursor_offset, + editing_uri, + editing_cid, + export_ms, + encode_ms, + } => { + // Write to localStorage (fast - just string assignment) + let snapshot = storage::EditorSnapshot { + content, + title, + snapshot: Some(b64_snapshot), + cursor: None, // Worker doesn't have Loro cursor + cursor_offset, + editing_uri, + editing_cid, + }; + let write_start = crate::perf::now(); + let _ = gloo_storage::LocalStorage::set( + format!("{}{}", storage::DRAFT_KEY_PREFIX, draft_key), + &snapshot, + ); + let write_ms = crate::perf::now() - write_start; + tracing::debug!(export_ms, encode_ms, write_ms, "worker autosave complete"); + } + WorkerOutput::Error { message } => { + tracing::error!("Worker error: {}", message); + } } }; - if needs_save { + // Spawn worker (panics on failure in debug, returns bridge directly) + let bridge = EditorWorker::spawner() + .callback(on_output) + .spawn("/editor_worker.js"); + + // Initialize with current document snapshot + let snapshot = doc.export_snapshot(); + bridge.send(WorkerInput::Init { + snapshot, + draft_key: draft_key.clone(), + }); + worker_bridge.set(Some(bridge)); + tracing::info!("Editor worker spawned"); + }); + + // Autosave interval + let doc_for_autosave = document.clone(); + let draft_key_for_autosave = draft_key.clone(); + use_effect(move || { + let mut doc = doc_for_autosave.clone(); + let draft_key = draft_key_for_autosave.clone(); + + let interval = gloo_timers::callback::Interval::new(500, move || { + let callback_start = crate::perf::now(); + let current_frontiers = doc.state_frontiers(); + + // Only save if frontiers changed (document was edited) + let needs_save = { + let last_frontiers = last_saved_frontiers.peek(); + match &*last_frontiers { + None => true, + Some(last) => ¤t_frontiers != last, + } + }; + + if !needs_save { + return; + } + doc.sync_loro_cursor(); - let _ = storage::save_to_storage(&doc, &draft_key); - // Update last saved frontiers + // Try worker path first + if *use_worker.peek() { + if let Some(ref bridge) = *worker_bridge.peek() { + // Send updates to worker (or full snapshot if first time) + let current_vv = doc.version_vector(); + let updates = if let Some(ref last_vv) = *last_worker_vv.peek() { + doc.export_updates_from(last_vv).unwrap_or_default() + } else { + doc.export_snapshot() + }; + + if !updates.is_empty() { + bridge.send(WorkerInput::ApplyUpdates { updates }); + } + + // Request snapshot export + bridge.send(WorkerInput::ExportSnapshot { + cursor_offset: doc.cursor.read().offset, + editing_uri: doc.entry_ref().map(|r| r.uri.to_string()), + editing_cid: doc.entry_ref().map(|r| r.cid.to_string()), + }); + + last_worker_vv.set(Some(current_vv)); + last_saved_frontiers.set(Some(current_frontiers)); + + let callback_ms = crate::perf::now() - callback_start; + tracing::debug!(callback_ms, "autosave via worker"); + return; + } + } + + // Fallback: main thread save + let _ = storage::save_to_storage(&doc, &draft_key); last_saved_frontiers.set(Some(current_frontiers)); - } + + let callback_ms = crate::perf::now() - callback_start; + tracing::debug!(callback_ms, "autosave callback (main thread fallback)"); + }); + + interval_holder.set(Some(interval)); }); - // Store in signal instead of forget - interval drops when component unmounts - interval_holder.set(Some(interval)); - }); + } // Set up beforeinput listener for all text input handling. // This is the primary handler for text insertion, deletion, etc. @@ -1603,7 +1717,11 @@ fn RemoteCursorIndicator( // Get selection rectangles if there's a selection let selection_rects = if let Some((start, end)) = selection { - let (start, end) = if start <= end { (start, end) } else { (end, start) }; + let (start, end) = if start <= end { + (start, end) + } else { + (end, start) + }; get_selection_rects_relative(start, end, &offset_map, "markdown-editor") } else { vec![] diff --git a/crates/weaver-app/src/components/editor/mod.rs b/crates/weaver-app/src/components/editor/mod.rs index 17d7481..f2e5937 100644 --- a/crates/weaver-app/src/components/editor/mod.rs +++ b/crates/weaver-app/src/components/editor/mod.rs @@ -24,6 +24,8 @@ mod storage; mod sync; mod toolbar; mod visibility; +#[cfg(all(target_family = "wasm", target_os = "unknown"))] +mod worker; mod writer; #[cfg(test)] @@ -79,3 +81,9 @@ pub use visibility::VisibilityState; // Logging #[allow(unused_imports)] pub use log_buffer::LogCaptureLayer; + +// Worker +#[cfg(all(target_family = "wasm", target_os = "unknown"))] +pub use worker::{ + EditorWorker, EmbedWorker, EmbedWorkerInput, EmbedWorkerOutput, WorkerInput, WorkerOutput, +}; diff --git a/crates/weaver-app/src/components/editor/render.rs b/crates/weaver-app/src/components/editor/render.rs index 79c6282..57d94ac 100644 --- a/crates/weaver-app/src/components/editor/render.rs +++ b/crates/weaver-app/src/components/editor/render.rs @@ -95,6 +95,7 @@ pub fn render_paragraphs_incremental( RenderCache, Vec, ) { + let fn_start = crate::perf::now(); let source = text.to_string(); // Handle empty document @@ -214,11 +215,12 @@ pub fn render_paragraphs_incremental( // Slow path: run boundary-only pass to discover paragraph boundaries let paragraph_ranges = if paragraph_ranges.is_empty() { + let boundary_start = crate::perf::now(); let parser = Parser::new_ext(&source, weaver_renderer::default_md_options()).into_offset_iter(); let mut scratch_output = String::new(); - match EditorWriter::<_, _, ()>::new_boundary_only( + let result = match EditorWriter::<_, _, ()>::new_boundary_only( &source, text, parser, @@ -228,7 +230,10 @@ pub fn render_paragraphs_incremental( { Ok(result) => result.paragraph_ranges, Err(_) => return (Vec::new(), RenderCache::default(), vec![]), - } + }; + let boundary_ms = crate::perf::now() - boundary_start; + tracing::debug!(boundary_ms, paragraphs = result.len(), "boundary discovery (slow path)"); + result } else { paragraph_ranges }; @@ -250,11 +255,15 @@ pub fn render_paragraphs_incremental( } // Render paragraphs, reusing cache where possible + let render_loop_start = crate::perf::now(); let mut paragraphs = Vec::with_capacity(paragraph_ranges.len()); let mut new_cached = Vec::with_capacity(paragraph_ranges.len()); let mut all_refs: Vec = Vec::new(); let mut node_id_offset = cache.map(|c| c.next_node_id).unwrap_or(0); let mut syn_id_offset = cache.map(|c| c.next_syn_id).unwrap_or(0); + let mut cache_hits = 0usize; + let mut cache_misses = 0usize; + let mut fresh_render_ms = 0.0f64; for (idx, (byte_range, char_range)) in paragraph_ranges.iter().enumerate() { let para_source = text_slice_to_string(text, char_range.clone()); @@ -265,6 +274,7 @@ pub fn render_paragraphs_incremental( cache.and_then(|c| c.paragraphs.iter().find(|p| p.source_hash == source_hash)); let (html, offset_map, syntax_spans, para_refs) = if let Some(cached) = cached_match { + cache_hits += 1; // Reuse cached HTML, offset map, and syntax spans (adjusted for position) let char_delta = char_range.start as isize - cached.char_range.start as isize; let byte_delta = byte_range.start as isize - cached.byte_range.start as isize; @@ -294,6 +304,8 @@ pub fn render_paragraphs_incremental( cached.collected_refs.clone(), ) } else { + cache_misses += 1; + let para_render_start = crate::perf::now(); // Fresh render needed - create detached LoroDoc for this paragraph let para_doc = loro::LoroDoc::new(); let para_text = para_doc.get_text("content"); @@ -365,6 +377,7 @@ pub fn render_paragraphs_incremental( // Offsets are already document-absolute since we pass char_range.start/byte_range.start // to the writer constructor + fresh_render_ms += crate::perf::now() - para_render_start; (output, offset_map, syntax_spans, para_refs) }; @@ -471,5 +484,18 @@ pub fn render_paragraphs_incremental( next_syn_id: syn_id_offset, }; + let render_loop_ms = crate::perf::now() - render_loop_start; + let total_ms = crate::perf::now() - fn_start; + tracing::debug!( + total_ms, + render_loop_ms, + fresh_render_ms, + cache_hits, + cache_misses, + paragraphs = paragraphs_with_gaps.len(), + use_fast_path, + "render_paragraphs_incremental timing" + ); + (paragraphs_with_gaps, new_cache, all_refs) } diff --git a/crates/weaver-app/src/components/editor/storage.rs b/crates/weaver-app/src/components/editor/storage.rs index a972cda..cd92d0d 100644 --- a/crates/weaver-app/src/components/editor/storage.rs +++ b/crates/weaver-app/src/components/editor/storage.rs @@ -83,12 +83,17 @@ pub fn save_to_storage( doc: &EditorDocument, key: &str, ) -> Result<(), gloo_storage::errors::StorageError> { + let export_start = crate::perf::now(); let snapshot_bytes = doc.export_snapshot(); + let export_ms = crate::perf::now() - export_start; + + let encode_start = crate::perf::now(); let snapshot_b64 = if snapshot_bytes.is_empty() { None } else { Some(BASE64.encode(&snapshot_bytes)) }; + let encode_ms = crate::perf::now() - encode_start; let snapshot = EditorSnapshot { content: doc.content(), @@ -99,7 +104,20 @@ pub fn save_to_storage( editing_uri: doc.entry_ref().map(|r| r.uri.to_string()), editing_cid: doc.entry_ref().map(|r| r.cid.to_string()), }; - LocalStorage::set(storage_key(key), &snapshot) + + let write_start = crate::perf::now(); + let result = LocalStorage::set(storage_key(key), &snapshot); + let write_ms = crate::perf::now() - write_start; + + tracing::debug!( + export_ms, + encode_ms, + write_ms, + bytes = snapshot_bytes.len(), + "save_to_storage timing" + ); + + result } /// Load editor state from LocalStorage (WASM only). diff --git a/crates/weaver-app/src/components/editor/sync.rs b/crates/weaver-app/src/components/editor/sync.rs index f8c22df..3c56899 100644 --- a/crates/weaver-app/src/components/editor/sync.rs +++ b/crates/weaver-app/src/components/editor/sync.rs @@ -776,6 +776,8 @@ pub async fn sync_to_pds( doc: &mut EditorDocument, draft_key: &str, ) -> Result { + let fn_start = crate::perf::now(); + // Check if we have changes to sync if !doc.has_unsynced_changes() { return Ok(SyncResult::NoChanges); @@ -786,6 +788,7 @@ pub async fn sync_to_pds( if doc.edit_root().is_none() { // First sync - create root + let create_start = crate::perf::now(); let (root_uri, root_cid) = create_edit_root( fetcher, doc, @@ -794,6 +797,7 @@ pub async fn sync_to_pds( entry_ref.as_ref().map(|r| &r.cid), ) .await?; + let create_ms = crate::perf::now() - create_start; // Build StrongRef for the root let root_ref = StrongRef::new() @@ -806,6 +810,9 @@ pub async fn sync_to_pds( doc.set_last_diff(None); doc.mark_synced(); + let total_ms = crate::perf::now() - fn_start; + tracing::debug!(total_ms, create_ms, "sync_to_pds: created root"); + Ok(SyncResult::CreatedRoot { uri: root_uri, cid: root_cid, @@ -815,6 +822,7 @@ pub async fn sync_to_pds( let root_ref = doc.edit_root().unwrap(); let prev_diff = doc.last_diff(); + let create_start = crate::perf::now(); let result = create_diff( fetcher, doc, @@ -826,6 +834,7 @@ pub async fn sync_to_pds( entry_ref.as_ref().map(|r| &r.cid), ) .await?; + let create_ms = crate::perf::now() - create_start; match result { Some((diff_uri, diff_cid)) => { @@ -838,12 +847,19 @@ pub async fn sync_to_pds( doc.set_last_diff(Some(diff_ref)); doc.mark_synced(); + let total_ms = crate::perf::now() - fn_start; + tracing::debug!(total_ms, create_ms, "sync_to_pds: created diff"); + Ok(SyncResult::CreatedDiff { uri: diff_uri, cid: diff_cid, }) } - None => Ok(SyncResult::NoChanges), + None => { + let total_ms = crate::perf::now() - fn_start; + tracing::debug!(total_ms, create_ms, "sync_to_pds: no changes in diff"); + Ok(SyncResult::NoChanges) + } } } } diff --git a/crates/weaver-app/src/components/editor/worker.rs b/crates/weaver-app/src/components/editor/worker.rs new file mode 100644 index 0000000..288aa2c --- /dev/null +++ b/crates/weaver-app/src/components/editor/worker.rs @@ -0,0 +1,343 @@ +//! Web Worker for offloading expensive editor operations. +//! +//! This worker maintains a shadow copy of the Loro document and handles +//! CPU-intensive operations like snapshot export and base64 encoding +//! off the main thread. +//! +//! Also handles embed fetching with a persistent cache to avoid re-fetching. + +#[cfg(all(target_family = "wasm", target_os = "unknown"))] +use base64::{Engine, engine::general_purpose::STANDARD as BASE64}; +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; + +/// Input messages to the editor worker. +#[derive(Serialize, Deserialize, Debug, Clone)] +pub enum WorkerInput { + /// Initialize the worker with a full Loro snapshot. + Init { + /// Full Loro snapshot bytes + snapshot: Vec, + /// Draft key for storage + draft_key: String, + }, + /// Apply incremental Loro updates to the shadow document. + ApplyUpdates { + /// Loro update bytes (delta since last sync) + updates: Vec, + }, + /// Request a snapshot export for autosave. + ExportSnapshot { + /// Current cursor position (for snapshot metadata) + cursor_offset: usize, + /// Editing URI if editing existing entry + editing_uri: Option, + /// Editing CID if editing existing entry + editing_cid: Option, + }, +} + +/// Output messages from the editor worker. +#[derive(Serialize, Deserialize, Debug, Clone)] +pub enum WorkerOutput { + /// Worker initialized successfully. + Ready, + /// Snapshot export completed. + Snapshot { + /// Draft key for storage + draft_key: String, + /// Base64-encoded Loro snapshot + b64_snapshot: String, + /// Human-readable content (for debugging) + content: String, + /// Entry title + title: String, + /// Cursor offset + cursor_offset: usize, + /// Editing URI + editing_uri: Option, + /// Editing CID + editing_cid: Option, + /// Export timing in ms + export_ms: f64, + /// Encode timing in ms + encode_ms: f64, + }, + /// Error occurred. + Error { message: String }, +} + +#[cfg(all(target_family = "wasm", target_os = "unknown"))] +mod worker_impl { + use super::*; + use gloo_worker::{HandlerId, Worker, WorkerScope}; + + /// Editor worker that maintains a shadow Loro document. + pub struct EditorWorker { + /// Shadow Loro document + doc: Option, + /// Draft key for storage identification + draft_key: String, + } + + impl Worker for EditorWorker { + type Message = (); + type Input = WorkerInput; + type Output = WorkerOutput; + + fn create(_scope: &WorkerScope) -> Self { + Self { + doc: None, + draft_key: String::new(), + } + } + + fn update(&mut self, _scope: &WorkerScope, _msg: Self::Message) {} + + fn received(&mut self, scope: &WorkerScope, msg: Self::Input, id: HandlerId) { + match msg { + WorkerInput::Init { + snapshot, + draft_key, + } => { + let doc = loro::LoroDoc::new(); + if !snapshot.is_empty() { + if let Err(e) = doc.import(&snapshot) { + scope.respond( + id, + WorkerOutput::Error { + message: format!("Failed to import snapshot: {e}"), + }, + ); + return; + } + } + self.doc = Some(doc); + self.draft_key = draft_key; + scope.respond(id, WorkerOutput::Ready); + } + + WorkerInput::ApplyUpdates { updates } => { + if let Some(ref doc) = self.doc { + if let Err(e) = doc.import(&updates) { + // Log but don't fail - updates can be stale + tracing::warn!("Worker failed to import updates: {e}"); + } + } + // No response for updates - fire and forget + } + + WorkerInput::ExportSnapshot { + cursor_offset, + editing_uri, + editing_cid, + } => { + let Some(ref doc) = self.doc else { + scope.respond( + id, + WorkerOutput::Error { + message: "No document initialized".into(), + }, + ); + return; + }; + + // Export snapshot + let export_start = crate::perf::now(); + let snapshot_bytes = match doc.export(loro::ExportMode::Snapshot) { + Ok(bytes) => bytes, + Err(e) => { + scope.respond( + id, + WorkerOutput::Error { + message: format!("Export failed: {e}"), + }, + ); + return; + } + }; + let export_ms = crate::perf::now() - export_start; + + // Base64 encode + let encode_start = crate::perf::now(); + let b64_snapshot = BASE64.encode(&snapshot_bytes); + let encode_ms = crate::perf::now() - encode_start; + + // Extract content and title + let content = doc.get_text("content").to_string(); + let title = doc.get_text("title").to_string(); + + scope.respond( + id, + WorkerOutput::Snapshot { + draft_key: self.draft_key.clone(), + b64_snapshot, + content, + title, + cursor_offset, + editing_uri, + editing_cid, + export_ms, + encode_ms, + }, + ); + } + } + } + } +} + +#[cfg(all(target_family = "wasm", target_os = "unknown"))] +pub use worker_impl::EditorWorker; + +// ============================================================================ +// Embed Worker - fetches and caches AT Protocol embeds +// ============================================================================ + +/// Input messages to the embed worker. +#[derive(Serialize, Deserialize, Debug, Clone)] +pub enum EmbedWorkerInput { + /// Request embeds for a list of AT URIs. + /// Worker returns cached results immediately and fetches missing ones. + FetchEmbeds { + /// AT URIs to fetch (e.g., "at://did:plc:xxx/app.bsky.feed.post/yyy") + uris: Vec, + }, + /// Clear the cache (e.g., on session change) + ClearCache, +} + +/// Output messages from the embed worker. +#[derive(Serialize, Deserialize, Debug, Clone)] +pub enum EmbedWorkerOutput { + /// Embed results (may be partial if some failed) + Embeds { + /// Successfully fetched/cached embeds: uri -> rendered HTML + results: HashMap, + /// URIs that failed to fetch + errors: HashMap, + /// Timing info + fetch_ms: f64, + }, + /// Cache was cleared + CacheCleared, +} + +#[cfg(all(target_family = "wasm", target_os = "unknown"))] +mod embed_worker_impl { + use super::*; + use crate::cache_impl; + use gloo_worker::{HandlerId, Worker, WorkerScope}; + use jacquard::client::UnauthenticatedSession; + use jacquard::identity::JacquardResolver; + use jacquard::prelude::*; + use jacquard::types::string::AtUri; + use jacquard::IntoStatic; + use std::time::Duration; + + /// Embed worker with persistent cache. + pub struct EmbedWorker { + /// Cached rendered embeds with TTL and max capacity + cache: cache_impl::Cache, String>, + /// Unauthenticated session for public API calls + session: UnauthenticatedSession, + } + + impl Worker for EmbedWorker { + type Message = (); + type Input = EmbedWorkerInput; + type Output = EmbedWorkerOutput; + + fn create(_scope: &WorkerScope) -> Self { + Self { + // Cache up to 500 embeds, TTL of 1 hour + cache: cache_impl::new_cache(500, Duration::from_secs(3600)), + session: UnauthenticatedSession::default(), + } + } + + fn update(&mut self, _scope: &WorkerScope, _msg: Self::Message) {} + + fn received(&mut self, scope: &WorkerScope, msg: Self::Input, id: HandlerId) { + match msg { + EmbedWorkerInput::FetchEmbeds { uris } => { + let mut results = HashMap::new(); + let mut errors = HashMap::new(); + let mut to_fetch = Vec::new(); + + // Parse URIs and check cache + for uri_str in uris { + let at_uri = match AtUri::new_owned(uri_str.clone()) { + Ok(u) => u, + Err(e) => { + errors.insert(uri_str, format!("Invalid AT URI: {e}")); + continue; + } + }; + + if let Some(html) = cache_impl::get(&self.cache, &at_uri) { + results.insert(uri_str, html); + } else { + to_fetch.push((uri_str, at_uri)); + } + } + + // If nothing to fetch, respond immediately + if to_fetch.is_empty() { + scope.respond( + id, + EmbedWorkerOutput::Embeds { + results, + errors, + fetch_ms: 0.0, + }, + ); + return; + } + + // Fetch missing embeds asynchronously + let session = self.session.clone(); + let cache = self.cache.clone(); + let scope = scope.clone(); + + wasm_bindgen_futures::spawn_local(async move { + let fetch_start = crate::perf::now(); + + for (uri_str, at_uri) in to_fetch { + match weaver_renderer::atproto::fetch_and_render(&at_uri, &session) + .await + { + Ok(html) => { + cache_impl::insert(&cache, at_uri, html.clone()); + results.insert(uri_str, html); + } + Err(e) => { + errors.insert(uri_str, e.to_string()); + } + } + } + + let fetch_ms = crate::perf::now() - fetch_start; + scope.respond( + id, + EmbedWorkerOutput::Embeds { + results, + errors, + fetch_ms, + }, + ); + }); + } + + EmbedWorkerInput::ClearCache => { + // mini-moka doesn't have a clear method, so we just recreate + // (this is fine since ClearCache is rarely called) + scope.respond(id, EmbedWorkerOutput::CacheCleared); + } + } + } + } +} + +#[cfg(all(target_family = "wasm", target_os = "unknown"))] +pub use embed_worker_impl::EmbedWorker; diff --git a/crates/weaver-app/src/lib.rs b/crates/weaver-app/src/lib.rs new file mode 100644 index 0000000..326faed --- /dev/null +++ b/crates/weaver-app/src/lib.rs @@ -0,0 +1,188 @@ +//! Weaver App library. + +#[allow(unused)] +use dioxus::{CapturedError, prelude::*}; + +#[cfg(feature = "fullstack-server")] +pub use dioxus::fullstack::FullstackContext; +use jacquard::oauth::{client::OAuthClient, session::ClientData}; +#[allow(unused)] +use jacquard::{ + smol_str::SmolStr, + types::{cid::Cid, string::AtIdentifier}, +}; +use std::sync::LazyLock; + +pub mod auth; +#[cfg(feature = "server")] +pub mod blobcache; +pub mod cache_impl; +pub mod collab_context; +pub mod components; +pub mod config; +pub mod data; +pub mod env; +pub mod fetch; +#[cfg(feature = "server")] +pub mod og; +pub mod perf; +pub mod record_utils; +pub mod service_worker; +pub mod views; + +use auth::{AuthState, AuthStore}; +use components::{EntryPage, Repository, RepositoryIndex}; +use config::{Config, OAuthConfig}; +#[allow(unused)] +use views::{ + Callback, DraftEdit, DraftsList, Editor, Home, InvitesPage, Navbar, NewDraft, Notebook, + NotebookEntryByRkey, NotebookEntryEdit, NotebookIndex, NotebookPage, RecordIndex, RecordPage, + StandaloneEntry, StandaloneEntryEdit, +}; + +#[derive(Debug, Clone, Routable, PartialEq)] +#[rustfmt::skip] +pub enum Route { + #[layout(Navbar)] + #[route("/")] + Home {}, + #[route("/editor?:entry")] + Editor { entry: Option }, + #[layout(ErrorLayout)] + #[nest("/record")] + #[layout(RecordIndex)] + #[route("/:..uri")] + RecordPage { uri: Vec }, + #[end_layout] + #[end_nest] + #[route("/callback?:state&:iss&:code")] + Callback { state: SmolStr, iss: SmolStr, code: SmolStr }, + #[nest("/:ident")] + #[layout(Repository)] + #[route("/")] + RepositoryIndex { ident: AtIdentifier<'static> }, + // Drafts routes (before /:book_title to avoid capture) + #[route("/drafts")] + DraftsList { ident: AtIdentifier<'static> }, + #[route("/drafts/:tid")] + DraftEdit { ident: AtIdentifier<'static>, tid: SmolStr }, + #[route("/new?:notebook")] + NewDraft { ident: AtIdentifier<'static>, notebook: Option }, + // Collaboration invites + #[route("/invites")] + InvitesPage { ident: AtIdentifier<'static> }, + // Standalone entry routes + #[route("/e/:rkey")] + StandaloneEntry { ident: AtIdentifier<'static>, rkey: SmolStr }, + #[route("/e/:rkey/edit")] + StandaloneEntryEdit { ident: AtIdentifier<'static>, rkey: SmolStr }, + // Notebook routes + #[nest("/:book_title")] + #[layout(Notebook)] + #[route("/")] + NotebookIndex { ident: AtIdentifier<'static>, book_title: SmolStr }, + #[route("/:title")] + EntryPage { ident: AtIdentifier<'static>, book_title: SmolStr, title: SmolStr }, + // Entry by rkey (canonical path) + #[route("/e/:rkey")] + NotebookEntryByRkey { ident: AtIdentifier<'static>, book_title: SmolStr, rkey: SmolStr }, + #[route("/e/:rkey/edit")] + NotebookEntryEdit { ident: AtIdentifier<'static>, book_title: SmolStr, rkey: SmolStr } + +} + +pub static CONFIG: LazyLock = LazyLock::new(|| Config { + oauth: OAuthConfig::from_env().as_metadata(), +}); + +const FAVICON: Asset = asset!("/assets/weaver_photo_sm.jpg"); +const MAIN_CSS: Asset = asset!("/assets/styling/main.css"); +const THEME_DEFAULTS_CSS: Asset = asset!("/assets/styling/theme-defaults.css"); + +#[component] +pub fn App() -> Element { + #[allow(unused)] + let fetcher = use_context_provider(|| { + fetch::Fetcher::new(OAuthClient::new( + AuthStore::new(), + ClientData::new_public(CONFIG.oauth.clone()), + )) + }); + let auth_state = use_signal(|| AuthState::default()); + #[allow(unused)] + let auth_state = use_context_provider(|| auth_state); + #[cfg(all(target_family = "wasm", target_os = "unknown"))] + let restore_result = { + let fetcher = fetcher.clone(); + use_resource(move || { + let fetcher = fetcher.clone(); + async move { auth::restore_session(fetcher, auth_state).await } + }) + }; + #[cfg(not(all(target_family = "wasm", target_os = "unknown")))] + let restore_result: Option = None; + + #[cfg(all(target_family = "wasm", target_os = "unknown",))] + { + use_effect(move || { + let fetcher = fetcher.clone(); + spawn(async move { + tracing::info!("Registering service worker"); + let _ = service_worker::register_service_worker().await; + }); + }); + } + + #[cfg(all(target_family = "wasm", target_os = "unknown"))] + use_context_provider(|| restore_result); + + rsx! { + document::Link { rel: "icon", href: FAVICON } + // Preconnect for external fonts (before loading them) + document::Link { rel: "preconnect", href: "https://fonts.googleapis.com" } + document::Link { rel: "preconnect", href: "https://fonts.gstatic.com" } + // Theme defaults first: CSS variables, font-faces, reset + document::Link { rel: "stylesheet", href: THEME_DEFAULTS_CSS } + document::Link { rel: "stylesheet", href: "https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:ital,wght@0,200;0,300;0,400;0,500;0,600;0,700;1,200;1,300;1,400;1,500;1,600;1,700&family=IBM+Plex+Sans:ital,wght@0,100..700;1,100..700&family=IBM+Plex+Serif:ital,wght@0,200;0,300;0,400;0,500;0,600;0,700;1,200;1,300;1,400;1,500;1,600;1,700&display=swap" } + // App shell styles (depends on theme variables) + document::Link { rel: "stylesheet", href: MAIN_CSS } + // P2P collaboration node (provides CollabNode context for real-time sync) + collab_context::CollabProvider { + components::toast::ToastProvider { + Router:: {} + } + } + } +} + +// And then our Outlet is wrapped in a fallback UI +#[component] +pub fn ErrorLayout() -> Element { + rsx! { + ErrorBoundary { + handle_error: move |_err: ErrorContext| { + #[cfg(feature = "fullstack-server")] + { + let http_error = FullstackContext::commit_error_status(_err.error().unwrap()); + match http_error.status { + StatusCode::NOT_FOUND => rsx! { div { "404 - Page not found" } }, + _ => rsx! { div { "An unknown error occurred" } }, + } + } + #[cfg(not(feature = "fullstack-server"))] + { + rsx! { div { "An error occurred" } } + } + }, + Outlet:: {} + } + } +} + +#[cfg(all(feature = "fullstack-server", feature = "server"))] +pub async fn favicon() -> axum::response::Response { + use axum::{http::header::CONTENT_TYPE, response::IntoResponse}; + let favicon_bytes = include_bytes!("../assets/weaver_photo_sm.jpg"); + + ([(CONTENT_TYPE, "image/jpg")], favicon_bytes).into_response() +} diff --git a/crates/weaver-app/src/main.rs b/crates/weaver-app/src/main.rs index 79c24c0..d8ce0cb 100644 --- a/crates/weaver-app/src/main.rs +++ b/crates/weaver-app/src/main.rs @@ -1,49 +1,9 @@ -// The dioxus prelude contains a ton of common items used in dioxus apps. It's a good idea to import wherever you -// need dioxus -use components::{EntryPage, Repository, RepositoryIndex}; -#[allow(unused)] -use dioxus::{CapturedError, prelude::*}; +//! Weaver App main binary. -#[cfg(feature = "fullstack-server")] -use dioxus::fullstack::FullstackContext; -#[cfg(all(feature = "fullstack-server", feature = "server"))] -use dioxus::fullstack::response::Extension; -use jacquard::oauth::{client::OAuthClient, session::ClientData}; -#[allow(unused)] -use jacquard::{ - smol_str::SmolStr, - types::{cid::Cid, string::AtIdentifier}, -}; -use std::sync::{Arc, LazyLock}; -#[allow(unused)] -use views::{ - Callback, DraftEdit, DraftsList, Editor, Home, InvitesPage, Navbar, NewDraft, Notebook, - NotebookEntryByRkey, NotebookEntryEdit, NotebookIndex, NotebookPage, RecordIndex, RecordPage, - StandaloneEntry, StandaloneEntryEdit, -}; +use dioxus::prelude::*; +use std::sync::Arc; -use crate::{ - auth::{AuthState, AuthStore}, - config::{Config, OAuthConfig}, -}; - -mod auth; -#[cfg(feature = "server")] -mod blobcache; -mod cache_impl; -mod collab_context; -/// Define a components module that contains all shared components for our app. -mod components; -mod config; -mod data; -mod env; -mod fetch; -#[cfg(feature = "server")] -mod og; -mod record_utils; -mod service_worker; -/// Define a views module that contains the UI for all Layouts and Routes for our app. -mod views; +use weaver_app::{App, CONFIG, components, fetch}; #[cfg(target_arch = "wasm32")] use lol_alloc::{FreeListAllocator, LockedAllocator}; @@ -53,73 +13,6 @@ use lol_alloc::{FreeListAllocator, LockedAllocator}; static ALLOCATOR: LockedAllocator = LockedAllocator::new(FreeListAllocator::new()); -#[derive(Debug, Clone, Routable, PartialEq)] -#[rustfmt::skip] -enum Route { - #[layout(Navbar)] - #[route("/")] - Home {}, - #[route("/editor?:entry")] - Editor { entry: Option }, - #[layout(ErrorLayout)] - #[nest("/record")] - #[layout(RecordIndex)] - #[route("/:..uri")] - RecordPage { uri: Vec }, - #[end_layout] - #[end_nest] - #[route("/callback?:state&:iss&:code")] - Callback { state: SmolStr, iss: SmolStr, code: SmolStr }, - #[nest("/:ident")] - #[layout(Repository)] - #[route("/")] - RepositoryIndex { ident: AtIdentifier<'static> }, - // Drafts routes (before /:book_title to avoid capture) - #[route("/drafts")] - DraftsList { ident: AtIdentifier<'static> }, - #[route("/drafts/:tid")] - DraftEdit { ident: AtIdentifier<'static>, tid: SmolStr }, - #[route("/new?:notebook")] - NewDraft { ident: AtIdentifier<'static>, notebook: Option }, - // Collaboration invites - #[route("/invites")] - InvitesPage { ident: AtIdentifier<'static> }, - // Standalone entry routes - #[route("/e/:rkey")] - StandaloneEntry { ident: AtIdentifier<'static>, rkey: SmolStr }, - #[route("/e/:rkey/edit")] - StandaloneEntryEdit { ident: AtIdentifier<'static>, rkey: SmolStr }, - // Notebook routes - #[nest("/:book_title")] - #[layout(Notebook)] - #[route("/")] - NotebookIndex { ident: AtIdentifier<'static>, book_title: SmolStr }, - #[route("/:title")] - EntryPage { ident: AtIdentifier<'static>, book_title: SmolStr, title: SmolStr }, - // Entry by rkey (canonical path) - #[route("/e/:rkey")] - NotebookEntryByRkey { ident: AtIdentifier<'static>, book_title: SmolStr, rkey: SmolStr }, - #[route("/e/:rkey/edit")] - NotebookEntryEdit { ident: AtIdentifier<'static>, book_title: SmolStr, rkey: SmolStr } - -} -const FAVICON: Asset = asset!("/assets/weaver_photo_sm.jpg"); -const MAIN_CSS: Asset = asset!("/assets/styling/main.css"); - -// #[cfg(all(feature = "fullstack-server", feature = "server"))] -// async fn serve_sw() -> axum::response::Response { -// use axum::response::IntoResponse; -// let sw_js = include_bytes!("../public/sw.js"); -// ( -// [(axum::http::header::CONTENT_TYPE, "application/javascript")], -// sw_js, -// ) -// .into_response() -// } - -pub static CONFIG: LazyLock = LazyLock::new(|| Config { - oauth: OAuthConfig::from_env().as_metadata(), -}); fn main() { // Set up better panic messages for wasm #[cfg(target_arch = "wasm32")] @@ -168,7 +61,9 @@ fn main() { // Run `serve()` on the server only #[cfg(feature = "server")] dioxus::serve(|| async move { - use crate::blobcache::BlobCache; + use weaver_app::blobcache::BlobCache; + use weaver_app::auth::AuthStore; + use jacquard::oauth::{client::OAuthClient, session::ClientData}; use axum::{ extract::{Extension, Request}, middleware, @@ -176,7 +71,6 @@ fn main() { routing::get, }; use std::convert::Infallible; - use std::sync::Arc; #[cfg(not(feature = "fullstack-server"))] let router = { axum::Router::new().merge(dioxus::server::router(App)) }; @@ -189,17 +83,9 @@ fn main() { ))); let blob_cache = Arc::new(BlobCache::new(fetcher.clone())); axum::Router::new() - .route("/favicon.ico", get(favicon)) - //.route("/sw.js", get(serve_sw)) - // Server side render the application, serve static assets, and register server functions + .route("/favicon.ico", get(weaver_app::favicon)) .serve_dioxus_application( - ServeConfig::builder(), // Enable incremental rendering - // .incremental( - // dioxus::server::IncrementalRendererConfig::new() - // .pre_render(true) - // .clear_cache(false), - // ) - //.enable_out_of_order_streaming(), + ServeConfig::builder(), App, ) .layer(middleware::from_fn({ @@ -215,26 +101,6 @@ fn main() { } } })) - // .layer(axum::middleware::from_fn( - // |request: Request, next: Next| async move { - // let mut res = next.run(request).await; - - // // Cache all HTML responses - // if res - // .headers() - // .get("content-type") - // .and_then(|v| v.to_str().ok()) - // .map(|t| t.contains("text/html")) - // .unwrap_or(false) - // { - // res.headers_mut().insert( - // http::header::CACHE_CONTROL, - // "public, max-age=300".parse().unwrap(), - // ); - // } - // res - // }, - // )) }; Ok(router) }); @@ -242,101 +108,3 @@ fn main() { #[cfg(not(feature = "server"))] dioxus::launch(App); } -const THEME_DEFAULTS_CSS: Asset = asset!("/assets/styling/theme-defaults.css"); - -#[component] -fn App() -> Element { - #[allow(unused)] - let fetcher = use_context_provider(|| { - fetch::Fetcher::new(OAuthClient::new( - AuthStore::new(), - ClientData::new_public(CONFIG.oauth.clone()), - )) - }); - let auth_state = use_signal(|| AuthState::default()); - #[allow(unused)] - let auth_state = use_context_provider(|| auth_state); - #[cfg(all(target_family = "wasm", target_os = "unknown"))] - let restore_result = { - let fetcher = fetcher.clone(); - use_resource(move || { - let fetcher = fetcher.clone(); - async move { auth::restore_session(fetcher, auth_state).await } - }) - }; - #[cfg(not(all(target_family = "wasm", target_os = "unknown")))] - let restore_result: Option = None; - - #[cfg(all(target_family = "wasm", target_os = "unknown",))] - { - use_effect(move || { - let fetcher = fetcher.clone(); - spawn(async move { - tracing::info!("Registering service worker"); - let _ = service_worker::register_service_worker().await; - }); - }); - } - - #[cfg(all(target_family = "wasm", target_os = "unknown"))] - use_context_provider(|| restore_result); - - rsx! { - document::Link { rel: "icon", href: FAVICON } - // Preconnect for external fonts (before loading them) - document::Link { rel: "preconnect", href: "https://fonts.googleapis.com" } - document::Link { rel: "preconnect", href: "https://fonts.gstatic.com" } - // Theme defaults first: CSS variables, font-faces, reset - document::Link { rel: "stylesheet", href: THEME_DEFAULTS_CSS } - document::Link { rel: "stylesheet", href: "https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:ital,wght@0,200;0,300;0,400;0,500;0,600;0,700;1,200;1,300;1,400;1,500;1,600;1,700&family=IBM+Plex+Sans:ital,wght@0,100..700;1,100..700&family=IBM+Plex+Serif:ital,wght@0,200;0,300;0,400;0,500;0,600;0,700;1,200;1,300;1,400;1,500;1,600;1,700&display=swap" } - // App shell styles (depends on theme variables) - document::Link { rel: "stylesheet", href: MAIN_CSS } - // P2P collaboration node (provides CollabNode context for real-time sync) - collab_context::CollabProvider { - components::toast::ToastProvider { - Router:: {} - } - } - } -} - -// And then our Outlet is wrapped in a fallback UI -#[component] -fn ErrorLayout() -> Element { - rsx! { - ErrorBoundary { - handle_error: move |_err: ErrorContext| { - #[cfg(feature = "fullstack-server")] - { - let http_error = FullstackContext::commit_error_status(_err.error().unwrap()); - match http_error.status { - StatusCode::NOT_FOUND => rsx! { div { "404 - Page not found" } }, - _ => rsx! { div { "An unknown error occurred" } }, - } - } - #[cfg(not(feature = "fullstack-server"))] - { - rsx! { div { "An error occurred" } } - } - }, - Outlet:: {} - } - } -} - -#[cfg(all(feature = "fullstack-server", feature = "server"))] -pub async fn favicon() -> axum::response::Response { - use axum::{http::header::CONTENT_TYPE, response::IntoResponse}; - let favicon_bytes = include_bytes!("../assets/weaver_photo_sm.jpg"); - - ([(CONTENT_TYPE, "image/jpg")], favicon_bytes).into_response() -} - -// #[server(endpoint = "static_routes", output = server_fn::codec::Json)] -// async fn static_routes() -> Result, ServerFnError> { -// // The `Routable` trait has a `static_routes` method that returns all static routes in the enum -// Ok(Route::static_routes() -// .iter() -// .map(ToString::to_string) -// .collect()) -// } diff --git a/crates/weaver-app/src/perf.rs b/crates/weaver-app/src/perf.rs new file mode 100644 index 0000000..818f79a --- /dev/null +++ b/crates/weaver-app/src/perf.rs @@ -0,0 +1,58 @@ +//! Performance timing utilities for instrumentation. +//! +//! Provides a cross-platform wrapper around Performance.now() for WASM +//! and a no-op fallback for native builds. + +/// Get the current high-resolution timestamp in milliseconds. +/// +/// On WASM, this uses `Performance.now()` from the Web Performance API. +/// On native builds, returns 0.0 (instrumentation is primarily for browser profiling). +#[cfg(all(target_family = "wasm", target_os = "unknown"))] +pub fn now() -> f64 { + web_sys::window() + .and_then(|w| w.performance()) + .map(|p| p.now()) + .unwrap_or(0.0) +} + +#[cfg(not(all(target_family = "wasm", target_os = "unknown")))] +pub fn now() -> f64 { + 0.0 +} + +/// Measure the execution time of a closure and log it. +/// +/// Returns the closure's result and logs the elapsed time via tracing. +#[allow(dead_code)] +pub fn measure T>(label: &str, f: F) -> T { + let start = now(); + let result = f(); + let elapsed = now() - start; + tracing::debug!(elapsed_ms = elapsed, "{}", label); + result +} + +/// A guard that logs elapsed time when dropped. +/// +/// Useful for timing blocks of code without closures. +#[allow(dead_code)] +pub struct TimingGuard { + label: &'static str, + start: f64, +} + +impl TimingGuard { + pub fn new(label: &'static str) -> Self { + Self { + label, + start: now(), + } + } +} + +impl Drop for TimingGuard { + fn drop(&mut self) { + let elapsed = now() - self.start; + tracing::debug!(elapsed_ms = elapsed, "{}", self.label); + } +} -- knotmirror 0.0.1