fix headless
@@ -76,6 +76,18 @@ dependencies = [
|
||||
"winit",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "accessory"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "850bb534b9dc04744fbbb71d30ad6d25a7e4cf6dc33e223c81ef3a92ebab4e0b"
|
||||
dependencies = [
|
||||
"macroific",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.58",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "addr2line"
|
||||
version = "0.21.0"
|
||||
@@ -339,7 +351,7 @@ checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.55",
|
||||
"syn 2.0.58",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -404,7 +416,7 @@ checksum = "f28243a43d821d11341ab73c80bed182dc015c514b951616cf79bd4af39af0c3"
|
||||
dependencies = [
|
||||
"concurrent-queue",
|
||||
"event-listener 5.2.0",
|
||||
"event-listener-strategy 0.5.0",
|
||||
"event-listener-strategy 0.5.1",
|
||||
"futures-core",
|
||||
"pin-project-lite",
|
||||
]
|
||||
@@ -434,9 +446,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "async-executor"
|
||||
version = "1.8.0"
|
||||
version = "1.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "17ae5ebefcc48e7452b4987947920dac9450be1110cadf34d1b8c116bdbaf97c"
|
||||
checksum = "10b3e585719c2358d2660232671ca8ca4ddb4be4ce8a1842d6c2dc8685303316"
|
||||
dependencies = [
|
||||
"async-lock 3.3.0",
|
||||
"async-task",
|
||||
@@ -601,7 +613,7 @@ checksum = "a507401cad91ec6a857ed5513a2073c82a9b9048762b885bb98655b306964681"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.55",
|
||||
"syn 2.0.58",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -724,7 +736,7 @@ checksum = "b3c488161a04a123e10273e16d4533945943fcfcf345f066242790e8977aee2d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.55",
|
||||
"syn 2.0.58",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -835,7 +847,7 @@ checksum = "adda0f1b91d2e34b132ea29a14dbe2adf273073c168950603ebddaf53a23947f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.55",
|
||||
"syn 2.0.58",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -847,7 +859,7 @@ dependencies = [
|
||||
"bevy_macro_utils",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.55",
|
||||
"syn 2.0.58",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -935,7 +947,7 @@ checksum = "7de77523d154e220a740e568a89f52fac7de481374bdecbbbeb283a37580ba34"
|
||||
dependencies = [
|
||||
"bevy_macro_utils",
|
||||
"quote",
|
||||
"syn 2.0.55",
|
||||
"syn 2.0.58",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -983,7 +995,7 @@ dependencies = [
|
||||
"bevy_macro_utils",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.55",
|
||||
"syn 2.0.58",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1031,7 +1043,7 @@ checksum = "fa29be733a02a5d7ca4507ef15f294711c1a0884b9a9a2730640ff4e7d0200ab"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.55",
|
||||
"syn 2.0.58",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1081,7 +1093,7 @@ dependencies = [
|
||||
"bevy_macro_utils",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.55",
|
||||
"syn 2.0.58",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1207,7 +1219,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rustc-hash",
|
||||
"syn 2.0.55",
|
||||
"syn 2.0.58",
|
||||
"toml_edit 0.21.1",
|
||||
]
|
||||
|
||||
@@ -1518,7 +1530,7 @@ dependencies = [
|
||||
"bevy_macro_utils",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.55",
|
||||
"syn 2.0.58",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
@@ -1576,7 +1588,7 @@ dependencies = [
|
||||
"bevy_macro_utils",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.55",
|
||||
"syn 2.0.58",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1754,7 +1766,7 @@ checksum = "014c80f466ed01821a2e602d63cd5076915c1af5de5fa3c074cc4a9ca898ada7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.55",
|
||||
"syn 2.0.58",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1838,7 +1850,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3e1ef1d5e328abe1b76df974245f78e17fd17867583883d5e77444c6a8223a64"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn 2.0.55",
|
||||
"syn 2.0.58",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1858,7 +1870,7 @@ dependencies = [
|
||||
"regex",
|
||||
"rustc-hash",
|
||||
"shlex",
|
||||
"syn 2.0.55",
|
||||
"syn 2.0.58",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2043,7 +2055,7 @@ checksum = "4da9a32f3fed317401fa3c862968128267c3106685286e15d5aaa3d7389c2f60"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.55",
|
||||
"syn 2.0.58",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2182,7 +2194,7 @@ dependencies = [
|
||||
"heck 0.5.0",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.55",
|
||||
"syn 2.0.58",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2614,6 +2626,18 @@ dependencies = [
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "delegate-display"
|
||||
version = "2.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "98a85201f233142ac819bbf6226e36d0b5e129a47bd325084674261c82d4cd66"
|
||||
dependencies = [
|
||||
"macroific",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.58",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_more"
|
||||
version = "0.99.17"
|
||||
@@ -2820,7 +2844,7 @@ checksum = "92959a9e8d13eaa13b8ae8c7b583c3bf1669ca7a8e7708a088d12587ba86effc"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.55",
|
||||
"syn 2.0.58",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2850,7 +2874,7 @@ checksum = "f282cfdfe92516eb26c2af8589c274c7c17681f5ecc03c18255fe741c6aa64eb"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.55",
|
||||
"syn 2.0.58",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2861,7 +2885,7 @@ checksum = "6fd000fd6988e73bbe993ea3db9b1aa64906ab88766d654973924340c8cddb42"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.55",
|
||||
"syn 2.0.58",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2960,9 +2984,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "event-listener-strategy"
|
||||
version = "0.5.0"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "feedafcaa9b749175d5ac357452a9d41ea2911da598fde46ce1fe02c37751291"
|
||||
checksum = "332f51cb23d20b0de8458b86580878211da09bcd4503cb579c225b3d124cabb3"
|
||||
dependencies = [
|
||||
"event-listener 5.2.0",
|
||||
"pin-project-lite",
|
||||
@@ -2984,6 +3008,18 @@ dependencies = [
|
||||
"zune-inflate",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fancy_constructor"
|
||||
version = "1.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f71f317e4af73b2f8f608fac190c52eac4b1879d2145df1db2fe48881ca69435"
|
||||
dependencies = [
|
||||
"macroific",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.58",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fastrand"
|
||||
version = "1.9.0"
|
||||
@@ -3072,7 +3108,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.55",
|
||||
"syn 2.0.58",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3197,7 +3233,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.55",
|
||||
"syn 2.0.58",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3416,7 +3452,7 @@ dependencies = [
|
||||
"inflections",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.55",
|
||||
"syn 2.0.58",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3521,9 +3557,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "h2"
|
||||
version = "0.4.3"
|
||||
version = "0.4.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "51ee2dd2e4f378392eeff5d51618cd9a63166a2513846bbc55f21cfacd9199d4"
|
||||
checksum = "816ec7294445779408f36fe57bc5b7fc1cf59664059096c65f905c1c61f58069"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"fnv",
|
||||
@@ -3815,9 +3851,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "image"
|
||||
version = "0.25.0"
|
||||
version = "0.25.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a9b4f005360d32e9325029b38ba47ebd7a56f3316df09249368939562d518645"
|
||||
checksum = "fd54d660e773627692c524beaad361aca785a4f9f5730ce91f42aabe5bce3d11"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
"byteorder",
|
||||
@@ -3858,6 +3894,23 @@ version = "1.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "44feda355f4159a7c757171a77de25daf6411e217b4cabd03bd6650690468126"
|
||||
|
||||
[[package]]
|
||||
name = "indexed_db_futures"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6cc2083760572ee02385ab8b7c02c20925d2dd1f97a1a25a8737a238608f1152"
|
||||
dependencies = [
|
||||
"accessory",
|
||||
"cfg-if",
|
||||
"delegate-display",
|
||||
"fancy_constructor",
|
||||
"js-sys",
|
||||
"uuid",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.2.6"
|
||||
@@ -3920,7 +3973,7 @@ checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.55",
|
||||
"syn 2.0.58",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4235,6 +4288,53 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "macroific"
|
||||
version = "1.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f05c00ac596022625d01047c421a0d97d7f09a18e429187b341c201cb631b9dd"
|
||||
dependencies = [
|
||||
"macroific_attr_parse",
|
||||
"macroific_core",
|
||||
"macroific_macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "macroific_attr_parse"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fd94d5da95b30ae6e10621ad02340909346ad91661f3f8c0f2b62345e46a2f67"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.58",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "macroific_core"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "13198c120864097a565ccb3ff947672d969932b7975ebd4085732c9f09435e55"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.58",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "macroific_macro"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b0c9853143cbed7f1e41dc39fee95f9b361bec65c8dc2a01bf609be01b61f5ae"
|
||||
dependencies = [
|
||||
"macroific_attr_parse",
|
||||
"macroific_core",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.58",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "malloc_buf"
|
||||
version = "0.0.6"
|
||||
@@ -4596,7 +4696,7 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.55",
|
||||
"syn 2.0.58",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4658,7 +4758,7 @@ dependencies = [
|
||||
"proc-macro-crate",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.55",
|
||||
"syn 2.0.58",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4824,7 +4924,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.55",
|
||||
"syn 2.0.58",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4835,9 +4935,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
|
||||
|
||||
[[package]]
|
||||
name = "openssl-sys"
|
||||
version = "0.9.101"
|
||||
version = "0.9.102"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dda2b0f344e78efc2facf7d195d098df0dd72151b26ab98da807afc26c198dff"
|
||||
checksum = "c597637d56fbc83893a35eb0dd04b2b8e7a50c91e64e9493e398b5df4fb45fa2"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
@@ -4970,14 +5070,14 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.55",
|
||||
"syn 2.0.58",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
version = "0.2.13"
|
||||
version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58"
|
||||
checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02"
|
||||
|
||||
[[package]]
|
||||
name = "pin-utils"
|
||||
@@ -5148,7 +5248,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8021cf59c8ec9c432cfc2526ac6b8aa508ecaf29cd415f271b8406c1b851c3fd"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn 2.0.55",
|
||||
"syn 2.0.58",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5785,9 +5885,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "security-framework"
|
||||
version = "2.9.2"
|
||||
version = "2.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de"
|
||||
checksum = "770452e37cad93e0a50d5abc3990d2bc351c36d0328f86cefec2f2fb206eaef6"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"core-foundation",
|
||||
@@ -5798,9 +5898,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "security-framework-sys"
|
||||
version = "2.9.1"
|
||||
version = "2.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a"
|
||||
checksum = "41f3cc463c0ef97e11c3461a9d3787412d30e8e7eb907c79180c4a57bf7c04ef"
|
||||
dependencies = [
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
@@ -5855,7 +5955,7 @@ checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.55",
|
||||
"syn 2.0.58",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5888,7 +5988,7 @@ checksum = "0b2e6b945e9d3df726b65d6ee24060aff8e3533d431f677a9695db04eff9dfdb"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.55",
|
||||
"syn 2.0.58",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -6301,9 +6401,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.11.0"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01"
|
||||
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||
|
||||
[[package]]
|
||||
name = "studio"
|
||||
@@ -6323,12 +6423,15 @@ dependencies = [
|
||||
"bytemuck",
|
||||
"clap",
|
||||
"console_error_panic_hook",
|
||||
"dashmap",
|
||||
"futures",
|
||||
"futures-intrusive",
|
||||
"futures-lite 2.3.0",
|
||||
"image 0.25.0",
|
||||
"image 0.25.1",
|
||||
"indexed_db_futures",
|
||||
"itertools",
|
||||
"js-sys",
|
||||
"once_cell",
|
||||
"parking_lot",
|
||||
"reqwest",
|
||||
"serde",
|
||||
@@ -6411,9 +6514,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.55"
|
||||
version = "2.0.58"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "002a1b3dbf967edfafc32655d0f377ab0bb7b994aa1d32c8cc7e9b8bf3ebb8f0"
|
||||
checksum = "44cfb93f38070beee36b3fef7d4f5a16f27751d94b187b666a5cc5e9b0d30687"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -6428,9 +6531,9 @@ checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160"
|
||||
|
||||
[[package]]
|
||||
name = "sysinfo"
|
||||
version = "0.30.7"
|
||||
version = "0.30.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c385888ef380a852a16209afc8cfad22795dd8873d69c9a14d2e2088f118d18"
|
||||
checksum = "4b1a378e48fb3ce3a5cf04359c456c9c98ff689bcf1c1bc6e6a31f247686f275"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"core-foundation-sys",
|
||||
@@ -6536,7 +6639,7 @@ checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.55",
|
||||
"syn 2.0.58",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -6771,7 +6874,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.55",
|
||||
"syn 2.0.58",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -7013,12 +7116,13 @@ checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "1.8.0"
|
||||
version = "1.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0"
|
||||
checksum = "5e395fcf16a7a3d8127ec99782007af141946b4795001f876d54fb0d55978560"
|
||||
dependencies = [
|
||||
"getrandom 0.2.12",
|
||||
"serde",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -7126,7 +7230,7 @@ dependencies = [
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.55",
|
||||
"syn 2.0.58",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
@@ -7160,7 +7264,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.55",
|
||||
"syn 2.0.58",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
@@ -7940,9 +8044,9 @@ checksum = "054a8e68b76250b253f671d1268cb7f1ae089ec35e195b2efb2a4e9a836d0621"
|
||||
|
||||
[[package]]
|
||||
name = "xml-rs"
|
||||
version = "0.8.19"
|
||||
version = "0.8.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fcb9cbac069e033553e8bb871be2fbdffcab578eb25bd0f7c508cedc6dcd75a"
|
||||
checksum = "791978798f0597cfc70478424c2b4fdc2b7a8024aaff78497ef00f24ef674193"
|
||||
|
||||
[[package]]
|
||||
name = "xmlwriter"
|
||||
@@ -7967,7 +8071,7 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.55",
|
||||
"syn 2.0.58",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
"@vitest/ui": "~0.32.0",
|
||||
"chalk": "^5.3.0",
|
||||
"glob": "^10.3.10",
|
||||
"idb": "8.0.0",
|
||||
"jsdom": "~22.1.0",
|
||||
"nx": "16.9.1",
|
||||
"prettier": "^2.6.2",
|
||||
@@ -7025,6 +7026,12 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/idb": {
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/idb/-/idb-8.0.0.tgz",
|
||||
"integrity": "sha512-l//qvlAKGmQO31Qn7xdzagVPPaHTxXx199MhrAFuVBTPqydcPYBWjkrbv4Y0ktB+GmWOiwHl237UUOrLmQxLvw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/ieee754": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
|
||||
|
||||
@@ -39,7 +39,8 @@
|
||||
"vite-plugin-wasm": "^3.2.2",
|
||||
"vite-plugin-web-components-hmr": "^0.1.3",
|
||||
"vitest": "~0.32.0",
|
||||
"which": "^4.0.0"
|
||||
"which": "^4.0.0",
|
||||
"idb": "8.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@fortawesome/fontawesome-pro": "^6.4.2",
|
||||
|
||||
|
After Width: | Height: | Size: 217 KiB |
|
After Width: | Height: | Size: 217 KiB |
|
After Width: | Height: | Size: 217 KiB |
|
After Width: | Height: | Size: 217 KiB |
|
After Width: | Height: | Size: 217 KiB |
|
After Width: | Height: | Size: 217 KiB |
|
After Width: | Height: | Size: 217 KiB |
|
After Width: | Height: | Size: 217 KiB |
|
After Width: | Height: | Size: 217 KiB |
@@ -11,11 +11,14 @@ import { DialogElement } from "@storyteller/studio-ui/dialog";
|
||||
import {
|
||||
SceneHierarchyElement,
|
||||
StorytellerApi,
|
||||
StorytellerLocalApi,
|
||||
StorytellerRemoteApi,
|
||||
StudioURLParams,
|
||||
mockApi,
|
||||
} from "@storyteller/studio-web";
|
||||
import { match } from "@storyteller/utility";
|
||||
import { LitElement, TemplateResult, html, nothing } from "lit";
|
||||
import { customElement, state } from "lit/decorators.js";
|
||||
import { customElement, property, state } from "lit/decorators.js";
|
||||
import { classMap } from "lit/directives/class-map.js";
|
||||
import { createRef, ref } from "lit/directives/ref.js";
|
||||
import { styleMap } from "lit/directives/style-map.js";
|
||||
@@ -40,7 +43,6 @@ import "./help.element";
|
||||
import "./temp-video-preview.element";
|
||||
|
||||
import "./app.element.scss";
|
||||
import { mock_save_load } from "./util";
|
||||
|
||||
@customElement("app-root")
|
||||
@provide({
|
||||
@@ -58,7 +60,7 @@ export class AppElement extends LitElement {
|
||||
|
||||
@state() currentSidebar : string = 'hierarchy';
|
||||
|
||||
private api = new StorytellerApi();
|
||||
@property({ attribute: false }) api: StorytellerApi = new StorytellerApi(mockApi ? new StorytellerLocalApi() : new StorytellerRemoteApi());
|
||||
|
||||
#addToSceneButtonRef = createRef<ButtonElement>();
|
||||
#videoStylePreviewBtnRef = createRef<ButtonElement>();
|
||||
@@ -76,43 +78,15 @@ export class AppElement extends LitElement {
|
||||
async onSceneSaved({ detail: scene }: SceneSavedEvent): Promise<void> {
|
||||
try {
|
||||
const file = new File([scene], "scene.scn.ron", { type: "text/ron" });
|
||||
if (mock_save_load()) {
|
||||
/*
|
||||
// NB(brandon, 2024-03-22): Hey Lee-Orr, I'm commenting this out since the file
|
||||
picker isn't defined and the Netlify builds are breaking. Feel free to uncomment
|
||||
this for your testing. We probably just need a local definition file so the
|
||||
builds are happy.
|
||||
|
||||
const token = await this.api.createScene(file);
|
||||
|
||||
let handle = await window.showSaveFilePicker({
|
||||
suggestedName: "scene",
|
||||
types: [{
|
||||
description: "Scene Files",
|
||||
accept: {
|
||||
"text/ron": [".scn.ron"]
|
||||
}
|
||||
}]
|
||||
});
|
||||
let writable = await handle.createWritable();
|
||||
await writable.write(file);
|
||||
await writable.close();
|
||||
let token = handle.name;
|
||||
|
||||
this.dispatchEvent(new CustomEvent("scene-uploaded", {
|
||||
detail: token,
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
}));
|
||||
*/
|
||||
|
||||
} else {
|
||||
const token = await this.api.createScene(file);
|
||||
|
||||
this.dispatchEvent(new CustomEvent("scene-uploaded", {
|
||||
detail: token,
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
}));
|
||||
}
|
||||
this.dispatchEvent(new CustomEvent("scene-uploaded", {
|
||||
detail: token,
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
}));
|
||||
|
||||
}
|
||||
catch (err) {
|
||||
console.error(err);
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
"dependencies": {
|
||||
"@storyteller/framework": "0.0.1",
|
||||
"@storyteller/studio": "0.0.1",
|
||||
"lit": "^2.8.0"
|
||||
"lit": "^2.8.0",
|
||||
"idb": "8.0.0"
|
||||
},
|
||||
"main": "./index.js",
|
||||
"module": "./index.mjs",
|
||||
|
||||
@@ -1,7 +1,24 @@
|
||||
import axios, { AxiosResponse } from "axios";
|
||||
import { v4 as uuid } from "uuid";
|
||||
import { DBSchema, IDBPDatabase, openDB } from "idb";
|
||||
import * as studio from "@storyteller/studio";
|
||||
|
||||
export class StorytellerApi {
|
||||
export const mockApi = window.location.search.includes("mock-api");
|
||||
|
||||
interface IStorytellerApi {
|
||||
createScene(file: File): Promise<MediaFileToken>
|
||||
session(): Promise<Session>
|
||||
listMediaFiles(options: ListMediaFilesOptions): Promise<ListMediaFilesResult>
|
||||
uploadMediaFile(
|
||||
file: File,
|
||||
{ subtype, mediaClass }: UploadMediaFileOptions,
|
||||
): Promise<MediaFileToken>
|
||||
renameMediaFile(token: MediaFileToken, name: string): Promise<void>
|
||||
convertFbxToGltf(token: MediaFileToken): Promise<void>
|
||||
mediaFile(token: MediaFileToken): Promise<MediaFile>
|
||||
}
|
||||
|
||||
export class StorytellerRemoteApi implements IStorytellerApi {
|
||||
#api = axios.create({
|
||||
baseURL: "https://api.fakeyou.com",
|
||||
withCredentials: true,
|
||||
@@ -14,8 +31,8 @@ export class StorytellerApi {
|
||||
uuid_idempotency_token: uuid(),
|
||||
file,
|
||||
}, {
|
||||
headers: { Accept: "text/plain" },
|
||||
},
|
||||
headers: { Accept: "text/plain" },
|
||||
},
|
||||
);
|
||||
this.#validateResponse(response);
|
||||
|
||||
@@ -105,10 +122,352 @@ export class StorytellerApi {
|
||||
}
|
||||
}
|
||||
|
||||
interface LocalDbSchema extends DBSchema {
|
||||
'media-files': {
|
||||
key: MediaFileToken,
|
||||
value: MediaFile,
|
||||
indexes: {
|
||||
'media-type': MediaFileType,
|
||||
'media-class': MediaFileClass
|
||||
}
|
||||
},
|
||||
'file-blobs': {
|
||||
key: string,
|
||||
value: Uint8Array
|
||||
}
|
||||
}
|
||||
|
||||
export class StorytellerLocalApi implements IStorytellerApi {
|
||||
database?: IDBPDatabase<LocalDbSchema>;
|
||||
dateFormat: Intl.DateTimeFormat = new Intl.DateTimeFormat("en-us")
|
||||
|
||||
constructor() {
|
||||
document.addEventListener("request-file", async (e: studio.RequestFileEvent) => {
|
||||
const token = e.detail;
|
||||
console.log("Loading Asset: ", token);
|
||||
try {
|
||||
let file = await this.mediaFile(token);
|
||||
console.log("Got Asset File: ", token, file);
|
||||
let path = file.public_bucket_path;
|
||||
let blob = await this.mediaBlob(path);
|
||||
console.log("Got Asset Blob", token);
|
||||
studio.requestedFileReceived(token, blob);
|
||||
console.log("Set File to Bevy", token)
|
||||
} catch (e) {
|
||||
console.error("Error Loading File", token, e);
|
||||
studio.requestedFileFailed(token, e);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
async getDatabase(): Promise<IDBPDatabase<LocalDbSchema>> {
|
||||
if (this.database) {
|
||||
return this.database;
|
||||
}
|
||||
this.database = await openDB<LocalDbSchema>('storyteller-local', 1, {
|
||||
upgrade(db) {
|
||||
const media_files = db.createObjectStore('media-files');
|
||||
media_files.createIndex('media-class', 'media_class');
|
||||
media_files.createIndex('media-type', 'media_type');
|
||||
|
||||
db.createObjectStore('file-blobs');
|
||||
}
|
||||
});
|
||||
return this.database
|
||||
}
|
||||
|
||||
async createScene(file: File): Promise<string> {
|
||||
let token = uuid();
|
||||
let original_file_name = file.name;
|
||||
let filename = token + "-" + file.name;
|
||||
|
||||
let media_file: MediaFile = {
|
||||
token: token,
|
||||
media_type: MediaFileType.ScnRon,
|
||||
maybe_media_subtype: MediaFileSubtype.StorytellerScene,
|
||||
media_class: MediaFileClass.Scene,
|
||||
public_bucket_path: filename,
|
||||
creator_set_visibility: Visibility.Public,
|
||||
is_emulated_media_file: false,
|
||||
maybe_title: filename,
|
||||
maybe_original_filename: original_file_name,
|
||||
stats: {
|
||||
positive_rating_count: 0,
|
||||
bookmark_count: 0
|
||||
},
|
||||
created_at: (new Date()).toLocaleString("en-us"),
|
||||
updated_at: (new Date()).toLocaleString("en-us"),
|
||||
};
|
||||
|
||||
|
||||
let buffer = new Uint8Array(await file.arrayBuffer(), 0);
|
||||
|
||||
let db = await this.getDatabase();
|
||||
let transaction = db.transaction(['media-files', 'file-blobs'], 'readwrite');
|
||||
|
||||
await Promise.all([
|
||||
transaction.objectStore('media-files').add(media_file, token),
|
||||
transaction.objectStore('file-blobs').add(buffer, filename),
|
||||
transaction.done,
|
||||
]);
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
async session(): Promise<Session> {
|
||||
return {
|
||||
success: true,
|
||||
logged_in: true,
|
||||
user: {
|
||||
username: "local_user",
|
||||
|
||||
can_access_studio: true,
|
||||
can_approve_w21_templates: true,
|
||||
can_ban_users: true,
|
||||
can_delete_other_users_tts_models: true,
|
||||
can_delete_other_users_tts_results: true,
|
||||
can_delete_other_users_w2l_results: true,
|
||||
can_delete_other_users_w2l_templates: true,
|
||||
can_delete_own_account: true,
|
||||
can_delete_own_tts_models: true,
|
||||
can_delete_own_tts_results: true,
|
||||
can_delete_own_w2l_results: true,
|
||||
can_delete_own_w2l_templates: true,
|
||||
can_delete_users: true,
|
||||
can_edit_other_users_profiles: true,
|
||||
can_edit_other_users_tts_models: true,
|
||||
can_edit_other_users_w2l_templates: true,
|
||||
can_upload_tts_models: true,
|
||||
can_upload_w2l_templates: true,
|
||||
can_use_tts: true,
|
||||
can_use_w2l: true,
|
||||
core_info: {
|
||||
default_avatar: {
|
||||
color_index: 0,
|
||||
image_index: 0
|
||||
},
|
||||
display_name: "Local User",
|
||||
gravatar_hash: "",
|
||||
user_token: "",
|
||||
username: "local_user"
|
||||
},
|
||||
display_name: "Local User",
|
||||
email_gravatar_hash: "",
|
||||
fakeyou_plan: FakeYouPlan.Pro,
|
||||
maybe_feature_flags: {},
|
||||
storyteller_stream_plan: StorytellerStreamPlan.Pro,
|
||||
user_token: "user_token"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async listMediaFiles({ filterMediaClasses, filterMediaType }: ListMediaFilesOptions): Promise<ListMediaFilesResult> {
|
||||
let db = await this.getDatabase();
|
||||
|
||||
if (!filterMediaClasses && !filterMediaType) {
|
||||
let mediaFiles = await db.getAll('media-files');
|
||||
let media: ListMediaFilesResult = {
|
||||
pagination: {
|
||||
current: 0,
|
||||
total_page_count: 1
|
||||
},
|
||||
results: mediaFiles.map((mediaFile) => {
|
||||
let listItem: MediaFileListItem = {
|
||||
created_at: mediaFile.created_at,
|
||||
creator_set_visibility: Visibility.Public,
|
||||
media_type: mediaFile.media_type,
|
||||
origin: {
|
||||
origin_category: MediaFileOriginCategory.Upload,
|
||||
product_category: MediaFileOriginProductCategory.Unknown,
|
||||
},
|
||||
origin_category: MediaFileOriginCategory.Upload,
|
||||
origin_product_category: MediaFileOriginProductCategory.Unknown,
|
||||
public_bucket_path: mediaFile.public_bucket_path,
|
||||
token: mediaFile.token,
|
||||
updated_at: mediaFile.created_at
|
||||
};
|
||||
return listItem
|
||||
}),
|
||||
success: true
|
||||
};
|
||||
return media;
|
||||
}
|
||||
|
||||
let media: Record<string, MediaFileListItem> = {};
|
||||
|
||||
let filteredResults = (await Promise.all([(filterMediaClasses ?? []).map((filter) =>
|
||||
db.getAllFromIndex('media-files', 'media-class', filter)
|
||||
), (filterMediaType ?? []).map((filter) =>
|
||||
db.getAllFromIndex('media-files', 'media-type', filter)
|
||||
)].flat())).flat();
|
||||
|
||||
for (const mediaFile of filteredResults) {
|
||||
if (media[mediaFile.token]) {
|
||||
continue;
|
||||
}
|
||||
media[mediaFile.token] = {
|
||||
created_at: mediaFile.created_at,
|
||||
creator_set_visibility: Visibility.Public,
|
||||
media_type: mediaFile.media_type,
|
||||
origin: {
|
||||
origin_category: MediaFileOriginCategory.Upload,
|
||||
product_category: MediaFileOriginProductCategory.Unknown,
|
||||
},
|
||||
origin_category: MediaFileOriginCategory.Upload,
|
||||
origin_product_category: MediaFileOriginProductCategory.Unknown,
|
||||
public_bucket_path: mediaFile.public_bucket_path,
|
||||
token: mediaFile.token,
|
||||
updated_at: mediaFile.created_at
|
||||
};
|
||||
}
|
||||
|
||||
let result: ListMediaFilesResult = {
|
||||
pagination: {
|
||||
current: 0,
|
||||
total_page_count: 1
|
||||
},
|
||||
results: Object.keys(media).map((key) => media[key]),
|
||||
success: true
|
||||
};
|
||||
return result;
|
||||
}
|
||||
|
||||
async uploadMediaFile(file: File, { subtype, mediaClass }: UploadMediaFileOptions): Promise<string> {
|
||||
let token = uuid();
|
||||
let original_file_name = file.name;
|
||||
let filename = token + "-" + file.name;
|
||||
|
||||
let media_type: MediaFileType | false = false;
|
||||
|
||||
if (mediaClass === MediaFileClass.Audio) {
|
||||
media_type = MediaFileType.Audio;
|
||||
} else if (mediaClass === MediaFileClass.Video) {
|
||||
media_type = MediaFileType.Video;
|
||||
} else if (mediaClass === MediaFileClass.Image) {
|
||||
media_type = MediaFileType.Image;
|
||||
} else if (mediaClass === MediaFileClass.Scene) {
|
||||
if (subtype === MediaFileSubtype.StorytellerScene) {
|
||||
media_type = MediaFileType.ScnRon;
|
||||
}
|
||||
} else if (mediaClass === MediaFileClass.Animation) {
|
||||
media_type = MediaFileType.Mocap
|
||||
} else if (file.name.endsWith("gltf")) {
|
||||
media_type = MediaFileType.Gltf;
|
||||
} else if (file.name.endsWith("glb")) {
|
||||
media_type = MediaFileType.Glb;
|
||||
} else if (file.name.endsWith("bvh")) {
|
||||
media_type = MediaFileType.Bvh;
|
||||
} else if (file.name.endsWith("fbx")) {
|
||||
media_type = MediaFileType.Fbx;
|
||||
}
|
||||
|
||||
if (!media_type) {
|
||||
throw new Error("Couldn't determine media file type");
|
||||
}
|
||||
|
||||
let media_file: MediaFile = {
|
||||
token: token,
|
||||
media_type,
|
||||
media_class: mediaClass,
|
||||
public_bucket_path: filename,
|
||||
creator_set_visibility: Visibility.Public,
|
||||
is_emulated_media_file: false,
|
||||
maybe_title: original_file_name,
|
||||
maybe_original_filename: original_file_name,
|
||||
maybe_media_subtype: subtype,
|
||||
stats: {
|
||||
positive_rating_count: 0,
|
||||
bookmark_count: 0
|
||||
},
|
||||
created_at: (new Date()).toLocaleString("en-us"),
|
||||
updated_at: (new Date()).toLocaleString("en-us"),
|
||||
};
|
||||
|
||||
let buffer = new Uint8Array(await file.arrayBuffer(), 0);
|
||||
|
||||
let db = await this.getDatabase();
|
||||
|
||||
let transaction = db.transaction(['media-files', 'file-blobs'], 'readwrite');
|
||||
await Promise.all([
|
||||
transaction.objectStore('media-files').add(media_file, token),
|
||||
transaction.objectStore('file-blobs').add(buffer, filename),
|
||||
transaction.done,
|
||||
]);
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
async renameMediaFile(token: string, name: string): Promise<void> {
|
||||
let db = await this.getDatabase();
|
||||
|
||||
let transaction = db.transaction('media-files', 'readwrite');
|
||||
if (transaction.store !== undefined) {
|
||||
let store = transaction.store;
|
||||
let item = await store.get(token);
|
||||
if (!item) { throw new Error("No Such File"); }
|
||||
item.maybe_title = name;
|
||||
await store.put(item, token);
|
||||
}
|
||||
await transaction.done;
|
||||
}
|
||||
|
||||
async convertFbxToGltf(token: string): Promise<void> {
|
||||
console.log("not implemented");
|
||||
}
|
||||
|
||||
async mediaFile(token: string): Promise<MediaFile> {
|
||||
let db = await this.getDatabase();
|
||||
let file = await db.get('media-files', token);
|
||||
if (!file) {
|
||||
throw new Error("No Such File");
|
||||
}
|
||||
return file;
|
||||
}
|
||||
|
||||
async mediaBlob(path: string): Promise<Uint8Array> {
|
||||
let db = await this.getDatabase();
|
||||
let file = await db.get('file-blobs', path);
|
||||
if (!file) {
|
||||
throw new Error("No Such File");
|
||||
}
|
||||
return file;
|
||||
}
|
||||
}
|
||||
|
||||
export class StorytellerApi implements IStorytellerApi {
|
||||
createScene(file: File): Promise<string> {
|
||||
return this.apiBase.createScene(file)
|
||||
}
|
||||
session(): Promise<Session> {
|
||||
return this.apiBase.session()
|
||||
}
|
||||
listMediaFiles(options: ListMediaFilesOptions): Promise<ListMediaFilesResult> {
|
||||
return this.apiBase.listMediaFiles(options)
|
||||
}
|
||||
uploadMediaFile(file: File, { subtype, mediaClass }: UploadMediaFileOptions): Promise<string> {
|
||||
return this.apiBase.uploadMediaFile(file, { subtype, mediaClass })
|
||||
}
|
||||
renameMediaFile(token: string, name: string): Promise<void> {
|
||||
return this.apiBase.renameMediaFile(token, name)
|
||||
}
|
||||
convertFbxToGltf(token: string): Promise<void> {
|
||||
return this.apiBase.convertFbxToGltf(token)
|
||||
}
|
||||
mediaFile(token: string): Promise<MediaFile> {
|
||||
return this.apiBase.mediaFile(token)
|
||||
}
|
||||
apiBase: IStorytellerApi;
|
||||
|
||||
constructor(apiBase: IStorytellerApi) {
|
||||
this.apiBase = apiBase
|
||||
}
|
||||
}
|
||||
|
||||
export class ApiError<T extends AxiosResponse = AxiosResponse> extends Error {
|
||||
detail: T;
|
||||
|
||||
constructor (detail: T) {
|
||||
constructor(detail: T) {
|
||||
super("API Error");
|
||||
this.detail = detail;
|
||||
}
|
||||
|
||||
@@ -38,6 +38,7 @@ web-sys = { version = "0.3", optional = true, features = [
|
||||
"Document",
|
||||
"HtmlElement",
|
||||
"Window",
|
||||
"Location",
|
||||
"WorkerGlobalScope"
|
||||
] }
|
||||
clap = { version = "4.4.18", features = ["derive"] }
|
||||
@@ -51,10 +52,13 @@ bytemuck = "1.14.1"
|
||||
space_editor = {git = "https://github.com/rewin123/space_editor", rev = "0f15c0d6b6b5864f4a05a2d57dfc2066e96d1a3b"}
|
||||
anyhow.workspace = true
|
||||
serde_repr = "0.1.18"
|
||||
wasm-bindgen-futures = "0.4"
|
||||
|
||||
async-compat = "0.2"
|
||||
uuid = { version = "1", features = ["v4", "serde"]}
|
||||
indexed_db_futures = { version = "0.4", optional = true}
|
||||
wasm-bindgen-futures = { version = "0.4", optional = true}
|
||||
dashmap = "5"
|
||||
once_cell = "1"
|
||||
bevy_tweening = "0.10.0"
|
||||
|
||||
[dependencies.reqwest]
|
||||
@@ -71,6 +75,8 @@ wasm = [
|
||||
"dep:js-sys",
|
||||
"dep:serde-wasm-bindgen",
|
||||
"dep:web-sys",
|
||||
"dep:indexed_db_futures",
|
||||
"dep:wasm-bindgen-futures",
|
||||
"bevy_rapier3d/wasm-bindgen",
|
||||
]
|
||||
desktop = []
|
||||
|
||||
@@ -209,13 +209,14 @@ fn start_timeline_animation(
|
||||
mut ev: EventWriter<TimelineInteractionEvent>,
|
||||
) {
|
||||
if keyframes.is_empty() {
|
||||
return;
|
||||
// return;
|
||||
}
|
||||
commands.remove_resource::<CameraMotion>();
|
||||
let total_time = keyframes
|
||||
.iter()
|
||||
.fold(0f32, |previous, keyframe| previous.max(keyframe.time))
|
||||
.max(3.0);
|
||||
.max(3.0)
|
||||
.min(1.0);
|
||||
|
||||
let frames = (total_time * headless_settings.fps).ceil() as u32;
|
||||
headless_settings.max_frames = frames;
|
||||
|
||||
@@ -6,6 +6,7 @@ use bevy::{
|
||||
prelude::*,
|
||||
utils::BoxedFuture,
|
||||
};
|
||||
|
||||
use reqwest::StatusCode;
|
||||
|
||||
use super::response::GetMediaFileSuccessResponse;
|
||||
@@ -78,6 +79,29 @@ impl RemoteAssetReader {
|
||||
let reader: Box<Reader> = Box::new(VecReader::new(bytes));
|
||||
Ok(reader)
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "wasm", target_family = "wasm"))]
|
||||
async fn load_local<'a>(&'a self, path: &'a Path) -> Result<Box<Reader<'a>>, AssetReaderError> {
|
||||
let token = path
|
||||
.ext_file_prefix()
|
||||
.ok_or_else(|| {
|
||||
AssetReaderError::Io(std::io::Error::new(
|
||||
ErrorKind::Other,
|
||||
format!("Failed to parse token from '{path:?}'"),
|
||||
))
|
||||
})?
|
||||
.to_str()
|
||||
.unwrap();
|
||||
|
||||
let bytes = wasm::LoadLocalFromIdb::new(token);
|
||||
info!("Sent request to bevy");
|
||||
let bytes = bytes
|
||||
.await
|
||||
.map_err(|e| std::io::Error::new(ErrorKind::Other, format!("{e:?}")))?;
|
||||
|
||||
let reader: Box<Reader> = Box::new(VecReader::new(bytes));
|
||||
Ok(reader)
|
||||
}
|
||||
}
|
||||
|
||||
impl AssetReader for RemoteAssetReader {
|
||||
@@ -85,15 +109,25 @@ impl AssetReader for RemoteAssetReader {
|
||||
&'a self,
|
||||
path: &'a Path,
|
||||
) -> BoxedFuture<'a, Result<Box<Reader<'a>>, AssetReaderError>> {
|
||||
let unboxed = async move { self.load(path).await };
|
||||
if cfg!(not(feature = "wasm")) {
|
||||
#[cfg(not(all(feature = "wasm", target_family = "wasm")))]
|
||||
{
|
||||
info!("wasm mode not detected");
|
||||
// NB: Headless mode (server rendering) is running without a Tokio runtime.
|
||||
// The reqwest HTTP client requires async-compat to function at runtime.
|
||||
Box::pin(Compat::new(unboxed))
|
||||
} else {
|
||||
Box::pin(Compat::new(async move { self.load(path).await }))
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "wasm", target_family = "wasm"))]
|
||||
{
|
||||
info!("wasm mode");
|
||||
Box::pin(unboxed)
|
||||
if let Some(window) = web_sys::window() {
|
||||
if let Ok(search) = window.location().search() {
|
||||
if search.contains("mock-api") {
|
||||
return Box::pin(async move { self.load_local(path).await });
|
||||
}
|
||||
}
|
||||
}
|
||||
Box::pin(async move { self.load(path).await })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,3 +165,129 @@ impl IntoError<AssetReaderError> for reqwest::Error {
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "wasm", target_family = "wasm"))]
|
||||
mod wasm {
|
||||
use std::task::Waker;
|
||||
|
||||
use bevy::log::info;
|
||||
use dashmap::DashMap;
|
||||
use futures::Future;
|
||||
use js_sys::Uint8Array;
|
||||
use once_cell::sync::Lazy;
|
||||
use wasm_bindgen::{prelude::*, JsValue};
|
||||
use web_sys::{CustomEvent, CustomEventInit};
|
||||
|
||||
#[wasm_bindgen(typescript_custom_section)]
|
||||
const TIMELINE_TYPES: &str = r#"
|
||||
export interface RequestFileEvent extends CustomEvent {
|
||||
type: "request-file";
|
||||
detail: string;
|
||||
}
|
||||
|
||||
declare global {
|
||||
export interface GlobalEventHandlersEventMap {
|
||||
"request-file": RequestFileEvent;
|
||||
}
|
||||
}
|
||||
"#;
|
||||
|
||||
// DashMap + Lazy allow us to create static, global hashmaps that work well concurrently
|
||||
static FILE_WAKERS: Lazy<DashMap<String, Waker>> = Lazy::new(DashMap::new);
|
||||
static FILE_RESPONSES: Lazy<DashMap<String, Result<Vec<u8>, String>>> = Lazy::new(DashMap::new);
|
||||
|
||||
/// Notifies us that the requested file couldn't be received
|
||||
#[wasm_bindgen(js_name = requestedFileFailed)]
|
||||
pub fn requested_file_failed(token: String, error: JsValue) {
|
||||
FILE_RESPONSES.insert(token.clone(), Err(format!("Error: {error:?}")));
|
||||
let waker = FILE_WAKERS.remove(&token);
|
||||
if let Some((_, waker)) = waker {
|
||||
waker.wake();
|
||||
}
|
||||
}
|
||||
|
||||
/// Provides us with the requested file's contents
|
||||
#[wasm_bindgen(js_name = requestedFileReceived)]
|
||||
pub fn requested_file_received(token: String, file: Uint8Array) {
|
||||
info!("Received File From Mock API {token}");
|
||||
FILE_RESPONSES.insert(token.clone(), Ok(file.to_vec()));
|
||||
let waker = FILE_WAKERS.remove(&token);
|
||||
if let Some((_, waker)) = waker {
|
||||
waker.wake();
|
||||
}
|
||||
}
|
||||
|
||||
pub enum LoadLocalFromIdb {
|
||||
RequestingFile { token: String },
|
||||
CheckingForFile { token: String },
|
||||
}
|
||||
|
||||
impl LoadLocalFromIdb {
|
||||
pub fn new(file: impl ToString) -> Self {
|
||||
Self::RequestingFile {
|
||||
token: file.to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We are implementing Future ourselves here because all the existing IndexedDb rust libraries
|
||||
// rely on JsValue, which is not Send - preventing us from using it in a BoxedFuture.
|
||||
// Instead, we ask the JS front end to grab the data from our mock API, and return it to us.
|
||||
impl Future for LoadLocalFromIdb {
|
||||
type Output = Result<Vec<u8>, String>;
|
||||
|
||||
fn poll(
|
||||
mut self: std::pin::Pin<&mut Self>,
|
||||
cx: &mut std::task::Context<'_>,
|
||||
) -> std::task::Poll<Self::Output> {
|
||||
match self.as_ref().get_ref() {
|
||||
LoadLocalFromIdb::RequestingFile { token } => {
|
||||
info!("Requesting file {token}");
|
||||
let Some(window) = web_sys::window() else {
|
||||
return std::task::Poll::Ready(Err(
|
||||
"Couldn't find browser window".to_string()
|
||||
));
|
||||
};
|
||||
let event = CustomEvent::new_with_event_init_dict(
|
||||
"request-file",
|
||||
CustomEventInit::new()
|
||||
.bubbles(true)
|
||||
.cancelable(true)
|
||||
.detail(&JsValue::from_str(token)),
|
||||
)
|
||||
.map_err(|err| format!("{err:?}"));
|
||||
let event = match event {
|
||||
Ok(e) => e,
|
||||
Err(err) => {
|
||||
return std::task::Poll::Ready(Err(err));
|
||||
}
|
||||
};
|
||||
FILE_WAKERS.insert(token.clone(), cx.waker().clone());
|
||||
if let Err(err) = window
|
||||
.document()
|
||||
.ok_or(JsValue::null())
|
||||
.and_then(|document| document.dispatch_event(&event))
|
||||
{
|
||||
return std::task::Poll::Ready(Err(format!(
|
||||
"Error Dispatching Message: {err:?}"
|
||||
)));
|
||||
}
|
||||
self.set(LoadLocalFromIdb::CheckingForFile {
|
||||
token: token.clone(),
|
||||
});
|
||||
std::task::Poll::Pending
|
||||
}
|
||||
LoadLocalFromIdb::CheckingForFile { token } => {
|
||||
info!("Checking on file {token}");
|
||||
match FILE_RESPONSES.remove(token) {
|
||||
Some((_, value)) => {
|
||||
info!("Found {token}");
|
||||
std::task::Poll::Ready(value)
|
||||
}
|
||||
None => std::task::Poll::Pending,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -195,7 +195,7 @@ fn check_loading(
|
||||
// `Handle<T>`s.
|
||||
if loading_queue.queue.is_empty()
|
||||
&& **r_scene_state == SceneState::Loading
|
||||
&& *frames_without_loading > 100
|
||||
&& *frames_without_loading > 200
|
||||
{
|
||||
info!("Transition to active");
|
||||
st_next.set(SceneState::Active);
|
||||
|
||||
@@ -148,7 +148,7 @@ fn extract_screen_capture_components(
|
||||
screen_capure_comps: Extract<Query<(Entity, &ScreenCapture), With<Camera>>>,
|
||||
scene_state: Extract<Res<State<SceneState>>>,
|
||||
) {
|
||||
if scene_state.get() != &SceneState::Active {
|
||||
if *scene_state.get() != SceneState::Active {
|
||||
return;
|
||||
};
|
||||
for (entity, screen_capture) in screen_capure_comps.iter() {
|
||||
|
||||
@@ -598,7 +598,7 @@ fn serialize_standart_materal(
|
||||
|
||||
ev_inspect.clear();
|
||||
|
||||
if let Some((_, handle)) = r_selection
|
||||
if let Some((_target, handle)) = r_selection
|
||||
.as_ref()
|
||||
.and_then(|selection| find_handle_in_self_or_children(***selection, &q_children, &q_m))
|
||||
.and_then(|target| q_m.get(target).ok().map(|h| (target, h.clone())))
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Update this to a recent scene to download it.
|
||||
scene_url="https://storage.googleapis.com/vocodes-public/media/d/2/d/s/e/d2dseec97gw06fk7mdavmk4pv87dk0dt/scene_d2dseec97gw06fk7mdavmk4pv87dk0dt.scn.ron"
|
||||
|
||||
filename="downloaded_scene.scn.ron"
|
||||
directory="studio/assets/"
|
||||
full_path="${directory}${filename}"
|
||||
|
||||
if [ ! -f "${full_path}" ]; then
|
||||
echo "Downloading ${scene_url}"
|
||||
mkdir -p "${directory}"
|
||||
wget "${scene_url}" -O "${full_path}"
|
||||
fi
|
||||
|
||||
cargo run -p studio --bin studio-headless \
|
||||
-- \
|
||||
scene \
|
||||
-o frames/ \
|
||||
--frames 100 \
|
||||
--skybox "00ff00" \
|
||||
$filename
|
||||
|
||||