diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f247b2d..2a397ea 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -40,9 +40,9 @@ jobs: with: python-version: "3.12" cache: "poetry" - - run: "SKIP_BUILD_FRONTEND=true poetry lock" - - run: "SKIP_BUILD_FRONTEND=true poetry install" - - run: "SKIP_BUILD_FRONTEND=true poetry build" + - run: "poetry lock" + - run: "poetry install" + - run: "poetry build" # Skips building frontend (not using task) build-logo: name: "Build logo" @@ -56,8 +56,8 @@ jobs: with: python-version: "3.12" cache: "poetry" - - run: "SKIP_BUILD_FRONTEND=true poetry lock" - - run: "SKIP_BUILD_FRONTEND=true poetry install" + - run: "poetry lock" + - run: "poetry install" - run: "poetry run python yacv_server/logo.py" - uses: "actions/upload-artifact@v4" with: @@ -77,8 +77,8 @@ jobs: with: python-version: "3.12" cache: "poetry" - - run: "SKIP_BUILD_FRONTEND=true poetry lock" - - run: "SKIP_BUILD_FRONTEND=true poetry install" + - run: "poetry lock" + - run: "poetry install" - run: "YACV_DISABLE_SERVER=true poetry run python example/object.py" - uses: "actions/upload-artifact@v4" with: diff --git a/.github/workflows/deploy2.yml b/.github/workflows/deploy2.yml index e638d84..554046e 100644 --- a/.github/workflows/deploy2.yml +++ b/.github/workflows/deploy2.yml @@ -35,11 +35,7 @@ jobs: - uses: "actions/download-artifact@v4" with: # Downloads all artifacts from the build job path: "./public" - - run: | # Merge the subdirectories of public into a single directory - for dir in public/*; do - mv "$dir/"* public/ - rmdir "$dir" - done + merge-multiple: true - uses: "actions/configure-pages@v5" - uses: "actions/upload-pages-artifact@v3" with: @@ -71,5 +67,6 @@ jobs: cache: "poetry" - run: "poetry install" - run: "poetry config pypi-token.pypi ${{ secrets.PYPI_TOKEN }}" - - run: "poetry publish --build" + - run: "poetry run task build" # This task also builds the frontend (with reduced features for less size) + - run: "poetry publish" diff --git a/assets/licenses.txt b/assets/licenses.txt index 4888780..4d68109 100644 --- a/assets/licenses.txt +++ b/assets/licenses.txt @@ -761,6 +761,36 @@ MIT License ----------- +The following npm package may be included in this product: + + - pako@2.1.0 + +This package contains the following license: + +(The MIT License) + +Copyright (C) 2014-2017 by Vitaly Puzrin and Andrei Tuputcyn + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +----------- + The following npm package may be included in this product: - lie@3.3.0 diff --git a/build.py b/build.py deleted file mode 100644 index 5e145c7..0000000 --- a/build.py +++ /dev/null @@ -1,9 +0,0 @@ -import os -import subprocess - -if __name__ == "__main__": - # Building the frontend is optional - if os.getenv('SKIP_BUILD_FRONTEND') is None and os.path.exists('package.json'): - # When building the backend, make sure the frontend is built first - subprocess.run(['yarn', 'install'], check=True) - subprocess.run(['yarn', 'build', '--outDir', 'yacv_server/frontend'], check=True) diff --git a/frontend/App.vue b/frontend/App.vue index d757e7a..fc3e384 100644 --- a/frontend/App.vue +++ b/frontend/App.vue @@ -13,6 +13,7 @@ import {Document} from "@gltf-transform/core"; import type ModelViewerWrapperT from "./viewer/ModelViewerWrapper.vue"; import {mdiPlus} from '@mdi/js' import SvgIcon from '@jamescoyle/vue-icon'; +import {toBuffer} from "./misc/gltf.ts"; // NOTE: The ModelViewer library is big (THREE.js), so we split it and import it asynchronously const ModelViewerWrapper = defineAsyncComponent({ @@ -82,14 +83,24 @@ networkMgr.addEventListener('update-early', networkMgr.addEventListener('update', (e) => onModelUpdateRequest(e as NetworkUpdateEvent)); (async () => { // Start loading all configured models ASAP let sett = await settings(); - watch(viewer, (newViewer) => { - if (newViewer) { - newViewer.setPosterText('Trying to load' + - ' models from:' + sett.preload.map((url: string) => '- ' + url + '').join("")); + if (sett.preload.length > 0) { + watch(viewer, (newViewer) => { + if (newViewer) { + newViewer.setPosterText('Trying to load' + + ' models from:' + sett.preload.map((url: string) => '- ' + url + '').join("")); + } + }); + for (let model of sett.preload) { + await networkMgr.load(model); } - }); - for (let model of sett.preload) { - await networkMgr.load(model); + } else { // Skip to interface without models (useful for playground mode) + console.debug("Showing empty gltf document to load the interface without models."); + // FIXME: Empty document breaks the playground-loaded models (using preload seems to fix this, maybe __helpers issue?) + let emptyDoc = new Document(); + emptyDoc.createScene(); + let buffer = await toBuffer(emptyDoc); + let blob = new Blob([buffer], {type: 'model/gltf-binary'}); + sceneUrl.value = URL.createObjectURL(blob); } })(); @@ -117,7 +128,7 @@ async function loadModelManual() { - + @@ -125,7 +136,7 @@ async function loadModelManual() { - + @@ -135,6 +146,6 @@ async function loadModelManual() { \ No newline at end of file diff --git a/frontend/misc/IfNotSmallBuild.vue b/frontend/misc/IfNotSmallBuild.vue new file mode 100644 index 0000000..c2f9be4 --- /dev/null +++ b/frontend/misc/IfNotSmallBuild.vue @@ -0,0 +1,30 @@ + + + + + \ No newline at end of file diff --git a/frontend/misc/gltf.ts b/frontend/misc/gltf.ts index 28dcf07..2c1614b 100644 --- a/frontend/misc/gltf.ts +++ b/frontend/misc/gltf.ts @@ -5,6 +5,9 @@ let io = new WebIO(); export let extrasNameKey = "__yacv_name"; export let extrasNameValueHelpers = "__helpers"; +// @ts-expect-error +let isSmallBuild = typeof __YACV_SMALL_BUILD__ !== 'undefined' && __YACV_SMALL_BUILD__; + /** * Loads a GLB model from a URL and adds it to the document or replaces it if the names match. * @@ -27,7 +30,7 @@ export async function mergePartial(url: string, name: string, document: Document try { // Try to load fast if no extensions are used newDoc = await io.readBinary(new Uint8Array(buffer)); } catch (e) { // Fallback to wait for download and register big extensions - if (e instanceof Error && e.message.toLowerCase().includes("khr_draco_mesh_compression")) { + if (!isSmallBuild && e instanceof Error && e.message.toLowerCase().includes("khr_draco_mesh_compression")) { if (alreadyTried["draco"]) throw e; else alreadyTried["draco"] = true; // WARNING: Draco decompression on web is really slow for non-trivial models! (it should work?) let {KHRDracoMeshCompression} = await import("@gltf-transform/extensions") @@ -38,7 +41,7 @@ export async function mergePartial(url: string, name: string, document: Document 'draco3d.decoder': await dracoDecoderWeb.default({}), 'draco3d.encoder': await dracoEncoderWeb.default({}) }); - } else if (e instanceof Error && e.message.toLowerCase().includes("ext_texture_webp")) { + } else if (!isSmallBuild && e instanceof Error && e.message.toLowerCase().includes("ext_texture_webp")) { if (alreadyTried["webp"]) throw e; else alreadyTried["webp"] = true; let {EXTTextureWebP} = await import("@gltf-transform/extensions") io.registerExtensions([EXTTextureWebP]); diff --git a/frontend/misc/settings.ts b/frontend/misc/settings.ts index 1041c32..d62787e 100644 --- a/frontend/misc/settings.ts +++ b/frontend/misc/settings.ts @@ -1,4 +1,7 @@ // These are the default values for the settings, which are overridden below +import {ungzip} from "pako"; +import {b66Decode} from "../tools/b66.ts"; + let settingsCache: any = null; export async function settings() { @@ -49,10 +52,23 @@ export async function settings() { if (key in settings) (settings as any)[key] = parseSetting(key, value, settings); }) + // Auto-decompress the code + if (settings.code.length > 0) { + try { + settings.code = ungzip(b66Decode(settings.code), {to: 'string'}); + } catch (error) { + console.warn("Failed to decompress code (assuming raw code):", error); + } + } + // Get the default preload URL if not overridden (requires a fetch that is avoided if possible) for (let i = 0; i < settings.preload.length; i++) { let url = settings.preload[i]; if (url === '') { + if (settings.code != "") { // means no preload URL if code is set + settings.preload = settings.preload.slice(0, i).concat(settings.preload.slice(i + 1)); + continue; // Skip this preload URL + } const possibleBackend = new URL("./?api_updates=true", window.location.href) await fetch(possibleBackend, {method: "HEAD"}).then((response) => { if (response.ok && response.headers.get("Content-Type") === "text/event-stream") { diff --git a/frontend/models/Models.vue b/frontend/models/Models.vue index ba160d5..e1e5bdc 100644 --- a/frontend/models/Models.vue +++ b/frontend/models/Models.vue @@ -7,7 +7,7 @@ import Model from "./Model.vue"; import {inject, ref, type Ref} from "vue"; const props = defineProps<{ viewer: InstanceType | null }>(); -const emit = defineEmits<{ remove: [string] }>() +const emit = defineEmits<{ removeModel: [string] }>() let {sceneDocument} = inject<{ sceneDocument: Ref }>('sceneDocument')!!; @@ -32,7 +32,7 @@ function meshName(mesh: Mesh) { } function onRemove(mesh: Mesh) { - emit('remove', meshName(mesh)) + emit('removeModel', meshName(mesh)) } function findModel(name: string) { diff --git a/frontend/tools/PlaygroundDialogContent.vue b/frontend/tools/PlaygroundDialogContent.vue index ff5a307..1e64ea0 100644 --- a/frontend/tools/PlaygroundDialogContent.vue +++ b/frontend/tools/PlaygroundDialogContent.vue @@ -1,15 +1,20 @@