├── .github └── workflows │ ├── ci.yaml │ └── release.yml ├── .gitignore ├── .prettierrc ├── .vscode ├── extensions.json └── settings.json ├── LICENSE ├── README.md ├── package.json ├── packages ├── @svelte-in-motion-agent │ ├── README.md │ ├── package.json │ ├── src │ │ ├── agent.ts │ │ ├── host.ts │ │ ├── index.ts │ │ └── rpc │ │ │ ├── encoding.ts │ │ │ └── rendering.ts │ └── tsconfig.json ├── @svelte-in-motion-animations │ ├── .gitignore │ ├── README.md │ ├── package.json │ ├── src │ │ ├── actions │ │ │ ├── actions.ts │ │ │ └── transition.ts │ │ ├── components │ │ │ ├── Playback.svelte │ │ │ ├── Transition.svelte │ │ │ ├── fade │ │ │ │ ├── FadeIn.svelte │ │ │ │ ├── FadeOut.svelte │ │ │ │ └── index.ts │ │ │ ├── rotate │ │ │ │ ├── RotateIn.svelte │ │ │ │ ├── RotateOut.svelte │ │ │ │ └── index.ts │ │ │ ├── scale │ │ │ │ ├── ScaleIn.svelte │ │ │ │ ├── ScaleOut.svelte │ │ │ │ └── index.ts │ │ │ └── translate │ │ │ │ ├── TranslateIn.svelte │ │ │ │ ├── TranslateOut.svelte │ │ │ │ └── index.ts │ │ ├── index.ts │ │ └── transitions │ │ │ ├── opacity.ts │ │ │ ├── rotate.ts │ │ │ ├── scale.ts │ │ │ ├── transitions.ts │ │ │ └── translate.ts │ └── tsconfig.json ├── @svelte-in-motion-builtin-extensions │ ├── README.md │ ├── package.json │ ├── src │ │ ├── extensions │ │ │ ├── about.ts │ │ │ ├── commands.ts │ │ │ ├── editor.ts │ │ │ ├── export.ts │ │ │ ├── grammars.ts │ │ │ ├── preview.ts │ │ │ ├── templates.ts │ │ │ └── workspace.ts │ │ ├── index.ts │ │ ├── templates │ │ │ ├── samples.ts │ │ │ └── welcome.ts │ │ └── util │ │ │ ├── errors.ts │ │ │ └── io.ts │ └── tsconfig.json ├── @svelte-in-motion-bundling │ ├── README.md │ ├── package.json │ ├── src │ │ ├── bundler.ts │ │ ├── index.ts │ │ └── plugins │ │ │ ├── javascript.ts │ │ │ ├── storage.ts │ │ │ └── svelte.ts │ └── tsconfig.json ├── @svelte-in-motion-configuration │ ├── README.md │ ├── package.json │ ├── src │ │ ├── configurations │ │ │ ├── configuration.ts │ │ │ ├── preferences.ts │ │ │ ├── workspace.ts │ │ │ └── workspaces.ts │ │ └── index.ts │ └── tsconfig.json ├── @svelte-in-motion-core │ ├── README.md │ ├── package.json │ ├── src │ │ ├── index.ts │ │ └── stores │ │ │ ├── advance.ts │ │ │ ├── completion.ts │ │ │ ├── duration.ts │ │ │ ├── frame.ts │ │ │ ├── framerate.ts │ │ │ ├── interpolate.ts │ │ │ ├── maxframes.ts │ │ │ ├── playing.ts │ │ │ ├── random.ts │ │ │ ├── seek.ts │ │ │ └── state.ts │ └── tsconfig.json ├── @svelte-in-motion-editor │ ├── README.md │ ├── index.html │ ├── package.json │ ├── preview.html │ ├── public │ │ ├── assets │ │ │ └── translations │ │ │ │ └── en-US.ftl │ │ ├── extern │ │ │ ├── scripts │ │ │ │ └── ffmpeg.min.js │ │ │ └── wasm │ │ │ │ └── esbuild.wasm │ │ └── favicon.ico │ ├── src │ │ ├── components │ │ │ ├── Loader.svelte │ │ │ ├── Tooltip.svelte │ │ │ ├── app │ │ │ │ ├── AppHeader.svelte │ │ │ │ ├── AppLayout.svelte │ │ │ │ ├── AppNotifications.svelte │ │ │ │ ├── AppPrompts.svelte │ │ │ │ └── AppStatus.svelte │ │ │ ├── editor │ │ │ │ ├── EditorFileTree.svelte │ │ │ │ ├── EditorLayout.svelte │ │ │ │ ├── EditorScript.svelte │ │ │ │ └── EditorStatus.svelte │ │ │ ├── form │ │ │ │ ├── FormBoolean.svelte │ │ │ │ ├── FormNumber.svelte │ │ │ │ ├── FormRender.svelte │ │ │ │ ├── FormString.svelte │ │ │ │ └── FormUnion.svelte │ │ │ ├── preview │ │ │ │ ├── PreviewControls.svelte │ │ │ │ ├── PreviewTimeline.svelte │ │ │ │ └── PreviewViewport.svelte │ │ │ └── prompts │ │ │ │ ├── AboutPrompt.svelte │ │ │ │ ├── AlertPrompt.svelte │ │ │ │ ├── ConfirmPrompt.svelte │ │ │ │ ├── CreateWorkspace.svelte │ │ │ │ ├── FormPrompt.svelte │ │ │ │ ├── LoaderPrompt.svelte │ │ │ │ └── SearchPrompt.svelte │ │ ├── global.d.ts │ │ ├── lib │ │ │ ├── app.ts │ │ │ ├── editor.ts │ │ │ ├── lucide-svelte.d.ts │ │ │ ├── preview.ts │ │ │ ├── router.ts │ │ │ ├── stores │ │ │ │ ├── commands.ts │ │ │ │ ├── editor.ts │ │ │ │ ├── encodes.ts │ │ │ │ ├── errors.ts │ │ │ │ ├── extensions.ts │ │ │ │ ├── grammars.ts │ │ │ │ ├── io.ts │ │ │ │ ├── jobs.ts │ │ │ │ ├── keybinds.ts │ │ │ │ ├── locale.ts │ │ │ │ ├── notifications.ts │ │ │ │ ├── prompts.ts │ │ │ │ ├── renders.ts │ │ │ │ ├── templates.ts │ │ │ │ └── translations.ts │ │ │ ├── types │ │ │ │ ├── messages.ts │ │ │ │ └── preview.ts │ │ │ ├── util │ │ │ │ ├── constants.ts │ │ │ │ ├── preview.ts │ │ │ │ ├── repl.ts │ │ │ │ └── storage.ts │ │ │ └── workspace.ts │ │ ├── main.ts │ │ ├── preview.ts │ │ └── routes │ │ │ ├── dashboard.svelte │ │ │ └── workspace.svelte │ ├── svelte.config.js │ ├── tsconfig.json │ ├── tsconfig.node.json │ ├── vite.config.ts │ └── vite │ │ └── deepkit-type-plugin.ts ├── @svelte-in-motion-encoding │ ├── README.md │ ├── package.json │ ├── src │ │ ├── encoding │ │ │ ├── codec.ts │ │ │ ├── crf.ts │ │ │ ├── dimensions.ts │ │ │ ├── encode.ts │ │ │ ├── framerate.ts │ │ │ └── pixel_format.ts │ │ └── index.ts │ └── tsconfig.json ├── @svelte-in-motion-extension │ ├── README.md │ ├── package.json │ ├── src │ │ ├── extensions.ts │ │ ├── index.ts │ │ ├── templates.ts │ │ └── types.ts │ └── tsconfig.json ├── @svelte-in-motion-rendering │ ├── README.md │ ├── package.json │ ├── src │ │ ├── index.ts │ │ └── render.ts │ └── tsconfig.json ├── @svelte-in-motion-rpc │ ├── README.md │ ├── package.json │ ├── src │ │ ├── index.ts │ │ └── rpc │ │ │ └── port.ts │ └── tsconfig.json ├── @svelte-in-motion-runtime │ ├── README.md │ ├── package.json │ ├── scripts │ │ └── build-template.js │ ├── src │ │ └── index.html │ ├── tsconfig.json │ └── vite.config.ts ├── @svelte-in-motion-screenshot │ ├── README.md │ ├── package.json │ ├── src │ │ ├── index.ts │ │ └── platforms │ │ │ └── browser.ts │ └── tsconfig.json ├── @svelte-in-motion-storage │ ├── README.md │ ├── package.json │ ├── src │ │ ├── drivers │ │ │ ├── driver.ts │ │ │ └── indexeddb.ts │ │ ├── index.ts │ │ ├── stores │ │ │ └── file.ts │ │ └── util │ │ │ ├── compression.ts │ │ │ ├── encoding.ts │ │ │ ├── mimetypes.ts │ │ │ └── zip.ts │ └── tsconfig.json ├── @svelte-in-motion-tauri │ ├── README.md │ ├── package.json │ └── src-tauri │ │ ├── .gitignore │ │ ├── Cargo.lock │ │ ├── Cargo.toml │ │ ├── build.rs │ │ ├── icons │ │ ├── 128x128.png │ │ ├── 128x128@2x.png │ │ ├── 32x32.png │ │ ├── Square107x107Logo.png │ │ ├── Square142x142Logo.png │ │ ├── Square150x150Logo.png │ │ ├── Square284x284Logo.png │ │ ├── Square30x30Logo.png │ │ ├── Square310x310Logo.png │ │ ├── Square44x44Logo.png │ │ ├── Square71x71Logo.png │ │ ├── Square89x89Logo.png │ │ ├── StoreLogo.png │ │ ├── icon.icns │ │ ├── icon.ico │ │ └── icon.png │ │ ├── src │ │ └── main.rs │ │ └── tauri.conf.json ├── @svelte-in-motion-temporal │ ├── README.md │ ├── package.json │ ├── src │ │ └── index.ts │ └── tsconfig.json ├── @svelte-in-motion-type │ ├── README.md │ ├── package.json │ ├── src │ │ ├── index.ts │ │ ├── serializers │ │ │ └── jsonserializer.ts │ │ ├── types │ │ │ ├── default.ts │ │ │ ├── interface.ts │ │ │ └── metadata.ts │ │ └── util │ │ │ ├── class.ts │ │ │ ├── dataclass.ts │ │ │ └── resolve.ts │ └── tsconfig.json └── @svelte-in-motion-utilities │ ├── README.md │ ├── package.json │ ├── src │ ├── events │ │ ├── channel.ts │ │ ├── event.ts │ │ └── message.ts │ ├── index.ts │ ├── stores │ │ ├── collection.ts │ │ ├── immutable.ts │ │ ├── map.ts │ │ └── types.ts │ └── util │ │ ├── contexts.ts │ │ ├── environment.ts │ │ ├── errors.ts │ │ ├── eval.ts │ │ ├── math.ts │ │ ├── object.ts │ │ ├── random.ts │ │ ├── router.ts │ │ ├── string.ts │ │ ├── timing.ts │ │ ├── url.ts │ │ └── web.ts │ └── tsconfig.json ├── pnpm-lock.yaml ├── pnpm-workspace.yaml └── vercel.json /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | name: Continuous Integration 2 | 3 | on: [push, pull_request] 4 | 5 | concurrency: 6 | group: ${{github.head_ref || github.ref}} 7 | cancel-in-progress: true 8 | 9 | jobs: 10 | build: 11 | strategy: 12 | matrix: 13 | platform: [ubuntu-latest] 14 | 15 | node-version: [16.x] 16 | pnpm-version: [6.32] 17 | 18 | runs-on: ${{ matrix.platform }} 19 | 20 | steps: 21 | - name: Checkout 22 | uses: actions/checkout@v2.3.1 23 | 24 | - name: Setup Node.js ${{ matrix.node-version }} 25 | uses: actions/setup-node@v2 26 | with: 27 | node-version: ${{ matrix.node-version }} 28 | 29 | - name: Setup PNPM 30 | uses: pnpm/action-setup@v2.2.2 31 | with: 32 | version: ${{ matrix.pnpm-version }} 33 | 34 | - name: Install Dependencies 35 | run: npm run install:all 36 | 37 | - name: Lint Codebase 38 | run: npm run lint:all 39 | 40 | - name: Build Packages 41 | run: npm run build:packages 42 | 43 | - name: Build Application 44 | run: npm run build:application 45 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | release: 5 | types: 6 | - published 7 | 8 | jobs: 9 | build: 10 | strategy: 11 | matrix: 12 | platform: [ubuntu-latest] 13 | 14 | node-version: [16.x] 15 | pnpm-version: [6.32] 16 | rust-version: [stable] 17 | 18 | runs-on: ${{ matrix.platform }} 19 | 20 | steps: 21 | - name: Checkout 22 | uses: actions/checkout@v2.3.1 23 | 24 | - name: Setup Node.js ${{ matrix.node-version }} 25 | uses: actions/setup-node@v2 26 | with: 27 | node-version: ${{ matrix.node-version }} 28 | 29 | - name: Setup PNPM 30 | uses: pnpm/action-setup@v2.2.2 31 | with: 32 | version: ${{ matrix.pnpm-version }} 33 | 34 | - name: Setup Rust 35 | uses: actions-rs/toolchain@v1 36 | with: 37 | toolchain: ${{ matrix.rust-version }} 38 | 39 | - name: Cache Web Application Version 40 | working-directory: ./packages/@svelte-in-motion-editor 41 | run: echo "APPLICATION_VERSION=$(node -p "require('./package.json').version")" >> $GITHUB_ENV 42 | 43 | - name: Install Ubuntu Dependencies 44 | if: ${{ matrix.platform == 'ubuntu-latest' }} 45 | run: | 46 | sudo apt-get update 47 | sudo apt-get install -y libgtk-3-dev webkit2gtk-4.0 libappindicator3-dev librsvg2-dev patchelf 48 | 49 | - name: Install Web Application Dependencies 50 | run: npm run install:all 51 | 52 | - name: Build Packages 53 | run: npm run build:packages 54 | 55 | - name: Build Web Application 56 | run: npm run build:application 57 | 58 | - name: Build Tauri Application 59 | run: npm run build:tauri 60 | 61 | - name: Upload Linux Artifacts 62 | if: ${{ matrix.platform == 'ubuntu-latest' }} 63 | uses: softprops/action-gh-release@v1 64 | with: 65 | files: | 66 | ./packages/@svelte-in-motion-tauri/src-tauri/target/release/bundle/appimage/svelte-in-motion_${{ env.APPLICATION_VERSION }}_amd64.AppImage 67 | ./packages/@svelte-in-motion-tauri/src-tauri/target/release/bundle/deb/svelte-in-motion_${{ env.APPLICATION_VERSION }}_amd64.deb 68 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules/ 2 | 3 | /packages/@svelte-in-motion-agent/dist/ 4 | /packages/@svelte-in-motion-agent/node_modules/ 5 | /packages/@svelte-in-motion-animations/node_modules/ 6 | /packages/@svelte-in-motion-builtin-extensions/dist/ 7 | /packages/@svelte-in-motion-builtin-extensions/node_modules/ 8 | /packages/@svelte-in-motion-bundling/node_modules/ 9 | /packages/@svelte-in-motion-configuration/dist/ 10 | /packages/@svelte-in-motion-configuration/node_modules/ 11 | /packages/@svelte-in-motion-core/node_modules/ 12 | /packages/@svelte-in-motion-editor/dist/ 13 | /packages/@svelte-in-motion-editor/node_modules/ 14 | /packages/@svelte-in-motion-editor/vite.config.ts.js 15 | /packages/@svelte-in-motion-encoding/dist/ 16 | /packages/@svelte-in-motion-encoding/node_modules/ 17 | /packages/@svelte-in-motion-extension/dist/ 18 | /packages/@svelte-in-motion-extension/node_modules/ 19 | /packages/@svelte-in-motion-rendering/dist/ 20 | /packages/@svelte-in-motion-rendering/node_modules/ 21 | /packages/@svelte-in-motion-rpc/dist/ 22 | /packages/@svelte-in-motion-rpc/node_modules/ 23 | /packages/@svelte-in-motion-runtime/dist/ 24 | /packages/@svelte-in-motion-runtime/node_modules/ 25 | /packages/@svelte-in-motion-screenshot/node_modules/ 26 | /packages/@svelte-in-motion-storage/node_modules/ 27 | /packages/@svelte-in-motion-utilities/node_modules/ 28 | /packages/@svelte-in-motion-tauri/node_modules/ 29 | /packages/@svelte-in-motion-temporal/dist/ 30 | /packages/@svelte-in-motion-temporal/node_modules/ 31 | /packages/@svelte-in-motion-type/dist/ 32 | /packages/@svelte-in-motion-type/node_modules/ -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "arrowParens": "always", 3 | "bracketSpacing": false, 4 | "endOfLine": "lf", 5 | "printWidth": 100, 6 | "proseWrap": "preserve", 7 | "semi": true, 8 | "singleQuote": false, 9 | "tabWidth": 4, 10 | "useTabs": false 11 | } 12 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": ["esbenp.prettier-vscode", "svelte.svelte-vscode"] 3 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.formatOnSave": false, 3 | 4 | "[css]": { 5 | "editor.formatOnSave": true 6 | }, 7 | 8 | "[html]": { 9 | "editor.formatOnSave": true 10 | }, 11 | 12 | "[javascript]": { 13 | "editor.formatOnSave": true 14 | }, 15 | 16 | "[json]": { 17 | "editor.formatOnSave": true 18 | }, 19 | 20 | "[json with comments]": { 21 | "editor.formatOnSave": true 22 | }, 23 | 24 | "[markdown]": { 25 | "editor.formatOnSave": true 26 | }, 27 | 28 | "[svelte]": { 29 | "editor.formatOnSave": true 30 | }, 31 | 32 | "[typescript]": { 33 | "editor.formatOnSave": true 34 | }, 35 | 36 | "[yaml]": { 37 | "editor.formatOnSave": true 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 NovacBN 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | > **NOTICE**: This project is archived due to lack of interest. All code is freely available if you want to continue Svelte-In-Motion or for use in your own project. 2 | 3 | # svelte-in-motion 4 | 5 | > Remotion clone, but in Svelte! 6 | 7 | > https://sim-test-novacbn.vercel.app 8 | 9 | https://user-images.githubusercontent.com/31122674/157776869-e734155d-8245-4925-aa6e-58a993370db0.mp4 10 | 11 | https://user-images.githubusercontent.com/31122674/157776819-eb4b03f1-3afc-4f08-92a3-d43aa8aaca60.mp4 12 | 13 | ## Documentation 14 | 15 | > Looking for documentation source? Visit [github.com/svelte-in-motion/docs.sim.nbn.dev](https://github.com/svelte-in-motion/docs.sim.nbn.dev). 16 | 17 | Visit the documentation at [docs.sim.nbn.dev](https://docs.sim.nbn.dev). 18 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "svelte-in-motion", 3 | "version": "0.0.2", 4 | "type": "module", 5 | "scripts": { 6 | "build:application": "pnpm run --filter \"*editor\" build:application", 7 | "build:tauri": "pnpm run --filter \"*tauri\" build:application", 8 | "build:packages": "pnpm run --filter \"*rpc\" build:package && pnpm run --filter \"*temporal\" build:package && pnpm run --filter \"*type\" build:package && pnpm run --filter \"*configuration\" build:package && pnpm run --filter \"*encoding\" build:package && pnpm run --filter \"*runtime\" build:package && pnpm run --filter \"*rendering\" build:package && pnpm run --filter \"*agent\" build:package && pnpm run --filter \"*n/extension\" build:package && pnpm run --filter \"*builtin-extensions\" build:package", 9 | "dev:application": "pnpm run --filter \"*editor\" dev:application", 10 | "dev:packages": "pnpm run --recursive dev:package", 11 | "install:all": "pnpm install --recursive", 12 | "preview:application": "pnpm run --filter \"*editor\" preview:application", 13 | "format:all": "pnpm run --recursive format", 14 | "lint:all": "pnpm run --recursive lint", 15 | "test:all": "pnpm run --recursive test" 16 | }, 17 | "devDependencies": { 18 | "prettier": "^2.7.0", 19 | "prettier-plugin-svelte": "^2.7.0", 20 | "svelte": "^3.48.0" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-agent/README.md: -------------------------------------------------------------------------------- 1 | # @svelte-in-motion/agent 2 | 3 | > Implementation library for hosting and calling out to an in-Process, external Process, or remote server for encoding and rendering jobs. 4 | 5 | ## TODO 6 | 7 | - Create implementation for network calls / hosting. 8 | - Create implementation for IPC calls / hosting. 9 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-agent/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@svelte-in-motion/agent", 3 | "author": "novacbn", 4 | "version": "0.0.1", 5 | "type": "module", 6 | "main": "./dist/index.js", 7 | "exports": { 8 | ".": "./dist/index.js", 9 | "./package.json": "./package.json" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "https://github.com/novacbn/svelte-in-motion/tree/main/packages/%40svelte-in-motion-agent" 14 | }, 15 | "scripts": { 16 | "build:package": "tsc", 17 | "dev:package": "tsc --watch", 18 | "format": "npm run format:package", 19 | "format:package": "prettier --config ../../.prettierrc --write ./src", 20 | "lint": "npm run lint:format", 21 | "lint:format": "prettier --config ../../.prettierrc --check ./src", 22 | "test": "npm run test:types", 23 | "test:types": "tsc --noEmit" 24 | }, 25 | "devDependencies": { 26 | "@deepkit/type-compiler": "^1.0.1-alpha.71", 27 | "prettier": "^2.3.1", 28 | "typescript": "^4.7.3" 29 | }, 30 | "dependencies": { 31 | "@svelte-in-motion/encoding": "workspace:^", 32 | "@svelte-in-motion/rendering": "workspace:^", 33 | "@svelte-in-motion/rpc": "workspace:^", 34 | "@svelte-in-motion/type": "workspace:^", 35 | "@svelte-in-motion/utilities": "workspace:^", 36 | "rxjs": "^7.5.5" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-agent/src/agent.ts: -------------------------------------------------------------------------------- 1 | import type {RemoteController, RpcClient} from "@svelte-in-motion/rpc"; 2 | import {DirectClient, RpcKernel} from "@svelte-in-motion/rpc"; 3 | 4 | import {IRPCEncodingAgentController} from "./rpc/encoding"; 5 | import {RPCEncodingAgentController} from "./rpc/encoding"; 6 | import {IRPCRenderingAgentController} from "./rpc/rendering"; 7 | import {RPCRenderingAgentController} from "./rpc/rendering"; 8 | 9 | export class Agent { 10 | public readonly encoding: RemoteController; 11 | 12 | public readonly rendering: RemoteController; 13 | 14 | static async connect_to(): Promise { 15 | // TODO: expand to support configuration for network 16 | // TODO: expand to support configuration for subprocess 17 | 18 | const kernel = new RpcKernel(); 19 | 20 | kernel.registerController(IRPCEncodingAgentController, RPCEncodingAgentController); 21 | kernel.registerController(IRPCRenderingAgentController, RPCRenderingAgentController); 22 | 23 | const client = new DirectClient(kernel); 24 | return new Agent(client); 25 | } 26 | 27 | constructor(protected client: RpcClient) { 28 | this.encoding = client.controller(IRPCEncodingAgentController); 29 | this.rendering = client.controller(IRPCRenderingAgentController); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-agent/src/host.ts: -------------------------------------------------------------------------------- 1 | export class Host {} 2 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-agent/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./agent"; 2 | export * from "./host"; 3 | 4 | export * from "./rpc/encoding"; 5 | export * from "./rpc/rendering"; 6 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-agent/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "moduleResolution": "node", 4 | "module": "ES2020", 5 | "target": "ES2019", 6 | "outDir": "./dist", 7 | "strict": true, 8 | "sourceMap": true, 9 | "declaration": true, 10 | "allowSyntheticDefaultImports": true, 11 | "esModuleInterop": true, 12 | "skipLibCheck": true, 13 | "downlevelIteration": true, 14 | "experimentalDecorators": true, 15 | "emitDecoratorMetadata": true 16 | }, 17 | "reflection": true, 18 | "include": ["src/**/*.d.ts", "src/**/*.ts"] 19 | } 20 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-animations/.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules/ 2 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-animations/README.md: -------------------------------------------------------------------------------- 1 | # @svelte-in-motion/animations 2 | 3 | > Support library containing plumbing for integrating animation with Svelte-In-Motion's engine. Along w/ prebuilt composable animations. 4 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-animations/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@svelte-in-motion/animations", 3 | "author": "novacbn", 4 | "version": "0.0.1", 5 | "type": "module", 6 | "main": "./src/index.ts", 7 | "exports": { 8 | ".": "./src/index.ts", 9 | "./package.json": "./package.json" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "https://github.com/novacbn/svelte-in-motion/tree/main/packages/%40svelte-in-motion-transitions" 14 | }, 15 | "scripts": { 16 | "format": "npm run format:package", 17 | "format:package": "prettier --config ../../.prettierrc --write ./src", 18 | "lint": "npm run lint:format", 19 | "lint:format": "prettier --config ../../.prettierrc --check ./src", 20 | "test": "npm run test:types", 21 | "test:types": "tsc --noEmit" 22 | }, 23 | "devDependencies": { 24 | "@svelte-in-motion/core": "workspace:^", 25 | "@svelte-in-motion/utilities": "workspace:^", 26 | "@tsconfig/svelte": "^2.0.1", 27 | "prettier": "^2.3.1", 28 | "svelte-check": "^2.2.7", 29 | "typescript": "^4.7.3" 30 | }, 31 | "dependencies": { 32 | "svelte": "^3.46.4" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-animations/src/actions/actions.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Represents the handle of an initialized Svelte Action 3 | */ 4 | export interface IActionHandle { 5 | /** 6 | * Destroys all bindings the Action was using 7 | */ 8 | destroy?: () => void; 9 | 10 | /** 11 | * Replaces the options that was initially passed to the Action 12 | */ 13 | update?: (options: T) => void; 14 | } 15 | 16 | /** 17 | * Represents the constructor of a Svelte Action 18 | */ 19 | export type IAction< 20 | NodeType extends Node, 21 | OptionsType = any, 22 | HandleType extends IActionHandle = IActionHandle 23 | > = (node: NodeType, options: OptionsType) => HandleType; 24 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-animations/src/components/Playback.svelte: -------------------------------------------------------------------------------- 1 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-animations/src/components/Transition.svelte: -------------------------------------------------------------------------------- 1 | 36 | 37 | {#if is === "span"} 38 | 47 | 48 | 49 | {:else} 50 |
59 | 60 |
61 | {/if} 62 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-animations/src/components/fade/FadeIn.svelte: -------------------------------------------------------------------------------- 1 | 21 | 22 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-animations/src/components/fade/FadeOut.svelte: -------------------------------------------------------------------------------- 1 | 21 | 22 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-animations/src/components/fade/index.ts: -------------------------------------------------------------------------------- 1 | export {default as In} from "./FadeIn.svelte"; 2 | export {default as Out} from "./FadeOut.svelte"; 3 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-animations/src/components/rotate/RotateIn.svelte: -------------------------------------------------------------------------------- 1 | 21 | 22 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-animations/src/components/rotate/RotateOut.svelte: -------------------------------------------------------------------------------- 1 | 21 | 22 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-animations/src/components/rotate/index.ts: -------------------------------------------------------------------------------- 1 | export {default as In} from "./RotateIn.svelte"; 2 | export {default as Out} from "./RotateOut.svelte"; 3 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-animations/src/components/scale/ScaleIn.svelte: -------------------------------------------------------------------------------- 1 | 21 | 22 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-animations/src/components/scale/ScaleOut.svelte: -------------------------------------------------------------------------------- 1 | 21 | 22 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-animations/src/components/scale/index.ts: -------------------------------------------------------------------------------- 1 | export {default as In} from "./ScaleIn.svelte"; 2 | export {default as Out} from "./ScaleOut.svelte"; 3 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-animations/src/components/translate/TranslateIn.svelte: -------------------------------------------------------------------------------- 1 | 21 | 22 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-animations/src/components/translate/TranslateOut.svelte: -------------------------------------------------------------------------------- 1 | 21 | 22 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-animations/src/components/translate/index.ts: -------------------------------------------------------------------------------- 1 | export {default as In} from "./TranslateIn.svelte"; 2 | export {default as Out} from "./TranslateOut.svelte"; 3 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-animations/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./actions/actions"; 2 | export * from "./actions/transition"; 3 | 4 | export {default as Playback} from "./components/Playback.svelte"; 5 | export {default as Transition} from "./components/Transition.svelte"; 6 | 7 | export * as Fade from "./components/fade"; 8 | export * as Scale from "./components/scale"; 9 | export * as Rotate from "./components/rotate"; 10 | export * as Translate from "./components/translate"; 11 | 12 | export * from "./transitions/rotate"; 13 | export * from "./transitions/scale"; 14 | export * from "./transitions/opacity"; 15 | export * from "./transitions/translate"; 16 | export * from "./transitions/transitions"; 17 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-animations/src/transitions/opacity.ts: -------------------------------------------------------------------------------- 1 | import {linear} from "svelte/easing"; 2 | 3 | import type {ITransition, ITransitionParameters} from "./transitions"; 4 | 5 | export interface IOpacityParameters extends ITransitionParameters { 6 | end?: number | string; 7 | 8 | start?: number | string; 9 | } 10 | 11 | export const opacity: ITransition = ( 12 | element, 13 | {delay = 0, duration = 0, easing = linear, end = 1, start = 0} = {} 14 | ) => { 15 | return { 16 | delay, 17 | duration, 18 | easing, 19 | css: (t) => `opacity: calc((${end} - ${start}) * ${t})`, 20 | }; 21 | }; 22 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-animations/src/transitions/rotate.ts: -------------------------------------------------------------------------------- 1 | import {linear} from "svelte/easing"; 2 | 3 | import type {ITransition, ITransitionParameters} from "./transitions"; 4 | 5 | export interface IRotateParameters extends ITransitionParameters { 6 | end?: number | string; 7 | 8 | start?: number | string; 9 | } 10 | 11 | export const rotate: ITransition = ( 12 | element, 13 | {delay = 0, duration = 0, easing = linear, end = "360deg", start = "0deg"} = {} 14 | ) => { 15 | return { 16 | delay, 17 | duration, 18 | easing, 19 | css: (t, u) => `transform: rotate(calc((${end} - ${start}) * ${u}))`, 20 | }; 21 | }; 22 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-animations/src/transitions/scale.ts: -------------------------------------------------------------------------------- 1 | import {linear} from "svelte/easing"; 2 | 3 | import type {ITransition, ITransitionParameters} from "./transitions"; 4 | 5 | export interface IScaleParameters extends ITransitionParameters { 6 | end_x?: number | string; 7 | start_x?: number | string; 8 | 9 | end_y?: number | string; 10 | start_y?: number | string; 11 | } 12 | 13 | export const scale: ITransition = ( 14 | element, 15 | {delay = 0, duration = 0, easing = linear, end_x = 1, start_x = 0, end_y = 1, start_y = 0} = {} 16 | ) => { 17 | return { 18 | delay, 19 | duration, 20 | easing, 21 | css: (t) => 22 | `transform: scale(calc((${end_x} - ${start_x}) * ${t}), calc((${end_y} - ${start_y}) * ${t}))`, 23 | }; 24 | }; 25 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-animations/src/transitions/transitions.ts: -------------------------------------------------------------------------------- 1 | import type {EasingFunction, TransitionConfig} from "svelte/transition"; 2 | 3 | // NOTE: Aliasing Svelte's built-in types here incase of 4 | // needing changes in the future 5 | 6 | export type IEasingFunction = EasingFunction; 7 | 8 | export type ITransitionConfig = TransitionConfig; 9 | 10 | export type ITransition = ( 11 | element: HTMLElement, 12 | parameters: T 13 | ) => ITransitionConfig; 14 | 15 | export interface ITransitionParameters { 16 | delay?: number; 17 | 18 | duration?: number; 19 | 20 | easing?: EasingFunction; 21 | } 22 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-animations/src/transitions/translate.ts: -------------------------------------------------------------------------------- 1 | import {linear} from "svelte/easing"; 2 | 3 | import type {ITransition, ITransitionParameters} from "../transitions/transitions"; 4 | 5 | export interface ITranslateParameters extends ITransitionParameters { 6 | end_x?: number | string; 7 | start_x?: number | string; 8 | 9 | end_y?: number | string; 10 | start_y?: number | string; 11 | } 12 | 13 | export const translate: ITransition = ( 14 | element, 15 | { 16 | delay = 0, 17 | duration = 0, 18 | easing = linear, 19 | end_x = "0px", 20 | start_x = "0px", 21 | end_y = "0px", 22 | start_y = "0px", 23 | } = {} 24 | ) => { 25 | return { 26 | delay, 27 | duration, 28 | easing, 29 | css: (t, u) => 30 | `transform: translate(calc((${end_x} - ${start_x}) * ${u}), calc((${end_y} - ${start_y}) * ${u}))`, 31 | }; 32 | }; 33 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-animations/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@tsconfig/svelte/tsconfig.json", 3 | "compilerOptions": { 4 | "target": "esnext", 5 | "module": "esnext", 6 | "strict": true, 7 | "useDefineForClassFields": true, 8 | "resolveJsonModule": true 9 | }, 10 | "include": ["src/**/*.d.ts", "src/**/*.ts", "src/**/*.svelte"] 11 | } 12 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-builtin-extensions/README.md: -------------------------------------------------------------------------------- 1 | # @svelte-in-motion/extensions 2 | 3 | > Subpackage containing all the built-in extensions provided by Svelte-In-Motion by default. 4 | 5 | ## TODO 6 | 7 | > Extension load one-to-one Extension<->WebWorker 8 | > Extension API utilizing DeepKit RPC 9 | > Dynamic per-extension namespaced configuration 10 | > Dynamic per-extension namespaced translations 11 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-builtin-extensions/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@svelte-in-motion/builtin-extensions", 3 | "author": "novacbn", 4 | "version": "0.0.1", 5 | "type": "module", 6 | "main": "./dist/index.js", 7 | "exports": { 8 | ".": "./dist/index.js", 9 | "./package.json": "./package.json" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "https://github.com/novacbn/svelte-in-motion/tree/main/packages/%40svelte-in-motion-builtin-extensions" 14 | }, 15 | "scripts": { 16 | "build:package": "tsc", 17 | "dev:package": "tsc --watch", 18 | "format": "npm run format:package", 19 | "format:package": "prettier --config ../../.prettierrc --write ./src", 20 | "lint": "npm run lint:format", 21 | "lint:format": "prettier --config ../../.prettierrc --check ./src", 22 | "test": "npm run test:types", 23 | "test:types": "tsc --noEmit" 24 | }, 25 | "devDependencies": { 26 | "@deepkit/type-compiler": "^1.0.1-alpha.71", 27 | "@svelte-in-motion/encoding": "workspace:^", 28 | "prettier": "^2.3.1", 29 | "typescript": "^4.7.3" 30 | }, 31 | "dependencies": { 32 | "@codemirror/lang-html": "^6.1.0", 33 | "@codemirror/lang-javascript": "^6.0.1", 34 | "@codemirror/lang-markdown": "^6.0.0", 35 | "@codemirror/lang-xml": "^6.0.0", 36 | "@svelte-in-motion/configuration": "workspace:^", 37 | "@svelte-in-motion/extension": "workspace:^", 38 | "@svelte-in-motion/storage": "workspace:^", 39 | "@svelte-in-motion/type": "workspace:^", 40 | "@svelte-in-motion/utilities": "workspace:^" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-builtin-extensions/src/extensions/about.ts: -------------------------------------------------------------------------------- 1 | import type {IAppContext, IKeybindEvent} from "@svelte-in-motion/extension"; 2 | import {define_extension} from "@svelte-in-motion/extension"; 3 | 4 | export const EXTENSION_ABOUT = define_extension({ 5 | identifier: "dev.nbn.sim.about", 6 | is_builtin: true, 7 | 8 | on_activate(app: IAppContext) { 9 | const {commands, keybinds} = app; 10 | 11 | commands.push({ 12 | identifier: "about.prompt.application", 13 | on_execute: this.command_prompt_about.bind(this), 14 | }); 15 | 16 | keybinds.push({ 17 | identifier: "about.prompt.application", 18 | binds: [["f2"]], 19 | on_bind: this.keybind_prompt_about.bind(this), 20 | }); 21 | 22 | commands.push({ 23 | identifier: "about.prompt.documentation", 24 | on_execute: this.command_prompt_documentation.bind(this), 25 | }); 26 | 27 | keybinds.push({ 28 | identifier: "about.prompt.documentation", 29 | binds: [["f1"]], 30 | on_bind: this.keybind_prompt_documentation.bind(this), 31 | }); 32 | }, 33 | 34 | async command_prompt_about(app: IAppContext) { 35 | const {prompts} = app; 36 | 37 | try { 38 | await prompts.prompt_about(); 39 | } catch (err) {} 40 | }, 41 | 42 | command_prompt_documentation(app: IAppContext) { 43 | open(`https://docs.sim.nbn.dev`, "_blank"); 44 | }, 45 | 46 | keybind_prompt_about(app: IAppContext, event: IKeybindEvent) { 47 | if (event.active) this.command_prompt_about(app); 48 | }, 49 | 50 | keybind_prompt_documentation(app: IAppContext, event: IKeybindEvent) { 51 | if (event.active) this.command_prompt_documentation(app); 52 | }, 53 | }); 54 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-builtin-extensions/src/extensions/grammars.ts: -------------------------------------------------------------------------------- 1 | import {html} from "@codemirror/lang-html"; 2 | import {javascript} from "@codemirror/lang-javascript"; 3 | import {markdown, markdownLanguage} from "@codemirror/lang-markdown"; 4 | import {xml} from "@codemirror/lang-xml"; 5 | 6 | import type {IAppContext} from "@svelte-in-motion/extension"; 7 | import {define_extension} from "@svelte-in-motion/extension"; 8 | 9 | export const EXTENSION_GRAMMARS = define_extension({ 10 | identifier: "dev.nbn.sim.grammars", 11 | is_builtin: true, 12 | 13 | on_activate(app: IAppContext) { 14 | const {grammars} = app; 15 | 16 | grammars.push({ 17 | identifier: "html", 18 | // TODO: Create custom Svelte parser 19 | extensions: [".html", ".svelte"], 20 | grammar: html(), 21 | }); 22 | 23 | grammars.push({ 24 | identifier: "javascript", 25 | extensions: [".js", ".jsx", ".ts", ".tsx"], 26 | grammar: javascript({ 27 | jsx: true, 28 | typescript: true, 29 | }), 30 | }); 31 | 32 | grammars.push({ 33 | identifier: "markdown", 34 | extensions: [".md"], 35 | grammar: markdown({ 36 | base: markdownLanguage, 37 | }), 38 | }); 39 | 40 | grammars.push({ 41 | identifier: "xml", 42 | extensions: [".xml"], 43 | grammar: xml(), 44 | }); 45 | }, 46 | }); 47 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-builtin-extensions/src/extensions/templates.ts: -------------------------------------------------------------------------------- 1 | import type {IAppContext} from "@svelte-in-motion/extension"; 2 | import {define_extension} from "@svelte-in-motion/extension"; 3 | 4 | import { 5 | TEMPLATE_SAMPLE_INTERPOLATION, 6 | TEMPLATE_SAMPLE_RANDOM, 7 | TEMPLATE_SAMPLE_TRANSITIONS, 8 | } from "../templates/samples"; 9 | import {TEMPLATE_WELCOME} from "../templates/welcome"; 10 | 11 | export const EXTENSION_TEMPLATES = define_extension({ 12 | identifier: "dev.nbn.sim.templates", 13 | is_builtin: true, 14 | 15 | on_activate(app: IAppContext) { 16 | const {templates} = app; 17 | 18 | templates.push(TEMPLATE_WELCOME); 19 | 20 | templates.push(TEMPLATE_SAMPLE_INTERPOLATION); 21 | templates.push(TEMPLATE_SAMPLE_RANDOM); 22 | templates.push(TEMPLATE_SAMPLE_TRANSITIONS); 23 | }, 24 | }); 25 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-builtin-extensions/src/index.ts: -------------------------------------------------------------------------------- 1 | export {EXTENSION_ABOUT} from "./extensions/about"; 2 | export {EXTENSION_COMMANDS} from "./extensions/commands"; 3 | export {EXTENSION_EDITOR} from "./extensions/editor"; 4 | export {EXTENSION_EXPORT} from "./extensions/export"; 5 | export {EXTENSION_GRAMMARS} from "./extensions/grammars"; 6 | export {EXTENSION_PREVIEW} from "./extensions/preview"; 7 | export {EXTENSION_TEMPLATES} from "./extensions/templates"; 8 | export {EXTENSION_WORKSPACE} from "./extensions/workspace"; 9 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-builtin-extensions/src/util/errors.ts: -------------------------------------------------------------------------------- 1 | import {UserError} from "@svelte-in-motion/utilities"; 2 | 3 | export class NoEditorUserError extends UserError { 4 | name = NoEditorUserError.name; 5 | } 6 | 7 | export class NoPreviewUserError extends UserError { 8 | name = NoPreviewUserError.name; 9 | } 10 | 11 | export class NoWorkspaceUserError extends UserError { 12 | name = NoWorkspaceUserError.name; 13 | } 14 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-builtin-extensions/src/util/io.ts: -------------------------------------------------------------------------------- 1 | import {zip_blobs} from "@svelte-in-motion/storage"; 2 | 3 | export async function zip_frames(frames: Uint8Array[]): Promise { 4 | const blobs = await Promise.all( 5 | frames.map((buffer, index) => { 6 | return { 7 | blob: new Blob([buffer], {type: "image/png"}), 8 | path: `${index}.png`, 9 | }; 10 | }) 11 | ); 12 | 13 | return zip_blobs(blobs); 14 | } 15 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-builtin-extensions/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "moduleResolution": "node", 4 | "module": "ES2020", 5 | "target": "ES2019", 6 | "outDir": "./dist", 7 | "strict": true, 8 | "sourceMap": true, 9 | "declaration": true, 10 | "allowSyntheticDefaultImports": true, 11 | "esModuleInterop": true, 12 | "skipLibCheck": true, 13 | "downlevelIteration": true, 14 | "experimentalDecorators": true, 15 | "emitDecoratorMetadata": true 16 | }, 17 | "reflection": true, 18 | "include": ["src/**/*.d.ts", "src/**/*.ts"] 19 | } 20 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-bundling/README.md: -------------------------------------------------------------------------------- 1 | # @svelte-in-motion/bundling 2 | 3 | > Support library for providing a simplified abstraction for bundling a JS codebase at runtime that works w/ Svelte-In-Motion's storage APIs. 4 | 5 | ## TODO 6 | 7 | - Add support for importing media files. 8 | - Add support for React. 9 | - Add support for Vue. 10 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-bundling/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@svelte-in-motion/bundling", 3 | "author": "novacbn", 4 | "version": "0.0.1", 5 | "type": "module", 6 | "main": "./src/index.ts", 7 | "exports": { 8 | ".": "./src/index.ts", 9 | "./package.json": "./package.json" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "https://github.com/novacbn/svelte-in-motion/tree/main/packages/%40svelte-in-motion-bundling" 14 | }, 15 | "scripts": { 16 | "format": "npm run format:package", 17 | "format:package": "prettier --config ../../.prettierrc --write ./src", 18 | "lint": "npm run lint:format", 19 | "lint:format": "prettier --config ../../.prettierrc --check ./src", 20 | "test": "npm run test:types", 21 | "test:types": "tsc --noEmit" 22 | }, 23 | "devDependencies": { 24 | "prettier": "^2.3.1", 25 | "typescript": "^4.7.3" 26 | }, 27 | "dependencies": { 28 | "@svelte-in-motion/storage": "workspace:^", 29 | "@svelte-in-motion/utilities": "workspace:^", 30 | "esbuild-wasm": "^0.14.25", 31 | "svelte": "^3.46.4" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-bundling/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./bundler"; 2 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-bundling/src/plugins/javascript.ts: -------------------------------------------------------------------------------- 1 | import type {Plugin} from "esbuild-wasm"; 2 | 3 | import type {IDriver} from "@svelte-in-motion/storage"; 4 | 5 | export interface IStorageOptions { 6 | storage: IDriver; 7 | } 8 | 9 | export function javascript_plugin(options: IStorageOptions): Plugin { 10 | const {storage} = options; 11 | 12 | return { 13 | name: "sim-javascript", 14 | 15 | setup(build) { 16 | build.onLoad({filter: /\.js$/}, async (args) => { 17 | const script = await storage.read_file_text(args.path); 18 | 19 | return { 20 | contents: script, 21 | loader: "js", 22 | }; 23 | }); 24 | }, 25 | }; 26 | } 27 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-bundling/src/plugins/storage.ts: -------------------------------------------------------------------------------- 1 | import type {Plugin} from "esbuild-wasm"; 2 | 3 | import {append_pathname} from "@svelte-in-motion/utilities"; 4 | import type {IDriver} from "@svelte-in-motion/storage"; 5 | 6 | export interface IStorageOptions { 7 | storage: IDriver; 8 | } 9 | 10 | export function storage_plugin(options: IStorageOptions): Plugin { 11 | const {storage} = options; 12 | 13 | return { 14 | name: "sim-storage", 15 | 16 | setup(build) { 17 | build.onResolve({filter: /.*/}, async (args) => { 18 | const path = append_pathname(args.resolveDir, args.path); 19 | const stats = await storage.stats(path); 20 | 21 | return stats && stats.is_file ? {path} : null; 22 | }); 23 | }, 24 | }; 25 | } 26 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-bundling/src/plugins/svelte.ts: -------------------------------------------------------------------------------- 1 | import type {Plugin} from "esbuild-wasm"; 2 | import {compile} from "svelte/compiler"; 3 | 4 | import type {IDriver} from "@svelte-in-motion/storage"; 5 | import {base_pathname} from "@svelte-in-motion/utilities"; 6 | 7 | export interface ISvelteOptions { 8 | storage: IDriver; 9 | } 10 | 11 | export function svelte_plugin(options: ISvelteOptions): Plugin { 12 | const {storage} = options; 13 | 14 | return { 15 | name: "sim-svelte", 16 | 17 | setup(build) { 18 | build.onLoad({filter: /\.svelte$/}, async (args) => { 19 | const file = base_pathname(args.path); 20 | const name = (file.split(".")[0] ?? "/App").slice(1); 21 | 22 | const script = await storage.read_file_text(args.path); 23 | 24 | const result = compile(script, { 25 | name, 26 | filename: file, 27 | 28 | dev: true, 29 | generate: "dom", 30 | format: "esm", 31 | css: true, 32 | }); 33 | 34 | return { 35 | contents: result.js.code, 36 | loader: "js", 37 | }; 38 | }); 39 | }, 40 | }; 41 | } 42 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-bundling/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "moduleResolution": "node", 4 | "module": "ES2020", 5 | "target": "ES2019", 6 | "strict": true, 7 | "sourceMap": true, 8 | "declaration": true, 9 | "allowSyntheticDefaultImports": true, 10 | "esModuleInterop": true, 11 | "skipLibCheck": true, 12 | "downlevelIteration": true, 13 | "experimentalDecorators": true, 14 | "emitDecoratorMetadata": true 15 | }, 16 | "include": ["src/**/*.d.ts", "src/**/*.ts"] 17 | } 18 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-configuration/README.md: -------------------------------------------------------------------------------- 1 | # @svelte-in-motion/configuration 2 | 3 | > ... 4 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-configuration/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@svelte-in-motion/configuration", 3 | "author": "novacbn", 4 | "version": "0.0.1", 5 | "type": "module", 6 | "main": "./dist/index.js", 7 | "exports": { 8 | ".": "./dist/index.js", 9 | "./package.json": "./package.json" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "https://github.com/novacbn/svelte-in-motion/tree/main/packages/%40svelte-in-motion-configuration" 14 | }, 15 | "scripts": { 16 | "build:package": "tsc", 17 | "dev:package": "tsc --watch", 18 | "format": "npm run format:package", 19 | "format:package": "prettier --config ../../.prettierrc --write ./src", 20 | "lint": "npm run lint:format", 21 | "lint:format": "prettier --config ../../.prettierrc --check ./src", 22 | "test": "npm run test:types", 23 | "test:types": "tsc --noEmit" 24 | }, 25 | "devDependencies": { 26 | "@deepkit/type-compiler": "^1.0.1-alpha.71", 27 | "@types/node": "^17.0.31", 28 | "prettier": "^2.3.1", 29 | "typescript": "^4.7.3" 30 | }, 31 | "dependencies": { 32 | "@svelte-in-motion/core": "workspace:^", 33 | "@svelte-in-motion/storage": "workspace:^", 34 | "@svelte-in-motion/temporal": "workspace:^", 35 | "@svelte-in-motion/type": "workspace:^", 36 | "svelte": "^3.48.0" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-configuration/src/configurations/preferences.ts: -------------------------------------------------------------------------------- 1 | import {DataClass} from "@svelte-in-motion/type"; 2 | 3 | import {Configuration} from "./configuration"; 4 | 5 | export const DEFAULT_LOCALE = "en-US"; 6 | 7 | export const SUPPORTED_LOCALES = ["en-US"] as const; 8 | 9 | export type ISupportedLocales = typeof SUPPORTED_LOCALES[number]; 10 | 11 | export class PreferencesLocaleConfiguration extends DataClass { 12 | preferred?: ISupportedLocales; 13 | } 14 | 15 | export class PreferencesUIEditorFileTreeConfiguration extends DataClass { 16 | enabled: boolean = true; 17 | } 18 | 19 | export class PreferencesUIEditorScriptConfiguration extends DataClass { 20 | enabled: boolean = true; 21 | } 22 | 23 | export class PreferencesUIEditorConfiguration extends DataClass { 24 | file_tree: PreferencesUIEditorFileTreeConfiguration = 25 | new PreferencesUIEditorFileTreeConfiguration(); 26 | 27 | script: PreferencesUIEditorScriptConfiguration = new PreferencesUIEditorScriptConfiguration(); 28 | } 29 | 30 | export class PreferencesUIPreviewCheckerboardConfiguration extends DataClass { 31 | enabled: boolean = true; 32 | } 33 | 34 | export class PreferencesUIPreviewControlsConfiguration extends DataClass { 35 | enabled: boolean = true; 36 | } 37 | 38 | export class PreferencesUIPreviewTimelineConfiguration extends DataClass { 39 | enabled: boolean = true; 40 | } 41 | 42 | export class PreferencesUIPreviewViewportConfiguration extends DataClass { 43 | enabled: boolean = true; 44 | } 45 | 46 | export class PreferencesUIPreviewConfiguration extends DataClass { 47 | checkerboard: PreferencesUIPreviewCheckerboardConfiguration = 48 | new PreferencesUIPreviewCheckerboardConfiguration(); 49 | 50 | controls: PreferencesUIPreviewControlsConfiguration = 51 | new PreferencesUIPreviewControlsConfiguration(); 52 | 53 | timeline: PreferencesUIPreviewTimelineConfiguration = 54 | new PreferencesUIPreviewTimelineConfiguration(); 55 | 56 | viewport: PreferencesUIPreviewViewportConfiguration = 57 | new PreferencesUIPreviewViewportConfiguration(); 58 | } 59 | 60 | export class PreferencesUIConfiguration extends DataClass { 61 | editor: PreferencesUIEditorConfiguration = new PreferencesUIEditorConfiguration(); 62 | 63 | preview: PreferencesUIPreviewConfiguration = new PreferencesUIPreviewConfiguration(); 64 | } 65 | 66 | export class PreferencesConfiguration extends Configuration { 67 | locale: PreferencesLocaleConfiguration = new PreferencesLocaleConfiguration(); 68 | 69 | ui: PreferencesUIConfiguration = new PreferencesUIConfiguration(); 70 | } 71 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-configuration/src/configurations/workspace.ts: -------------------------------------------------------------------------------- 1 | import {Maximum, Minimum} from "@svelte-in-motion/type"; 2 | 3 | import {Configuration} from "./configuration"; 4 | 5 | export class WorkspaceConfiguration extends Configuration { 6 | framerate: number & Minimum<16> & Maximum<120> = 60; 7 | 8 | height: number & Minimum<0> & Maximum<4320> = 720; 9 | 10 | // NOTE: Not sure who would wait forever for an 8K render... Also 11 | // resolution is platform / codec dependant as well. But seems like a good default? 12 | 13 | maxframes: number & Minimum<0> = 60 * 60; // 60 seconds 14 | 15 | width: number & Minimum<0> & Maximum<7680> = 1280; 16 | } 17 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-configuration/src/configurations/workspaces.ts: -------------------------------------------------------------------------------- 1 | import type {IDriver} from "@svelte-in-motion/storage"; 2 | import {indexeddb} from "@svelte-in-motion/storage"; 3 | import type {Duration} from "@svelte-in-motion/temporal"; 4 | import {Instant, Now} from "@svelte-in-motion/temporal"; 5 | import {Ascii, MaxLength, MinLength, UUID, DataClass, uuid} from "@svelte-in-motion/type"; 6 | 7 | import {Configuration} from "./configuration"; 8 | 9 | const STORAGE_DRIVERS = { 10 | indexeddb: { 11 | async make(metadata: WorkspacesItemConfiguration): Promise { 12 | const {identifier} = metadata; 13 | 14 | return indexeddb(`sim:workspace:${identifier}`); 15 | }, 16 | }, 17 | }; 18 | 19 | export class WorkspacesItemStorageConfiguration extends DataClass { 20 | // TODO: This needs to be dynamic some how... Otherwise extensions wouldn't be able to add more drivers 21 | driver: "indexeddb" = "indexeddb"; 22 | 23 | configuration?: Record; 24 | } 25 | 26 | export class WorkspacesItemConfiguration extends DataClass { 27 | accessed_at: Instant = Now.instant(); 28 | 29 | created_at: Instant = Now.instant(); 30 | 31 | name!: string & Ascii & MinLength<1> & MaxLength<32>; 32 | 33 | identifier: UUID = uuid(); 34 | 35 | storage: WorkspacesItemStorageConfiguration = new WorkspacesItemStorageConfiguration(); 36 | 37 | get_accessed_duration(): Duration { 38 | const current_instant = Now.instant(); 39 | 40 | return this.accessed_at.until(current_instant, {largestUnit: "hour"}); 41 | } 42 | 43 | format_accessed(): string { 44 | // HACK: Nothing supports `Intl.DurationFormat` yet, so have to manually handle output 45 | const duration = this.get_accessed_duration(); 46 | const relative = new Intl.RelativeTimeFormat(); 47 | 48 | if (duration.hours > 0) return relative.format(-duration.hours, "hour"); 49 | else if (duration.minutes > 0) return relative.format(-duration.minutes, "minutes"); 50 | return relative.format(-duration.seconds, "seconds"); 51 | } 52 | 53 | make_driver(): Promise { 54 | // TODO: this needs to become extendable via extensions 55 | const {driver: driver_name} = this.storage; 56 | const driver = STORAGE_DRIVERS[driver_name]; 57 | 58 | if (!driver) { 59 | throw new ReferenceError( 60 | `bad argument #0 to 'make_driver' (invalid driver name '${driver_name}')` 61 | ); 62 | } 63 | 64 | return driver.make(this); 65 | } 66 | } 67 | 68 | export class WorkspacesConfiguration extends Configuration { 69 | workspaces: WorkspacesItemConfiguration[] = []; 70 | } 71 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-configuration/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./configurations/configuration"; 2 | export * from "./configurations/preferences"; 3 | export * from "./configurations/workspace"; 4 | export * from "./configurations/workspaces"; 5 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-configuration/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "moduleResolution": "node", 4 | "module": "ES2020", 5 | "target": "ES2019", 6 | "outDir": "./dist", 7 | "strict": true, 8 | "sourceMap": true, 9 | "declaration": true, 10 | "allowSyntheticDefaultImports": true, 11 | "esModuleInterop": true, 12 | "skipLibCheck": true, 13 | "downlevelIteration": true, 14 | "experimentalDecorators": true, 15 | "emitDecoratorMetadata": true 16 | }, 17 | "reflection": true, 18 | "include": ["src/**/*.d.ts", "src/**/*.ts"] 19 | } 20 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-core/README.md: -------------------------------------------------------------------------------- 1 | # @svelte-in-motion/core 2 | 3 | > Support library that provides Svelte-In-Motion's main animation engine implementation. 4 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-core/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@svelte-in-motion/core", 3 | "author": "novacbn", 4 | "version": "0.0.1", 5 | "type": "module", 6 | "main": "./src/index.ts", 7 | "exports": { 8 | ".": "./src/index.ts", 9 | "./package.json": "./package.json" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "https://github.com/novacbn/svelte-in-motion/tree/main/packages/%40svelte-in-motion-core" 14 | }, 15 | "scripts": { 16 | "format": "npm run format:package", 17 | "format:package": "prettier --config ../../.prettierrc --write ./src", 18 | "lint": "npm run lint:format", 19 | "lint:format": "prettier --config ../../.prettierrc --check ./src", 20 | "test": "npm run test:types", 21 | "test:types": "tsc --noEmit" 22 | }, 23 | "devDependencies": { 24 | "prettier": "^2.3.1", 25 | "typescript": "^4.7.3" 26 | }, 27 | "dependencies": { 28 | "@svelte-in-motion/utilities": "workspace:^", 29 | "svelte": "^3.46.4" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-core/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./stores/advance"; 2 | export * from "./stores/completion"; 3 | export * from "./stores/duration"; 4 | export * from "./stores/frame"; 5 | export * from "./stores/framerate"; 6 | export * from "./stores/interpolate"; 7 | export * from "./stores/maxframes"; 8 | export * from "./stores/playing"; 9 | export * from "./stores/random"; 10 | export * from "./stores/seek"; 11 | export * from "./stores/state"; 12 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-core/src/stores/completion.ts: -------------------------------------------------------------------------------- 1 | import type {Readable} from "svelte/store"; 2 | import {derived} from "svelte/store"; 3 | 4 | import type {ReadableOnly} from "@svelte-in-motion/utilities"; 5 | 6 | import type {IFrameStore} from "./frame"; 7 | import {CONTEXT_FRAME} from "./frame"; 8 | import type {IMaxFramesStore} from "./maxframes"; 9 | import {CONTEXT_MAXFRAMES} from "./maxframes"; 10 | 11 | export type ICompletionStore = Readable; 12 | 13 | export interface ICompletionOptions { 14 | frame: ReadableOnly; 15 | 16 | maxframes: ReadableOnly; 17 | } 18 | 19 | export const CONTEXT_COMPLETION = { 20 | has() { 21 | return CONTEXT_FRAME.has() && CONTEXT_MAXFRAMES.has(); 22 | }, 23 | 24 | get() { 25 | const frame = CONTEXT_FRAME.get(); 26 | if (!frame) { 27 | throw new ReferenceError( 28 | `bad dispatch to 'CONTEXT_COMPLETION.get' (context 'CONTEXT_FRAME' not available)` 29 | ); 30 | } 31 | 32 | const maxframes = CONTEXT_MAXFRAMES.get(); 33 | if (!maxframes) { 34 | throw new ReferenceError( 35 | `bad dispatch to 'CONTEXT_COMPLETION.get' (contect 'CONTEXT_MAXFRAMES' not available)` 36 | ); 37 | } 38 | 39 | return completion({frame, maxframes}); 40 | }, 41 | }; 42 | 43 | export function completion(options: ICompletionOptions): ICompletionStore { 44 | const {frame, maxframes} = options; 45 | 46 | return derived([frame, maxframes], ([$frame, $maxframes]) => $frame / $maxframes); 47 | } 48 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-core/src/stores/duration.ts: -------------------------------------------------------------------------------- 1 | import type {Readable} from "svelte/store"; 2 | import {derived} from "svelte/store"; 3 | 4 | import type {ReadableOnly} from "@svelte-in-motion/utilities"; 5 | 6 | import type {IFrameRateStore} from "./framerate"; 7 | import {CONTEXT_FRAMERATE} from "./framerate"; 8 | import type {IMaxFramesStore} from "./maxframes"; 9 | import {CONTEXT_MAXFRAMES} from "./maxframes"; 10 | 11 | export type IDurationStore = Readable; 12 | 13 | export interface IDurationOptions { 14 | framerate: ReadableOnly; 15 | 16 | maxframes: ReadableOnly; 17 | } 18 | 19 | export const CONTEXT_DURATION = { 20 | has() { 21 | return CONTEXT_FRAMERATE.has() && CONTEXT_MAXFRAMES.has(); 22 | }, 23 | 24 | get() { 25 | const framerate = CONTEXT_FRAMERATE.get(); 26 | if (!framerate) { 27 | throw new ReferenceError( 28 | `bad dispatch to 'CONTEXT_DURATION.get' (context 'CONTEXT_FRAMERATE' not available)` 29 | ); 30 | } 31 | 32 | const maxframes = CONTEXT_MAXFRAMES.get(); 33 | if (!maxframes) { 34 | throw new ReferenceError( 35 | `bad dispatch to 'CONTEXT_DURATION.get' (contect 'CONTEXT_MAXFRAMES' not available)` 36 | ); 37 | } 38 | 39 | return duration({framerate, maxframes}); 40 | }, 41 | }; 42 | 43 | export function duration(options: IDurationOptions): IDurationStore { 44 | const {framerate, maxframes} = options; 45 | 46 | return derived([framerate, maxframes], ([$framerate, $maxframes]) => $maxframes / $framerate); 47 | } 48 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-core/src/stores/frame.ts: -------------------------------------------------------------------------------- 1 | import type {Writable} from "svelte/store"; 2 | import {writable} from "svelte/store"; 3 | 4 | import {make_scoped_context} from "@svelte-in-motion/utilities"; 5 | 6 | export type IFrameStore = Writable; 7 | 8 | export const CONTEXT_FRAME = make_scoped_context("frame"); 9 | 10 | export function frame(value: number = 0): IFrameStore { 11 | return writable(value); 12 | } 13 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-core/src/stores/framerate.ts: -------------------------------------------------------------------------------- 1 | import type {Writable} from "svelte/store"; 2 | import {writable} from "svelte/store"; 3 | 4 | import {make_scoped_context} from "@svelte-in-motion/utilities"; 5 | 6 | export type IFrameRateStore = Writable; 7 | 8 | export const CONTEXT_FRAMERATE = make_scoped_context("framerate"); 9 | 10 | export function framerate(value: number = 0): IFrameRateStore { 11 | return writable(value); 12 | } 13 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-core/src/stores/interpolate.ts: -------------------------------------------------------------------------------- 1 | import type {StartStopNotifier, Writable} from "svelte/store"; 2 | import {derived, writable} from "svelte/store"; 3 | import type {EasingFunction} from "svelte/types/runtime/transition"; 4 | 5 | export enum EXTRAPOLATE_MODES { 6 | clamp = "clamp", 7 | 8 | extend = "extend", 9 | 10 | wrap = "wrap", 11 | } 12 | 13 | export type IExtrapolateModesLiteral = `${EXTRAPOLATE_MODES}`; 14 | 15 | export type IInterpolateStore = Writable; 16 | 17 | export interface IInterpolateRangeOptions { 18 | extrapolate?: IExtrapolateModesLiteral; 19 | 20 | value?: number; 21 | } 22 | 23 | export interface IInterpolateOptions { 24 | easing?: EasingFunction; 25 | 26 | end?: IInterpolateRangeOptions | number; 27 | 28 | start?: IInterpolateRangeOptions | number; 29 | } 30 | 31 | export function interpolate( 32 | state: number = 0, 33 | options: IInterpolateOptions = {}, 34 | callback?: StartStopNotifier 35 | ): IInterpolateStore { 36 | const {easing, end = {}, start = {}} = options; 37 | 38 | const {extrapolate: end_extrapolate = EXTRAPOLATE_MODES.clamp, value: end_value = 1} = 39 | typeof end === "number" ? {value: end} : end; 40 | 41 | const {extrapolate: start_extrapolate = EXTRAPOLATE_MODES.clamp, value: start_value = 0} = 42 | typeof start === "number" ? {value: start} : start; 43 | 44 | const state_store = writable(state, callback); 45 | const interpolated_store = derived(state_store, ($state) => { 46 | if (end_extrapolate === EXTRAPOLATE_MODES.clamp) $state = Math.min($state, 1); 47 | else if (end_extrapolate === EXTRAPOLATE_MODES.wrap && $state > 1) { 48 | $state = Math.floor($state) % 2 === 0 ? $state % 1 : 1 - ($state % 1); 49 | } 50 | 51 | if (start_extrapolate === EXTRAPOLATE_MODES.clamp) $state = Math.max($state, 0); 52 | else if (start_extrapolate === EXTRAPOLATE_MODES.wrap && $state < 0) { 53 | $state = Math.abs($state); 54 | $state = Math.floor($state) % 2 === 0 ? $state % 1 : 1 - ($state % 1); 55 | } 56 | 57 | if (easing) $state = easing($state); 58 | return (end_value - start_value) * $state + start_value; 59 | }); 60 | 61 | return { 62 | set: state_store.set, 63 | subscribe: interpolated_store.subscribe, 64 | update: state_store.update, 65 | }; 66 | } 67 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-core/src/stores/maxframes.ts: -------------------------------------------------------------------------------- 1 | import type {Writable} from "svelte/store"; 2 | import {writable} from "svelte/store"; 3 | 4 | import {make_scoped_context} from "@svelte-in-motion/utilities"; 5 | 6 | export type IMaxFramesStore = Writable; 7 | 8 | export const CONTEXT_MAXFRAMES = make_scoped_context("maxframes"); 9 | 10 | export function maxframes(value: number = 0): IMaxFramesStore { 11 | return writable(value); 12 | } 13 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-core/src/stores/playing.ts: -------------------------------------------------------------------------------- 1 | import type {Writable} from "svelte/store"; 2 | import {writable} from "svelte/store"; 3 | 4 | import {make_scoped_context} from "@svelte-in-motion/utilities"; 5 | 6 | export type IPlayingStore = Writable; 7 | 8 | export const CONTEXT_PLAYING = make_scoped_context("playing"); 9 | 10 | export function playing(value: boolean = false): IPlayingStore { 11 | return writable(value); 12 | } 13 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-core/src/stores/seek.ts: -------------------------------------------------------------------------------- 1 | import type {Readable} from "svelte/store"; 2 | import {derived} from "svelte/store"; 3 | 4 | import type {ReadableOnly} from "@svelte-in-motion/utilities"; 5 | 6 | import type {IFrameStore} from "./frame"; 7 | import {CONTEXT_FRAME} from "./frame"; 8 | import type {IFrameRateStore} from "./framerate"; 9 | import {CONTEXT_FRAMERATE} from "./framerate"; 10 | import type {IMaxFramesStore} from "./maxframes"; 11 | import {CONTEXT_MAXFRAMES} from "./maxframes"; 12 | 13 | export type ISeekStore = Readable; 14 | 15 | export interface ISeekOptions { 16 | frame: ReadableOnly; 17 | 18 | framerate: ReadableOnly; 19 | 20 | maxframes: ReadableOnly; 21 | } 22 | 23 | export const CONTEXT_SEEK = { 24 | has() { 25 | return CONTEXT_FRAME.has() && CONTEXT_MAXFRAMES.has(); 26 | }, 27 | 28 | get() { 29 | const frame = CONTEXT_FRAME.get(); 30 | if (!frame) { 31 | throw new ReferenceError( 32 | `bad dispatch to 'CONTEXT_SEEK.get' (context 'CONTEXT_FRAME' not available)` 33 | ); 34 | } 35 | 36 | const framerate = CONTEXT_FRAMERATE.get(); 37 | if (!framerate) { 38 | throw new ReferenceError( 39 | `bad dispatch to 'CONTEXT_SEEK.get' (context 'CONTEXT_FRAMERATE' not available)` 40 | ); 41 | } 42 | 43 | const maxframes = CONTEXT_MAXFRAMES.get(); 44 | if (!maxframes) { 45 | throw new ReferenceError( 46 | `bad dispatch to 'CONTEXT_SEEK.get' (contect 'CONTEXT_MAXFRAMES' not available)` 47 | ); 48 | } 49 | 50 | return seek({frame, framerate, maxframes}); 51 | }, 52 | }; 53 | 54 | export function seek(options: ISeekOptions): ISeekStore { 55 | const {frame, framerate, maxframes} = options; 56 | 57 | return derived( 58 | [frame, framerate, maxframes], 59 | ([$frame, $framerate, $maxframes]) => ($frame / $maxframes) * ($maxframes / $framerate) 60 | ); 61 | } 62 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-core/src/stores/state.ts: -------------------------------------------------------------------------------- 1 | import type {Readable} from "svelte/store"; 2 | import {derived} from "svelte/store"; 3 | 4 | import type {ReadableOnly} from "@svelte-in-motion/utilities"; 5 | 6 | import type {IFrameStore} from "./frame"; 7 | import {CONTEXT_FRAME} from "./frame"; 8 | import type {IFrameRateStore} from "./framerate"; 9 | import {CONTEXT_FRAMERATE} from "./framerate"; 10 | import type {IInterpolateOptions} from "./interpolate"; 11 | import {interpolate} from "./interpolate"; 12 | 13 | export type IStateStore = Readable; 14 | 15 | export interface IStateOptions extends IInterpolateOptions { 16 | delay?: number; 17 | 18 | duration?: number; 19 | 20 | frame: ReadableOnly; 21 | 22 | framerate: ReadableOnly; 23 | } 24 | 25 | export const CONTEXT_STATE = { 26 | has() { 27 | return CONTEXT_FRAME.has() && CONTEXT_FRAMERATE.has(); 28 | }, 29 | 30 | get(options: Omit = {}) { 31 | const frame = CONTEXT_FRAME.get(); 32 | if (!frame) { 33 | throw new ReferenceError( 34 | `bad dispatch to 'CONTEXT_STATE.get' (context 'CONTEXT_FRAME' not available)` 35 | ); 36 | } 37 | 38 | const framerate = CONTEXT_FRAMERATE.get(); 39 | if (!framerate) { 40 | throw new ReferenceError( 41 | `bad dispatch to 'CONTEXT_STATE.get' (context 'CONTEXT_FRAMERATE' not available)` 42 | ); 43 | } 44 | 45 | return state({...options, frame, framerate}); 46 | }, 47 | }; 48 | 49 | export function state(options: IStateOptions): IStateStore { 50 | const {delay = 0, duration = 0, frame, framerate, ...extended_options} = options; 51 | 52 | return interpolate(0, extended_options, (set) => { 53 | const destroy = derived([frame, framerate], ([$frame, $framerate]) => { 54 | const _delay = delay * $framerate; 55 | const _duration = duration * $framerate; 56 | 57 | return ($frame - _delay) / _duration; 58 | }).subscribe((state) => set(state)); 59 | 60 | return () => destroy(); 61 | }); 62 | } 63 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-core/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "moduleResolution": "node", 4 | "module": "ES2020", 5 | "target": "ES2019", 6 | "strict": true, 7 | "sourceMap": true, 8 | "declaration": true, 9 | "allowSyntheticDefaultImports": true, 10 | "esModuleInterop": true, 11 | "skipLibCheck": true, 12 | "downlevelIteration": true, 13 | "experimentalDecorators": true, 14 | "emitDecoratorMetadata": true 15 | }, 16 | "include": ["src/**/*.d.ts", "src/**/*.ts"] 17 | } 18 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-editor/README.md: -------------------------------------------------------------------------------- 1 | # @svelte-in-motion/editor 2 | 3 | > Implementation for the main UI that allows users to create, preview, and render Svelte-In-Motion projects. 4 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-editor/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Svelte In Motion 8 | 9 | 31 | 32 | 33 | 34 |
Svelte-In-Motion
35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-editor/preview.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Preview :: Svelte In Motion 8 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-editor/public/extern/wasm/esbuild.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/novacbn/svelte-in-motion/de24fb83d3b5b8a15675baa3bdb7b7d510bffeda/packages/@svelte-in-motion-editor/public/extern/wasm/esbuild.wasm -------------------------------------------------------------------------------- /packages/@svelte-in-motion-editor/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/novacbn/svelte-in-motion/de24fb83d3b5b8a15675baa3bdb7b7d510bffeda/packages/@svelte-in-motion-editor/public/favicon.ico -------------------------------------------------------------------------------- /packages/@svelte-in-motion-editor/src/components/Loader.svelte: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 |
15 |
16 |
17 |
18 | 19 | 24 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-editor/src/components/Tooltip.svelte: -------------------------------------------------------------------------------- 1 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-editor/src/components/app/AppHeader.svelte: -------------------------------------------------------------------------------- 1 | 14 | 15 | 16 | 23 | 24 | Svelte-In-Motion 25 | 26 | 27 | 28 | 29 | 30 | 31 | 36 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-editor/src/components/app/AppLayout.svelte: -------------------------------------------------------------------------------- 1 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 33 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-editor/src/components/app/AppStatus.svelte: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 16 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-editor/src/components/editor/EditorFileTree.svelte: -------------------------------------------------------------------------------- 1 | 36 | 37 | 65 | 66 | 80 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-editor/src/components/editor/EditorLayout.svelte: -------------------------------------------------------------------------------- 1 | 17 | 18 |
23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 |
31 | 32 | 76 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-editor/src/components/editor/EditorStatus.svelte: -------------------------------------------------------------------------------- 1 | 10 | 11 | 12 | 13 | {$metadata.name} 14 | 15 | 16 | 17 | 18 | 19 | {$configuration.width}x{$configuration.height} 20 | 21 | 22 | 23 | 24 | 25 | {$configuration.framerate} FPS 26 | 27 | 28 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-editor/src/components/form/FormBoolean.svelte: -------------------------------------------------------------------------------- 1 | 26 | 27 | 28 | 29 | {#if description || label} 30 | 31 | {#if $translations.has(label)} 32 | {$translations.format(label)} 33 | {/if} 34 | 35 | {#if $translations.has(description)} 36 | 37 | {$translations.format(description)} 38 | 39 | {/if} 40 | 41 | {/if} 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-editor/src/components/form/FormNumber.svelte: -------------------------------------------------------------------------------- 1 | 42 | 43 | 44 | {#if $translations.has(label)} 45 | {$translations.format(label)} 46 | {/if} 47 | 48 | 58 | 59 | {#if $translations.has(description)} 60 | 61 | {$translations.format(description)} 62 | 63 | {/if} 64 | 65 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-editor/src/components/form/FormString.svelte: -------------------------------------------------------------------------------- 1 | 42 | 43 | 44 | {#if $translations.has(label)} 45 | {$translations.format(label)} 46 | {/if} 47 | 48 | 58 | 59 | {#if $translations.has(description)} 60 | 61 | {$translations.format(description)} 62 | 63 | {/if} 64 | 65 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-editor/src/components/preview/PreviewTimeline.svelte: -------------------------------------------------------------------------------- 1 | 30 | 31 | 42 | 43 | 48 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-editor/src/components/prompts/AboutPrompt.svelte: -------------------------------------------------------------------------------- 1 | 27 | 28 | 29 | 30 | 31 | {$translations.format("prompts-about-version-label")} 32 | 33 | 34 | v{APPLICATION_VERSION} 35 | 36 | 37 | 38 | 39 | {$translations.format("prompts-about-source-label")} 40 | 41 | 42 | 48 | github.com/novacbn/svelte-in-motion 49 | 50 | 51 | 52 | 53 | 54 | 57 | 58 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-editor/src/components/prompts/AlertPrompt.svelte: -------------------------------------------------------------------------------- 1 | 27 | 28 | 29 | 30 | {$translations.format(`prompts-${namespace}-description`)} 31 | 32 | 33 | 34 | 35 | 38 | 39 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-editor/src/components/prompts/ConfirmPrompt.svelte: -------------------------------------------------------------------------------- 1 | 33 | 34 | 35 | 36 | {$translations.format(`prompts-${namespace}-description`)} 37 | 38 | 39 | 40 | 41 | 44 | 45 | 48 | 49 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-editor/src/components/prompts/CreateWorkspace.svelte: -------------------------------------------------------------------------------- 1 | 31 | 32 | Create Workspace 33 | 34 | 35 | 36 | 37 | 38 | Name 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 49 | 50 | 59 | 60 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-editor/src/components/prompts/FormPrompt.svelte: -------------------------------------------------------------------------------- 1 | 48 | 49 | 50 |
51 | 52 | 53 | 54 | 55 | 56 |
57 |
58 | 59 | 60 | {#if $prompts?.is_dismissible} 61 | 64 | {/if} 65 | 66 | 75 | 76 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-editor/src/components/prompts/LoaderPrompt.svelte: -------------------------------------------------------------------------------- 1 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-editor/src/global.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-editor/src/lib/editor.ts: -------------------------------------------------------------------------------- 1 | import type {IDriver, IFileTextStore} from "@svelte-in-motion/storage"; 2 | import {preload_text} from "@svelte-in-motion/storage"; 3 | import {make_scoped_context} from "@svelte-in-motion/utilities"; 4 | 5 | import type {IEditorViewStore} from "./stores/editor"; 6 | import {editorview as make_editorview_store} from "./stores/editor"; 7 | import type {IFilePathStore} from "./stores/io"; 8 | import {filepath as make_filepath_store} from "./stores/io"; 9 | 10 | export const CONTEXT_EDITOR = make_scoped_context("editor"); 11 | 12 | export interface IEditorContext { 13 | file_path: IFilePathStore; 14 | 15 | text: IFileTextStore; 16 | 17 | view: IEditorViewStore; 18 | } 19 | 20 | export async function editor(storage: IDriver, file_path: string): Promise { 21 | const text = await preload_text(storage, file_path); 22 | 23 | return { 24 | file_path: make_filepath_store(file_path), 25 | text, 26 | view: make_editorview_store(), 27 | }; 28 | } 29 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-editor/src/lib/lucide-svelte.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | // HACK: Even though SVG allows for CSS units, Lucide Icons doesn't 5 | // accept `size: string` by default. So we need to override 6 | 7 | declare module "lucide-svelte" { 8 | import {SvelteComponentTyped} from "svelte"; 9 | 10 | interface IconProps extends Partial> { 11 | color?: string; 12 | size?: number | string; 13 | strokeWidth?: number | string; 14 | class?: string; 15 | } 16 | 17 | interface IconEvents { 18 | [evt: string]: CustomEvent; 19 | } 20 | 21 | export type Icon = SvelteComponentTyped; 22 | 23 | // NOTE: Only put icons here being used by the Application 24 | 25 | export declare class Archive extends SvelteComponentTyped {} 26 | export declare class Check extends SvelteComponentTyped {} 27 | export declare class Clock extends SvelteComponentTyped {} 28 | export declare class Download extends SvelteComponentTyped {} 29 | export declare class FileCode extends SvelteComponentTyped {} 30 | export declare class Edit extends SvelteComponentTyped {} 31 | export declare class Film extends SvelteComponentTyped {} 32 | export declare class Grid extends SvelteComponentTyped {} 33 | export declare class PackageX extends SvelteComponentTyped {} 34 | export declare class Palmtree extends SvelteComponentTyped {} 35 | export declare class Pause extends SvelteComponentTyped {} 36 | export declare class Play extends SvelteComponentTyped {} 37 | export declare class SkipBack extends SvelteComponentTyped {} 38 | export declare class SkipForward extends SvelteComponentTyped {} 39 | export declare class Slash extends SvelteComponentTyped {} 40 | export declare class Video extends SvelteComponentTyped {} 41 | export declare class X extends SvelteComponentTyped {} 42 | } 43 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-editor/src/lib/preview.ts: -------------------------------------------------------------------------------- 1 | import type {IFrameStore, IPlayingStore} from "@svelte-in-motion/core"; 2 | import {frame as make_frame_store, playing as make_playing_store} from "@svelte-in-motion/core"; 3 | import {make_scoped_context} from "@svelte-in-motion/utilities"; 4 | 5 | import type {IFilePathStore} from "./stores/io"; 6 | import {filepath as make_filepath_store} from "./stores/io"; 7 | 8 | export const CONTEXT_PREVIEW = make_scoped_context("preview"); 9 | 10 | export interface IPreviewContext { 11 | file_path: IFilePathStore; 12 | 13 | frame: IFrameStore; 14 | 15 | playing: IPlayingStore; 16 | } 17 | 18 | export async function preview(file_path: string): Promise { 19 | return { 20 | file_path: make_filepath_store(file_path), 21 | frame: make_frame_store(0), 22 | playing: make_playing_store(false), 23 | }; 24 | } 25 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-editor/src/lib/stores/commands.ts: -------------------------------------------------------------------------------- 1 | import type {TypeObjectLiteral} from "@svelte-in-motion/type"; 2 | import {validate} from "@svelte-in-motion/type"; 3 | import type {ICollectionItem, ICollectionStore} from "@svelte-in-motion/utilities"; 4 | import {UserError, collection} from "@svelte-in-motion/utilities"; 5 | 6 | import type {IAppContext} from "../app"; 7 | 8 | export interface ICommandItem extends ICollectionItem { 9 | identifier: string; 10 | 11 | is_disabled?: boolean | (() => boolean); 12 | 13 | is_visible?: boolean | (() => boolean); 14 | } 15 | 16 | export interface ICommandTypedItem extends ICommandItem { 17 | type: TypeObjectLiteral; 18 | 19 | on_execute: (app: IAppContext, args: T) => void | Promise; 20 | } 21 | 22 | export interface ICommandUntypedItem extends ICommandItem { 23 | on_execute: (app: IAppContext) => void | Promise; 24 | } 25 | 26 | export interface ICommandsStore 27 | extends ICollectionStore | ICommandUntypedItem> { 28 | execute: ((command: string) => Promise) & 29 | ((command: string, args: T) => Promise); 30 | 31 | push: ((item: ICommandUntypedItem) => ICommandUntypedItem) & 32 | ((item: ICommandTypedItem) => ICommandTypedItem); 33 | } 34 | 35 | export function commands(app: IAppContext): ICommandsStore { 36 | const {errors} = app; 37 | 38 | const {find, has, push, subscribe, remove, update, watch} = collection< 39 | ICommandTypedItem | ICommandUntypedItem 40 | >(); 41 | 42 | return { 43 | // @ts-expect-error 44 | async execute(identifier: string, args: T) { 45 | const item = find("identifier", identifier); 46 | if (!item) { 47 | throw new ReferenceError( 48 | `bad argument #0 to 'commands.execute' (command '${identifier}' not found)` 49 | ); 50 | } 51 | 52 | if ("type" in item) { 53 | const [first_error] = validate(args, item.type); 54 | if (first_error) { 55 | throw new Error(first_error.message); 56 | } 57 | } 58 | 59 | try { 60 | await item.on_execute(app, args); 61 | } catch (err) { 62 | if (err instanceof UserError) { 63 | errors.push(err); 64 | return; 65 | } 66 | 67 | throw err; 68 | } 69 | }, 70 | 71 | find, 72 | has, 73 | 74 | // @ts-expect-error 75 | push, 76 | subscribe, 77 | 78 | remove, 79 | update, 80 | watch, 81 | }; 82 | } 83 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-editor/src/lib/stores/editor.ts: -------------------------------------------------------------------------------- 1 | import type {EditorView} from "@codemirror/view"; 2 | import type {Writable} from "svelte/store"; 3 | import {writable} from "svelte/store"; 4 | 5 | export type IEditorViewStore = Writable; 6 | 7 | export function editorview(view?: EditorView): IEditorViewStore { 8 | return writable(view ?? null); 9 | } 10 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-editor/src/lib/stores/errors.ts: -------------------------------------------------------------------------------- 1 | //import {Slash} from "lucide-svelte"; 2 | 3 | import type {SvelteComponent} from "svelte"; 4 | 5 | import type {ICollectionItem, ICollectionStore} from "@svelte-in-motion/utilities"; 6 | import {collection, format_snake_case, generate_uuid} from "@svelte-in-motion/utilities"; 7 | 8 | import type {IAppContext} from "../app"; 9 | 10 | export interface IErrorItem extends ICollectionItem { 11 | identifier: string; 12 | 13 | icon?: typeof SvelteComponent; 14 | 15 | message: string; 16 | 17 | name: string; 18 | 19 | stack: string; 20 | } 21 | 22 | export interface IErrorTypedItem extends IErrorItem { 23 | tokens: T; 24 | } 25 | 26 | export interface IErrorUntypedItem extends IErrorItem {} 27 | 28 | export interface IErrorsStore 29 | extends ICollectionStore | IErrorUntypedItem> { 30 | push(item: Omit | IErrorUntypedItem, "identifier">): IErrorItem; 31 | } 32 | 33 | export function errors(app: IAppContext): IErrorsStore { 34 | const {notifications} = app; 35 | 36 | const {find, has, push, subscribe, remove, update, watch} = collection< 37 | IErrorTypedItem | IErrorUntypedItem 38 | >(); 39 | 40 | return { 41 | find, 42 | has, 43 | 44 | push(item) { 45 | const {icon, message, name, stack, tokens} = item; 46 | 47 | const error_identifier = generate_uuid(); 48 | 49 | notifications.push({ 50 | //icon: item.icon ? item.icon : Slash, 51 | palette: "negative", 52 | is_dismissible: true, 53 | 54 | namespace: `errors-${format_snake_case(name)}`, 55 | tokens: stack ? {...(tokens ?? {}), stack} : tokens, 56 | 57 | on_remove: () => remove(error_identifier), 58 | }); 59 | 60 | return push({ 61 | icon, 62 | identifier: error_identifier, 63 | message, 64 | name, 65 | stack, 66 | } as IErrorItem); 67 | }, 68 | 69 | subscribe, 70 | remove, 71 | update, 72 | watch, 73 | }; 74 | } 75 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-editor/src/lib/stores/extensions.ts: -------------------------------------------------------------------------------- 1 | import type {ICollectionItem, ICollectionStore} from "@svelte-in-motion/utilities"; 2 | import {collection} from "@svelte-in-motion/utilities"; 3 | 4 | import type {IAppContext} from "../app"; 5 | 6 | export interface IExtensionItem extends ICollectionItem { 7 | identifier: string; 8 | 9 | is_builtin?: boolean; 10 | 11 | on_activate?: (app: IAppContext) => void; 12 | } 13 | 14 | export interface IExtensionsStore extends ICollectionStore {} 15 | 16 | export function extensions(app: IAppContext): IExtensionsStore { 17 | const {find, has, push, subscribe, remove, update, watch} = collection(); 18 | 19 | return { 20 | find, 21 | has, 22 | 23 | push(item) { 24 | if (item.on_activate) item.on_activate(app); 25 | return push(item); 26 | }, 27 | 28 | subscribe, 29 | remove, 30 | update, 31 | watch, 32 | }; 33 | } 34 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-editor/src/lib/stores/grammars.ts: -------------------------------------------------------------------------------- 1 | import type {LanguageSupport} from "@codemirror/language"; 2 | 3 | import type {ICollectionItem, ICollectionStore} from "@svelte-in-motion/utilities"; 4 | import {collection} from "@svelte-in-motion/utilities"; 5 | 6 | export interface IGrammarItem extends ICollectionItem { 7 | identifier: string; 8 | 9 | extensions: string[]; 10 | 11 | grammar: LanguageSupport; 12 | } 13 | 14 | export interface IGrammarsStore extends ICollectionStore {} 15 | 16 | export function grammars(): IGrammarsStore { 17 | const {find, has, push, subscribe, remove, update, watch} = collection(); 18 | 19 | return { 20 | find, 21 | has, 22 | 23 | push, 24 | subscribe, 25 | 26 | remove, 27 | update, 28 | watch, 29 | }; 30 | } 31 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-editor/src/lib/stores/io.ts: -------------------------------------------------------------------------------- 1 | import type {Writable} from "svelte/store"; 2 | import {writable} from "svelte/store"; 3 | 4 | export type IFilePathStore = Writable; 5 | 6 | export function filepath(file_path: string): IFilePathStore { 7 | return writable(file_path); 8 | } 9 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-editor/src/lib/stores/locale.ts: -------------------------------------------------------------------------------- 1 | import {negotiateLanguages} from "@fluent/langneg"; 2 | import type {Readable} from "svelte/store"; 3 | import {derived} from "svelte/store"; 4 | 5 | import type {ISupportedLocales} from "@svelte-in-motion/configuration"; 6 | import {DEFAULT_LOCALE, SUPPORTED_LOCALES} from "@svelte-in-motion/configuration"; 7 | 8 | import type {IAppContext} from "../app"; 9 | 10 | export type ILocaleStore = Readable; 11 | 12 | export function locale(app: IAppContext): ILocaleStore { 13 | const {preferences} = app; 14 | 15 | return derived([preferences], ([$preferences]) => { 16 | return ( 17 | $preferences.locale.preferred ?? 18 | (negotiateLanguages(navigator.languages, SUPPORTED_LOCALES, { 19 | defaultLocale: DEFAULT_LOCALE, 20 | })[0] as ISupportedLocales) 21 | ); 22 | }); 23 | } 24 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-editor/src/lib/stores/notifications.ts: -------------------------------------------------------------------------------- 1 | import type {PROPERTY_PALETTE} from "@kahi-ui/framework"; 2 | import type {SvelteComponent} from "svelte"; 3 | 4 | import type {ICollectionItem, ICollectionStore} from "@svelte-in-motion/utilities"; 5 | import {collection, generate_uuid} from "@svelte-in-motion/utilities"; 6 | 7 | export interface INotificationItem extends ICollectionItem { 8 | icon?: typeof SvelteComponent; 9 | 10 | identifier: string; 11 | 12 | is_dismissible?: boolean; 13 | 14 | namespace: string; 15 | 16 | palette?: PROPERTY_PALETTE; 17 | 18 | on_remove?: (notification: INotificationItem) => void; 19 | } 20 | 21 | export interface INotificationTypedItem extends INotificationItem { 22 | tokens: T; 23 | } 24 | 25 | export interface INotificationUntypedItem extends INotificationItem {} 26 | 27 | export interface INotificationsStore 28 | extends ICollectionStore | INotificationUntypedItem> { 29 | push( 30 | item: Omit | INotificationUntypedItem, "identifier"> 31 | ): INotificationItem; 32 | } 33 | 34 | export function notifications(): INotificationsStore { 35 | const {find, has, push, subscribe, remove, update, watch} = collection< 36 | INotificationTypedItem | INotificationUntypedItem 37 | >(); 38 | 39 | return { 40 | find, 41 | has, 42 | 43 | push(item) { 44 | const identifier = generate_uuid(); 45 | 46 | return push({...item, identifier} as INotificationItem); 47 | }, 48 | 49 | subscribe, 50 | 51 | remove(predicate, value?) { 52 | const notification = remove(predicate, value); 53 | 54 | if (notification.on_remove) notification.on_remove(notification); 55 | return notification; 56 | }, 57 | 58 | update, 59 | watch, 60 | }; 61 | } 62 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-editor/src/lib/stores/translations.ts: -------------------------------------------------------------------------------- 1 | import {FluentBundle, FluentResource} from "@fluent/bundle"; 2 | import type {Readable} from "svelte/store"; 3 | import {derived} from "svelte/store"; 4 | 5 | import {debounce} from "@svelte-in-motion/utilities"; 6 | 7 | import {APPLICATION_URL} from "../util/constants"; 8 | 9 | import type {IAppContext} from "../app"; 10 | 11 | export type ITranslationTokens = Record; 12 | 13 | export interface ITranslationsHandle { 14 | format(identifier: string, tokens?: ITranslationTokens): string; 15 | 16 | has(identifier: string): boolean; 17 | } 18 | 19 | export type ITranslationsStore = Readable; 20 | 21 | const DEFAULT_HANDLE: ITranslationsHandle = { 22 | format: (identifier) => identifier, 23 | has: () => false, 24 | }; 25 | 26 | export function translations(app: IAppContext): ITranslationsStore { 27 | const {locale} = app; 28 | 29 | const on_update = debounce( 30 | async ($locale: string, set: (value: ITranslationsHandle) => void) => { 31 | const url = new URL(`/assets/translations/${$locale}.ftl`, APPLICATION_URL); 32 | const response = await fetch(url); 33 | 34 | const text = await response.text(); 35 | const resource = new FluentResource(text); 36 | 37 | const bundle = new FluentBundle($locale); 38 | bundle.addResource(resource); 39 | 40 | set({ 41 | format: (identifier, tokens) => { 42 | const message = bundle.getMessage(identifier); 43 | if (message && message.value) { 44 | return bundle.formatPattern(message.value, tokens); 45 | } 46 | 47 | return identifier; 48 | }, 49 | 50 | has: (identifier) => { 51 | return bundle.hasMessage(identifier); 52 | }, 53 | }); 54 | } 55 | ); 56 | 57 | return derived( 58 | [locale], 59 | ([$locale], set) => { 60 | on_update($locale, set); 61 | }, 62 | DEFAULT_HANDLE 63 | ); 64 | } 65 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-editor/src/lib/types/messages.ts: -------------------------------------------------------------------------------- 1 | export type IMessage< 2 | Name extends String, 3 | Detail extends Record | undefined = undefined 4 | > = Detail extends undefined 5 | ? {name: Name} 6 | : { 7 | name: Name; 8 | 9 | detail: Detail; 10 | }; 11 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-editor/src/lib/types/preview.ts: -------------------------------------------------------------------------------- 1 | import type {IMessage} from "./messages"; 2 | 3 | export enum MESSAGES_PREVIEW { 4 | destroy = "PREVIEW_DESTROY", 5 | error = "PREVIEW_ERROR", 6 | mount = "PREVIEW_MOUNT", 7 | ready = "PREVIEW_READY", 8 | 9 | frame = "PREVIEW_FRAME", 10 | playing = "PREVIEW_PLAYING", 11 | } 12 | 13 | export type IPreviewDestroyMessage = IMessage; 14 | 15 | export type IPreviewErrorMessage = IMessage< 16 | MESSAGES_PREVIEW.error, 17 | { 18 | message: string; 19 | 20 | name: string; 21 | } 22 | >; 23 | 24 | export type IPreviewFrameMessage = IMessage< 25 | MESSAGES_PREVIEW.frame, 26 | { 27 | frame: number; 28 | } 29 | >; 30 | 31 | export type IPreviewMountMessage = IMessage; 32 | 33 | export type IPreviewPlayingMessage = IMessage< 34 | MESSAGES_PREVIEW.playing, 35 | { 36 | playing: boolean; 37 | } 38 | >; 39 | 40 | export type IPreviewReadyMessage = IMessage; 41 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-editor/src/lib/util/constants.ts: -------------------------------------------------------------------------------- 1 | // @ts-ignore 2 | import PACKAGE from "../../../package.json"; 3 | 4 | import {append_pathname} from "@svelte-in-motion/utilities"; 5 | 6 | export const APPLICATION_VERSION = PACKAGE.version; 7 | 8 | // @ts-ignore 9 | export const APPLICATION_URL = append_pathname(location.origin, import.meta.env.BASE_URL); 10 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-editor/src/lib/util/preview.ts: -------------------------------------------------------------------------------- 1 | const SUPPORTED_EXTENSIONS = [".svelte"]; 2 | 3 | export function can_preview_file(file_path: string): boolean { 4 | file_path = file_path.toLowerCase(); 5 | 6 | for (const extension of SUPPORTED_EXTENSIONS) if (file_path.endsWith(extension)) return true; 7 | return false; 8 | } 9 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-editor/src/lib/util/repl.ts: -------------------------------------------------------------------------------- 1 | import type {IEvaluationContext, IEvaluationImports} from "@svelte-in-motion/utilities"; 2 | 3 | import * as svelte from "svelte"; 4 | import * as svelte_animate from "svelte/animate"; 5 | import * as svelte_easing from "svelte/easing"; 6 | import * as svelte_internal from "svelte/internal"; 7 | import * as svelte_motion from "svelte/motion"; 8 | import * as svelte_store from "svelte/store"; 9 | import * as svelte_transition from "svelte/transition"; 10 | 11 | import * as sim_animations from "@svelte-in-motion/animations"; 12 | import * as sim_core from "@svelte-in-motion/core"; 13 | import * as sim_utilities from "@svelte-in-motion/utilities"; 14 | 15 | export const REPL_CONTEXT: IEvaluationContext = {}; 16 | 17 | export const REPL_IMPORTS: IEvaluationImports = { 18 | svelte: svelte, 19 | "svelte/animate": svelte_animate, 20 | "svelte/easing": svelte_easing, 21 | "svelte/internal": svelte_internal, 22 | "svelte/motion": svelte_motion, 23 | "svelte/store": svelte_store, 24 | "svelte/transition": svelte_transition, 25 | 26 | "@svelte-in-motion/animations": sim_animations, 27 | "@svelte-in-motion/core": sim_core, 28 | "@svelte-in-motion/utilities": sim_utilities, 29 | }; 30 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-editor/src/lib/util/storage.ts: -------------------------------------------------------------------------------- 1 | import {indexeddb} from "@svelte-in-motion/storage"; 2 | 3 | export const FILE_CONFIGURATION_PREFERENCES = "preferences.json"; 4 | 5 | export const FILE_CONFIGURATION_WORKSPACE = ".svelte-in-motion.json"; 6 | 7 | export const FILE_CONFIGURATION_WORKSPACES = "workspaces.json"; 8 | 9 | export const STORAGE_USER = indexeddb("svelte-in-motion"); 10 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-editor/src/lib/workspace.ts: -------------------------------------------------------------------------------- 1 | import type {Readable} from "svelte/store"; 2 | import {derived, get} from "svelte/store"; 3 | 4 | import type {IPreloadedConfigurationFileStore} from "@svelte-in-motion/configuration"; 5 | import {WorkspaceConfiguration, WorkspacesItemConfiguration} from "@svelte-in-motion/configuration"; 6 | import type {IDriver} from "@svelte-in-motion/storage"; 7 | import {make_scoped_context} from "@svelte-in-motion/utilities"; 8 | 9 | import {FILE_CONFIGURATION_WORKSPACE} from "./util/storage"; 10 | 11 | import type {IAppContext} from "./app"; 12 | import type {IEditorContext} from "./editor"; 13 | import type {IPreviewContext} from "./preview"; 14 | 15 | export const CONTEXT_WORKSPACE = make_scoped_context("workspace"); 16 | 17 | export interface IWorkspaceContext { 18 | configuration: IPreloadedConfigurationFileStore; 19 | 20 | editor?: IEditorContext; 21 | 22 | identifier: string; 23 | 24 | metadata: Readable; 25 | 26 | preview?: IPreviewContext; 27 | 28 | storage: IDriver; 29 | } 30 | 31 | function is_workspace_prepared(storage: IDriver): Promise { 32 | return storage.exists(FILE_CONFIGURATION_WORKSPACE); 33 | } 34 | 35 | export async function workspace(identifier: string, app: IAppContext): Promise { 36 | const {workspaces} = app; 37 | 38 | const metadata = derived(workspaces, ($workspaces) => { 39 | const workspace = $workspaces.workspaces.find( 40 | (workspace) => workspace.identifier === identifier 41 | ); 42 | 43 | if (!workspace) { 44 | throw new ReferenceError( 45 | `bad argument #0 to 'workspace' (workspace '${identifier}' not found)` 46 | ); 47 | } 48 | 49 | return workspace; 50 | }); 51 | 52 | const $metadata = get(metadata); 53 | const storage = await $metadata.make_driver(); 54 | 55 | if (!(await is_workspace_prepared(storage))) { 56 | throw new ReferenceError( 57 | `bad argument #0 to 'workspace' (workspace '${identifier}' is not available to be opened)` 58 | ); 59 | } 60 | 61 | const configuration = await WorkspaceConfiguration.preload( 62 | storage, 63 | FILE_CONFIGURATION_WORKSPACE, 64 | { 65 | parse: {ignore_errors: true}, 66 | stringify: {is_formatted: true}, 67 | } 68 | ); 69 | 70 | return { 71 | configuration, 72 | identifier, 73 | metadata, 74 | storage, 75 | }; 76 | } 77 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-editor/src/main.ts: -------------------------------------------------------------------------------- 1 | import "@kahi-ui/framework/dist/kahi-ui.framework.min.css"; 2 | import "@kahi-ui/framework/dist/kahi-ui.theme.default.min.css"; 3 | 4 | import type {SvelteComponent} from "svelte"; 5 | 6 | import { 7 | EXTENSION_ABOUT, 8 | EXTENSION_COMMANDS, 9 | EXTENSION_EDITOR, 10 | EXTENSION_EXPORT, 11 | EXTENSION_GRAMMARS, 12 | EXTENSION_PREVIEW, 13 | EXTENSION_TEMPLATES, 14 | EXTENSION_WORKSPACE, 15 | } from "@svelte-in-motion/builtin-extensions"; 16 | 17 | import {CONTEXT_APP, app as make_app_context} from "./lib/app"; 18 | import {app_router} from "./lib/router"; 19 | 20 | import * as Dashboard from "./routes/dashboard.svelte"; 21 | import * as Workspace from "./routes/workspace.svelte"; 22 | 23 | (async () => { 24 | const app = await make_app_context(); 25 | 26 | app.extensions.push(EXTENSION_ABOUT); 27 | app.extensions.push(EXTENSION_COMMANDS); 28 | app.extensions.push(EXTENSION_EXPORT); 29 | app.extensions.push(EXTENSION_EDITOR); 30 | app.extensions.push(EXTENSION_GRAMMARS); 31 | app.extensions.push(EXTENSION_PREVIEW); 32 | app.extensions.push(EXTENSION_WORKSPACE); 33 | app.extensions.push(EXTENSION_TEMPLATES); 34 | 35 | const [_, router] = app_router({ 36 | context: { 37 | [CONTEXT_APP.key]: app, 38 | }, 39 | 40 | routes: [Dashboard, Workspace], 41 | }); 42 | 43 | let component: SvelteComponent | null = null; 44 | 45 | window.addEventListener("keydown", (event) => app.keybinds.execute(event, true)); 46 | window.addEventListener("keyup", (event) => app.keybinds.execute(event, false)); 47 | 48 | router.subscribe((route) => { 49 | const splash_element = document.querySelector(".sim--splash"); 50 | if (splash_element) splash_element.remove(); 51 | 52 | if (component) { 53 | component.$destroy(); 54 | component = null; 55 | } 56 | 57 | if (!route) return; 58 | const {Component, context = {}, props} = route; 59 | 60 | component = new Component({ 61 | target: document.body, 62 | context: new Map([...Object.entries(context), [CONTEXT_APP.key, app]]), 63 | 64 | props, 65 | }); 66 | }); 67 | 68 | // @ts-expect-error - HACK: For debugging purposes only 69 | window.APP_CONTEXT = app; 70 | 71 | // HACK: DeepKit RPC tries to reference `global` for platform features, however it doesn't exist normally 72 | // https://github.com/deepkit/deepkit-framework/issues/26#issuecomment-605295794 73 | window.global = window; 74 | })(); 75 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-editor/svelte.config.js: -------------------------------------------------------------------------------- 1 | import sveltePreprocess from "svelte-preprocess"; 2 | 3 | export default { 4 | // Consult https://github.com/sveltejs/svelte-preprocess 5 | // for more information about preprocessors 6 | preprocess: sveltePreprocess(), 7 | }; 8 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-editor/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@tsconfig/svelte/tsconfig.json", 3 | "compilerOptions": { 4 | "target": "esnext", 5 | "module": "esnext", 6 | "strict": true, 7 | "useDefineForClassFields": true, 8 | "resolveJsonModule": true 9 | }, 10 | "include": ["src/**/*.d.ts", "src/**/*.ts", "src/**/*.svelte"], 11 | "references": [{"path": "./tsconfig.node.json"}] 12 | } 13 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-editor/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "module": "esnext", 5 | "moduleResolution": "node", 6 | "resolveJsonModule": true, 7 | }, 8 | "include": ["vite/*.ts", "vite/*.d.ts", "vite.config.ts", "tsconfig.json"] 9 | } 10 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-editor/vite.config.ts: -------------------------------------------------------------------------------- 1 | import {resolve} from "path"; 2 | 3 | import {defineConfig} from "vite"; 4 | import {svelte} from "@sveltejs/vite-plugin-svelte"; 5 | 6 | // import {DeepkitTypePlugin} from "./vite/deepkit-type-plugin"; 7 | 8 | // https://vitejs.dev/config/ 9 | export default defineConfig({ 10 | build: { 11 | sourcemap: true, 12 | 13 | rollupOptions: { 14 | input: { 15 | index: resolve(__dirname, "index.html"), 16 | preview: resolve(__dirname, "preview.html"), 17 | }, 18 | }, 19 | }, 20 | 21 | server: { 22 | headers: { 23 | "Cross-Origin-Embedder-Policy": "require-corp", 24 | "Cross-Origin-Opener-Policy": "same-origin", 25 | }, 26 | }, 27 | 28 | plugins: [ 29 | svelte(), 30 | // DeepkitTypePlugin() 31 | ], 32 | }); 33 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-editor/vite/deepkit-type-plugin.ts: -------------------------------------------------------------------------------- 1 | import {declarationTransformer, transformer} from "@deepkit/type-compiler"; 2 | import {transpileModule} from "typescript"; 3 | import {Plugin} from "vite"; 4 | 5 | // @ts-expect-error - HACK: just treat this as any 6 | import TSCONFIG from "../tsconfig.json"; 7 | 8 | // HACK: DeepKit currently only works via injecting the local TypeScript compiler w/ 9 | // code to generate typing metadata for runtime. Which doesn't work for tools like esbuild 10 | // which don't use the compiler 11 | 12 | export function DeepkitTypePlugin(): Plugin { 13 | return { 14 | name: "deepkit-type", 15 | enforce: "pre", 16 | 17 | transform(code, file_name) { 18 | if (!file_name.endsWith(".type.ts")) return; 19 | 20 | const transformed = transpileModule(code, { 21 | fileName: file_name, 22 | compilerOptions: TSCONFIG.compilerOptions, 23 | 24 | transformers: { 25 | before: [transformer], 26 | afterDeclarations: [declarationTransformer], 27 | }, 28 | }); 29 | 30 | return { 31 | code: transformed.outputText, 32 | map: transformed.sourceMapText, 33 | }; 34 | }, 35 | }; 36 | } 37 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-encoding/README.md: -------------------------------------------------------------------------------- 1 | # @svelte-in-motion/encoding 2 | 3 | > Platform abstraction library for encoding raw PNG frames into video files. 4 | 5 | ## TODO 6 | 7 | - Callout to native `ffmpeg` when running in NodeJS / Deno. 8 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-encoding/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@svelte-in-motion/encoding", 3 | "author": "novacbn", 4 | "version": "0.0.1", 5 | "type": "module", 6 | "main": "./dist/index.js", 7 | "exports": { 8 | ".": "./dist/index.js", 9 | "./package.json": "./package.json" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "https://github.com/novacbn/svelte-in-motion/tree/main/packages/%40svelte-in-motion-encoding" 14 | }, 15 | "scripts": { 16 | "build:package": "tsc", 17 | "dev:package": "tsc --watch", 18 | "format": "npm run format:package", 19 | "format:package": "prettier --config ../../.prettierrc --write ./src", 20 | "lint": "npm run lint:format", 21 | "lint:format": "prettier --config ../../.prettierrc --check ./src", 22 | "test": "npm run test:types", 23 | "test:types": "tsc --noEmit" 24 | }, 25 | "devDependencies": { 26 | "@deepkit/type-compiler": "^1.0.1-alpha.71", 27 | "prettier": "^2.3.1", 28 | "typescript": "^4.7.3" 29 | }, 30 | "dependencies": { 31 | "@ffmpeg/ffmpeg": "^0.10.1", 32 | "@svelte-in-motion/rpc": "workspace:^", 33 | "@svelte-in-motion/type": "workspace:^", 34 | "@svelte-in-motion/utilities": "workspace:^" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-encoding/src/encoding/codec.ts: -------------------------------------------------------------------------------- 1 | import {IS_BROWSER} from "@svelte-in-motion/utilities"; 2 | 3 | export enum SUPPORTED_CODECS { 4 | vp9 = "libvpx-vp9", 5 | } 6 | 7 | export type ICodecNames = keyof typeof SUPPORTED_CODECS; 8 | 9 | export type ICodecEncoders = `${SUPPORTED_CODECS}`; 10 | 11 | export function get_available_codecs(): ICodecNames[] { 12 | if (IS_BROWSER) { 13 | return ["vp9"]; 14 | } 15 | 16 | throw new Error("bad dispatch to 'get_available_codecs' (platform not supported)"); 17 | } 18 | 19 | export function get_default_codec(): ICodecNames { 20 | if (IS_BROWSER) return "vp9"; 21 | 22 | throw new Error("bad dispatch to 'get_default_codec' (platform not supported)"); 23 | } 24 | 25 | export function get_codec(codec: ICodecNames): ICodecEncoders { 26 | switch (codec) { 27 | case "vp9": 28 | return SUPPORTED_CODECS.vp9; 29 | } 30 | 31 | throw new Error(`bad argument #0 to 'get_codec' (codec '${codec}' not supported)`); 32 | } 33 | 34 | export function get_codec_arguments(codec: ICodecNames): string[] { 35 | switch (codec) { 36 | case "vp9": 37 | return ["-row-mt", "1"]; 38 | } 39 | 40 | throw new Error(`bad argument #0 to 'get_codec_argument' (codec '${codec}' not supported)`); 41 | } 42 | 43 | export function get_codec_extension(codec: ICodecNames): string { 44 | switch (codec) { 45 | case "vp9": 46 | return "webm"; 47 | } 48 | 49 | throw new Error(`bad argument #0 to 'get_codec_extension' (codec '${codec}' not supported)`); 50 | } 51 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-encoding/src/encoding/crf.ts: -------------------------------------------------------------------------------- 1 | import type {ICodecNames} from "./codec"; 2 | 3 | export function get_available_crf_range(codec: ICodecNames): [number, number] { 4 | // TODO: Investigate each codec's CRF range that ffmpeg supports 5 | 6 | return [0, 99]; 7 | } 8 | 9 | export function get_default_crf(codec: ICodecNames): number { 10 | switch (codec) { 11 | case "vp9": 12 | return 31; 13 | } 14 | 15 | throw new Error(`bad argument #0 to 'get_default_crf' (codec '${codec}' not supported)`); 16 | } 17 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-encoding/src/encoding/dimensions.ts: -------------------------------------------------------------------------------- 1 | import type {ICodecNames} from "./codec"; 2 | 3 | export function get_available_dimensions_ranges( 4 | codec: ICodecNames 5 | ): [[number, number], [number, number]] { 6 | // NOTE: Depending on platform it might be wise to limit codecs to 7 | // specific diemensions. To account for OOM (out-of-memory) errors for instance 8 | 9 | return [ 10 | [0, 4096], 11 | [0, 2160], 12 | ]; 13 | } 14 | 15 | export function get_default_dimensions(codec: ICodecNames): [number, number] { 16 | return [1280, 720]; 17 | } 18 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-encoding/src/encoding/framerate.ts: -------------------------------------------------------------------------------- 1 | import type {ICodecNames} from "./codec"; 2 | 3 | export function get_available_framerate_range(codec: ICodecNames): [number, number] { 4 | // NOTE: Certain encoders or platforms might have limitations here that needs investigating 5 | 6 | // NOTE: There's a hard maximum of 120fps to support in-Browser previews 7 | 8 | return [0, 120]; 9 | } 10 | 11 | export function get_default_framerate(codec: ICodecNames): number { 12 | return 60; 13 | } 14 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-encoding/src/encoding/pixel_format.ts: -------------------------------------------------------------------------------- 1 | import type {ICodecNames} from "./codec"; 2 | 3 | export enum SUPPORTED_PIXEL_FORMATS { 4 | yuv420p = "yuv420p", 5 | } 6 | 7 | export type IPixelFormatNames = keyof typeof SUPPORTED_PIXEL_FORMATS; 8 | 9 | export type IPixelFormatFormats = `${SUPPORTED_PIXEL_FORMATS}`; 10 | 11 | export function get_available_pixel_formats(codec: ICodecNames): IPixelFormatNames[] { 12 | // NOTE: This might also change based on if in Browser, depending on compatibility 13 | 14 | switch (codec) { 15 | case "vp9": 16 | return ["yuv420p"]; 17 | } 18 | 19 | throw new Error( 20 | `bad argument #0 to 'get_available_pixel_formats' (codec '${codec}' not supported)` 21 | ); 22 | } 23 | 24 | export function get_default_pixel_format(codec: ICodecNames): IPixelFormatNames { 25 | switch (codec) { 26 | case "vp9": 27 | return "yuv420p"; 28 | } 29 | 30 | throw new Error( 31 | `bad argument #0 to 'get_default_pixel_formats' (codec '${codec}' not supported)` 32 | ); 33 | } 34 | 35 | export function get_pixel_format(name: IPixelFormatNames): IPixelFormatFormats { 36 | switch (name) { 37 | case "yuv420p": 38 | return "yuv420p"; 39 | } 40 | 41 | throw new Error(`bad argument #0 to 'get_pixel_format' (pixel format '${name}' not supported)`); 42 | } 43 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-encoding/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./encoding/codec"; 2 | export * from "./encoding/crf"; 3 | export * from "./encoding/dimensions"; 4 | export * from "./encoding/encode"; 5 | export * from "./encoding/framerate"; 6 | export * from "./encoding/pixel_format"; 7 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-encoding/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "moduleResolution": "node", 4 | "module": "ES2020", 5 | "target": "ES2019", 6 | "outDir": "./dist", 7 | "strict": true, 8 | "sourceMap": true, 9 | "declaration": true, 10 | "allowSyntheticDefaultImports": true, 11 | "esModuleInterop": true, 12 | "skipLibCheck": true, 13 | "downlevelIteration": true, 14 | "experimentalDecorators": true, 15 | "emitDecoratorMetadata": true 16 | }, 17 | "reflection": true, 18 | "include": ["src/**/*.d.ts", "src/**/*.ts"] 19 | } 20 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-extension/README.md: -------------------------------------------------------------------------------- 1 | # @svelte-in-motion/extension 2 | 3 | > Extension API for Developers 4 | 5 | ## TODO 6 | 7 | - Most of this subpackage currently references internal Application infrastructure. That'll be removed in the future when Extensions are hosted in WebWorkers and communicate over a restricted IPC-based API. 8 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-extension/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@svelte-in-motion/extension", 3 | "author": "novacbn", 4 | "version": "0.0.1", 5 | "type": "module", 6 | "main": "./dist/index.js", 7 | "exports": { 8 | ".": "./dist/index.js", 9 | "./package.json": "./package.json" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "https://github.com/novacbn/svelte-in-motion/tree/main/packages/%40svelte-in-motion-extension" 14 | }, 15 | "scripts": { 16 | "build:package": "tsc", 17 | "dev:package": "tsc --watch", 18 | "format": "npm run format:package", 19 | "format:package": "prettier --config ../../.prettierrc --write ./src", 20 | "lint": "npm run lint:format", 21 | "lint:format": "prettier --config ../../.prettierrc --check ./src", 22 | "test": "npm run test:types", 23 | "test:types": "tsc --noEmit" 24 | }, 25 | "devDependencies": { 26 | "@deepkit/type-compiler": "^1.0.1-alpha.71", 27 | "@svelte-in-motion/editor": "workspace:^", 28 | "prettier": "^2.3.1", 29 | "typescript": "^4.7.3" 30 | }, 31 | "dependencies": {} 32 | } 33 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-extension/src/extensions.ts: -------------------------------------------------------------------------------- 1 | import type {IAppContext} from "./types"; 2 | 3 | export interface IExtension { 4 | identifier: string; 5 | 6 | is_builtin?: boolean; 7 | 8 | on_activate?: (app: IAppContext) => void; 9 | } 10 | 11 | export function define_extension(extension: T): T { 12 | return extension; 13 | } 14 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-extension/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./extensions"; 2 | export * from "./templates"; 3 | export * from "./types"; 4 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-extension/src/templates.ts: -------------------------------------------------------------------------------- 1 | import {ITemplateTypedItem, ITemplateUntypedItem} from "./types"; 2 | 3 | type IDefinedTemplate = T extends void ? ITemplateUntypedItem : ITemplateTypedItem; 4 | 5 | export function define_template(template: IDefinedTemplate): IDefinedTemplate { 6 | return template; 7 | } 8 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-extension/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "moduleResolution": "node", 4 | "module": "ES2020", 5 | "target": "ES2019", 6 | "outDir": "./dist", 7 | "strict": true, 8 | "sourceMap": true, 9 | "declaration": true, 10 | "allowSyntheticDefaultImports": true, 11 | "esModuleInterop": true, 12 | "skipLibCheck": true, 13 | "downlevelIteration": true, 14 | "experimentalDecorators": true, 15 | "emitDecoratorMetadata": true 16 | }, 17 | "reflection": true, 18 | "include": ["src/**/*.d.ts", "src/**/*.ts"] 19 | } 20 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-rendering/README.md: -------------------------------------------------------------------------------- 1 | # @svelte-in-motion/rendering 2 | 3 | > Platform abstraction library for creating a WebView for hosting a Svelte-In-Motion runtime and project. 4 | 5 | ## TODO 6 | 7 | - Create [microsoft/playwright](https://github.com/microsoft/playwright) implementation when running on NodeJS. 8 | - Investigate options for Deno. 9 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-rendering/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@svelte-in-motion/rendering", 3 | "author": "novacbn", 4 | "version": "0.0.1", 5 | "type": "module", 6 | "main": "./dist/index.js", 7 | "exports": { 8 | ".": "./dist/index.js", 9 | "./package.json": "./package.json" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "https://github.com/novacbn/svelte-in-motion/tree/main/packages/%40svelte-in-motion-rendering" 14 | }, 15 | "scripts": { 16 | "build:package": "tsc", 17 | "dev:package": "tsc --watch", 18 | "format": "npm run format:package", 19 | "format:package": "prettier --config ../../.prettierrc --write ./src", 20 | "lint": "npm run lint:format", 21 | "lint:format": "prettier --config ../../.prettierrc --check ./src", 22 | "test": "npm run test:types", 23 | "test:types": "tsc --noEmit" 24 | }, 25 | "devDependencies": { 26 | "@deepkit/type-compiler": "^1.0.1-alpha.71", 27 | "prettier": "^2.3.1", 28 | "svelte": "^3.48.0", 29 | "typescript": "^4.7.3" 30 | }, 31 | "dependencies": { 32 | "@svelte-in-motion/runtime": "workspace:^", 33 | "@svelte-in-motion/type": "workspace:^", 34 | "@svelte-in-motion/utilities": "workspace:^" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-rendering/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./render"; 2 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-rendering/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "moduleResolution": "node", 4 | "module": "ES2020", 5 | "target": "ES2019", 6 | "outDir": "./dist", 7 | "strict": true, 8 | "sourceMap": true, 9 | "declaration": true, 10 | "allowSyntheticDefaultImports": true, 11 | "esModuleInterop": true, 12 | "skipLibCheck": true, 13 | "downlevelIteration": true, 14 | "experimentalDecorators": true, 15 | "emitDecoratorMetadata": true 16 | }, 17 | "reflection": true, 18 | "include": ["src/**/*.d.ts", "src/**/*.ts"] 19 | } 20 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-rpc/README.md: -------------------------------------------------------------------------------- 1 | # @svelte-in-motion/rpc 2 | 3 | > Stub package to pin [`@deepkit/rpc`](https://deepkit.io/library/rpc) along w/ custom utilities. 4 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-rpc/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@svelte-in-motion/rpc", 3 | "author": "novacbn", 4 | "version": "0.0.1", 5 | "type": "module", 6 | "main": "./dist/index.js", 7 | "exports": { 8 | ".": "./dist/index.js", 9 | "./package.json": "./package.json" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "https://github.com/novacbn/svelte-in-motion/tree/main/packages/%40svelte-in-motion-rpc" 14 | }, 15 | "scripts": { 16 | "build:package": "tsc", 17 | "dev:package": "tsc --watch", 18 | "format": "npm run format:package", 19 | "format:package": "prettier --config ../../.prettierrc --write ./src", 20 | "lint": "npm run lint:format", 21 | "lint:format": "prettier --config ../../.prettierrc --check ./src", 22 | "test": "npm run test:types", 23 | "test:types": "tsc --noEmit" 24 | }, 25 | "devDependencies": { 26 | "@deepkit/type-compiler": "^1.0.1-alpha.71", 27 | "prettier": "^2.3.1", 28 | "typescript": "^4.7.3" 29 | }, 30 | "dependencies": { 31 | "@deepkit/bson": "^1.0.1-alpha.71", 32 | "@deepkit/core": "^1.0.1-alpha.65", 33 | "@deepkit/core-rxjs": "^1.0.1-alpha.65", 34 | "@deepkit/crypto": "^1.0.1-alpha.65", 35 | "@deepkit/injector": "^1.0.1-alpha.71", 36 | "@deepkit/logger": "^1.0.1-alpha.65", 37 | "@deepkit/rpc": "^1.0.1-alpha.71", 38 | "@deepkit/type": "^1.0.1-alpha.71", 39 | "rxjs": "^7.5.5" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-rpc/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "@deepkit/rpc"; 2 | 3 | export * from "./rpc/port"; 4 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-rpc/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "moduleResolution": "node", 4 | "module": "ES2020", 5 | "target": "ES2019", 6 | "outDir": "./dist", 7 | "strict": true, 8 | "sourceMap": true, 9 | "declaration": true, 10 | "allowSyntheticDefaultImports": true, 11 | "esModuleInterop": true, 12 | "skipLibCheck": true, 13 | "downlevelIteration": true, 14 | "experimentalDecorators": true, 15 | "emitDecoratorMetadata": true 16 | }, 17 | "reflection": true, 18 | "include": ["src/**/*.d.ts", "src/**/*.ts"] 19 | } 20 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-runtime/README.md: -------------------------------------------------------------------------------- 1 | # @svelte-in-motion/runtime 2 | 3 | > Templating package containing the bare-minimum imported supporting libraries to be injected into iframes 4 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-runtime/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@svelte-in-motion/runtime", 3 | "author": "novacbn", 4 | "version": "0.0.1", 5 | "type": "module", 6 | "main": "./dist/index.js", 7 | "exports": { 8 | ".": "./dist/index.js", 9 | "./package.json": "./package.json" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "https://github.com/novacbn/svelte-in-motion/tree/main/packages/%40svelte-in-motion-runtime" 14 | }, 15 | "scripts": { 16 | "build:package": "npm run build:runtime && npm run build:template", 17 | "build:runtime": "vite build", 18 | "build:template": "node ./scripts/build-template.js", 19 | "dev:runtime": "vite build --watch", 20 | "format": "npm run format:package", 21 | "format:package": "prettier --config ../../.prettierrc --write ./src", 22 | "lint": "npm run lint:format", 23 | "lint:format": "prettier --config ../../.prettierrc --check ./src", 24 | "test": "npm run test:types", 25 | "test:types": "tsc --noEmit" 26 | }, 27 | "devDependencies": { 28 | "@sveltejs/vite-plugin-svelte": "1.0.0-next.49", 29 | "prettier": "^2.3.1", 30 | "svelte": "^3.48.0", 31 | "svelte-preprocess": "^4.9.8", 32 | "typescript": "^4.7.3", 33 | "vite": "2.9.12", 34 | "vite-plugin-singlefile": "^0.10.0-beta.2" 35 | }, 36 | "dependencies": { 37 | "@svelte-in-motion/animations": "workspace:^", 38 | "@svelte-in-motion/core": "workspace:^", 39 | "@svelte-in-motion/screenshot": "workspace:^", 40 | "@svelte-in-motion/utilities": "workspace:^" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-runtime/scripts/build-template.js: -------------------------------------------------------------------------------- 1 | import {readFileSync, writeFileSync} from "fs"; 2 | 3 | const RUNTIME = JSON.stringify(readFileSync("./dist/src/index.html").toString()) 4 | .slice(1, -1) 5 | .replace(/`/g, "\\`") 6 | .replace(/\$\{/g, "\\${") 7 | .replace(/__SIM_PAYLOAD/, "${payload}"); 8 | 9 | writeFileSync("./dist/index.js", `export const TEMPLATE_RUNTIME = ({payload}) => \`${RUNTIME}\``); 10 | 11 | writeFileSync( 12 | "./dist/index.d.ts", 13 | `export declare const TEMPLATE_RUNTIME: (options: {payload: string}) => string;` 14 | ); 15 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-runtime/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "moduleResolution": "node", 4 | "module": "ES2020", 5 | "target": "ES2019", 6 | "outDir": "./dist", 7 | "strict": true, 8 | "sourceMap": true, 9 | "declaration": true, 10 | "allowSyntheticDefaultImports": true, 11 | "importsNotUsedAsValues": "error", 12 | "esModuleInterop": true, 13 | "skipLibCheck": true, 14 | "downlevelIteration": true, 15 | "experimentalDecorators": true, 16 | "emitDecoratorMetadata": true 17 | }, 18 | "reflection": true, 19 | "include": ["src/**/*.d.ts", "src/**/*.ts"] 20 | } 21 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-runtime/vite.config.ts: -------------------------------------------------------------------------------- 1 | import {resolve} from "path"; 2 | 3 | import preprocess from "svelte-preprocess"; 4 | import {defineConfig} from "vite"; 5 | import {viteSingleFile} from "vite-plugin-singlefile"; 6 | import {svelte} from "@sveltejs/vite-plugin-svelte"; 7 | 8 | export default defineConfig({ 9 | esbuild: { 10 | minify: true, 11 | }, 12 | 13 | build: { 14 | minify: "terser", 15 | rollupOptions: { 16 | input: { 17 | index: resolve(__dirname, "src", "index.html"), 18 | }, 19 | }, 20 | }, 21 | 22 | plugins: [ 23 | svelte({ 24 | preprocess: preprocess(), 25 | }), 26 | viteSingleFile({removeViteModuleLoader: true}), 27 | ], 28 | }); 29 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-screenshot/README.md: -------------------------------------------------------------------------------- 1 | # @svelte-in-motion/screenshot 2 | 3 | > Platform abstraction library for taking screenshots of DOM Nodes. 4 | 5 | ## TODO: 6 | 7 | - Callout to native screenshot capabilities when running in Tauri / NodeJS / Deno. 8 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-screenshot/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@svelte-in-motion/screenshot", 3 | "author": "novacbn", 4 | "version": "0.0.1", 5 | "type": "module", 6 | "main": "./src/index.ts", 7 | "exports": { 8 | ".": "./src/index.ts", 9 | "./package.json": "./package.json" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "https://github.com/novacbn/svelte-in-motion/tree/main/packages/%40svelte-in-motion-screenshot" 14 | }, 15 | "scripts": { 16 | "format": "npm run format:package", 17 | "format:package": "prettier --config ../../.prettierrc --write ./src", 18 | "lint": "npm run lint:format", 19 | "lint:format": "prettier --config ../../.prettierrc --check ./src", 20 | "test": "npm run test:types", 21 | "test:types": "tsc --noEmit" 22 | }, 23 | "devDependencies": { 24 | "prettier": "^2.3.1", 25 | "typescript": "^4.7.3" 26 | }, 27 | "dependencies": { 28 | "html-to-image": "^1.9.0" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-screenshot/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./platforms/browser"; 2 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-screenshot/src/platforms/browser.ts: -------------------------------------------------------------------------------- 1 | import {toPng} from "html-to-image"; 2 | 3 | export async function screenshot_node(element: HTMLElement): Promise { 4 | const datauri = await toPng(element); 5 | 6 | const response = await fetch(datauri); 7 | const buffer = await response.arrayBuffer(); 8 | 9 | return new Uint8Array(buffer); 10 | } 11 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-screenshot/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "moduleResolution": "node", 4 | "module": "ES2020", 5 | "target": "ES2019", 6 | "strict": true, 7 | "sourceMap": true, 8 | "declaration": true, 9 | "allowSyntheticDefaultImports": true, 10 | "esModuleInterop": true, 11 | "skipLibCheck": true, 12 | "downlevelIteration": true, 13 | "experimentalDecorators": true, 14 | "emitDecoratorMetadata": true 15 | }, 16 | "include": ["src/**/*.d.ts", "src/**/*.ts"] 17 | } 18 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-storage/README.md: -------------------------------------------------------------------------------- 1 | # @svelte-in-motion/storage 2 | 3 | > Support package for creating pluggable Storage provides to power Svelte-In-Motion's workspaces. 4 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-storage/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@svelte-in-motion/storage", 3 | "author": "novacbn", 4 | "version": "0.0.1", 5 | "type": "module", 6 | "main": "./src/index.ts", 7 | "exports": { 8 | ".": "./src/index.ts", 9 | "./package.json": "./package.json" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "https://github.com/novacbn/svelte-in-motion/tree/main/packages/%40svelte-in-motion-storage" 14 | }, 15 | "scripts": { 16 | "format": "npm run format:package", 17 | "format:package": "prettier --config ../../.prettierrc --write ./src", 18 | "lint": "npm run lint:format", 19 | "lint:format": "prettier --config ../../.prettierrc --check ./src", 20 | "test": "npm run test:types", 21 | "test:types": "tsc --noEmit" 22 | }, 23 | "devDependencies": { 24 | "prettier": "^2.3.1", 25 | "typescript": "^4.7.3" 26 | }, 27 | "dependencies": { 28 | "@svelte-in-motion/utilities": "workspace:^", 29 | "@zip.js/zip.js": "^2.4.7", 30 | "idb-keyval": "^6.1.0", 31 | "lzutf8": "^0.6.1", 32 | "svelte": "^3.48.0", 33 | "urlpattern-polyfill": "^1.0.0-rc5" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-storage/src/drivers/driver.ts: -------------------------------------------------------------------------------- 1 | import type {IEvent} from "@svelte-in-motion/utilities"; 2 | 3 | export enum WATCH_EVENT_TYPES { 4 | create = "create", 5 | 6 | remove = "remove", 7 | 8 | update = "update", 9 | } 10 | 11 | export interface IDirectoryOptions { 12 | readonly exclude_directories?: boolean; 13 | 14 | readonly exclude_files?: boolean; 15 | 16 | readonly pattern?: string; 17 | } 18 | 19 | export interface IHotlinkHandle { 20 | readonly destroy: () => Promise; 21 | 22 | readonly path: string; 23 | 24 | readonly url: string; 25 | } 26 | 27 | export interface IHotlinkOptions { 28 | readonly mimetype?: string; 29 | } 30 | 31 | export interface IStats { 32 | readonly is_directory: boolean; 33 | 34 | readonly is_file: boolean; 35 | } 36 | 37 | export type IWatchCallback = (event: IWatchEvent) => void; 38 | 39 | export type IWatchUnsubscriber = () => void; 40 | 41 | export interface IWatchEvent { 42 | readonly stats: IStats; 43 | 44 | readonly path: string; 45 | 46 | readonly type: `${WATCH_EVENT_TYPES}`; 47 | } 48 | 49 | export interface IWatchOptions extends IDirectoryOptions { 50 | readonly on_watch: IWatchCallback; 51 | } 52 | 53 | export type IWatchEventTarget = IEvent; 54 | 55 | export interface IDriver { 56 | create_directory(path: string): Promise; 57 | 58 | create_file(path: string): Promise; 59 | 60 | create_hotlink(path: string, options?: IHotlinkOptions): Promise; 61 | 62 | create_view(path: string): IDriver; 63 | 64 | exists(path: string): Promise; 65 | 66 | read_directory(path: string, options?: IDirectoryOptions): Promise; 67 | 68 | read_file(path: string): Promise; 69 | 70 | remove_directory(path: string): Promise; 71 | 72 | remove_file(path: string): Promise; 73 | 74 | stats(path: string): Promise; 75 | 76 | read_file(path: string): Promise; 77 | 78 | read_file_text(path: string): Promise; 79 | 80 | watch_directory(path: string, options: IWatchOptions): Promise; 81 | 82 | watch_file(path: string, callback: IWatchCallback): Promise; 83 | 84 | write_file(path: string, buffer: Uint8Array): Promise; 85 | 86 | write_file_text(path: string, text: string): Promise; 87 | } 88 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-storage/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./drivers/driver"; 2 | export * from "./drivers/indexeddb"; 3 | 4 | export * from "./stores/file"; 5 | 6 | export * from "./util/compression"; 7 | export * from "./util/encoding"; 8 | export * from "./util/mimetypes"; 9 | export * from "./util/zip"; 10 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-storage/src/util/compression.ts: -------------------------------------------------------------------------------- 1 | import {compress as compress_lzutf8, decompress as decompress_lzutf8} from "lzutf8"; 2 | 3 | export function compress(buffer: Uint8Array): Uint8Array { 4 | return compress_lzutf8(buffer, { 5 | inputEncoding: "ByteArray", 6 | outputEncoding: "ByteArray", 7 | }); 8 | } 9 | 10 | export function decompress(buffer: Uint8Array): Uint8Array { 11 | return decompress_lzutf8(buffer, { 12 | inputEncoding: "ByteArray", 13 | outputEncoding: "ByteArray", 14 | }); 15 | } 16 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-storage/src/util/encoding.ts: -------------------------------------------------------------------------------- 1 | export function decode_text(buffer: Uint8Array): string { 2 | const decoder = new TextDecoder(); 3 | 4 | return decoder.decode(buffer); 5 | } 6 | 7 | export function encode_text(text: string): Uint8Array { 8 | const encoder = new TextEncoder(); 9 | 10 | return encoder.encode(text); 11 | } 12 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-storage/src/util/mimetypes.ts: -------------------------------------------------------------------------------- 1 | import {ext_pathname} from "@svelte-in-motion/utilities"; 2 | 3 | const MIMETYPE_UNKNOWN = "application/octet-stream"; 4 | 5 | const MIMETYPES_EXTENSIONS = { 6 | txt: "text/plain", 7 | 8 | css: "text/css", 9 | html: "text/html", 10 | js: "text/javascript", 11 | json: "application/json", 12 | ts: "text/typescript", 13 | 14 | flac: "audio/flac", 15 | mp3: "audio/mp3", 16 | ogg: "audio/ogg", 17 | 18 | apng: "image/apng", 19 | bmp: "image/bmp", 20 | gif: "image/gif", 21 | jpg: "image/jpeg", 22 | jpeg: "image/jpeg", 23 | png: "image/png", 24 | svg: "image/svg+xm", 25 | webp: "image/webp", 26 | 27 | mp4: "video/mp4", 28 | webm: "video/webm", 29 | } as const; 30 | 31 | const MIMETYPES_PLAINTEXT = [ 32 | MIMETYPES_EXTENSIONS.css, 33 | MIMETYPES_EXTENSIONS.html, 34 | MIMETYPES_EXTENSIONS.js, 35 | MIMETYPES_EXTENSIONS.json, 36 | MIMETYPES_EXTENSIONS.ts, 37 | MIMETYPES_EXTENSIONS.txt, 38 | ] as const; 39 | 40 | const LOOKUP_PLAINTEXT = new Set(MIMETYPES_PLAINTEXT); 41 | 42 | export type IMimetypes = 43 | | typeof MIMETYPES_EXTENSIONS[keyof typeof MIMETYPES_EXTENSIONS] 44 | | typeof MIMETYPE_UNKNOWN; 45 | 46 | export type IPlainTextMimetypes = typeof MIMETYPES_PLAINTEXT[number]; 47 | 48 | export function get_mimetype(path: string): IMimetypes { 49 | const extension = ext_pathname(path); 50 | 51 | return (MIMETYPES_EXTENSIONS as any)[extension] ?? MIMETYPE_UNKNOWN; 52 | } 53 | 54 | export function is_plaintext(path: string): boolean { 55 | const mimetype = get_mimetype(path); 56 | 57 | return LOOKUP_PLAINTEXT.has(mimetype); 58 | } 59 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-storage/src/util/zip.ts: -------------------------------------------------------------------------------- 1 | import {BlobReader, BlobWriter, ZipWriter} from "@zip.js/zip.js"; 2 | 3 | export interface IBlobDefinition { 4 | blob: Blob; 5 | 6 | path: string; 7 | } 8 | 9 | export async function zip_blobs(blobs: IBlobDefinition[]): Promise { 10 | const blob_writer = new BlobWriter("application/zip"); 11 | const zip_writer = new ZipWriter(blob_writer); 12 | 13 | await Promise.all( 14 | blobs.map((definition, index) => { 15 | const {blob, path} = definition; 16 | const blob_reader = new BlobReader(blob); 17 | 18 | return zip_writer.add(path, blob_reader, {useWebWorkers: true}); 19 | }) 20 | ); 21 | 22 | return zip_writer.close(); 23 | } 24 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-storage/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "moduleResolution": "node", 4 | "module": "ES2020", 5 | "target": "ES2019", 6 | "strict": true, 7 | "sourceMap": true, 8 | "declaration": true, 9 | "allowSyntheticDefaultImports": true, 10 | "esModuleInterop": true, 11 | "skipLibCheck": true, 12 | "downlevelIteration": true, 13 | "experimentalDecorators": true, 14 | "emitDecoratorMetadata": true 15 | }, 16 | "include": ["src/**/*.d.ts", "src/**/*.ts"] 17 | } 18 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-tauri/README.md: -------------------------------------------------------------------------------- 1 | # @svelte-in-motion/tauri 2 | 3 | > Packaging solution for shipping Svelte-In-Motion as a standalone [Tauri](https://tauri.app) Desktop application. 4 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-tauri/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@svelte-in-motion/tauri", 3 | "version": "0.0.1", 4 | "private": true, 5 | "scripts": { 6 | "build:application": "tauri build" 7 | }, 8 | "devDependencies": { 9 | "@tauri-apps/cli": "1.0.0-rc.9" 10 | }, 11 | "dependencies": { 12 | "@svelte-in-motion/editor": "workspace:^" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-tauri/src-tauri/.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | WixTools 5 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-tauri/src-tauri/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "svelte-in-motion" 3 | version = "0.0.1" 4 | description = "Programatic videos via Web APIs." 5 | authors = ["novacbn"] 6 | license = "MIT" 7 | repository = "git@github.com:novacbn/svelte-in-motion.git" 8 | default-run = "svelte-in-motion" 9 | edition = "2021" 10 | rust-version = "1.57" 11 | 12 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 13 | 14 | [build-dependencies] 15 | tauri-build = { version = "1.0.0-rc.7", features = [] } 16 | 17 | [dependencies] 18 | serde_json = "1.0" 19 | serde = { version = "1.0", features = ["derive"] } 20 | tauri = { version = "1.0.0-rc.7", features = ["api-all"] } 21 | 22 | [features] 23 | # by default Tauri runs in production mode 24 | # when `tauri dev` runs it is executed with `cargo run --no-default-features` if `devPath` is an URL 25 | default = [ "custom-protocol" ] 26 | # this feature is used used for production builds where `devPath` points to the filesystem 27 | # DO NOT remove this 28 | custom-protocol = [ "tauri/custom-protocol" ] 29 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-tauri/src-tauri/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | tauri_build::build() 3 | } 4 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-tauri/src-tauri/icons/128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/novacbn/svelte-in-motion/de24fb83d3b5b8a15675baa3bdb7b7d510bffeda/packages/@svelte-in-motion-tauri/src-tauri/icons/128x128.png -------------------------------------------------------------------------------- /packages/@svelte-in-motion-tauri/src-tauri/icons/128x128@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/novacbn/svelte-in-motion/de24fb83d3b5b8a15675baa3bdb7b7d510bffeda/packages/@svelte-in-motion-tauri/src-tauri/icons/128x128@2x.png -------------------------------------------------------------------------------- /packages/@svelte-in-motion-tauri/src-tauri/icons/32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/novacbn/svelte-in-motion/de24fb83d3b5b8a15675baa3bdb7b7d510bffeda/packages/@svelte-in-motion-tauri/src-tauri/icons/32x32.png -------------------------------------------------------------------------------- /packages/@svelte-in-motion-tauri/src-tauri/icons/Square107x107Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/novacbn/svelte-in-motion/de24fb83d3b5b8a15675baa3bdb7b7d510bffeda/packages/@svelte-in-motion-tauri/src-tauri/icons/Square107x107Logo.png -------------------------------------------------------------------------------- /packages/@svelte-in-motion-tauri/src-tauri/icons/Square142x142Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/novacbn/svelte-in-motion/de24fb83d3b5b8a15675baa3bdb7b7d510bffeda/packages/@svelte-in-motion-tauri/src-tauri/icons/Square142x142Logo.png -------------------------------------------------------------------------------- /packages/@svelte-in-motion-tauri/src-tauri/icons/Square150x150Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/novacbn/svelte-in-motion/de24fb83d3b5b8a15675baa3bdb7b7d510bffeda/packages/@svelte-in-motion-tauri/src-tauri/icons/Square150x150Logo.png -------------------------------------------------------------------------------- /packages/@svelte-in-motion-tauri/src-tauri/icons/Square284x284Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/novacbn/svelte-in-motion/de24fb83d3b5b8a15675baa3bdb7b7d510bffeda/packages/@svelte-in-motion-tauri/src-tauri/icons/Square284x284Logo.png -------------------------------------------------------------------------------- /packages/@svelte-in-motion-tauri/src-tauri/icons/Square30x30Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/novacbn/svelte-in-motion/de24fb83d3b5b8a15675baa3bdb7b7d510bffeda/packages/@svelte-in-motion-tauri/src-tauri/icons/Square30x30Logo.png -------------------------------------------------------------------------------- /packages/@svelte-in-motion-tauri/src-tauri/icons/Square310x310Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/novacbn/svelte-in-motion/de24fb83d3b5b8a15675baa3bdb7b7d510bffeda/packages/@svelte-in-motion-tauri/src-tauri/icons/Square310x310Logo.png -------------------------------------------------------------------------------- /packages/@svelte-in-motion-tauri/src-tauri/icons/Square44x44Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/novacbn/svelte-in-motion/de24fb83d3b5b8a15675baa3bdb7b7d510bffeda/packages/@svelte-in-motion-tauri/src-tauri/icons/Square44x44Logo.png -------------------------------------------------------------------------------- /packages/@svelte-in-motion-tauri/src-tauri/icons/Square71x71Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/novacbn/svelte-in-motion/de24fb83d3b5b8a15675baa3bdb7b7d510bffeda/packages/@svelte-in-motion-tauri/src-tauri/icons/Square71x71Logo.png -------------------------------------------------------------------------------- /packages/@svelte-in-motion-tauri/src-tauri/icons/Square89x89Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/novacbn/svelte-in-motion/de24fb83d3b5b8a15675baa3bdb7b7d510bffeda/packages/@svelte-in-motion-tauri/src-tauri/icons/Square89x89Logo.png -------------------------------------------------------------------------------- /packages/@svelte-in-motion-tauri/src-tauri/icons/StoreLogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/novacbn/svelte-in-motion/de24fb83d3b5b8a15675baa3bdb7b7d510bffeda/packages/@svelte-in-motion-tauri/src-tauri/icons/StoreLogo.png -------------------------------------------------------------------------------- /packages/@svelte-in-motion-tauri/src-tauri/icons/icon.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/novacbn/svelte-in-motion/de24fb83d3b5b8a15675baa3bdb7b7d510bffeda/packages/@svelte-in-motion-tauri/src-tauri/icons/icon.icns -------------------------------------------------------------------------------- /packages/@svelte-in-motion-tauri/src-tauri/icons/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/novacbn/svelte-in-motion/de24fb83d3b5b8a15675baa3bdb7b7d510bffeda/packages/@svelte-in-motion-tauri/src-tauri/icons/icon.ico -------------------------------------------------------------------------------- /packages/@svelte-in-motion-tauri/src-tauri/icons/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/novacbn/svelte-in-motion/de24fb83d3b5b8a15675baa3bdb7b7d510bffeda/packages/@svelte-in-motion-tauri/src-tauri/icons/icon.png -------------------------------------------------------------------------------- /packages/@svelte-in-motion-tauri/src-tauri/src/main.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr( 2 | all(not(debug_assertions), target_os = "windows"), 3 | windows_subsystem = "windows" 4 | )] 5 | 6 | fn main() { 7 | tauri::Builder::default() 8 | .run(tauri::generate_context!()) 9 | .expect("error while running tauri application"); 10 | } 11 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-tauri/src-tauri/tauri.conf.json: -------------------------------------------------------------------------------- 1 | { 2 | "package": { 3 | "productName": "svelte-in-motion", 4 | "version": "0.0.1" 5 | }, 6 | "build": { 7 | "distDir": "../node_modules/@svelte-in-motion/editor/dist" 8 | }, 9 | "tauri": { 10 | "bundle": { 11 | "active": true, 12 | "targets": "all", 13 | "identifier": "dev.nbn.sim", 14 | "icon": [ 15 | "icons/32x32.png", 16 | "icons/128x128.png", 17 | "icons/128x128@2x.png", 18 | "icons/icon.icns", 19 | "icons/icon.ico" 20 | ], 21 | "resources": [], 22 | "externalBin": [], 23 | "copyright": "", 24 | "category": "DeveloperTool", 25 | "shortDescription": "Programatic videos via Web APIs.", 26 | "longDescription": "", 27 | "deb": { 28 | "depends": [] 29 | }, 30 | "macOS": { 31 | "frameworks": [], 32 | "exceptionDomain": "", 33 | "signingIdentity": null, 34 | "providerShortName": "svelte-in-motion", 35 | "entitlements": null 36 | }, 37 | "windows": { 38 | "certificateThumbprint": null, 39 | "digestAlgorithm": "sha256", 40 | "timestampUrl": "" 41 | } 42 | }, 43 | "updater": { 44 | "active": false 45 | }, 46 | "allowlist": { 47 | "all": true 48 | }, 49 | "windows": [ 50 | { 51 | "title": "Svelte-In-Motion", 52 | "width": 1280, 53 | "height": 720, 54 | "resizable": true, 55 | "fullscreen": false 56 | } 57 | ], 58 | "security": { 59 | "csp": null 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-temporal/README.md: -------------------------------------------------------------------------------- 1 | # @svelte-in-motion/temporal 2 | 3 | > Stub package to pin [`@js-temporal/polyfill`](https://github.com/js-temporal/temporal-polyfill) along w/ custom utilities. 4 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-temporal/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@svelte-in-motion/temporal", 3 | "author": "novacbn", 4 | "version": "0.0.1", 5 | "type": "module", 6 | "main": "./dist/index.js", 7 | "exports": { 8 | ".": "./dist/index.js", 9 | "./package.json": "./package.json" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "https://github.com/novacbn/svelte-in-motion/tree/main/packages/%40svelte-in-motion-temporal" 14 | }, 15 | "scripts": { 16 | "build:package": "tsc", 17 | "dev:package": "tsc --watch", 18 | "format": "npm run format:package", 19 | "format:package": "prettier --config ../../.prettierrc --write ./src", 20 | "lint": "npm run lint:format", 21 | "lint:format": "prettier --config ../../.prettierrc --check ./src", 22 | "test": "npm run test:types", 23 | "test:types": "tsc --noEmit" 24 | }, 25 | "devDependencies": { 26 | "@deepkit/type-compiler": "^1.0.1-alpha.71", 27 | "prettier": "^2.3.1", 28 | "typescript": "^4.7.3" 29 | }, 30 | "dependencies": { 31 | "@deepkit/core": "^1.0.1-alpha.65", 32 | "@deepkit/type": "^1.0.1-alpha.71", 33 | "@js-temporal/polyfill": "^0.4.1" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-temporal/src/index.ts: -------------------------------------------------------------------------------- 1 | import {Temporal} from "@js-temporal/polyfill"; 2 | 3 | type OverrideReturn any, R> = (...args: Parameters) => R; 4 | 5 | export const Now = Temporal.Now as { 6 | instant: OverrideReturn; 7 | 8 | plainDate: OverrideReturn; 9 | 10 | plainDateISO: OverrideReturn; 11 | 12 | plainDateTime: OverrideReturn; 13 | 14 | plainDateTimeISO: OverrideReturn; 15 | 16 | timeZone: OverrideReturn; 17 | 18 | zonedDateTime: OverrideReturn; 19 | 20 | zonedDateTimeISO: OverrideReturn; 21 | }; 22 | 23 | // HACK: need to stub the class primitives to support serialization via DeepKit 24 | 25 | export class Calendar extends Temporal.Calendar {} 26 | 27 | export class Duration extends Temporal.Duration {} 28 | 29 | export class Instant extends Temporal.Instant {} 30 | 31 | export class PlainDate extends Temporal.PlainDate {} 32 | 33 | export class PlainDateTime extends Temporal.PlainDateTime {} 34 | 35 | export class PlainMonthDay extends Temporal.PlainMonthDay {} 36 | 37 | export class PlainTime extends Temporal.PlainTime {} 38 | 39 | export class PlainYearMonth extends Temporal.PlainYearMonth {} 40 | 41 | export class TimeZone extends Temporal.TimeZone {} 42 | 43 | export class ZonedDateTime extends Temporal.ZonedDateTime {} 44 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-temporal/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "moduleResolution": "node", 4 | "module": "ES2020", 5 | "target": "ES2019", 6 | "outDir": "./dist", 7 | "strict": true, 8 | "sourceMap": true, 9 | "declaration": true, 10 | "allowSyntheticDefaultImports": true, 11 | "esModuleInterop": true, 12 | "skipLibCheck": true, 13 | "downlevelIteration": true, 14 | "experimentalDecorators": true, 15 | "emitDecoratorMetadata": true 16 | }, 17 | "reflection": true, 18 | "include": ["src/**/*.d.ts", "src/**/*.ts"] 19 | } 20 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-type/README.md: -------------------------------------------------------------------------------- 1 | # @svelte-in-motion/type 2 | 3 | > Stub package to pin [`@deepkit/type`](https://deepkit.io/library/type) along w/ custom utilities. 4 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-type/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@svelte-in-motion/type", 3 | "author": "novacbn", 4 | "version": "0.0.1", 5 | "type": "module", 6 | "main": "./dist/index.js", 7 | "exports": { 8 | ".": "./dist/index.js", 9 | "./package.json": "./package.json" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "https://github.com/novacbn/svelte-in-motion/tree/main/packages/%40svelte-in-motion-type" 14 | }, 15 | "scripts": { 16 | "build:package": "tsc", 17 | "dev:package": "tsc --watch", 18 | "format": "npm run format:package", 19 | "format:package": "prettier --config ../../.prettierrc --write ./src", 20 | "lint": "npm run lint:format", 21 | "lint:format": "prettier --config ../../.prettierrc --check ./src", 22 | "test": "npm run test:types", 23 | "test:types": "tsc --noEmit" 24 | }, 25 | "devDependencies": { 26 | "@deepkit/type-compiler": "^1.0.1-alpha.71", 27 | "prettier": "^2.3.1", 28 | "typescript": "^4.7.3" 29 | }, 30 | "dependencies": { 31 | "@deepkit/core": "^1.0.1-alpha.65", 32 | "@deepkit/type": "^1.0.1-alpha.71", 33 | "@svelte-in-motion/temporal": "workspace:^", 34 | "jsonc-parser": "^3.0.0" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-type/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "@deepkit/type"; 2 | 3 | export * from "./serializers/jsonserializer"; 4 | 5 | export * from "./types/default"; 6 | export * from "./types/interface"; 7 | export * from "./types/metadata"; 8 | 9 | export * from "./util/class"; 10 | export * from "./util/dataclass"; 11 | export * from "./util/resolve"; 12 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-type/src/types/default.ts: -------------------------------------------------------------------------------- 1 | export type Default = {__meta?: ["default", T]}; 2 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-type/src/types/interface.ts: -------------------------------------------------------------------------------- 1 | export type Description = {__meta?: ["description", T]}; 2 | 3 | export type Placeholder = {__meta?: ["placeholder", T]}; 4 | 5 | export type Label = {__meta?: ["label", T]}; 6 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-type/src/types/metadata.ts: -------------------------------------------------------------------------------- 1 | export type Namespace = {__meta?: ["namespace", T]}; 2 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-type/src/util/class.ts: -------------------------------------------------------------------------------- 1 | export type Class = new (...args: any[]) => T; 2 | 3 | export type ClassProperties = { 4 | [K in keyof T]: T[T[K] extends Function ? never : K]; 5 | }; 6 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-type/src/util/dataclass.ts: -------------------------------------------------------------------------------- 1 | import {cast, is, resolveReceiveType, serialize} from "@deepkit/type"; 2 | import {parse} from "jsonc-parser"; 3 | 4 | import {JSON_SERIALIZER} from "../serializers/jsonserializer"; 5 | 6 | export interface IDataClassParseOptions {} 7 | 8 | export interface IDataClassStringifyOptions { 9 | is_formatted?: boolean; 10 | } 11 | 12 | export class DataClass { 13 | static from>( 14 | this: B, 15 | properties: any 16 | ): I | never { 17 | const data = cast( 18 | properties, 19 | undefined, 20 | JSON_SERIALIZER, 21 | undefined, 22 | resolveReceiveType(this) 23 | ); 24 | 25 | return data; 26 | } 27 | 28 | static is>(this: B, value: any): value is I { 29 | return is(value, JSON_SERIALIZER, undefined, resolveReceiveType(this)); 30 | } 31 | 32 | static parse>( 33 | this: B, 34 | text: string, 35 | options: IDataClassParseOptions = {} 36 | ): I | never { 37 | const properties = parse(text); 38 | 39 | return this.from(properties); 40 | } 41 | 42 | static stringify>( 43 | this: B, 44 | properties: any, 45 | options: IDataClassStringifyOptions = {} 46 | ): string | never { 47 | const {is_formatted = false} = options; 48 | 49 | const serialized = serialize( 50 | properties, 51 | undefined, 52 | JSON_SERIALIZER, 53 | undefined, 54 | resolveReceiveType(this) 55 | ); 56 | 57 | return JSON.stringify(serialized, null, is_formatted ? 4 : undefined); 58 | } 59 | 60 | clone(): this { 61 | return (this.constructor as typeof DataClass).from(this); 62 | } 63 | 64 | stringify(options?: IDataClassStringifyOptions): string | never { 65 | return (this.constructor as typeof DataClass).stringify(this, options); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-type/src/util/resolve.ts: -------------------------------------------------------------------------------- 1 | import type {TypeLiteral, metaAnnotation, validationAnnotation} from "@deepkit/type"; 2 | 3 | export type MetaAnnotation = ReturnType[0]; 4 | 5 | export type ValidationAnnotation = ReturnType[0]; 6 | 7 | export function resolveMetaLiteral( 8 | annotations: MetaAnnotation[], 9 | name: string, 10 | index: number = 0 11 | ): T | undefined { 12 | const annotation = annotations.find((annotation) => annotation.name === name); 13 | if (annotation) { 14 | const type = annotation.options[index] as TypeLiteral; 15 | 16 | // @ts-expect-error 17 | return type.literal as T; 18 | } 19 | } 20 | 21 | export function resolveValidationLiteral( 22 | annotations: ValidationAnnotation[], 23 | name: string, 24 | index: number = 0 25 | ): T | undefined { 26 | const annotation = annotations.find((annotation) => annotation.name === name); 27 | if (annotation) { 28 | const type = annotation.args[index] as TypeLiteral; 29 | 30 | // @ts-expect-error 31 | return type.literal as T; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-type/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "moduleResolution": "node", 4 | "module": "ES2020", 5 | "target": "ES2019", 6 | "outDir": "./dist", 7 | "strict": true, 8 | "sourceMap": true, 9 | "declaration": true, 10 | "allowSyntheticDefaultImports": true, 11 | "esModuleInterop": true, 12 | "skipLibCheck": true, 13 | "downlevelIteration": true, 14 | "experimentalDecorators": true, 15 | "emitDecoratorMetadata": true 16 | }, 17 | "reflection": true, 18 | "include": ["src/**/*.d.ts", "src/**/*.ts"] 19 | } 20 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-utilities/README.md: -------------------------------------------------------------------------------- 1 | # @svelte-in-motion/utilities 2 | 3 | > Support package implementing common utilities used by Svelte-In-Motion's codebase, extensions, and projects. 4 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-utilities/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@svelte-in-motion/utilities", 3 | "author": "novacbn", 4 | "version": "0.0.1", 5 | "type": "module", 6 | "main": "./src/index.ts", 7 | "exports": { 8 | ".": "./src/index.ts", 9 | "./package.json": "./package.json" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "https://github.com/novacbn/svelte-in-motion/tree/main/packages/%40svelte-in-motion-utilities" 14 | }, 15 | "scripts": { 16 | "format": "npm run format:package", 17 | "format:package": "prettier --config ../../.prettierrc --write ./src", 18 | "lint": "npm run lint:format", 19 | "lint:format": "prettier --config ../../.prettierrc --check ./src", 20 | "test": "npm run test:types", 21 | "test:types": "tsc --noEmit" 22 | }, 23 | "devDependencies": { 24 | "@types/github-slugger": "^1.3.0", 25 | "@types/mersenne-twister": "^1.1.2", 26 | "@types/node": "^17.0.31", 27 | "@types/string-hash": "^1.1.1", 28 | "prettier": "^2.3.1", 29 | "typescript": "^4.7.3" 30 | }, 31 | "dependencies": { 32 | "dot-prop": "^7.2.0", 33 | "github-slugger": "^1.4.0", 34 | "mersenne-twister": "^1.1.0", 35 | "string-hash": "^1.1.3", 36 | "svelte": "^3.46.4", 37 | "urlpattern-polyfill": "1.0.0-rc5" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-utilities/src/events/channel.ts: -------------------------------------------------------------------------------- 1 | import type {IEvent, IEventNotifier} from "./event"; 2 | import {event} from "./event"; 3 | 4 | export interface IChannelEvent extends IEvent { 5 | close(): void; 6 | } 7 | 8 | export function channel( 9 | channel: BroadcastChannel | string, 10 | start?: IEventNotifier 11 | ): IChannelEvent { 12 | channel = typeof channel === "string" ? new BroadcastChannel(channel) : channel; 13 | 14 | const {dispatch, subscribe} = event((dispatch) => { 15 | function on_message(event: MessageEvent) { 16 | dispatch(event.data); 17 | } 18 | 19 | (channel as BroadcastChannel).addEventListener("message", on_message); 20 | 21 | let destroy = start ? start(dispatch) : null; 22 | 23 | return () => { 24 | (channel as BroadcastChannel).removeEventListener("message", on_message); 25 | 26 | if (destroy) { 27 | destroy(); 28 | destroy = null; 29 | } 30 | }; 31 | }); 32 | 33 | return { 34 | close() { 35 | (channel as BroadcastChannel).close(); 36 | }, 37 | 38 | dispatch(detail) { 39 | (channel as BroadcastChannel).postMessage(detail); 40 | dispatch(detail); 41 | }, 42 | 43 | subscribe, 44 | }; 45 | } 46 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-utilities/src/events/event.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Represents the callback supplied by subscribers to be called every dispatch 3 | */ 4 | export type IEventCallback = (detail: T) => void; 5 | 6 | /** 7 | * Reprevents the function used to dispatch events into the bus 8 | */ 9 | export type IEventDispatch = (detail: T) => void; 10 | 11 | /** 12 | * Represents the notification subscription used whenever a first subscription is added 13 | */ 14 | export type IEventNotifier = (dispatch: IEventDispatch) => IEventUnsubscriber; 15 | 16 | /** 17 | * Represents the unsubscribe function returned by [[IEvent.subscribe]] 18 | */ 19 | export type IEventUnsubscriber = () => void; 20 | 21 | /** 22 | * Represents the tuple that internally represents a subscription 23 | */ 24 | type IEventSubscriber = [IEventCallback]; 25 | 26 | /** 27 | * Represents an interface to publish event data via a singleton instance, that is compatible with Svelte Store subscriptions 28 | */ 29 | export interface IEvent { 30 | /** 31 | * Dispatches new event details to every subscriber 32 | * @param details 33 | */ 34 | dispatch: IEventDispatch; 35 | 36 | /** 37 | * Subscribes to new incoming event dispatches 38 | * @param run 39 | */ 40 | subscribe: (run: IEventCallback) => IEventUnsubscriber; 41 | } 42 | 43 | /** 44 | * Returns a new [[IEvent]] instance, for handling event publishing in non-DOM related contexts 45 | * 46 | * @param start 47 | */ 48 | export function event(start?: IEventNotifier): IEvent { 49 | const subscribers: Set> = new Set(); 50 | 51 | let stop: IEventUnsubscriber | null; 52 | 53 | const dispatch = (detail: T) => { 54 | for (const [run] of subscribers) run(detail); 55 | }; 56 | 57 | const subscribe = (run: IEventCallback) => { 58 | const subscriber: IEventSubscriber = [run]; 59 | 60 | subscribers.add(subscriber); 61 | if (start && subscribers.size === 1) stop = start(dispatch); 62 | 63 | return () => { 64 | subscribers.delete(subscriber); 65 | 66 | if (stop && subscribers.size === 0) { 67 | stop(); 68 | stop = null; 69 | } 70 | }; 71 | }; 72 | 73 | return {dispatch, subscribe}; 74 | } 75 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-utilities/src/events/message.ts: -------------------------------------------------------------------------------- 1 | import type {IEventCallback, IEventNotifier, IEventUnsubscriber} from "./event"; 2 | import {event} from "./event"; 3 | 4 | export interface IMessageEventTarget { 5 | addEventListener: ( 6 | type: "message", 7 | listener: (this: EventTarget, ev: MessageEvent) => any, 8 | options?: boolean | AddEventListenerOptions 9 | ) => void; 10 | 11 | removeEventListener: ( 12 | type: "message", 13 | listener: (this: EventTarget, ev: MessageEvent) => any, 14 | options?: boolean | EventListenerOptions 15 | ) => void; 16 | } 17 | 18 | export interface IMessageEvent { 19 | dispatch: (message: T, transfer?: Transferable[], origin?: string) => void; 20 | 21 | subscribe: (run: IEventCallback) => IEventUnsubscriber; 22 | } 23 | 24 | export interface IPortEventTarget extends IMessageEventTarget { 25 | postMessage: (message: any, transfer?: Transferable[]) => void; 26 | } 27 | 28 | export interface IWindowEventTarget extends IMessageEventTarget { 29 | postMessage: (message: any, targetOrigin: string, transfer?: Transferable[]) => void; 30 | } 31 | 32 | export function message( 33 | target: IPortEventTarget | IWindowEventTarget, 34 | start?: IEventNotifier 35 | ): IMessageEvent { 36 | const {subscribe} = event((dispatch) => { 37 | function on_message(event: MessageEvent) { 38 | dispatch(event.data); 39 | } 40 | 41 | target.addEventListener("message", on_message); 42 | 43 | let destroy = start ? start(dispatch) : null; 44 | 45 | return () => { 46 | target.removeEventListener("message", on_message); 47 | 48 | if (destroy) { 49 | destroy(); 50 | destroy = null; 51 | } 52 | }; 53 | }); 54 | 55 | return { 56 | dispatch(message, transfer, origin = location.origin) { 57 | if (typeof window === "object" && target === window) { 58 | window.postMessage(message, origin, transfer); 59 | } else (target as IPortEventTarget).postMessage(message, transfer); 60 | }, 61 | 62 | subscribe, 63 | }; 64 | } 65 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-utilities/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./events/channel"; 2 | export * from "./events/event"; 3 | export * from "./events/message"; 4 | 5 | export * from "./stores/collection"; 6 | export * from "./stores/immutable"; 7 | export * from "./stores/map"; 8 | export * from "./stores/types"; 9 | 10 | export * from "./util/contexts"; 11 | export * from "./util/errors"; 12 | export * from "./util/environment"; 13 | export * from "./util/eval"; 14 | export * from "./util/math"; 15 | export * from "./util/random"; 16 | export * from "./util/router"; 17 | export * from "./util/string"; 18 | export * from "./util/timing"; 19 | export * from "./util/url"; 20 | export * from "./util/web"; 21 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-utilities/src/stores/immutable.ts: -------------------------------------------------------------------------------- 1 | import type {Writable} from "svelte/store"; 2 | import {writable} from "svelte/store"; 3 | 4 | export type IImmutableStore = Writable; 5 | 6 | export function immutable(store: Writable = writable()): IImmutableStore { 7 | const {set, subscribe, update} = store; 8 | 9 | return { 10 | set(value) { 11 | set(structuredClone(value)); 12 | }, 13 | 14 | subscribe(callback) { 15 | return subscribe((value) => { 16 | callback(structuredClone(value)); 17 | }); 18 | }, 19 | 20 | update(updater) { 21 | update((value) => { 22 | return updater(structuredClone(value)); 23 | }); 24 | }, 25 | }; 26 | } 27 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-utilities/src/stores/map.ts: -------------------------------------------------------------------------------- 1 | import {deleteProperty, getProperty, hasProperty, setProperty} from "dot-prop"; 2 | import type {Readable, Writable} from "svelte/store"; 3 | import {derived, get} from "svelte/store"; 4 | 5 | export type IMap = Record; 6 | 7 | export interface IMapStore extends Readable { 8 | get(path: string): V | undefined; 9 | 10 | has(path: string): boolean; 11 | 12 | set(path: string, value: any): void; 13 | 14 | remove(path: string): void; 15 | 16 | watch(path: string): Readable; 17 | } 18 | 19 | export function map(store: Writable): IMapStore { 20 | const {set, subscribe} = store; 21 | 22 | return { 23 | subscribe, 24 | 25 | get(path) { 26 | const $map = get(store); 27 | 28 | return getProperty($map, path); 29 | }, 30 | 31 | has(path) { 32 | const $map = get(store); 33 | 34 | return hasProperty($map, path); 35 | }, 36 | 37 | set(path, value) { 38 | const $map = get(store); 39 | 40 | setProperty($map, path, value); 41 | set($map); 42 | }, 43 | 44 | remove(path) { 45 | const $map = get(store); 46 | 47 | if (!hasProperty($map, path)) { 48 | throw new ReferenceError(`bad argument #0 to 'map.remove' (path not valid)`); 49 | } 50 | 51 | deleteProperty($map, path); 52 | set($map); 53 | }, 54 | 55 | watch(path) { 56 | return derived(store, ($map) => { 57 | return getProperty($map, path); 58 | }); 59 | }, 60 | }; 61 | } 62 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-utilities/src/stores/types.ts: -------------------------------------------------------------------------------- 1 | import {Writable} from "svelte/store"; 2 | 3 | export type ReadableOnly> = Pick; 4 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-utilities/src/util/contexts.ts: -------------------------------------------------------------------------------- 1 | import {getContext, hasContext, setContext} from "svelte"; 2 | 3 | /** 4 | * Represents the return value of [[make_scoped_context]] 5 | */ 6 | export interface IContextScope { 7 | key: string; 8 | 9 | get(): T | undefined; 10 | 11 | has(): boolean; 12 | 13 | set(value: T | undefined): void; 14 | } 15 | 16 | /** 17 | * Returns Svelte Context Scoped helpers 18 | * 19 | * ```javascript 20 | * const {get, has, set} = make_scoped_context("my-context"); 21 | * ``` 22 | * 23 | * @param key 24 | * @returns 25 | */ 26 | export function make_scoped_context(key: string): IContextScope { 27 | return { 28 | key, 29 | 30 | get() { 31 | return getContext(key); 32 | }, 33 | 34 | has() { 35 | return hasContext(key); 36 | }, 37 | 38 | set(value) { 39 | setContext(key, value); 40 | }, 41 | }; 42 | } 43 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-utilities/src/util/environment.ts: -------------------------------------------------------------------------------- 1 | // TODO: This can pick up false positives (e.g. Deno) due to various build tools polyfilling, maybe a feature flag during build 2 | export const IS_BROWSER: boolean = typeof window === "object"; 3 | 4 | // TODO: This can pick up false positives (e.g. Polyfills) due to various build tools polyfilling, maybe a feature flag during build 5 | export const IS_NODE: boolean = typeof process === "object"; 6 | 7 | // TODO: Figure out how to have this detection work, maybe a feature flag during build? Is there a global Tauri namespace? 8 | export const IS_TAURI: boolean = false; 9 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-utilities/src/util/errors.ts: -------------------------------------------------------------------------------- 1 | import type {SvelteComponent} from "svelte"; 2 | 3 | export class PromptError extends Error { 4 | name = PromptError.name; 5 | } 6 | 7 | export class PromptDismissError extends PromptError { 8 | message = "Prompt was dismissed"; 9 | 10 | name = PromptDismissError.name; 11 | } 12 | 13 | export class TranslatedError extends Error { 14 | name = TranslatedError.name; 15 | 16 | tokens?: T; 17 | 18 | constructor(tokens?: T) { 19 | super(); 20 | 21 | this.tokens = tokens; 22 | } 23 | } 24 | 25 | export class UserError extends TranslatedError { 26 | // TODO: Once icon situation is resolved, should default to the X icon 27 | icon?: typeof SvelteComponent; 28 | 29 | name = UserError.name; 30 | 31 | constructor(tokens?: T, icon?: typeof SvelteComponent) { 32 | super(tokens); 33 | 34 | this.icon = icon; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-utilities/src/util/eval.ts: -------------------------------------------------------------------------------- 1 | export type IEvaluationContext = Record; 2 | 3 | export type IEvalulationModule = { 4 | default: Default; 5 | exports: Exports; 6 | }; 7 | 8 | export type IEvaluationImports = Record; 9 | 10 | export type IEvaluationRequire = (name: string) => IEvalulationModule; 11 | 12 | export function evaluate_code( 13 | script: string, 14 | context: IEvaluationContext = {}, 15 | imports: IEvaluationImports = {} 16 | ): IEvalulationModule { 17 | const keys = Object.keys(context); 18 | const values = Object.values(context); 19 | 20 | const module = {exports: {}}; 21 | const require = make_require(imports); 22 | 23 | const func = new Function( 24 | ...keys, 25 | "require", 26 | "module", 27 | "exports", 28 | `return (function () { 29 | "use strict"; 30 | ${script} 31 | })` 32 | )(...values, require, module, module.exports); 33 | 34 | func(); 35 | 36 | const {default: module_default, ...module_exports} = module.exports as {default: Default}; 37 | 38 | return { 39 | default: module_default, 40 | exports: module_exports as Exports, 41 | }; 42 | } 43 | 44 | export function make_require(imports: IEvaluationImports = {}): IEvaluationRequire { 45 | return (name) => { 46 | if (name in imports) return imports[name]; 47 | throw new ReferenceError(`bad argument #0 to 'require' (module '${name}' not found)`); 48 | }; 49 | } 50 | 51 | export function validate_code(script: string): [false, string]; 52 | export function validate_code(script: string): [true, null]; 53 | export function validate_code(script: string): [boolean, string | null] { 54 | try { 55 | new Function(script); 56 | } catch (error: any) { 57 | return [false, error.message]; 58 | } 59 | 60 | return [true, null]; 61 | } 62 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-utilities/src/util/math.ts: -------------------------------------------------------------------------------- 1 | export function clamp(value: number, min: number, max: number): number { 2 | return Math.min(Math.max(value, min), max); 3 | } 4 | 5 | export function truncate(value: number, precision: number): number { 6 | const place = Math.pow(10, precision); 7 | 8 | return Math.floor(value * place) / place; 9 | } 10 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-utilities/src/util/object.ts: -------------------------------------------------------------------------------- 1 | import {deepKeys, getProperty, setProperty} from "dot-prop"; 2 | 3 | export function deep_assign( 4 | target: Record, 5 | source: Record 6 | ): Record { 7 | for (const path of deepKeys(source)) { 8 | const value = getProperty(source, path); 9 | 10 | setProperty(target, path, value); 11 | } 12 | 13 | return target; 14 | } 15 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-utilities/src/util/random.ts: -------------------------------------------------------------------------------- 1 | import MersenneTwister from "mersenne-twister"; 2 | 3 | import {hash_string} from "./string"; 4 | 5 | export interface IRandomGenerator { 6 | float(min: number, max: number): number; 7 | 8 | integer(min: number, max: number): number; 9 | 10 | next(): number; 11 | } 12 | 13 | export function generate_uint32(): number { 14 | // NOTE: Proxying incase polyfills or implementation changes are wanted later 15 | 16 | const view = new Uint32Array(1); 17 | crypto.getRandomValues(view); 18 | 19 | return view[0]; 20 | } 21 | 22 | export function generate_uuid(): string { 23 | // NOTE: Proxying incase polyfills or implementation changes are wanted later 24 | 25 | return crypto.randomUUID(); 26 | } 27 | 28 | export function random(seed: number | string = generate_uint32()): IRandomGenerator { 29 | if (typeof seed === "string") seed = hash_string(seed); 30 | 31 | const generator = new MersenneTwister(seed); 32 | 33 | return { 34 | float(min, max) { 35 | const difference = (max - min) * generator.random(); 36 | 37 | return min + difference; 38 | }, 39 | 40 | integer(min, max) { 41 | const difference = (max - min) * generator.random(); 42 | 43 | return Math.floor(min + difference); 44 | }, 45 | 46 | next() { 47 | return generator.random(); 48 | }, 49 | }; 50 | } 51 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-utilities/src/util/router.ts: -------------------------------------------------------------------------------- 1 | import type {URLPatternResult} from "urlpattern-polyfill/dist/url-pattern.interfaces"; 2 | import {URLPattern} from "urlpattern-polyfill"; 3 | 4 | import {DEFAULT_ORIGIN} from "./url"; 5 | 6 | export type IRouterDefinitions = Record; 7 | 8 | export interface IRouter { 9 | exec(uri: string, base_url?: string): IRouterMatch | null; 10 | } 11 | 12 | export interface IRouterMatch { 13 | result: T; 14 | 15 | url: URLPatternResult; 16 | } 17 | 18 | export function router( 19 | definitions: IRouterDefinitions, 20 | base_url: string = DEFAULT_ORIGIN 21 | ): IRouter { 22 | // HACK: Need to cache this here for `router.exec` 23 | const global_base_url = base_url; 24 | 25 | const mapped_routes: [URLPattern, T][] = Object.entries(definitions).map((route) => { 26 | const [pattern, result] = route; 27 | 28 | return [new URLPattern(pattern, base_url), result]; 29 | }); 30 | 31 | return { 32 | exec(uri, base_url = global_base_url) { 33 | for (const route of mapped_routes) { 34 | const [pattern, result] = route; 35 | 36 | const url = pattern.exec(uri, base_url); 37 | if (url) { 38 | return { 39 | result, 40 | url, 41 | }; 42 | } 43 | } 44 | 45 | return null; 46 | }, 47 | }; 48 | } 49 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-utilities/src/util/string.ts: -------------------------------------------------------------------------------- 1 | import GithubSlugger from "github-slugger"; 2 | import stringHash from "string-hash"; 3 | 4 | export function format_camel_case(text: string): string { 5 | return text.replace(/-(\w)/g, (match, character) => character.toUpperCase()); 6 | } 7 | 8 | export function format_dash_case(text: string): string { 9 | return text 10 | .replace(/([A-Z])/g, (match, character) => "-" + character.toLowerCase()) 11 | .replace(/([\s\.])/g, "-") 12 | .replace(/([\-\-]+)/g, "-") 13 | .replace(/(^\-)/, "") 14 | .replace(/(\-$)/, ""); 15 | } 16 | 17 | export function format_slug(text: string): string { 18 | // NOTE: Proxying incase implementation changes are wanted later 19 | 20 | const slugger = new GithubSlugger(); 21 | return slugger.slug(text); 22 | } 23 | 24 | export function format_snake_case(text: string): string { 25 | return text 26 | .replace(/([A-Z])/g, (match, character) => "_" + character.toLowerCase()) 27 | .replace(/([\s\.])/g, "_") 28 | .replace(/([__]+)/g, "_") 29 | .replace(/(^_)/, "") 30 | .replace(/(_$)/, ""); 31 | } 32 | 33 | export function hash_string(text: string): number { 34 | // NOTE: Proxying incase implementation changes are wanted later 35 | 36 | return stringHash(text); 37 | } 38 | 39 | export function replace_tokens(text: string, tokens: Record = {}): string { 40 | return text.replace(/\$\{([\w_\.]+)\}/g, (match, token) => tokens[token].toString()); 41 | } 42 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-utilities/src/util/timing.ts: -------------------------------------------------------------------------------- 1 | export function animation(): Promise { 2 | return new Promise((resolve, reject) => { 3 | requestAnimationFrame(() => { 4 | resolve(); 5 | }); 6 | }); 7 | } 8 | 9 | export function debounce any | Promise>( 10 | func: F, 11 | duration: number = 0 12 | ): (...args: Parameters) => void | Promise { 13 | let identifier: number | undefined; 14 | 15 | return (...args: any[]) => { 16 | if (identifier !== undefined) { 17 | clearTimeout(identifier); 18 | identifier = undefined; 19 | } 20 | 21 | // @ts-ignore - HACK: NodeJS doesn't follow spec 22 | identifier = setTimeout(() => func(...args), duration); 23 | }; 24 | } 25 | 26 | export function idle(): Promise { 27 | return new Promise((resolve, reject) => { 28 | requestIdleCallback(() => { 29 | resolve(); 30 | }); 31 | }); 32 | } 33 | 34 | export function throttle any | Promise>( 35 | func: F, 36 | duration: number = 0 37 | ): (...args: Parameters) => void | Promise { 38 | let previous_call = Number.MIN_SAFE_INTEGER; 39 | 40 | return (...args: any[]) => { 41 | const current_call = Date.now(); 42 | if (current_call - previous_call >= duration) { 43 | func(...args); 44 | previous_call = current_call; 45 | } 46 | }; 47 | } 48 | 49 | export function timeout(delay: number): Promise { 50 | return new Promise((resolve, reject) => { 51 | setTimeout(() => { 52 | resolve(); 53 | }, delay); 54 | }); 55 | } 56 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-utilities/src/util/web.ts: -------------------------------------------------------------------------------- 1 | import {format_camel_case} from "./string"; 2 | 3 | export function download_blob(blob: Blob, file_name: string): void { 4 | const url = URL.createObjectURL(blob); 5 | const anchor_element = document.createElement("A"); 6 | 7 | anchor_element.setAttribute("download", file_name); 8 | anchor_element.setAttribute("href", url); 9 | 10 | anchor_element.click(); 11 | 12 | URL.revokeObjectURL(url); 13 | anchor_element.remove(); 14 | } 15 | 16 | export function download_buffer(buffer: Uint8Array, file_name: string, mimetype: string): void { 17 | const blob = new Blob([buffer], {type: mimetype}); 18 | 19 | download_blob(blob, file_name); 20 | } 21 | 22 | export function parse_style(text: string): Record { 23 | const declarations: Record = {}; 24 | const tokens = text.split(";").filter((token) => !!token); 25 | 26 | for (const declaration of tokens) { 27 | const [property, value] = declaration.split(":"); 28 | 29 | declarations[format_camel_case(property)] = value; 30 | } 31 | 32 | return declarations; 33 | } 34 | -------------------------------------------------------------------------------- /packages/@svelte-in-motion-utilities/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "moduleResolution": "node", 4 | "module": "ES2020", 5 | "target": "ES2019", 6 | "strict": true, 7 | "sourceMap": true, 8 | "declaration": true, 9 | "allowSyntheticDefaultImports": true, 10 | "esModuleInterop": true, 11 | "skipLibCheck": true, 12 | "downlevelIteration": true, 13 | "experimentalDecorators": true, 14 | "emitDecoratorMetadata": true 15 | }, 16 | "include": ["src/**/*.d.ts", "src/**/*.ts"] 17 | } 18 | -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | - 'packages/*' -------------------------------------------------------------------------------- /vercel.json: -------------------------------------------------------------------------------- 1 | { 2 | "headers": [ 3 | { 4 | "source": "/(.*)", 5 | "headers": [ 6 | { 7 | "key": "Cross-Origin-Embedder-Policy", 8 | "value": "require-corp" 9 | }, 10 | { 11 | "key": "Cross-Origin-Opener-Policy", 12 | "value": "same-origin" 13 | } 14 | ] 15 | } 16 | ] 17 | } 18 | --------------------------------------------------------------------------------