├── .github ├── FUNDING.yml ├── actions │ ├── install-desktop-deps │ │ └── action.yml │ ├── setup-js │ │ └── action.yml │ └── setup-rust │ │ └── action.yml ├── assets │ ├── og-image.jpg │ └── readme.png └── workflows │ ├── cache-factory.yaml │ ├── ci.yaml │ └── release-desktop.yaml ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── apps ├── desktop │ ├── .env.example │ ├── .gitignore │ ├── .prettierrc │ ├── app.config.ts │ ├── package.json │ ├── postcss.config.js │ ├── public │ │ ├── tauri.svg │ │ └── vite.svg │ ├── src-tauri │ │ ├── .gitignore │ │ ├── Cargo.lock │ │ ├── Cargo.toml │ │ ├── assets │ │ │ └── dmg-background.png │ │ ├── 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 │ │ │ ├── fs.rs │ │ │ ├── http │ │ │ │ ├── commands.rs │ │ │ │ ├── config.rs │ │ │ │ ├── error.rs │ │ │ │ └── mod.rs │ │ │ ├── main.rs │ │ │ ├── oauth.rs │ │ │ ├── shell.rs │ │ │ └── websocket.rs │ │ └── tauri.conf.json │ ├── src │ │ ├── Editor.tsx │ │ ├── api.ts │ │ ├── app.css │ │ ├── app.tsx │ │ ├── app │ │ │ └── index.tsx │ │ ├── auto-imports.d.ts │ │ ├── commands.ts │ │ ├── core.ts │ │ ├── entry-client.tsx │ │ ├── entry-server.tsx │ │ ├── env.ts │ │ ├── http.ts │ │ ├── platform.ts │ │ └── rspc │ │ │ ├── index.ts │ │ │ └── types.ts │ ├── tailwind.config.js │ └── tsconfig.json ├── new-playground │ ├── .gitignore │ ├── index.html │ ├── package.json │ ├── public │ │ └── favicon.png │ ├── src │ │ ├── Runtime.ts │ │ ├── auto-imports.d.ts │ │ ├── components │ │ │ ├── Graph.tsx │ │ │ └── Node.tsx │ │ ├── dev-server.ts │ │ ├── domain │ │ │ ├── CloudApi │ │ │ │ ├── ApiClient.ts │ │ │ │ ├── AuthState.ts │ │ │ │ └── CredentialsCache.ts │ │ │ ├── Graph │ │ │ │ ├── data.ts │ │ │ │ ├── error.ts │ │ │ │ └── rpc.ts │ │ │ ├── Node │ │ │ │ ├── data.ts │ │ │ │ └── rpc.ts │ │ │ ├── Package │ │ │ │ └── data.ts │ │ │ ├── Project │ │ │ │ ├── Actions.ts │ │ │ │ ├── NodeIdCounter.ts │ │ │ │ ├── Packages.ts │ │ │ │ └── data.ts │ │ │ ├── Realtime │ │ │ │ ├── Connection.ts │ │ │ │ ├── Presence.ts │ │ │ │ └── PubSub.ts │ │ │ └── Rpc │ │ │ │ └── Middleware.ts │ │ ├── entry-client.tsx │ │ ├── entry-server.ts │ │ ├── errors.ts │ │ ├── index.ts │ │ ├── io.ts │ │ ├── obs-package │ │ │ ├── Settings.tsx │ │ │ ├── index.ts │ │ │ └── shared.ts │ │ ├── package-settings-utils.tsx │ │ ├── package-utils.ts │ │ ├── package.ts │ │ ├── prod-server.ts │ │ ├── project.ts │ │ ├── schema.ts │ │ ├── shared.ts │ │ ├── style.css │ │ ├── twitch-package │ │ │ ├── Settings.tsx │ │ │ ├── eventSub.ts │ │ │ ├── helix.ts │ │ │ ├── index.ts │ │ │ └── shared.ts │ │ ├── types.ts │ │ ├── util-package │ │ │ └── index.ts │ │ ├── util.ts │ │ └── vite-env.d.ts │ ├── tsconfig.json │ ├── uno.config.ts │ └── vite.config.ts ├── storybook │ ├── .gitignore │ ├── .storybook │ │ ├── main.ts │ │ └── preview.ts │ ├── README.md │ ├── index.html │ ├── package.json │ ├── public │ │ └── vite.svg │ └── vite.config.ts └── web │ ├── .env.example │ ├── .gitignore │ ├── app.config.ts │ ├── drizzle.config.ts │ ├── drizzle │ ├── 0000_fresh_speedball.sql │ ├── meta │ │ ├── 0000_snapshot.json │ │ └── _journal.json │ └── migrate.ts │ ├── package.json │ ├── postcss.config.js │ ├── public │ ├── favicon.png │ └── paddle-verification.txt │ ├── src │ ├── api.tsx │ ├── app.tsx │ ├── app │ │ ├── (app).tsx │ │ ├── (app) │ │ │ └── credentials.tsx │ │ ├── (auth) │ │ │ ├── Forms.tsx │ │ │ ├── login.tsx │ │ │ ├── signup.tsx │ │ │ └── utils.ts │ │ ├── (landing) │ │ │ ├── DesktopDownloadDropdown.tsx │ │ │ ├── Socials.tsx │ │ │ ├── index.tsx │ │ │ └── logos │ │ │ │ ├── discord.png │ │ │ │ ├── goxlr.png │ │ │ │ ├── midi.svg │ │ │ │ ├── obs.png │ │ │ │ ├── openai.png │ │ │ │ ├── speaker-bot.svg │ │ │ │ ├── stream-deck.jpg │ │ │ │ ├── streamlabs.png │ │ │ │ ├── twitch.png │ │ │ │ ├── vtube-studio.png │ │ │ │ └── websockets.webp │ │ ├── DesktopListener.tsx │ │ ├── HeaderAuthSection.tsx │ │ ├── Logo.tsx │ │ ├── api │ │ │ ├── [...api].ts │ │ │ └── desktop │ │ │ │ └── download │ │ │ │ └── latest │ │ │ │ └── [target].ts │ │ ├── auth │ │ │ ├── [provider] │ │ │ │ ├── callback.tsx │ │ │ │ ├── login.ts │ │ │ │ ├── refresh.ts │ │ │ │ └── types.ts │ │ │ ├── actions.ts │ │ │ ├── providers.ts │ │ │ ├── proxy.tsx │ │ │ ├── responderScript-web.js │ │ │ └── responderScript.js │ │ ├── docs │ │ │ └── [...catchall].md │ │ └── playground │ │ │ ├── Editor.tsx │ │ │ ├── index.tsx │ │ │ └── playground.css │ ├── assets │ │ └── App Logo.png │ ├── auth.ts │ ├── auto-imports.d.ts │ ├── drizzle │ │ ├── index.ts │ │ └── schema.ts │ ├── entry-client.tsx │ ├── entry-server.tsx │ ├── env │ │ ├── client.ts │ │ └── server.ts │ ├── global.d.ts │ ├── lib │ │ ├── crypto.ts │ │ └── releases.ts │ ├── lucia.ts │ └── posthog │ │ └── server.ts │ ├── tailwind.config.js │ └── tsconfig.json ├── assets ├── App Icon.png └── Twitter PFP.png ├── biome.json ├── crates ├── deno_experiments │ ├── Cargo.toml │ ├── main.ts │ └── src │ │ └── main.rs ├── tauri-plugin-kb-mouse │ ├── Cargo.toml │ ├── guest-js │ │ ├── bindings.ts │ │ └── index.ts │ ├── package.json │ └── src │ │ └── lib.rs └── tauri-plugin-midi │ ├── .gitignore │ ├── Cargo.toml │ ├── README.md │ ├── guest-js │ ├── bindings.ts │ └── index.ts │ ├── package.json │ ├── src │ └── lib.rs │ └── tsconfig.json ├── interface ├── package.json ├── src │ ├── ActionHistory.tsx │ ├── ConfigDialog.tsx │ ├── ConnectionsDialog.tsx │ ├── Sidebar │ │ ├── CommentBox │ │ │ ├── Info.tsx │ │ │ └── index.tsx │ │ ├── Graph │ │ │ ├── Outline.tsx │ │ │ ├── Variables.tsx │ │ │ └── index.tsx │ │ ├── InlineTextEditor.tsx │ │ ├── Node │ │ │ ├── Info.tsx │ │ │ ├── Properties.tsx │ │ │ └── index.tsx │ │ ├── Project │ │ │ ├── CustomTypes.tsx │ │ │ ├── Graphs.tsx │ │ │ ├── PrintOutput.tsx │ │ │ ├── Resources.tsx │ │ │ ├── Variables.tsx │ │ │ └── index.tsx │ │ ├── SearchInput.tsx │ │ ├── Variables.tsx │ │ └── index.tsx │ ├── actions.ts │ ├── auto-imports.d.ts │ ├── components │ │ ├── CommandDialog │ │ │ └── index.tsx │ │ ├── Graph │ │ │ ├── CommentBox.tsx │ │ │ ├── Connection │ │ │ │ ├── ConnectionRenderer.tsx │ │ │ │ └── index.ts │ │ │ ├── Context.tsx │ │ │ ├── ContextMenu.tsx │ │ │ ├── Graph.tsx │ │ │ ├── IO │ │ │ │ ├── DataInput.tsx │ │ │ │ ├── DataOutput.tsx │ │ │ │ ├── DataPin.tsx │ │ │ │ ├── ExecInput.tsx │ │ │ │ ├── ExecOutput.tsx │ │ │ │ ├── ScopeInput.tsx │ │ │ │ ├── ScopeOutput.tsx │ │ │ │ ├── index.ts │ │ │ │ └── usePin.ts │ │ │ ├── Node.css │ │ │ ├── Node.tsx │ │ │ ├── index.ts │ │ │ └── util.tsx │ │ ├── SchemaMenu │ │ │ ├── index.tsx │ │ │ └── state.ts │ │ ├── Sidebar.tsx │ │ ├── TypeEditor.tsx │ │ └── ui │ │ │ ├── CheckBox.tsx │ │ │ ├── EnumInput.tsx │ │ │ ├── FloatInput.tsx │ │ │ ├── Input.tsx │ │ │ ├── IntInput.tsx │ │ │ ├── TextInput.tsx │ │ │ └── index.tsx │ ├── context.ts │ ├── global.css │ ├── index.tsx │ ├── platform.ts │ ├── settings │ │ ├── index.tsx │ │ └── ui.tsx │ └── util.tsx ├── tailwind.config.js └── tsconfig.json ├── package.json ├── packages ├── action-history │ ├── package.json │ ├── src │ │ └── index.ts │ └── tsconfig.json ├── api-contract │ ├── package.json │ ├── src │ │ └── index.ts │ └── tsconfig.json ├── clipboard │ ├── package.json │ ├── src │ │ └── index.ts │ └── tsconfig.json ├── config │ ├── package.json │ └── tsconfig.json ├── effect-server-fn │ ├── package.json │ ├── src │ │ ├── ServerFn.ts │ │ └── index.ts │ └── tsconfig.json ├── http-client │ ├── package.json │ ├── src │ │ └── index.ts │ └── tsconfig.json ├── json │ ├── package.json │ ├── src │ │ ├── conversion.ts │ │ ├── index.test.ts │ │ ├── index.ts │ │ ├── pkg.ts │ │ └── type.ts │ └── tsconfig.json ├── option │ ├── package.json │ ├── src │ │ └── index.ts │ └── tsconfig.json ├── packages │ ├── package.json │ ├── src │ │ ├── audio.ts │ │ ├── customEvents.ts │ │ ├── discord │ │ │ ├── Settings.tsx │ │ │ ├── api.ts │ │ │ ├── auth.ts │ │ │ ├── gateway.ts │ │ │ ├── index.ts │ │ │ ├── resource.ts │ │ │ └── schemas.ts │ │ ├── elevenlabs │ │ │ ├── Settings.tsx │ │ │ ├── ctx.ts │ │ │ ├── index.ts │ │ │ └── sends.ts │ │ ├── fs.ts │ │ ├── github │ │ │ ├── Settings.tsx │ │ │ ├── auth.ts │ │ │ ├── ctx.ts │ │ │ └── index.ts │ │ ├── globalKeyboardMouse.ts │ │ ├── google │ │ │ ├── Settings.tsx │ │ │ ├── ctx.ts │ │ │ └── index.ts │ │ ├── goxlr │ │ │ ├── Settings.tsx │ │ │ ├── ctx.ts │ │ │ ├── events.ts │ │ │ ├── index.ts │ │ │ ├── sends.ts │ │ │ └── types.ts │ │ ├── http.ts │ │ ├── httpEndpoint.ts │ │ ├── index.tsx │ │ ├── keyboard.ts │ │ ├── list.ts │ │ ├── localStorage.ts │ │ ├── logic.ts │ │ ├── map.ts │ │ ├── midi │ │ │ ├── Settings.tsx │ │ │ ├── ctx.ts │ │ │ ├── events.ts │ │ │ ├── index.ts │ │ │ ├── requests.ts │ │ │ └── resource.ts │ │ ├── obs │ │ │ ├── Settings.tsx │ │ │ ├── ctx.ts │ │ │ ├── events.ts │ │ │ ├── index.ts │ │ │ ├── requests.ts │ │ │ ├── resource.ts │ │ │ ├── types.ts │ │ │ └── ws.ts │ │ ├── openai │ │ │ ├── Settings.tsx │ │ │ ├── ctx.ts │ │ │ ├── index.ts │ │ │ └── sends.ts │ │ ├── patreon │ │ │ ├── Settings.tsx │ │ │ ├── ctx.ts │ │ │ └── index.ts │ │ ├── shell.ts │ │ ├── speakerbot │ │ │ ├── Settings.tsx │ │ │ ├── ctx.ts │ │ │ ├── index.ts │ │ │ └── sends.ts │ │ ├── spotify │ │ │ ├── Settings.tsx │ │ │ ├── ctx.ts │ │ │ └── index.ts │ │ ├── streamdeck │ │ │ ├── Settings.tsx │ │ │ ├── ctx.ts │ │ │ └── index.ts │ │ ├── streamlabs │ │ │ ├── Settings.tsx │ │ │ ├── ctx.ts │ │ │ ├── events.ts │ │ │ └── index.ts │ │ ├── twitch │ │ │ ├── Settings.tsx │ │ │ ├── auth.ts │ │ │ ├── chat.ts │ │ │ ├── ctx.ts │ │ │ ├── eventSub │ │ │ │ ├── index.ts │ │ │ │ └── types.ts │ │ │ ├── helix.ts │ │ │ ├── index.ts │ │ │ ├── resource.ts │ │ │ └── types.ts │ │ ├── utils.test.ts │ │ ├── utils.ts │ │ ├── variables.ts │ │ ├── voicemod │ │ │ ├── ctx.ts │ │ │ ├── index.ts │ │ │ └── settings.tsx │ │ ├── vtubeStudio │ │ │ ├── Settings.tsx │ │ │ ├── ctx.ts │ │ │ ├── index.tsx │ │ │ ├── requests.ts │ │ │ ├── resource.ts │ │ │ └── types.ts │ │ ├── websocketClient │ │ │ ├── Settings.tsx │ │ │ ├── ctx.ts │ │ │ └── index.ts │ │ └── websocketServer │ │ │ ├── Settings.tsx │ │ │ ├── ctx.ts │ │ │ └── index.ts │ └── tsconfig.json ├── runtime-rendering │ ├── package.json │ ├── src │ │ ├── index.test.ts │ │ └── index.ts │ └── tsconfig.json ├── runtime-serde │ ├── package.json │ ├── src │ │ ├── deserialize.ts │ │ ├── index.ts │ │ ├── serde.ts │ │ └── serialize.ts │ └── tsconfig.json ├── runtime │ ├── package.json │ ├── src │ │ ├── index.ts │ │ ├── models │ │ │ ├── CommentBox.ts │ │ │ ├── Core.ts │ │ │ ├── CustomEnum.ts │ │ │ ├── CustomEvent.ts │ │ │ ├── CustomStruct.ts │ │ │ ├── Graph.ts │ │ │ ├── IO.ts │ │ │ ├── Node.ts │ │ │ ├── NodeSchema.ts │ │ │ ├── Package.ts │ │ │ ├── Project.ts │ │ │ ├── Variable.ts │ │ │ └── index.ts │ │ ├── reset.d.ts │ │ └── utils │ │ │ ├── index.ts │ │ │ └── pins.ts │ └── tsconfig.json ├── typesystem │ ├── package.json │ ├── src │ │ ├── base.ts │ │ ├── enum.ts │ │ ├── field.ts │ │ ├── index.ts │ │ ├── list.ts │ │ ├── map.ts │ │ ├── option.ts │ │ ├── primitive.ts │ │ ├── serialized.ts │ │ ├── struct.ts │ │ ├── t.ts │ │ ├── utils.ts │ │ ├── value.ts │ │ ├── wildcard.test.ts │ │ └── wildcard.ts │ └── tsconfig.json ├── ui │ ├── package.json │ ├── src │ │ ├── Card.tsx │ │ ├── auto-imports.d.ts │ │ ├── badge.tsx │ │ ├── button.tsx │ │ ├── dialog.tsx │ │ ├── dropdown-menu.tsx │ │ ├── global.css │ │ ├── index.ts │ │ ├── input.tsx │ │ ├── lib │ │ │ └── utils.ts │ │ └── popover.tsx │ ├── tailwind.preset.js │ ├── tsconfig.json │ ├── ui.config.json │ └── vite.js ├── utils │ ├── package.json │ ├── src │ │ └── index.ts │ └── tsconfig.json └── web-api │ ├── package.json │ ├── src │ └── index.ts │ └── tsconfig.json ├── patches └── @kobalte__core@0.13.7.patch ├── pnpm-lock.yaml ├── pnpm-workspace.yaml ├── tsconfig.json ├── turbo.json └── vitest.config.ts /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: Brendonovich 2 | -------------------------------------------------------------------------------- /.github/actions/install-desktop-deps/action.yml: -------------------------------------------------------------------------------- 1 | name: Install desktop-specific dependencies 2 | description: Installs desktop-specific dependencies for the current platform 3 | runs: 4 | using: "composite" 5 | steps: 6 | - name: install dependencies (ubuntu only) 7 | if: ${{ runner.os == 'Linux' }} 8 | shell: bash 9 | run: | 10 | sudo apt-get update 11 | sudo apt-get install -y libgtk-3-dev libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf libasound2-dev libxtst-dev libevdev-dev libxdo-dev 12 | -------------------------------------------------------------------------------- /.github/actions/setup-js/action.yml: -------------------------------------------------------------------------------- 1 | name: Setup JS (pnpm & node) 2 | description: Sets up pnpm & node with dependencies and caching 3 | runs: 4 | using: "composite" 5 | steps: 6 | - name: Install pnpm 7 | uses: pnpm/action-setup@v2 8 | with: 9 | version: 9 10 | run_install: false 11 | 12 | - name: Setup node 13 | uses: actions/setup-node@v3 14 | with: 15 | node-version: 18 16 | cache: pnpm 17 | 18 | - name: Install frontend dependencies 19 | shell: bash 20 | run: pnpm install 21 | -------------------------------------------------------------------------------- /.github/actions/setup-rust/action.yml: -------------------------------------------------------------------------------- 1 | name: Setup Rust 2 | description: Sets up rust and caching 3 | inputs: 4 | save-cache: 5 | description: "Whether to save the cache" 6 | required: false 7 | default: "false" 8 | target: 9 | description: "Toolchain target triple" 10 | required: true 11 | runs: 12 | using: "composite" 13 | steps: 14 | - name: Install Rust stable 15 | uses: dtolnay/rust-toolchain@stable 16 | with: 17 | toolchain: 1.80.0 18 | target: ${{ inputs.target }} 19 | components: clippy 20 | 21 | - name: Cache Rust Dependencies 22 | uses: Swatinem/rust-cache@v2 23 | with: 24 | save-if: ${{ inputs.save-cache }} 25 | shared-key: ${{ inputs.target }} 26 | 27 | - name: Create desktop distDir 28 | shell: bash 29 | run: mkdir -p apps/desktop/.output/public 30 | -------------------------------------------------------------------------------- /.github/assets/og-image.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Brendonovich/MacroGraph/abfa1cb37f8165d8bd82e672810d0937f29e6501/.github/assets/og-image.jpg -------------------------------------------------------------------------------- /.github/assets/readme.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Brendonovich/MacroGraph/abfa1cb37f8165d8bd82e672810d0937f29e6501/.github/assets/readme.png -------------------------------------------------------------------------------- /.github/workflows/cache-factory.yaml: -------------------------------------------------------------------------------- 1 | # This workflow _produces_ caches which are used to speed up pull request builds. 2 | # Originally from https://github.com/libp2p/rust-libp2p/blob/master/.github/workflows/cache-factory.yml 3 | 4 | name: Cache Factory 5 | 6 | on: 7 | push: 8 | branches: 9 | - main 10 | 11 | concurrency: 12 | group: ${{ github.workflow }}-${{ github.ref }} 13 | cancel-in-progress: true 14 | 15 | jobs: 16 | make_cache: 17 | name: "Make Cache" 18 | runs-on: ${{ matrix.settings.host }} 19 | strategy: 20 | fail-fast: true 21 | matrix: 22 | settings: 23 | - host: macos-latest 24 | target: x86_64-apple-darwin 25 | - host: macos-latest 26 | target: aarch64-apple-darwin 27 | - host: windows-latest 28 | target: x86_64-pc-windows-msvc 29 | - host: ubuntu-latest 30 | target: x86_64-unknown-linux-gnu 31 | steps: 32 | - uses: actions/checkout@v3 33 | 34 | - uses: ./.github/actions/setup-rust 35 | with: 36 | save-cache: "true" 37 | target: ${{ matrix.settings.target }} 38 | 39 | - uses: ./.github/actions/install-desktop-deps 40 | 41 | - run: mkdir apps/desktop/dist 42 | 43 | - name: Clippy (debug) 44 | run: cargo clippy --all --all-features --target ${{ matrix.settings.target }} 45 | 46 | - name: Compile (debug) 47 | run: cargo test --all --all-features --no-run --target ${{ matrix.settings.target }} 48 | 49 | - name: Compile (release) 50 | run: cargo test --all --all-features --no-run --target ${{ matrix.settings.target }} --release 51 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | .solid/ 3 | .output/ 4 | .vercel/ 5 | .netlify/ 6 | .vinxi/ 7 | .nitro/ 8 | netlify/ 9 | .turbo/ 10 | target 11 | 12 | # Environment 13 | .env 14 | .env*.local 15 | 16 | # dependencies 17 | node_modules/ 18 | 19 | # IDEs and editors 20 | /.idea 21 | .project 22 | .classpath 23 | *.launch 24 | .settings/ 25 | 26 | # Temp 27 | gitignore 28 | 29 | # System Files 30 | .DS_Store 31 | Thumbs.db 32 | 33 | tsconfig.tsbuildinfo 34 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | members = ["apps/desktop/src-tauri", "crates/*"] 4 | 5 | [patch.crates-io] 6 | rspc = { git = "https://github.com/oscartbeaumont/rspc", rev = "799eec5df7533edf331f41d3f1be03de07e038d7" } 7 | -------------------------------------------------------------------------------- /apps/desktop/.env.example: -------------------------------------------------------------------------------- 1 | VITE_MACROGRAPH_API_URL=https://macrograph.brendonovich.dev 2 | -------------------------------------------------------------------------------- /apps/desktop/.gitignore: -------------------------------------------------------------------------------- 1 | !.output/public/.gitkeep 2 | -------------------------------------------------------------------------------- /apps/desktop/.prettierrc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Brendonovich/MacroGraph/abfa1cb37f8165d8bd82e672810d0937f29e6501/apps/desktop/.prettierrc -------------------------------------------------------------------------------- /apps/desktop/app.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "@solidjs/start/config"; 2 | 3 | import interfacePlugin from "../../packages/ui/vite"; 4 | 5 | export default defineConfig({ 6 | ssr: false, 7 | routeDir: "app", 8 | server: { 9 | preset: "static", 10 | }, 11 | // https://vitejs.dev/config/ 12 | vite: { 13 | plugins: [interfacePlugin], 14 | // Vite options tailored for Tauri development and only applied in `tauri dev` or `tauri build` 15 | // prevent vite from obscuring rust errors 16 | // to make use of `TAURI_DEBUG` and other env variables 17 | // https://tauri.studio/v1/api/config#buildconfig.beforedevcommand 18 | envPrefix: ["VITE_", "TAURI_"], 19 | build: { 20 | // Tauri supports es2021 21 | target: 22 | process.env.TAURI_PLATFORM === "windows" ? "chrome105" : "safari13", 23 | // don't minify for debug builds 24 | // minify: !process.env.TAURI_DEBUG ? "esbuild" : false, 25 | // produce sourcemaps for debug builds 26 | sourcemap: !!process.env.TAURI_DEBUG, 27 | minify: false, 28 | }, 29 | }, 30 | }); 31 | -------------------------------------------------------------------------------- /apps/desktop/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@macrograph/desktop", 3 | "type": "module", 4 | "scripts": { 5 | "dev": "vinxi dev", 6 | "build": "vinxi build", 7 | "start": "vinxi start", 8 | "tauri": "tauri", 9 | "typecheck": "tsc -b" 10 | }, 11 | "dependencies": { 12 | "@iconify/json": "^2.2.196", 13 | "@kobalte/core": "^0.13.7", 14 | "@macrograph/api-contract": "workspace:*", 15 | "@macrograph/interface": "workspace:*", 16 | "@macrograph/packages": "workspace:*", 17 | "@macrograph/runtime": "workspace:*", 18 | "@macrograph/runtime-serde": "workspace:*", 19 | "@macrograph/ui": "workspace:*", 20 | "@rspc/client": "0.0.0-main-6ed8cc98", 21 | "@rspc/solid": "0.0.0-main-6ed8cc98", 22 | "@rspc/tauri": "0.0.0-main-6ed8cc98", 23 | "@solid-primitives/storage": "^2.1.4", 24 | "@solidjs/router": "^0.14.3", 25 | "@solidjs/start": "1.1.3", 26 | "@t3-oss/env-core": "^0.6.1", 27 | "@tanstack/solid-query": "^4.36.1", 28 | "@tauri-apps/api": "^1.5.3", 29 | "@ts-rest/core": "^3.52.1", 30 | "@ts-rest/solid-query": "^3.41.1", 31 | "solid-js": "^1.9.7", 32 | "solid-sonner": "^0.2.5", 33 | "tauri-plugin-midi": "workspace:*", 34 | "valibot": "^0.37.0", 35 | "vinxi": "0.5.6", 36 | "zod": "^3.25.36" 37 | }, 38 | "devDependencies": { 39 | "@kobalte/tailwindcss": "^0.6.5", 40 | "@macrograph/config": "workspace:*", 41 | "@tailwindcss/forms": "^0.5.7", 42 | "@tauri-apps/cli": "^1.6.3", 43 | "autoprefixer": "^10.4.19", 44 | "postcss": "^8.4.38", 45 | "tailwindcss": "^3.4.3", 46 | "unplugin-auto-import": "^0.17.5", 47 | "unplugin-icons": "^0.18.5", 48 | "vite": "^6.3.5" 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /apps/desktop/postcss.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | }; 7 | -------------------------------------------------------------------------------- /apps/desktop/public/vite.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /apps/desktop/src-tauri/.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | -------------------------------------------------------------------------------- /apps/desktop/src-tauri/assets/dmg-background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Brendonovich/MacroGraph/abfa1cb37f8165d8bd82e672810d0937f29e6501/apps/desktop/src-tauri/assets/dmg-background.png -------------------------------------------------------------------------------- /apps/desktop/src-tauri/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | tauri_build::build() 3 | } 4 | -------------------------------------------------------------------------------- /apps/desktop/src-tauri/icons/128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Brendonovich/MacroGraph/abfa1cb37f8165d8bd82e672810d0937f29e6501/apps/desktop/src-tauri/icons/128x128.png -------------------------------------------------------------------------------- /apps/desktop/src-tauri/icons/128x128@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Brendonovich/MacroGraph/abfa1cb37f8165d8bd82e672810d0937f29e6501/apps/desktop/src-tauri/icons/128x128@2x.png -------------------------------------------------------------------------------- /apps/desktop/src-tauri/icons/32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Brendonovich/MacroGraph/abfa1cb37f8165d8bd82e672810d0937f29e6501/apps/desktop/src-tauri/icons/32x32.png -------------------------------------------------------------------------------- /apps/desktop/src-tauri/icons/Square107x107Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Brendonovich/MacroGraph/abfa1cb37f8165d8bd82e672810d0937f29e6501/apps/desktop/src-tauri/icons/Square107x107Logo.png -------------------------------------------------------------------------------- /apps/desktop/src-tauri/icons/Square142x142Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Brendonovich/MacroGraph/abfa1cb37f8165d8bd82e672810d0937f29e6501/apps/desktop/src-tauri/icons/Square142x142Logo.png -------------------------------------------------------------------------------- /apps/desktop/src-tauri/icons/Square150x150Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Brendonovich/MacroGraph/abfa1cb37f8165d8bd82e672810d0937f29e6501/apps/desktop/src-tauri/icons/Square150x150Logo.png -------------------------------------------------------------------------------- /apps/desktop/src-tauri/icons/Square284x284Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Brendonovich/MacroGraph/abfa1cb37f8165d8bd82e672810d0937f29e6501/apps/desktop/src-tauri/icons/Square284x284Logo.png -------------------------------------------------------------------------------- /apps/desktop/src-tauri/icons/Square30x30Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Brendonovich/MacroGraph/abfa1cb37f8165d8bd82e672810d0937f29e6501/apps/desktop/src-tauri/icons/Square30x30Logo.png -------------------------------------------------------------------------------- /apps/desktop/src-tauri/icons/Square310x310Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Brendonovich/MacroGraph/abfa1cb37f8165d8bd82e672810d0937f29e6501/apps/desktop/src-tauri/icons/Square310x310Logo.png -------------------------------------------------------------------------------- /apps/desktop/src-tauri/icons/Square44x44Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Brendonovich/MacroGraph/abfa1cb37f8165d8bd82e672810d0937f29e6501/apps/desktop/src-tauri/icons/Square44x44Logo.png -------------------------------------------------------------------------------- /apps/desktop/src-tauri/icons/Square71x71Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Brendonovich/MacroGraph/abfa1cb37f8165d8bd82e672810d0937f29e6501/apps/desktop/src-tauri/icons/Square71x71Logo.png -------------------------------------------------------------------------------- /apps/desktop/src-tauri/icons/Square89x89Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Brendonovich/MacroGraph/abfa1cb37f8165d8bd82e672810d0937f29e6501/apps/desktop/src-tauri/icons/Square89x89Logo.png -------------------------------------------------------------------------------- /apps/desktop/src-tauri/icons/StoreLogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Brendonovich/MacroGraph/abfa1cb37f8165d8bd82e672810d0937f29e6501/apps/desktop/src-tauri/icons/StoreLogo.png -------------------------------------------------------------------------------- /apps/desktop/src-tauri/icons/icon.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Brendonovich/MacroGraph/abfa1cb37f8165d8bd82e672810d0937f29e6501/apps/desktop/src-tauri/icons/icon.icns -------------------------------------------------------------------------------- /apps/desktop/src-tauri/icons/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Brendonovich/MacroGraph/abfa1cb37f8165d8bd82e672810d0937f29e6501/apps/desktop/src-tauri/icons/icon.ico -------------------------------------------------------------------------------- /apps/desktop/src-tauri/icons/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Brendonovich/MacroGraph/abfa1cb37f8165d8bd82e672810d0937f29e6501/apps/desktop/src-tauri/icons/icon.png -------------------------------------------------------------------------------- /apps/desktop/src-tauri/src/fs.rs: -------------------------------------------------------------------------------- 1 | use rspc::alpha::AlphaRouter; 2 | use serde::Serialize; 3 | use specta::Type; 4 | 5 | use crate::R; 6 | 7 | pub fn router() -> AlphaRouter { 8 | #[derive(Type, Serialize)] 9 | enum Entry { 10 | Dir(String), 11 | File(String), 12 | } 13 | 14 | R.router().procedure( 15 | "list", 16 | R.query(|_, path: String| async move { 17 | std::fs::read_dir(path) 18 | .unwrap() 19 | .map(|e| { 20 | let e = e.unwrap(); 21 | let path = e.path(); 22 | let path = path.file_name().unwrap().to_str().unwrap().to_string(); 23 | if e.file_type().unwrap().is_dir() { 24 | Entry::Dir(path) 25 | } else { 26 | Entry::File(path) 27 | } 28 | }) 29 | .collect::>() 30 | }), 31 | ) 32 | } 33 | -------------------------------------------------------------------------------- /apps/desktop/src-tauri/src/http/config.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /apps/desktop/src-tauri/src/http/error.rs: -------------------------------------------------------------------------------- 1 | use serde::{Serialize, Serializer}; 2 | 3 | use super::RequestId; 4 | 5 | #[derive(Debug, thiserror::Error)] 6 | pub enum Error { 7 | #[error(transparent)] 8 | Json(#[from] serde_json::Error), 9 | #[error(transparent)] 10 | Io(#[from] std::io::Error), 11 | #[error(transparent)] 12 | Network(#[from] reqwest::Error), 13 | #[error(transparent)] 14 | Http(#[from] http::Error), 15 | #[error(transparent)] 16 | HttpInvalidHeaderName(#[from] http::header::InvalidHeaderName), 17 | #[error(transparent)] 18 | HttpInvalidHeaderValue(#[from] http::header::InvalidHeaderValue), 19 | /// URL not allowed by the scope. 20 | #[error(transparent)] 21 | UrlParse(#[from] url::ParseError), 22 | /// HTTP method error. 23 | #[error(transparent)] 24 | HttpMethod(#[from] http::method::InvalidMethod), 25 | #[error("scheme {0} not supported")] 26 | SchemeNotSupport(String), 27 | #[error("Request canceled")] 28 | RequestCanceled, 29 | #[error("failed to process data url")] 30 | DataUrl, 31 | #[error("failed to decode data url into bytes")] 32 | DataUrlDecode, 33 | #[error("invalid request id: {0}")] 34 | InvalidRequestId(RequestId), 35 | #[error(transparent)] 36 | Utf8(#[from] std::string::FromUtf8Error), 37 | } 38 | 39 | impl Serialize for Error { 40 | fn serialize(&self, serializer: S) -> std::result::Result 41 | where 42 | S: Serializer, 43 | { 44 | serializer.serialize_str(self.to_string().as_ref()) 45 | } 46 | } 47 | 48 | pub type Result = std::result::Result; 49 | -------------------------------------------------------------------------------- /apps/desktop/src-tauri/src/http/mod.rs: -------------------------------------------------------------------------------- 1 | use std::sync::atomic::AtomicU32; 2 | use std::{collections::HashMap, future::Future, pin::Pin}; 3 | 4 | pub use reqwest; 5 | use reqwest::Response; 6 | use tauri::async_runtime::Mutex; 7 | use tauri::{AppHandle, Manager, Runtime}; 8 | 9 | pub use error::{Error, Result}; 10 | 11 | mod commands; 12 | mod config; 13 | mod error; 14 | 15 | pub use commands::*; 16 | 17 | type RequestId = u32; 18 | type CancelableResponseResult = Result>; 19 | type CancelableResponseFuture = 20 | Pin + Send + Sync>>; 21 | type RequestTable = HashMap; 22 | type ResponseTable = HashMap; 23 | 24 | pub struct FetchRequest(Mutex); 25 | impl FetchRequest { 26 | fn new(f: CancelableResponseFuture) -> Self { 27 | Self(Mutex::new(f)) 28 | } 29 | } 30 | 31 | pub struct State { 32 | #[allow(dead_code)] 33 | pub app: AppHandle, 34 | pub current_id: AtomicU32, 35 | pub requests: Mutex, 36 | pub responses: Mutex, 37 | } 38 | 39 | impl State { 40 | pub fn new(app: AppHandle) -> Self { 41 | Self { 42 | app, 43 | current_id: 0.into(), 44 | requests: Default::default(), 45 | responses: Default::default(), 46 | } 47 | } 48 | 49 | fn next_id(&self) -> RequestId { 50 | self.current_id 51 | .fetch_add(1, std::sync::atomic::Ordering::Relaxed) 52 | } 53 | } 54 | 55 | trait HttpExt { 56 | fn http(&self) -> &State; 57 | } 58 | 59 | impl> HttpExt for T { 60 | fn http(&self) -> &State { 61 | self.state::>().inner() 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /apps/desktop/src-tauri/src/shell.rs: -------------------------------------------------------------------------------- 1 | use std::process::{Command, Stdio}; 2 | 3 | use rspc::alpha::AlphaRouter; 4 | use serde::Serialize; 5 | use specta::Type; 6 | 7 | use crate::R; 8 | 9 | pub fn router() -> AlphaRouter { 10 | R.router().procedure( 11 | "execute", 12 | R.mutation(|_, cmd: String| async move { 13 | tokio::task::spawn_blocking(move || { 14 | let mut segments = cmd.split(" "); 15 | 16 | let command = segments.next().unwrap(); 17 | let args: Vec<_> = segments.collect(); 18 | 19 | Command::new(command) 20 | .args(args) 21 | .stdout(Stdio::inherit()) 22 | .stderr(Stdio::inherit()) 23 | .output() 24 | .unwrap(); 25 | }) 26 | .await 27 | .unwrap(); 28 | }), 29 | ) 30 | } 31 | -------------------------------------------------------------------------------- /apps/desktop/src/api.ts: -------------------------------------------------------------------------------- 1 | import { contract } from "@macrograph/api-contract"; 2 | import { makePersisted } from "@solid-primitives/storage"; 3 | import { initClient } from "@ts-rest/core"; 4 | import { initQueryClient } from "@ts-rest/solid-query"; 5 | import { createSignal } from "solid-js"; 6 | 7 | import { action } from "@solidjs/router"; 8 | import { env } from "./env"; 9 | import { fetch } from "./http"; 10 | import { queryClient } from "./rspc"; 11 | 12 | export const [sessionToken, setSessionToken] = makePersisted( 13 | createSignal(null), 14 | { name: "mg-auth-token" }, 15 | ); 16 | 17 | export const rawApi = initClient(contract, { 18 | api: (args) => 19 | fetch(args.path, args).then(async (r) => ({ 20 | status: r.status, 21 | body: await r.json(), 22 | headers: r.headers, 23 | })), 24 | baseUrl: `${env.VITE_MACROGRAPH_API_URL}/api`, 25 | get baseHeaders(): Record { 26 | const token = sessionToken(); 27 | return token ? { Authorization: `Bearer ${token}` } : {}; 28 | }, 29 | }); 30 | 31 | export const api = initQueryClient(contract, { 32 | api: (args) => 33 | fetch(args.path, args).then(async (r) => { 34 | const body = await r.json(); 35 | return { 36 | status: r.status, 37 | body, 38 | headers: r.headers, 39 | }; 40 | }), 41 | baseUrl: `${env.VITE_MACROGRAPH_API_URL}/api`, 42 | get baseHeaders(): Record { 43 | const token = sessionToken(); 44 | return token ? { Authorization: `Bearer ${token}` } : {}; 45 | }, 46 | }); 47 | 48 | export const logOutAction = action(async () => { 49 | setSessionToken(null); 50 | queryClient.clear(); 51 | }); 52 | -------------------------------------------------------------------------------- /apps/desktop/src/app.css: -------------------------------------------------------------------------------- 1 | html { 2 | overflow: hidden; 3 | height: 100%; 4 | overscroll-behavior: none; 5 | } 6 | 7 | body { 8 | height: 100%; 9 | overflow: auto; 10 | } 11 | 12 | /* Works on Firefox */ 13 | * { 14 | scrollbar-width: thin; 15 | scrollbar-color: rgb(229, 231, 235) rgb(38, 38, 38); 16 | } 17 | 18 | /* Works on Chrome, Edge, and Safari */ 19 | *::-webkit-scrollbar { 20 | width: 12px; 21 | } 22 | 23 | *::-webkit-scrollbar-track { 24 | background: rgb(38, 38, 38); 25 | } 26 | 27 | *::-webkit-scrollbar-thumb { 28 | background-color: rgb(229, 231, 235); 29 | border-radius: 20px; 30 | border: 2px solid rgb(38, 38, 38); 31 | } 32 | -------------------------------------------------------------------------------- /apps/desktop/src/app.tsx: -------------------------------------------------------------------------------- 1 | import "@macrograph/ui/global.css"; 2 | import { Router } from "@solidjs/router"; 3 | import { FileRoutes } from "@solidjs/start/router"; 4 | import { Suspense } from "solid-js"; 5 | import { Toaster } from "solid-sonner"; 6 | 7 | import { client, queryClient, rspc } from "./rspc"; 8 | 9 | import "./app.css"; 10 | 11 | export default function App() { 12 | return ( 13 | ( 15 | 16 | {props.children} 17 | 18 | 19 | )} 20 | > 21 | 22 | 23 | ); 24 | } 25 | -------------------------------------------------------------------------------- /apps/desktop/src/commands.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | // This file was generated by [tauri-specta](https://github.com/oscartbeaumont/tauri-specta). Do not edit this file manually. 3 | 4 | declare global { 5 | interface Window { 6 | __TAURI_INVOKE__(cmd: string, args?: Record): Promise; 7 | } 8 | } 9 | 10 | // Function avoids 'window not defined' in SSR 11 | const invoke = () => window.__TAURI_INVOKE__; 12 | 13 | export function fetch(method: string, url: string, headers: ([string, string])[], data: number[] | null, connectTimeout: number | null, maxRedirections: number | null) { 14 | return invoke()("fetch", { method,url,headers,data,connectTimeout,maxRedirections }) 15 | } 16 | 17 | export function fetchCancel(rid: number) { 18 | return invoke()("fetch_cancel", { rid }) 19 | } 20 | 21 | export function fetchSend(rid: number) { 22 | return invoke()("fetch_send", { rid }) 23 | } 24 | 25 | export function fetchReadBody(rid: number) { 26 | return invoke()("fetch_read_body", { rid }) 27 | } 28 | 29 | export type FetchResponse = { status: number; statusText: string; headers: ([string, string])[]; url: string } 30 | -------------------------------------------------------------------------------- /apps/desktop/src/core.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Core, 3 | type RefreshedOAuthToken, 4 | createWsProvider, 5 | } from "@macrograph/runtime"; 6 | import "tauri-plugin-midi"; 7 | 8 | import { rawApi } from "./api"; 9 | import { env } from "./env"; 10 | import { fetch } from "./http"; 11 | import { client } from "./rspc"; 12 | 13 | const AUTH_URL = `${env.VITE_MACROGRAPH_API_URL}/auth`; 14 | 15 | export const core = new Core({ 16 | fetch: fetch as any, 17 | api: rawApi, 18 | oauth: { 19 | authorize: (provider) => 20 | new Promise((res) => { 21 | client.addSubscription( 22 | ["oauth.authorize", `${AUTH_URL}/${provider}/login`], 23 | { 24 | onData(data) { 25 | res({ ...data, issued_at: Date.now() / 1000 }); 26 | }, 27 | }, 28 | ); 29 | }), 30 | refresh: async (provider, refreshToken) => { 31 | const res = await fetch(`${AUTH_URL}/${provider}/refresh`, { 32 | method: "POST", 33 | headers: { "content-type": "application/json" }, 34 | body: JSON.stringify({ refreshToken }), 35 | }); 36 | 37 | return { 38 | ...((await res.json()) as RefreshedOAuthToken), 39 | issued_at: Date.now() / 1000, 40 | }; 41 | }, 42 | }, 43 | }); 44 | 45 | export const wsProvider = createWsProvider({ 46 | async startServer(port, onData) { 47 | return client.addSubscription(["websocket.server", port], { 48 | onData: (d) => onData(d), 49 | }); 50 | }, 51 | async stopServer(unsubscribe) { 52 | unsubscribe(); 53 | }, 54 | async sendMessage(data) { 55 | return client.mutation([ 56 | "websocket.send", 57 | { port: data.port, client: data.client, data: data.data }, 58 | ]); 59 | }, 60 | }); 61 | -------------------------------------------------------------------------------- /apps/desktop/src/entry-client.tsx: -------------------------------------------------------------------------------- 1 | import { StartClient, mount } from "@solidjs/start/client"; 2 | 3 | mount(() => , document.getElementById("app")!); 4 | -------------------------------------------------------------------------------- /apps/desktop/src/entry-server.tsx: -------------------------------------------------------------------------------- 1 | import { StartServer, createHandler } from "@solidjs/start/server"; 2 | 3 | export default createHandler(() => ( 4 | ( 6 | 7 | 8 | 9 | 10 | 11 | MacroGraph 12 | {assets} 13 | 14 | 15 | 16 | 17 | 18 | 22 | {children} 23 | 24 | 25 | 26 | {scripts} 27 | 28 | 29 | )} 30 | /> 31 | )); 32 | -------------------------------------------------------------------------------- /apps/desktop/src/env.ts: -------------------------------------------------------------------------------- 1 | import { createEnv } from "@t3-oss/env-core"; 2 | import { z } from "zod"; 3 | 4 | export const env = createEnv({ 5 | clientPrefix: "VITE_", 6 | client: { 7 | VITE_MACROGRAPH_API_URL: z 8 | .string() 9 | .default("https://macrograph.brendonovich.dev"), 10 | }, 11 | runtimeEnv: import.meta.env, 12 | }); 13 | -------------------------------------------------------------------------------- /apps/desktop/src/http.ts: -------------------------------------------------------------------------------- 1 | import * as commands from "./commands"; 2 | 3 | export interface ClientOptions { 4 | /** 5 | * Defines the maximum number of redirects the client should follow. 6 | * If set to 0, no redirects will be followed. 7 | */ 8 | maxRedirections?: number; 9 | /** Timeout in milliseconds */ 10 | connectTimeout?: number; 11 | } 12 | 13 | export async function fetch( 14 | input: URL | Request | string, 15 | init?: RequestInit & ClientOptions, 16 | ): Promise { 17 | const maxRedirections = init?.maxRedirections; 18 | const connectTimeout = init?.maxRedirections; 19 | 20 | // Remove these fields before creating the request 21 | if (init) { 22 | init.maxRedirections = undefined; 23 | init.connectTimeout = undefined; 24 | } 25 | 26 | const req = new Request(input, init); 27 | const buffer = await req.arrayBuffer(); 28 | const reqData = buffer.byteLength ? Array.from(new Uint8Array(buffer)) : null; 29 | 30 | const rid = await commands.fetch( 31 | req.method, 32 | req.url, 33 | Array.from(req.headers.entries()), 34 | reqData, 35 | maxRedirections ?? null, 36 | connectTimeout ?? null, 37 | ); 38 | 39 | req.signal.addEventListener("abort", () => { 40 | commands.fetchCancel(rid); 41 | }); 42 | 43 | const { status, statusText, url, headers } = await commands.fetchSend(rid); 44 | 45 | const body = await commands.fetchReadBody(rid); 46 | 47 | const res = 48 | body.length === 0 49 | ? new Response(new Uint8Array(body)) 50 | : new Response(new Uint8Array(body), { 51 | headers, 52 | status, 53 | statusText, 54 | }); 55 | 56 | // url is read only but seems like we can do this 57 | Object.defineProperty(res, "url", { value: url }); 58 | 59 | return res; 60 | } 61 | -------------------------------------------------------------------------------- /apps/desktop/src/rspc/index.ts: -------------------------------------------------------------------------------- 1 | import { createClient } from "@rspc/client"; 2 | import { createSolidQueryHooks } from "@rspc/solid"; 3 | import { TauriTransport } from "@rspc/tauri"; 4 | import { QueryClient } from "@tanstack/solid-query"; 5 | import type { Procedures } from "./types"; 6 | 7 | export const client = createClient({ 8 | transport: new TauriTransport(), 9 | }); 10 | 11 | export const rspc: ReturnType> = 12 | createSolidQueryHooks(); 13 | export const queryClient = new QueryClient(); 14 | -------------------------------------------------------------------------------- /apps/desktop/src/rspc/types.ts: -------------------------------------------------------------------------------- 1 | // This file was generated by [rspc](https://github.com/oscartbeaumont/rspc). Do not edit this file manually. 2 | 3 | export type Procedures = { 4 | queries: 5 | { key: "fs.list", input: string, result: Entry[] }, 6 | mutations: 7 | { key: "shell.execute", input: string, result: null } | 8 | { key: "websocket.send", input: { port: number; client: number | null; data: string }, result: null }, 9 | subscriptions: 10 | { key: "loginListen", input: never, result: string | null } | 11 | { key: "oauth.authorize", input: string, result: any | null } | 12 | { key: "websocket.server", input: number, result: [number, Message] } 13 | }; 14 | 15 | export type Message = { Text: string } | "Connected" | "Disconnected" 16 | 17 | export type Entry = { Dir: string } | { File: string } 18 | -------------------------------------------------------------------------------- /apps/desktop/tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = require("@macrograph/interface/tailwind.config.js"); 2 | -------------------------------------------------------------------------------- /apps/desktop/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "module": "ESNext", 5 | "moduleResolution": "node", 6 | "allowSyntheticDefaultImports": true, 7 | "esModuleInterop": true, 8 | "jsx": "preserve", 9 | "jsxImportSource": "solid-js", 10 | "allowJs": true, 11 | "strict": true, 12 | "noEmit": true, 13 | "types": ["vinxi/client", "vite/client", "unplugin-icons/types/solid"], 14 | "isolatedModules": true, 15 | "skipLibCheck": true, 16 | "paths": { 17 | "~/*": ["./src/*"] 18 | } 19 | }, 20 | "references": [ 21 | { "path": "../../interface" }, 22 | { "path": "../../packages/runtime" }, 23 | { "path": "../../packages/packages" } 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /apps/new-playground/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /apps/new-playground/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 11 | 12 | MacroGraph 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /apps/new-playground/public/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Brendonovich/MacroGraph/abfa1cb37f8165d8bd82e672810d0937f29e6501/apps/new-playground/public/favicon.png -------------------------------------------------------------------------------- /apps/new-playground/src/Runtime.ts: -------------------------------------------------------------------------------- 1 | import { Context, Effect, Schema } from "effect"; 2 | import { NoSuchElementException } from "effect/Cause"; 3 | 4 | import { NotComputationNode } from "./errors"; 5 | import { DataInputRef, DataOutputRef } from "./io"; 6 | import { Node } from "./domain/Node/data"; 7 | 8 | export class NodeRuntime extends Context.Tag("NodeRuntime")< 9 | NodeRuntime, 10 | { 11 | emitEvent: ( 12 | packageId: string, 13 | eventId: string, 14 | data?: any, 15 | ) => Effect.Effect; 16 | } 17 | >() {} 18 | 19 | export type SchemaRunGeneratorEffect = Effect.Effect< 20 | any, 21 | NoSuchElementException | NotComputationNode, 22 | RunFunctionAvailableRequirements 23 | >; 24 | 25 | export class Logger extends Context.Tag("Logger")< 26 | Logger, 27 | { print: (value: string) => Effect.Effect } 28 | >() {} 29 | 30 | export type RunFunctionAvailableRequirements = 31 | | Logger 32 | | ExecutionContext 33 | | NodeExecutionContext; 34 | 35 | export class ExecutionContext extends Context.Tag("ExecutionContext")< 36 | ExecutionContext, 37 | { 38 | traceId: string; 39 | getInput>( 40 | input: DataInputRef, 41 | ): Effect.Effect< 42 | T["Encoded"], 43 | NoSuchElementException | NotComputationNode, 44 | NodeExecutionContext | RunFunctionAvailableRequirements 45 | >; 46 | setOutput>( 47 | output: DataOutputRef, 48 | data: T, 49 | ): Effect.Effect; 50 | } 51 | >() {} 52 | 53 | export class NodeExecutionContext extends Context.Tag("NodeExecutionContext")< 54 | NodeExecutionContext, 55 | { node: Node } 56 | >() {} 57 | -------------------------------------------------------------------------------- /apps/new-playground/src/auto-imports.d.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* prettier-ignore */ 3 | // @ts-nocheck 4 | // noinspection JSUnusedGlobalSymbols 5 | // Generated by unplugin-auto-import 6 | // biome-ignore lint: disable 7 | export {} 8 | declare global { 9 | const IconLineMdChevronDown: typeof import('~icons/line-md/chevron-down.jsx')['default'] 10 | const IconLineMdChevronSmallDown: typeof import('~icons/line-md/chevron-small-down.jsx')['default'] 11 | const IconLucideChevronDown: typeof import('~icons/lucide/chevron-down.jsx')['default'] 12 | const IconMaterialSymbolsDeleteOutline: typeof import('~icons/material-symbols/delete-outline.jsx')['default'] 13 | } 14 | -------------------------------------------------------------------------------- /apps/new-playground/src/dev-server.ts: -------------------------------------------------------------------------------- 1 | import { NodeHttpServer, NodeRuntime } from "@effect/platform-node"; 2 | import { HttpServer } from "@effect/platform"; 3 | import { createServer } from "node:http"; 4 | import { Layer, Option, Fiber } from "effect"; 5 | import * as Effect from "effect/Effect"; 6 | 7 | import { DepsLive, ServerLive } from "./entry-server"; 8 | 9 | const HMRAwareNodeHttpServerLayer = NodeHttpServer.layer( 10 | () => { 11 | const server = createServer(); 12 | 13 | const fiber = Option.getOrThrow(Fiber.getCurrentFiber()); 14 | 15 | if (import.meta.hot) { 16 | import.meta.hot.accept(() => { 17 | Fiber.interrupt(fiber).pipe(Effect.runPromise); 18 | server.closeAllConnections(); 19 | server.close(); 20 | }); 21 | } 22 | 23 | return server; 24 | }, 25 | { port: 5678, host: "0.0.0.0" }, 26 | ); 27 | 28 | Effect.gen(function* () { 29 | const server = yield* ServerLive; 30 | 31 | return yield* Layer.launch( 32 | server.pipe(HttpServer.serve(), Layer.provide(HMRAwareNodeHttpServerLayer)), 33 | ); 34 | }).pipe(Effect.provide(DepsLive), Effect.scoped, NodeRuntime.runMain); 35 | -------------------------------------------------------------------------------- /apps/new-playground/src/domain/CloudApi/ApiClient.ts: -------------------------------------------------------------------------------- 1 | import { 2 | FetchHttpClient, 3 | HttpApiClient, 4 | HttpClient, 5 | HttpClientRequest, 6 | } from "@effect/platform"; 7 | import { Api } from "@macrograph/web-api"; 8 | import { Effect } from "effect"; 9 | 10 | const API_BEARER_TOKEN = "nafenstk26btmporqdg5eacuaibeozrbypktyueg"; 11 | const API_URL = "https://www.macrograph.app"; 12 | 13 | export class CloudAPIClient extends Effect.Service()( 14 | "CloudAPIClient", 15 | { 16 | effect: Effect.gen(function* () { 17 | const apiClient = yield* HttpApiClient.make(Api, { 18 | baseUrl: API_URL, 19 | transformClient: HttpClient.mapRequest( 20 | HttpClientRequest.bearerToken(API_BEARER_TOKEN), 21 | ), 22 | }); 23 | 24 | return apiClient; 25 | }), 26 | dependencies: [FetchHttpClient.layer], 27 | }, 28 | ) {} 29 | -------------------------------------------------------------------------------- /apps/new-playground/src/domain/CloudApi/AuthState.ts: -------------------------------------------------------------------------------- 1 | import { Effect, SubscriptionRef } from "effect"; 2 | import { CloudAPIClient } from "./ApiClient"; 3 | 4 | export class CloudApiAuthState extends Effect.Service()( 5 | "Auth", 6 | { 7 | effect: Effect.gen(function* () { 8 | const apiClient = yield* CloudAPIClient; 9 | const auth = yield* SubscriptionRef.make(null); 10 | 11 | const refetch = Effect.gen(function* () { 12 | const user = yield* apiClient.getUser(); 13 | console.log({ user }); 14 | yield* SubscriptionRef.set(auth, user); 15 | }); 16 | 17 | yield* refetch.pipe(Effect.fork); 18 | 19 | return { 20 | get: auth.get, 21 | refetch, 22 | changes: auth.changes, 23 | }; 24 | }), 25 | dependencies: [CloudAPIClient.Default], 26 | }, 27 | ) {} 28 | -------------------------------------------------------------------------------- /apps/new-playground/src/domain/CloudApi/CredentialsCache.ts: -------------------------------------------------------------------------------- 1 | import { Effect, Cache } from "effect"; 2 | import { CloudAPIClient } from "./ApiClient"; 3 | 4 | export class CredentialsCache extends Effect.Service()( 5 | "CredentialsCache", 6 | { 7 | effect: Effect.gen(function* () { 8 | const apiClient = yield* CloudAPIClient; 9 | 10 | const credentialsCache = yield* Cache.make({ 11 | capacity: 1, 12 | timeToLive: "1 minute", 13 | lookup: (_: void) => apiClient.getCredentials(), 14 | }); 15 | 16 | return credentialsCache; 17 | }), 18 | dependencies: [CloudAPIClient.Default], 19 | }, 20 | ) {} 21 | -------------------------------------------------------------------------------- /apps/new-playground/src/domain/Graph/data.ts: -------------------------------------------------------------------------------- 1 | import { Schema } from "effect"; 2 | 3 | import { Node, NodeId } from "../Node/data"; 4 | 5 | export const GraphId = Schema.Int.pipe(Schema.brand("Graph ID")); 6 | export type GraphId = (typeof GraphId)["Type"]; 7 | 8 | export const Graph = Schema.Struct({ 9 | id: GraphId, 10 | name: Schema.String, 11 | nodes: Schema.Array(Node), 12 | connections: Schema.Record({ 13 | key: Schema.String, 14 | value: Schema.Record({ 15 | key: Schema.String, 16 | value: Schema.Array(Schema.Tuple(NodeId, Schema.String)), 17 | }), 18 | }), 19 | }); 20 | export type Graph = (typeof Graph)["Type"]; 21 | -------------------------------------------------------------------------------- /apps/new-playground/src/domain/Graph/error.ts: -------------------------------------------------------------------------------- 1 | import { Schema } from "effect"; 2 | import { GraphId } from "./data"; 3 | 4 | export class GraphNotFoundError extends Schema.TaggedError( 5 | "GraphNotFoundError", 6 | )("GraphNotFoundError", { graphId: GraphId }) {} 7 | -------------------------------------------------------------------------------- /apps/new-playground/src/domain/Graph/rpc.ts: -------------------------------------------------------------------------------- 1 | import { Effect, Option } from "effect"; 2 | import { project } from "../../project"; 3 | import { GraphId } from "./data"; 4 | 5 | export class Graphs extends Effect.Service()("Graphs", { 6 | sync: () => { 7 | return { 8 | get: Effect.fn(function* (id: GraphId) { 9 | return Option.fromNullable(project.graphs.get(id)); 10 | }), 11 | }; 12 | }, 13 | }) {} 14 | -------------------------------------------------------------------------------- /apps/new-playground/src/domain/Node/data.ts: -------------------------------------------------------------------------------- 1 | import { Schema as S } from "effect"; 2 | 3 | import { SchemaRef } from "../Package/data"; 4 | 5 | const IOType = S.Union( 6 | S.Struct({ variant: S.Literal("exec") }), 7 | S.Struct({ 8 | variant: S.Literal("data"), 9 | data: S.Literal("string", "bool", "float", "int"), 10 | }), 11 | ); 12 | export type IOType = S.Schema.Type; 13 | 14 | export const IOVariant = S.Union(S.Literal("exec"), S.Literal("data")); 15 | export const NodeVariant = S.Union( 16 | S.Literal("exec"), 17 | S.Literal("base"), 18 | S.Literal("pure"), 19 | S.Literal("event"), 20 | ); 21 | 22 | export const XY = S.Struct({ 23 | x: S.Number, 24 | y: S.Number, 25 | }); 26 | 27 | export const NodeId = S.Int.pipe(S.brand("Node ID")); 28 | export type NodeId = (typeof NodeId)["Type"]; 29 | 30 | export const NodeIO = S.Struct({ 31 | inputs: S.Array( 32 | S.extend(S.Struct({ id: S.String, name: S.optional(S.String) }), IOType), 33 | ), 34 | outputs: S.Array( 35 | S.extend(S.Struct({ id: S.String, name: S.optional(S.String) }), IOType), 36 | ), 37 | }); 38 | export type NodeIO = (typeof NodeIO)["Type"]; 39 | 40 | export const Node = S.extend( 41 | S.Struct({ 42 | id: NodeId, 43 | name: S.optional(S.String), 44 | position: XY, 45 | schema: SchemaRef, 46 | }), 47 | NodeIO, 48 | ); 49 | export type Node = (typeof Node)["Type"]; 50 | -------------------------------------------------------------------------------- /apps/new-playground/src/domain/Package/data.ts: -------------------------------------------------------------------------------- 1 | import { Schema as S } from "effect"; 2 | 3 | export const SchemaRef = S.Struct({ 4 | pkgId: S.String, 5 | schemaId: S.String, 6 | }); 7 | export type SchemaRef = S.Schema.Type; 8 | -------------------------------------------------------------------------------- /apps/new-playground/src/domain/Project/NodeIdCounter.ts: -------------------------------------------------------------------------------- 1 | import { NodeId } from "../Node/data"; 2 | 3 | let nodeCounter = 0 as NodeId; 4 | 5 | export function getNextNodeId() { 6 | return NodeId.make(++nodeCounter); 7 | } 8 | -------------------------------------------------------------------------------- /apps/new-playground/src/domain/Project/Packages.ts: -------------------------------------------------------------------------------- 1 | import { Effect, Option, Scope, SubscriptionRef } from "effect"; 2 | import { Package, PackageBuildReturn } from "../../package"; 3 | import { HttpApp } from "@effect/platform"; 4 | 5 | export type PackageEntry = { 6 | pkg: Package; 7 | state: Option.Option>; 8 | rpcServer: Option.Option>; 9 | ret: PackageBuildReturn; 10 | }; 11 | 12 | export class ProjectPackages extends Effect.Service()( 13 | "ProjectPackages", 14 | { 15 | effect: Effect.gen(function* () { 16 | const packages = new Map(); 17 | 18 | return packages; 19 | }), 20 | }, 21 | ) {} 22 | -------------------------------------------------------------------------------- /apps/new-playground/src/domain/Project/data.ts: -------------------------------------------------------------------------------- 1 | import { Schema as S } from "effect"; 2 | -------------------------------------------------------------------------------- /apps/new-playground/src/domain/Realtime/Connection.ts: -------------------------------------------------------------------------------- 1 | import { Context, Schema } from "effect"; 2 | 3 | export const RealtimeConnectionId = Schema.Number.pipe( 4 | Schema.brand("Realtime Client ID"), 5 | ); 6 | export type RealtimeConnectionId = (typeof RealtimeConnectionId)["Type"]; 7 | 8 | export class RealtimeConnection extends Context.Tag("RealtimeConnection")< 9 | RealtimeConnection, 10 | { id: RealtimeConnectionId } 11 | >() {} 12 | -------------------------------------------------------------------------------- /apps/new-playground/src/domain/Realtime/PubSub.ts: -------------------------------------------------------------------------------- 1 | import { Effect, PubSub, Stream } from "effect"; 2 | import { ProjectEvent } from "../../shared"; 3 | import { RealtimeConnection, RealtimeConnectionId } from "./Connection"; 4 | 5 | export class RealtimePubSub extends Effect.Service()( 6 | "ProjectRealtime", 7 | { 8 | effect: Effect.gen(function* () { 9 | const pubsub = 10 | yield* PubSub.unbounded<[RealtimeConnectionId, ProjectEvent]>(); 11 | 12 | return { 13 | publish: Effect.fn(function* (v: (typeof ProjectEvent)["Type"]) { 14 | const realtimeClient = yield* RealtimeConnection; 15 | 16 | return yield* pubsub.publish([realtimeClient.id, v]); 17 | }), 18 | subscribe: () => Stream.fromPubSub(pubsub), 19 | }; 20 | }), 21 | }, 22 | ) {} 23 | -------------------------------------------------------------------------------- /apps/new-playground/src/domain/Rpc/Middleware.ts: -------------------------------------------------------------------------------- 1 | import { RpcMiddleware } from "@effect/rpc"; 2 | 3 | import { RealtimeConnection } from "../Realtime/Connection"; 4 | 5 | export class RpcRealtimeMiddleware extends RpcMiddleware.Tag()( 6 | "Middleware", 7 | { 8 | provides: RealtimeConnection, 9 | }, 10 | ) {} 11 | -------------------------------------------------------------------------------- /apps/new-playground/src/errors.ts: -------------------------------------------------------------------------------- 1 | import { Data, Schema } from "effect"; 2 | 3 | export class NotComputationNode extends Data.TaggedError( 4 | "NotComputationNode", 5 | ) {} 6 | 7 | export class NotEventNode extends Data.TaggedError("NotEventNode") {} 8 | 9 | export class SchemaNotFound extends Schema.TaggedError()( 10 | "SchemaNotFound", 11 | { 12 | pkgId: Schema.String, 13 | schemaId: Schema.String, 14 | }, 15 | ) {} 16 | 17 | export class NodeNotFound extends Schema.TaggedError()( 18 | "NodeNotFound", 19 | { 20 | nodeId: Schema.Int, 21 | }, 22 | ) {} 23 | 24 | export class IONotFound extends Schema.TaggedError()("IONotFound", { 25 | type: Schema.Union(Schema.Literal("in"), Schema.Literal("out")), 26 | nodeId: Schema.Int, 27 | }) {} 28 | -------------------------------------------------------------------------------- /apps/new-playground/src/io.ts: -------------------------------------------------------------------------------- 1 | import { Brand } from "effect"; 2 | 3 | export type IOId = string & Brand.Brand<"IOId">; 4 | 5 | export class ExecInputRef { 6 | constructor(public id: string) {} 7 | } 8 | 9 | export class ExecOutputRef { 10 | constructor(public id: IOId) {} 11 | } 12 | 13 | export class DataInputRef { 14 | constructor( 15 | public id: IOId, 16 | public type: T, 17 | ) {} 18 | } 19 | 20 | export class DataOutputRef { 21 | constructor( 22 | public id: string, 23 | public type: T, 24 | ) {} 25 | } 26 | -------------------------------------------------------------------------------- /apps/new-playground/src/obs-package/shared.ts: -------------------------------------------------------------------------------- 1 | import { Rpc, RpcGroup } from "@effect/rpc"; 2 | import { Schema as S } from "effect"; 3 | 4 | export class ConnectionFailed extends S.TaggedError()( 5 | "ConnectionFailed", 6 | {}, 7 | ) {} 8 | 9 | export const RPCS = RpcGroup.make().add( 10 | Rpc.make("AddSocket", { 11 | payload: S.Struct({ 12 | address: S.String, 13 | password: S.optional(S.String), 14 | }), 15 | error: ConnectionFailed, 16 | }), 17 | Rpc.make("RemoveSocket", { 18 | payload: S.Struct({ address: S.String }), 19 | }), 20 | Rpc.make("DisconnectSocket", { 21 | payload: S.Struct({ address: S.String }), 22 | }), 23 | Rpc.make("ConnectSocket", { 24 | payload: S.Struct({ 25 | address: S.String, 26 | password: S.optional(S.String), 27 | }), 28 | error: ConnectionFailed, 29 | }), 30 | ); 31 | 32 | export const STATE = S.Struct({ 33 | connections: S.Array( 34 | S.Struct({ 35 | address: S.String, 36 | password: S.optional(S.String), 37 | state: S.Union( 38 | S.Literal("connected"), 39 | S.Literal("connecting"), 40 | S.Literal("disconnected"), 41 | ), 42 | }), 43 | ), 44 | }); 45 | -------------------------------------------------------------------------------- /apps/new-playground/src/package-utils.ts: -------------------------------------------------------------------------------- 1 | import { Effect, Schema } from "effect"; 2 | 3 | import { DataInputRef, DataOutputRef } from "./io"; 4 | import { ExecutionContext } from "./Runtime"; 5 | 6 | export const getInput = >(ref: DataInputRef) => 7 | Effect.andThen(ExecutionContext, (ctx) => ctx.getInput(ref)); 8 | 9 | export const setOutput = >( 10 | ref: DataOutputRef, 11 | data: T["Encoded"], 12 | ) => Effect.andThen(ExecutionContext, (ctx) => ctx.setOutput(ref, data)); 13 | -------------------------------------------------------------------------------- /apps/new-playground/src/prod-server.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Headers, 3 | HttpMiddleware, 4 | HttpRouter, 5 | HttpServer, 6 | HttpServerRequest, 7 | HttpServerResponse, 8 | } from "@effect/platform"; 9 | import { Effect, Layer } from "effect"; 10 | import { NodeHttpServer, NodeRuntime } from "@effect/platform-node"; 11 | import { createServer } from "node:http"; 12 | 13 | import { DepsLive, ServerLive } from "./entry-server"; 14 | 15 | Layer.unwrapEffect( 16 | Effect.gen(function* () { 17 | return HttpRouter.empty.pipe( 18 | HttpRouter.all( 19 | "*", 20 | Effect.sync(() => HttpServerResponse.empty({ status: 494 })), 21 | ), 22 | HttpRouter.use( 23 | HttpMiddleware.make(() => 24 | Effect.gen(function* () { 25 | const httpServerRequest = 26 | yield* HttpServerRequest.HttpServerRequest; 27 | let { url } = httpServerRequest; 28 | if (url === "/") url = "/index.html"; 29 | 30 | let response = yield* HttpServerResponse.file(`dist/client${url}`); 31 | 32 | if (url.startsWith("/assets")) 33 | response = response.pipe( 34 | HttpServerResponse.setHeader( 35 | "cache-control", 36 | "public,immutable,max-age=31536000", 37 | ), 38 | ); 39 | 40 | return response; 41 | }), 42 | ), 43 | ), 44 | HttpRouter.mountApp("/api", yield* ServerLive), 45 | HttpServer.serve(), 46 | ); 47 | }), 48 | ).pipe( 49 | Layer.provide(NodeHttpServer.layer(createServer, { port: 23456 })), 50 | Layer.launch, 51 | Effect.provide(DepsLive), 52 | Effect.scoped, 53 | NodeRuntime.runMain, 54 | ); 55 | -------------------------------------------------------------------------------- /apps/new-playground/src/project.ts: -------------------------------------------------------------------------------- 1 | import { GraphId } from "./domain/Graph/data"; 2 | import { NodeId, NodeIO } from "./domain/Node/data"; 3 | import { DeepWriteable } from "./types"; 4 | 5 | export type NodeConnections = { 6 | in?: Map>; 7 | out?: Map>; 8 | }; 9 | 10 | export type Project = { 11 | name: string; 12 | graphs: Map< 13 | GraphId, 14 | { 15 | id: GraphId; 16 | name: string; 17 | nodes: Array< 18 | { 19 | id: NodeId; 20 | name?: string; 21 | position: { x: number; y: number }; 22 | schema: { pkgId: string; schemaId: string }; 23 | } & DeepWriteable 24 | >; 25 | connections?: Map; 26 | } 27 | >; 28 | }; 29 | 30 | export const project: Project = { 31 | name: "", 32 | graphs: new Map([ 33 | [ 34 | GraphId.make(0), 35 | { 36 | id: GraphId.make(0), 37 | name: "New Graph", 38 | nodes: [], 39 | }, 40 | ], 41 | ]), 42 | }; 43 | -------------------------------------------------------------------------------- /apps/new-playground/src/style.css: -------------------------------------------------------------------------------- 1 | @import "@radix-ui/colors/gray.css"; 2 | @import "@radix-ui/colors/gray-dark.css"; 3 | @import "@radix-ui/colors/red.css"; 4 | @import "@radix-ui/colors/red-dark.css"; 5 | 6 | html { 7 | user-select: none; 8 | } 9 | 10 | button:hover { 11 | cursor: default; 12 | } 13 | 14 | a:hover { 15 | cursor: default; 16 | } 17 | 18 | a:hover[rel="external"] { 19 | cursor: inherit; 20 | } 21 | -------------------------------------------------------------------------------- /apps/new-playground/src/twitch-package/shared.ts: -------------------------------------------------------------------------------- 1 | import { Rpc, RpcGroup } from "@effect/rpc"; 2 | import { Schema as S } from "effect"; 3 | 4 | export const RPCS = RpcGroup.make().add( 5 | Rpc.make("ConnectEventSub", { 6 | payload: S.Struct({ 7 | accountId: S.String, 8 | }), 9 | }), 10 | Rpc.make("DisconnectEventSub", { 11 | payload: S.Struct({ 12 | accountId: S.String, 13 | }), 14 | }), 15 | ); 16 | 17 | export const STATE = S.Union( 18 | S.Struct({ 19 | accounts: S.Array( 20 | S.Struct({ 21 | id: S.String, 22 | displayName: S.String, 23 | eventSubSocket: S.Union( 24 | S.Struct({ 25 | state: S.Literal("disconnected"), 26 | }), 27 | S.Struct({ 28 | state: S.Literal("connecting"), 29 | }), 30 | S.Struct({ 31 | state: S.Literal("connected"), 32 | }), 33 | ), 34 | }), 35 | ), 36 | }), 37 | ); 38 | -------------------------------------------------------------------------------- /apps/new-playground/src/types.ts: -------------------------------------------------------------------------------- 1 | import { Brand } from "effect"; 2 | 3 | export type DeepWriteable = 4 | T extends Brand.Brand 5 | ? T 6 | : T extends object 7 | ? { -readonly [P in keyof T]: DeepWriteable } 8 | : T; 9 | -------------------------------------------------------------------------------- /apps/new-playground/src/util.ts: -------------------------------------------------------------------------------- 1 | import { isMobile } from "@solid-primitives/platform"; 2 | 3 | export const isTouchDevice = isMobile || navigator.maxTouchPoints > 0; 4 | -------------------------------------------------------------------------------- /apps/new-playground/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | declare module "macrograph:package-settings" { 4 | export default {} as Record Promise>; 5 | } 6 | 7 | declare interface PackageSettingsModule { 8 | default: import("solid-js").Component< 9 | import("./package-settings-utils").SettingsProps 10 | >; 11 | Rpcs: import("@effect/rpc/RpcGroup").RpcGroup; 12 | } 13 | -------------------------------------------------------------------------------- /apps/new-playground/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "useDefineForClassFields": true, 5 | "module": "ESNext", 6 | "lib": ["ES2020", "DOM", "DOM.Iterable"], 7 | "skipLibCheck": true, 8 | "jsx": "preserve", 9 | "jsxImportSource": "solid-js", 10 | 11 | /* Bundler mode */ 12 | "moduleResolution": "bundler", 13 | "allowImportingTsExtensions": true, 14 | "isolatedModules": true, 15 | "moduleDetection": "force", 16 | "noEmit": true, 17 | 18 | /* Linting */ 19 | "strict": true, 20 | // "noUnusedLocals": true, 21 | // "noUnusedParameters": true, 22 | "noUncheckedIndexedAccess": true, 23 | "noFallthroughCasesInSwitch": true, 24 | "types": ["unplugin-icons/types/solid"], 25 | "plugins": [{ "name": "@effect/language-service" }] 26 | }, 27 | "include": ["src"] 28 | } 29 | -------------------------------------------------------------------------------- /apps/new-playground/uno.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig, presetWind3, transformerVariantGroup } from "unocss"; 2 | import { presetKobalte } from "unocss-preset-primitives"; 3 | import { presetAnimations } from "unocss-preset-animations"; 4 | 5 | function getColorScale(name: string, alpha = false) { 6 | let scale = {}; 7 | for (let i = 1; i <= 12; i++) { 8 | scale[i] = `var(--${name}-${i})`; 9 | // next line only needed if using alpha values 10 | if (alpha) scale[`a${i}`] = `var(--${name}-a${i})`; 11 | } 12 | 13 | return scale; 14 | } 15 | 16 | export default defineConfig({ 17 | presets: [presetWind3(), presetAnimations(), presetKobalte() as any], 18 | transformers: [transformerVariantGroup()], 19 | theme: { 20 | colors: { 21 | gray: getColorScale("gray"), 22 | red: getColorScale("red"), 23 | }, 24 | }, 25 | }); 26 | -------------------------------------------------------------------------------- /apps/storybook/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | 26 | *storybook.log -------------------------------------------------------------------------------- /apps/storybook/.storybook/main.ts: -------------------------------------------------------------------------------- 1 | import type { StorybookConfig } from "storybook-solidjs-vite"; 2 | 3 | import { dirname, join } from "node:path"; 4 | 5 | /** 6 | * This function is used to resolve the absolute path of a package. 7 | * It is needed in projects that use Yarn PnP or are set up within a monorepo. 8 | */ 9 | function getAbsolutePath(value: string): any { 10 | return dirname(require.resolve(join(value, "package.json"))); 11 | } 12 | const config: StorybookConfig = { 13 | stories: [ 14 | "../src/**/*.mdx", 15 | "../../../packages/ui/src/**/*.stories.@(js|jsx|mjs|ts|tsx)", 16 | ], 17 | addons: [ 18 | getAbsolutePath("@storybook/addon-links"), 19 | getAbsolutePath("@storybook/addon-essentials"), 20 | getAbsolutePath("@chromatic-com/storybook"), 21 | getAbsolutePath("@storybook/addon-interactions"), 22 | ], 23 | framework: { 24 | name: getAbsolutePath("storybook-solidjs-vite"), 25 | options: {}, 26 | }, 27 | }; 28 | export default config; 29 | -------------------------------------------------------------------------------- /apps/storybook/.storybook/preview.ts: -------------------------------------------------------------------------------- 1 | const preview: Preview = { 2 | parameters: { 3 | controls: { 4 | matchers: { 5 | color: /(background|color)$/i, 6 | date: /Date$/i, 7 | }, 8 | }, 9 | }, 10 | }; 11 | 12 | export default preview; 13 | -------------------------------------------------------------------------------- /apps/storybook/README.md: -------------------------------------------------------------------------------- 1 | ## Usage 2 | 3 | ```bash 4 | $ npm install # or pnpm install or yarn install 5 | ``` 6 | 7 | ### Learn more on the [Solid Website](https://solidjs.com) and come chat with us on our [Discord](https://discord.com/invite/solidjs) 8 | 9 | ## Available Scripts 10 | 11 | In the project directory, you can run: 12 | 13 | ### `npm run dev` 14 | 15 | Runs the app in the development mode. 16 | Open [http://localhost:5173](http://localhost:5173) to view it in the browser. 17 | 18 | ### `npm run build` 19 | 20 | Builds the app for production to the `dist` folder. 21 | It correctly bundles Solid in production mode and optimizes the build for the best performance. 22 | 23 | The build is minified and the filenames include the hashes. 24 | Your app is ready to be deployed! 25 | 26 | ## Deployment 27 | 28 | Learn more about deploying your application with the [documentations](https://vitejs.dev/guide/static-deploy.html) 29 | -------------------------------------------------------------------------------- /apps/storybook/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + Solid + TS 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /apps/storybook/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "storybook", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "storybook dev -p 6006", 8 | "build": "storybook build" 9 | }, 10 | "dependencies": { 11 | "@storybook/docs-tools": "^8.2.7", 12 | "solid-js": "^1.9.7" 13 | }, 14 | "devDependencies": { 15 | "@chromatic-com/storybook": "^1.6.1", 16 | "@storybook/addon-essentials": "^8.2.7", 17 | "@storybook/addon-interactions": "^8.2.7", 18 | "@storybook/addon-links": "^8.2.7", 19 | "@storybook/blocks": "^8.2.7", 20 | "@storybook/testing-library": "^0.2.2", 21 | "storybook": "^8.2.7", 22 | "storybook-solidjs": "^1.0.0-beta.2", 23 | "storybook-solidjs-vite": "^1.0.0-beta.2", 24 | "typescript": "^5.2.2", 25 | "vite": "^6.3.5", 26 | "vite-plugin-solid": "^2.10.2" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /apps/storybook/public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/storybook/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vite"; 2 | import solid from "vite-plugin-solid"; 3 | 4 | import macrographUI from "../../packages/ui/vite"; 5 | 6 | export default defineConfig({ 7 | plugins: [solid(), macrographUI], 8 | }); 9 | -------------------------------------------------------------------------------- /apps/web/.env.example: -------------------------------------------------------------------------------- 1 | AUTH_SECRET= 2 | AUTH_REDIRECT_PROXY_URL=https://macrograph.brendonovich.dev 3 | 4 | TWITCH_CLIENT_ID= 5 | TWITCH_CLIENT_SECRET= 6 | 7 | DISCORD_CLIENT_ID= 8 | DISCORD_CLIENT_SECRET= 9 | 10 | SPOTIFY_CLIENT_ID= 11 | SPOTIFY_CLIENT_SECRET= 12 | 13 | GOOGLE_CLIENT_ID= 14 | GOOGLE_CLIENT_SECRET= 15 | 16 | STREAMLABS_CLIENT_ID= 17 | STREAMLABS_CLIENT_SECRET= 18 | 19 | -------------------------------------------------------------------------------- /apps/web/.gitignore: -------------------------------------------------------------------------------- 1 | .vercel 2 | -------------------------------------------------------------------------------- /apps/web/app.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "@solidjs/start/config"; 2 | // @ts-expect-error 3 | import mdx from "@vinxi/plugin-mdx"; 4 | import dotenv from "dotenv"; 5 | // import unfonts from "unplugin-fonts/vite"; 6 | 7 | import interfacePlugin from "../../packages/ui/vite"; 8 | 9 | dotenv.config({ path: ".env.local" }); 10 | 11 | export default defineConfig({ 12 | ssr: true, 13 | routeDir: "app", 14 | extensions: ["md", "mdx"], 15 | vite: { 16 | plugins: [ 17 | interfacePlugin, 18 | mdx.default.withImports({})({ 19 | jsx: true, 20 | jsxImportSource: "solid-js", 21 | providerImportSource: "solid-mdx", 22 | }), 23 | // unfonts({ 24 | // fontsource: { 25 | // families: [ 26 | // { 27 | // name: "Geist Sans", 28 | // weights: [400, 500, 600, 700, 800, 900], 29 | // }, 30 | // ], 31 | // }, 32 | // }), 33 | ], 34 | }, 35 | server: { 36 | preset: "vercel", 37 | prerender: { 38 | crawlLinks: true, 39 | routes: ["/landing"], 40 | }, 41 | }, 42 | }); 43 | -------------------------------------------------------------------------------- /apps/web/drizzle.config.ts: -------------------------------------------------------------------------------- 1 | import dotenv from "dotenv"; 2 | import { defineConfig } from "drizzle-kit"; 3 | import { serverEnv } from "./src/env/server"; 4 | 5 | dotenv.config({ 6 | path: ".env", 7 | }); 8 | 9 | if ("DATABASE_URL" in process.env === false) 10 | throw new Error("'DATABASE_URL' not set in env"); 11 | 12 | export default defineConfig({ 13 | schema: "./src/drizzle/schema.ts", 14 | driver: "pg", 15 | out: "./drizzle", 16 | dbCredentials: { 17 | connectionString: serverEnv.DATABASE_URL, 18 | }, 19 | }); 20 | -------------------------------------------------------------------------------- /apps/web/drizzle/0000_fresh_speedball.sql: -------------------------------------------------------------------------------- 1 | DO $$ BEGIN 2 | CREATE TYPE "ClientType" AS ENUM('web', 'desktop'); 3 | EXCEPTION 4 | WHEN duplicate_object THEN null; 5 | END $$; 6 | --> statement-breakpoint 7 | CREATE TABLE IF NOT EXISTS "oauth_credential" ( 8 | "provider_id" varchar(255) NOT NULL, 9 | "user_id" varchar(255) NOT NULL, 10 | "provider_user_id" varchar(255) NOT NULL, 11 | "token" json NOT NULL, 12 | "display_name" varchar(255), 13 | CONSTRAINT "oauth_credential_provider_id_user_id_provider_user_id_pk" PRIMARY KEY("provider_id","user_id","provider_user_id") 14 | ); 15 | --> statement-breakpoint 16 | CREATE TABLE IF NOT EXISTS "project" ( 17 | "id" serial PRIMARY KEY NOT NULL, 18 | "owner_id" varchar(255) NOT NULL, 19 | "name" varchar(255) NOT NULL, 20 | "client_type" "ClientType" NOT NULL, 21 | "data" json, 22 | "last_updated" timestamp NOT NULL 23 | ); 24 | --> statement-breakpoint 25 | CREATE TABLE IF NOT EXISTS "session" ( 26 | "id" varchar(255) PRIMARY KEY NOT NULL, 27 | "user_id" varchar(255) NOT NULL, 28 | "expires_at" timestamp NOT NULL 29 | ); 30 | --> statement-breakpoint 31 | CREATE TABLE IF NOT EXISTS "user" ( 32 | "id" varchar(255) PRIMARY KEY NOT NULL, 33 | "email" varchar(255) NOT NULL, 34 | "hashed_password" varchar(255) NOT NULL, 35 | CONSTRAINT "user_email_unique" UNIQUE("email") 36 | ); 37 | -------------------------------------------------------------------------------- /apps/web/drizzle/meta/_journal.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "5", 3 | "dialect": "pg", 4 | "entries": [ 5 | { 6 | "idx": 0, 7 | "version": "5", 8 | "when": 1712225305949, 9 | "tag": "0000_fresh_speedball", 10 | "breakpoints": true 11 | } 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /apps/web/drizzle/migrate.ts: -------------------------------------------------------------------------------- 1 | import "dotenv/config"; 2 | import { migrate } from "drizzle-orm/node-postgres/migrator"; 3 | 4 | import { connection, db } from "../src/drizzle"; 5 | // This will run migrations on the database, skipping the ones already applied 6 | await migrate(db, { migrationsFolder: "./drizzle" }); 7 | // Don't forget to close the connection, otherwise the script will hang 8 | await connection.end(); 9 | -------------------------------------------------------------------------------- /apps/web/postcss.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | }; 7 | -------------------------------------------------------------------------------- /apps/web/public/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Brendonovich/MacroGraph/abfa1cb37f8165d8bd82e672810d0937f29e6501/apps/web/public/favicon.png -------------------------------------------------------------------------------- /apps/web/public/paddle-verification.txt: -------------------------------------------------------------------------------- 1 | paddle-verification=09588bd4 2 | -------------------------------------------------------------------------------- /apps/web/src/app.tsx: -------------------------------------------------------------------------------- 1 | // @refresh reload 2 | import { MetaProvider, Title } from "@solidjs/meta"; 3 | import { Router } from "@solidjs/router"; 4 | import { clientOnly } from "@solidjs/start"; 5 | import { FileRoutes } from "@solidjs/start/router"; 6 | import { ErrorBoundary, Suspense } from "solid-js"; 7 | import { Toaster } from "solid-sonner"; 8 | 9 | // import "unfonts.css"; 10 | import "@macrograph/ui/global.css"; 11 | 12 | const DesktopListener = clientOnly(() => import("./app/DesktopListener")); 13 | 14 | export default function App() { 15 | return ( 16 | ( 18 | 19 | MacroGraph 20 | {props.children} 21 | 22 | 23 | 24 | 25 | 26 | )} 27 | > 28 | 29 | 30 | ); 31 | } 32 | -------------------------------------------------------------------------------- /apps/web/src/app/(app).tsx: -------------------------------------------------------------------------------- 1 | import type { ParentProps } from "solid-js"; 2 | 3 | export default function (props: ParentProps) { 4 | return ( 5 | 6 | {props.children} 7 | 8 | ); 9 | } 10 | -------------------------------------------------------------------------------- /apps/web/src/app/(auth)/login.tsx: -------------------------------------------------------------------------------- 1 | import { useNavigate } from "@solidjs/router"; 2 | import { LoginForm } from "./Forms"; 3 | 4 | export default function () { 5 | const navigate = useNavigate(); 6 | 7 | return ( 8 | navigate("/signup")} 10 | onLogin={() => navigate("/")} 11 | /> 12 | ); 13 | } 14 | -------------------------------------------------------------------------------- /apps/web/src/app/(auth)/signup.tsx: -------------------------------------------------------------------------------- 1 | import { useNavigate } from "@solidjs/router"; 2 | import { SignUpForm } from "./Forms"; 3 | 4 | export default function () { 5 | const navigate = useNavigate(); 6 | 7 | return ( 8 | navigate("/login")} 10 | onSignup={() => navigate("/")} 11 | /> 12 | ); 13 | } 14 | -------------------------------------------------------------------------------- /apps/web/src/app/(auth)/utils.ts: -------------------------------------------------------------------------------- 1 | import { action, reload } from "@solidjs/router"; 2 | import * as v from "valibot"; 3 | import { deleteCookie } from "vinxi/http"; 4 | 5 | import { getAuthState, getUser } from "~/api"; 6 | import { lucia } from "~/lucia"; 7 | 8 | export const CREDENTIALS = v.object({ 9 | email: v.pipe(v.string(), v.email()), 10 | password: v.pipe(v.string(), v.minLength(8)), 11 | }); 12 | 13 | export const IS_LOGGED_IN = "isLoggedIn"; 14 | 15 | export const logOutAction = action(async () => { 16 | "use server"; 17 | 18 | const authState = await getAuthState(); 19 | 20 | if (authState) await lucia.invalidateSession(authState.session.id); 21 | 22 | deleteCookie(IS_LOGGED_IN); 23 | 24 | throw reload({ 25 | revalidate: [getUser.key], 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /apps/web/src/app/(landing)/Socials.tsx: -------------------------------------------------------------------------------- 1 | export function Socials(props: { iconClass?: string }) { 2 | return ( 3 | <> 4 | 10 | 11 | 12 | 18 | 19 | 20 | 26 | 27 | 28 | > 29 | ); 30 | } 31 | -------------------------------------------------------------------------------- /apps/web/src/app/(landing)/logos/discord.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Brendonovich/MacroGraph/abfa1cb37f8165d8bd82e672810d0937f29e6501/apps/web/src/app/(landing)/logos/discord.png -------------------------------------------------------------------------------- /apps/web/src/app/(landing)/logos/goxlr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Brendonovich/MacroGraph/abfa1cb37f8165d8bd82e672810d0937f29e6501/apps/web/src/app/(landing)/logos/goxlr.png -------------------------------------------------------------------------------- /apps/web/src/app/(landing)/logos/midi.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /apps/web/src/app/(landing)/logos/obs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Brendonovich/MacroGraph/abfa1cb37f8165d8bd82e672810d0937f29e6501/apps/web/src/app/(landing)/logos/obs.png -------------------------------------------------------------------------------- /apps/web/src/app/(landing)/logos/openai.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Brendonovich/MacroGraph/abfa1cb37f8165d8bd82e672810d0937f29e6501/apps/web/src/app/(landing)/logos/openai.png -------------------------------------------------------------------------------- /apps/web/src/app/(landing)/logos/stream-deck.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Brendonovich/MacroGraph/abfa1cb37f8165d8bd82e672810d0937f29e6501/apps/web/src/app/(landing)/logos/stream-deck.jpg -------------------------------------------------------------------------------- /apps/web/src/app/(landing)/logos/streamlabs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Brendonovich/MacroGraph/abfa1cb37f8165d8bd82e672810d0937f29e6501/apps/web/src/app/(landing)/logos/streamlabs.png -------------------------------------------------------------------------------- /apps/web/src/app/(landing)/logos/twitch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Brendonovich/MacroGraph/abfa1cb37f8165d8bd82e672810d0937f29e6501/apps/web/src/app/(landing)/logos/twitch.png -------------------------------------------------------------------------------- /apps/web/src/app/(landing)/logos/vtube-studio.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Brendonovich/MacroGraph/abfa1cb37f8165d8bd82e672810d0937f29e6501/apps/web/src/app/(landing)/logos/vtube-studio.png -------------------------------------------------------------------------------- /apps/web/src/app/(landing)/logos/websockets.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Brendonovich/MacroGraph/abfa1cb37f8165d8bd82e672810d0937f29e6501/apps/web/src/app/(landing)/logos/websockets.webp -------------------------------------------------------------------------------- /apps/web/src/app/Logo.tsx: -------------------------------------------------------------------------------- 1 | export function Logo() { 2 | return ( 3 | 4 | MacroGraph 5 | 6 | ); 7 | } 8 | -------------------------------------------------------------------------------- /apps/web/src/app/api/desktop/download/latest/[target].ts: -------------------------------------------------------------------------------- 1 | import { redirect } from "@solidjs/router"; 2 | import type { APIEvent } from "@solidjs/start/server"; 3 | import { appendResponseHeader } from "vinxi/http"; 4 | import { type DownloadTarget, getDownloadURL } from "~/lib/releases"; 5 | 6 | export async function GET({ params }: APIEvent) { 7 | appendResponseHeader("CDN-Cache-Control", `public, max-age=${60 * 60 * 24}`); 8 | 9 | return redirect(await getDownloadURL(params.target as DownloadTarget)); 10 | } 11 | -------------------------------------------------------------------------------- /apps/web/src/app/auth/[provider]/login.ts: -------------------------------------------------------------------------------- 1 | import { redirect } from "@solidjs/router"; 2 | import type { APIEvent } from "@solidjs/start/server"; 3 | 4 | import { getOAuthLoginURL } from "../actions"; 5 | import { AuthProviders } from "../providers"; 6 | 7 | export const GET = async (event: APIEvent) => { 8 | const { provider } = event.params as { provider: string }; 9 | 10 | const providerConfig = AuthProviders[provider]; 11 | if (!providerConfig) throw new Error(`Unknown provider ${provider}`); 12 | 13 | const url = new URL(event.request.url); 14 | 15 | const stateFromSearchParam = JSON.parse( 16 | Buffer.from(url.searchParams.get("state")!, "base64").toString(), 17 | ) as any; 18 | 19 | return redirect( 20 | await getOAuthLoginURL(provider, url.origin, stateFromSearchParam), 21 | ); 22 | }; 23 | -------------------------------------------------------------------------------- /apps/web/src/app/auth/[provider]/refresh.ts: -------------------------------------------------------------------------------- 1 | import { json } from "@solidjs/router"; 2 | import type { APIHandler } from "@solidjs/start/server"; 3 | import { z } from "zod"; 4 | 5 | import { CORS_HEADERS } from "~/auth"; 6 | import { refreshToken } from "../actions"; 7 | import { AuthProviders } from "../providers"; 8 | 9 | export const prerender = false; 10 | 11 | const BODY = z.object({ refreshToken: z.string() }); 12 | 13 | export const POST: APIHandler = async ({ request, params }) => { 14 | const { provider } = params as { provider: string }; 15 | 16 | const providerConfig = AuthProviders[provider]; 17 | if (!providerConfig) throw new Error(`Unknown provider ${provider}`); 18 | 19 | const body = BODY.parse(await request.json()); 20 | 21 | return json(await refreshToken(providerConfig, body.refreshToken), { 22 | headers: CORS_HEADERS, 23 | }); 24 | }; 25 | 26 | export const OPTIONS: APIHandler = async () => 27 | new Response(undefined, { 28 | headers: CORS_HEADERS, 29 | }); 30 | -------------------------------------------------------------------------------- /apps/web/src/app/auth/[provider]/types.ts: -------------------------------------------------------------------------------- 1 | import { z } from "zod"; 2 | 3 | export const OAUTH_STATE = z 4 | .union([ 5 | z.object({ 6 | env: z.literal("desktop"), 7 | port: z.number(), 8 | }), 9 | z.object({ 10 | env: z.literal("web"), 11 | targetOrigin: z.string(), 12 | }), 13 | z.object({ 14 | env: z.literal("credentials"), 15 | targetOrigin: z.string(), 16 | }), 17 | ]) 18 | .and( 19 | z.object({ 20 | redirect_uri: z.string(), 21 | }), 22 | ); 23 | 24 | export const CALLBACK_SEARCH_PARAMS = z.object({ 25 | code: z.string(), 26 | state: OAUTH_STATE, 27 | }); 28 | 29 | export const REFRESHED_TOKEN = z.object({ 30 | access_token: z.string(), 31 | expires_in: z.number(), 32 | }); 33 | -------------------------------------------------------------------------------- /apps/web/src/app/auth/proxy.tsx: -------------------------------------------------------------------------------- 1 | import type { APIHandler } from "@solidjs/start/server"; 2 | import * as jose from "jose"; 3 | import { serverEnv } from "~/env/server"; 4 | 5 | import { OAUTH_STATE } from "./[provider]/types"; 6 | 7 | export const GET: APIHandler = async (event) => { 8 | const { searchParams } = new URL(event.request.url); 9 | 10 | const { payload } = await jose.jwtVerify( 11 | searchParams.get("state")!, 12 | new TextEncoder().encode(serverEnv.AUTH_SECRET), 13 | ); 14 | 15 | const state = OAUTH_STATE.parse(payload); 16 | 17 | return Response.redirect(new URL(`${state.redirect_uri}?${searchParams}`)); 18 | }; 19 | -------------------------------------------------------------------------------- /apps/web/src/app/auth/responderScript-web.js: -------------------------------------------------------------------------------- 1 | const token = document.currentScript.getAttribute("data-token"); 2 | const targetOrigin = document.currentScript.getAttribute("data-target-origin"); 3 | 4 | const opener = window.opener; 5 | opener.postMessage(JSON.parse(token), targetOrigin); 6 | window.close(); 7 | -------------------------------------------------------------------------------- /apps/web/src/app/auth/responderScript.js: -------------------------------------------------------------------------------- 1 | const targetOrigin = document.currentScript.getAttribute("data-target-origin"); 2 | 3 | const opener = window.opener; 4 | opener.postMessage(location.search, targetOrigin); 5 | console.log({ targetOrigin }); 6 | // window.close(); 7 | -------------------------------------------------------------------------------- /apps/web/src/app/docs/[...catchall].md: -------------------------------------------------------------------------------- 1 | # todo 2 | -------------------------------------------------------------------------------- /apps/web/src/app/playground/playground.css: -------------------------------------------------------------------------------- 1 | body { 2 | overscroll-behavior-y: none; 3 | } 4 | -------------------------------------------------------------------------------- /apps/web/src/assets/App Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Brendonovich/MacroGraph/abfa1cb37f8165d8bd82e672810d0937f29e6501/apps/web/src/assets/App Logo.png -------------------------------------------------------------------------------- /apps/web/src/auth.ts: -------------------------------------------------------------------------------- 1 | export const CORS_HEADERS = { 2 | "Access-Control-Allow-Origin": "*", 3 | "Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, OPTIONS", 4 | "Access-Control-Allow-Headers": "Content-Type, Authorization", 5 | }; 6 | -------------------------------------------------------------------------------- /apps/web/src/drizzle/index.ts: -------------------------------------------------------------------------------- 1 | import { drizzle } from "drizzle-orm/postgres-js"; 2 | import postgres from "postgres"; 3 | 4 | import { serverEnv } from "~/env/server"; 5 | 6 | import * as schema from "./schema"; 7 | export { schema }; 8 | 9 | // Disable prefetch as it is not supported for "Transaction" pool mode 10 | export const connection = postgres(serverEnv.DATABASE_URL, { prepare: false }); 11 | 12 | export const db = drizzle(connection, { schema }); 13 | -------------------------------------------------------------------------------- /apps/web/src/entry-client.tsx: -------------------------------------------------------------------------------- 1 | import { StartClient, mount } from "@solidjs/start/client"; 2 | import posthog from "posthog-js"; 3 | 4 | mount(() => , document.getElementById("app")!); 5 | 6 | posthog.init("phc_7anSDyS3p1frzGL7bHWlkiNG8kJ9pxcHB8H7QjBMEMB", { 7 | api_host: "https://us.i.posthog.com", 8 | person_profiles: "identified_only", 9 | }); 10 | -------------------------------------------------------------------------------- /apps/web/src/entry-server.tsx: -------------------------------------------------------------------------------- 1 | import { StartServer, createHandler } from "@solidjs/start/server"; 2 | 3 | export default createHandler( 4 | () => ( 5 | ( 7 | 8 | 9 | 10 | 11 | {assets} 12 | 13 | 14 | {children} 15 | {scripts} 16 | 17 | 18 | )} 19 | /> 20 | ), 21 | { mode: "async" }, 22 | ); 23 | -------------------------------------------------------------------------------- /apps/web/src/env/client.ts: -------------------------------------------------------------------------------- 1 | import { createEnv } from "@t3-oss/env-core"; 2 | import { z } from "zod"; 3 | 4 | export const clientEnv = createEnv({ 5 | clientPrefix: "VITE_", 6 | client: { 7 | VITE_VERCEL_URL: z 8 | .string() 9 | .optional() 10 | .transform((d) => (d ? `https://${d}` : "")), 11 | }, 12 | runtimeEnv: { 13 | ...import.meta.env, 14 | }, 15 | }); 16 | -------------------------------------------------------------------------------- /apps/web/src/env/server.ts: -------------------------------------------------------------------------------- 1 | import { createEnv } from "@t3-oss/env-core"; 2 | import "dotenv/config"; 3 | import { z } from "zod"; 4 | 5 | export const serverEnv = createEnv({ 6 | server: { 7 | VERCEL_URL: z 8 | .string() 9 | .transform((d) => (d ? `https://${d}` : "http://localhost:4321")), 10 | VERCEL_BRANCH_URL: z 11 | .string() 12 | .transform((d) => (d ? `https://${d}` : "http://localhost:4321")), 13 | AUTH_REDIRECT_PROXY_URL: z.string(), 14 | AUTH_SECRET: z.string(), 15 | TWITCH_CLIENT_ID: z.string(), 16 | TWITCH_CLIENT_SECRET: z.string(), 17 | DISCORD_CLIENT_ID: z.string(), 18 | DISCORD_CLIENT_SECRET: z.string(), 19 | SPOTIFY_CLIENT_ID: z.string(), 20 | SPOTIFY_CLIENT_SECRET: z.string(), 21 | GOOGLE_CLIENT_ID: z.string(), 22 | GOOGLE_CLIENT_SECRET: z.string(), 23 | STREAMLABS_CLIENT_ID: z.string(), 24 | STREAMLABS_CLIENT_SECRET: z.string(), 25 | PATREON_CLIENT_ID: z.string(), 26 | PATREON_CLIENT_SECRET: z.string(), 27 | GITHUB_CLIENT_ID: z.string(), 28 | GITHUB_CLIENT_SECRET: z.string(), 29 | DATABASE_URL: z.string(), 30 | RESEND_API_KEY: z.string(), 31 | }, 32 | runtimeEnv: { 33 | VERCEL_URL: "http://localhost:4321", 34 | AUTH_REDIRECT_PROXY_URL: "http://localhost:4321", 35 | ...process.env, 36 | }, 37 | skipValidation: process.env.NODE_ENV === "development", 38 | }); 39 | -------------------------------------------------------------------------------- /apps/web/src/global.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /apps/web/src/lucia.ts: -------------------------------------------------------------------------------- 1 | import { DrizzlePostgreSQLAdapter } from "@lucia-auth/adapter-drizzle"; 2 | import { Lucia } from "lucia"; 3 | 4 | import { db, schema } from "./drizzle"; 5 | 6 | const adapter = new DrizzlePostgreSQLAdapter(db, schema.sessions, schema.users); 7 | 8 | export const lucia = new Lucia(adapter, { 9 | sessionCookie: { 10 | attributes: { 11 | // set to `true` when using HTTPS 12 | secure: import.meta.env.PROD, 13 | }, 14 | }, 15 | getUserAttributes: (attributes: { email: string }) => { 16 | return { 17 | // we don't need to expose the hashed password! 18 | email: attributes.email, 19 | }; 20 | }, 21 | }); 22 | 23 | declare module "lucia" { 24 | interface Register { 25 | Lucia: typeof lucia; 26 | DatabaseUserAttributes: { email: string }; 27 | } 28 | 29 | interface UserAttributes { 30 | email: string; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /apps/web/src/posthog/server.ts: -------------------------------------------------------------------------------- 1 | import { EventMessage, PostHog } from "posthog-node"; 2 | 3 | const posthogServer = new PostHog( 4 | "phc_7anSDyS3p1frzGL7bHWlkiNG8kJ9pxcHB8H7QjBMEMB", 5 | { 6 | host: "https://us.i.posthog.com", 7 | disabled: import.meta.env.DEV, 8 | flushAt: 1, 9 | flushInterval: 0, 10 | }, 11 | ); 12 | 13 | export type PostHogEvent = { 14 | "user signed up": undefined; 15 | "user logged in": undefined; 16 | "credential connected": { 17 | providerId: string; 18 | providerUserId: string; 19 | }; 20 | "credential refreshed": { 21 | providerId: string; 22 | providerUserId: string; 23 | }; 24 | }; 25 | 26 | export function posthogIdentify(userId: string, properties: { email: string }) { 27 | return posthogServer.identify({ distinctId: userId, properties }); 28 | } 29 | 30 | export function posthogCapture( 31 | props: Omit & { 32 | event: T; 33 | } & (PostHogEvent[T] extends undefined 34 | ? {} 35 | : { properties: PostHogEvent[T] }), 36 | ) { 37 | return posthogServer.capture(props); 38 | } 39 | 40 | export function posthogShutdown() { 41 | return posthogServer.shutdown(); 42 | } 43 | -------------------------------------------------------------------------------- /apps/web/tailwind.config.js: -------------------------------------------------------------------------------- 1 | import preset from "@macrograph/interface/tailwind.config.js"; 2 | import typography from "@tailwindcss/typography"; 3 | 4 | /** @type {import('tailwindcss').Config} */ 5 | module.exports = { 6 | darkMode: ["class"], 7 | presets: [preset], 8 | plugins: [typography], 9 | }; 10 | -------------------------------------------------------------------------------- /apps/web/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "module": "ESNext", 5 | "moduleResolution": "node", 6 | "allowSyntheticDefaultImports": true, 7 | "esModuleInterop": true, 8 | "jsx": "preserve", 9 | "jsxImportSource": "solid-js", 10 | "allowJs": true, 11 | "strict": true, 12 | "noEmit": true, 13 | "types": [ 14 | "vinxi/client", 15 | "vite/client", 16 | "unplugin-icons/types/solid", 17 | "@macrograph/ui/types" 18 | ], 19 | "isolatedModules": true, 20 | "skipLibCheck": true, 21 | "paths": { 22 | "~/*": ["./src/*"] 23 | } 24 | }, 25 | "references": [ 26 | { 27 | "path": "../../interface" 28 | }, 29 | { 30 | "path": "../../packages/runtime" 31 | }, 32 | { 33 | "path": "../../packages/packages" 34 | }, 35 | { 36 | "path": "../../packages/effect-server-fn" 37 | } 38 | ] 39 | } 40 | -------------------------------------------------------------------------------- /assets/App Icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Brendonovich/MacroGraph/abfa1cb37f8165d8bd82e672810d0937f29e6501/assets/App Icon.png -------------------------------------------------------------------------------- /assets/Twitter PFP.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Brendonovich/MacroGraph/abfa1cb37f8165d8bd82e672810d0937f29e6501/assets/Twitter PFP.png -------------------------------------------------------------------------------- /biome.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://biomejs.dev/schemas/1.5.3/schema.json", 3 | "files": { 4 | "include": [ 5 | "apps/*/src/**", 6 | "apps/*/*", 7 | "interface/**", 8 | "packages/*/src/**", 9 | "packages/*/*", 10 | "*" 11 | ], 12 | "ignore": [ 13 | "auto-imports.d.ts", 14 | "node_modules", 15 | "dist", 16 | "bindings.ts", 17 | "rspc/types.ts", 18 | "commands.ts" 19 | ] 20 | }, 21 | "organizeImports": { 22 | "enabled": true 23 | }, 24 | "linter": { 25 | "enabled": true, 26 | "rules": { 27 | "recommended": true, 28 | "suspicious": { 29 | "noExplicitAny": "off" 30 | }, 31 | "style": { 32 | "noNonNullAssertion": "off" 33 | }, 34 | "correctness": { 35 | "noConstructorReturn": "off", 36 | "noChildrenProp": "off", 37 | "noUnusedImports": "warn" 38 | } 39 | } 40 | }, 41 | "formatter": { 42 | "indentStyle": "tab" 43 | }, 44 | "vcs": { 45 | "enabled": true, 46 | "clientKind": "git", 47 | "useIgnoreFile": true, 48 | "defaultBranch": "main" 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /crates/deno_experiments/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "deno_experiments" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | # deno_runtime = "0.157.0" 10 | tokio = { version = "1.37.0", features = ["macros"] } 11 | -------------------------------------------------------------------------------- /crates/deno_experiments/main.ts: -------------------------------------------------------------------------------- 1 | export function bruh() { 2 | return 1 + 2; 3 | } 4 | 5 | console.log(Deno); 6 | -------------------------------------------------------------------------------- /crates/deno_experiments/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::path::Path; 2 | 3 | // use deno_runtime::{ 4 | // deno_core::{error::AnyError, ModuleSpecifier}, 5 | // deno_napi::v8, 6 | // permissions::PermissionsContainer, 7 | // worker::MainWorker, 8 | // }; 9 | 10 | #[tokio::main] 11 | async fn main() -> Result<(), ()> { 12 | let js_path = Path::new(env!("CARGO_MANIFEST_DIR")).join("main.ts"); 13 | // let main_module = ModuleSpecifier::from_file_path(js_path).unwrap(); 14 | 15 | // let mut worker = MainWorker::bootstrap_from_options( 16 | // main_module.clone(), 17 | // PermissionsContainer::allow_all(), 18 | // Default::default(), 19 | // ); 20 | 21 | // let module_id = worker.preload_main_module(&main_module).await?; 22 | // worker.evaluate_module(module_id).await?; 23 | 24 | // let global = worker.js_runtime.get_module_namespace(module_id).unwrap(); 25 | // let scope = &mut worker.js_runtime.handle_scope(); 26 | 27 | // let key = v8::String::new(scope, "bruh").unwrap().into(); 28 | // let value = global.open(scope).get(scope, key).unwrap(); 29 | // let func = v8::Local::::try_from(value).unwrap(); 30 | 31 | // let recv = v8::Local::new(scope, global).into(); 32 | // let result = func.call(scope, recv, &[]).unwrap(); 33 | // dbg!(result.int32_value(scope)); 34 | 35 | Ok(()) 36 | } 37 | -------------------------------------------------------------------------------- /crates/tauri-plugin-kb-mouse/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "tauri-plugin-kb-mouse" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | enigo = "0.1.3" 10 | rdev = { git = "https://github.com/brendonovich/rdev", features = [ 11 | "macos_noloop", 12 | "serialize", 13 | "specta", 14 | ] } 15 | serde = "1.0.204" 16 | specta = "=2.0.0-rc.7" 17 | tauri = { version = "1.5.4" } 18 | tauri-specta = { version = "=2.0.0-rc.4", features = [ 19 | "javascript", 20 | "typescript", 21 | ] } 22 | tokio = { version = "1.37.0", features = ["time"] } 23 | -------------------------------------------------------------------------------- /crates/tauri-plugin-kb-mouse/guest-js/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./bindings"; 2 | -------------------------------------------------------------------------------- /crates/tauri-plugin-kb-mouse/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tauri-plugin-kb-mouse", 3 | "version": "0.0.0", 4 | "sideEffects": true, 5 | "main": "./guest-js/index.ts", 6 | "types": "./guest-js/index.ts", 7 | "scripts": { 8 | "build:types": "tsc" 9 | }, 10 | "dependencies": { 11 | "@tanstack/solid-query": "^4.36.1", 12 | "@tauri-apps/api": "^1.5.3" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /crates/tauri-plugin-midi/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /Cargo.lock 3 | -------------------------------------------------------------------------------- /crates/tauri-plugin-midi/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "tauri-plugin-midi" 3 | description = "A WebMIDI-compatible plugin for Tauri" 4 | license = "MIT" 5 | version = "0.0.0" 6 | authors = ["You"] 7 | edition = "2021" 8 | rust-version = "1.60" 9 | exclude = ["/examples"] 10 | 11 | [dependencies] 12 | midir = "0.9.1" 13 | serde = "1.0.195" 14 | specta = "=2.0.0-rc.7" 15 | tauri = { version = "1.5.4" } 16 | tauri-specta = { version = "=2.0.0-rc.4", features = [ 17 | "javascript", 18 | "typescript", 19 | ] } 20 | tokio = { version = "1.35.1", features = ["time"] } 21 | 22 | [target.'cfg(target_os = "macos")'.dependencies] 23 | coremidi-hotplug-notification = "0.1.1" 24 | -------------------------------------------------------------------------------- /crates/tauri-plugin-midi/README.md: -------------------------------------------------------------------------------- 1 | # tauri-plugin-midi 2 | 3 | A Tauri plugin designed to emulate the WebMIDI API within Tauri applications. 4 | 5 | Still a huge WIP. 6 | -------------------------------------------------------------------------------- /crates/tauri-plugin-midi/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tauri-plugin-midi", 3 | "version": "0.0.0", 4 | "sideEffects": true, 5 | "main": "./guest-js/index.ts", 6 | "types": "./guest-js/index.ts", 7 | "scripts": { 8 | "build:types": "tsc" 9 | }, 10 | "dependencies": { 11 | "@tanstack/solid-query": "^4.36.1", 12 | "@tauri-apps/api": "^1.5.3" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /interface/src/ActionHistory.tsx: -------------------------------------------------------------------------------- 1 | import { For, Show } from "solid-js"; 2 | import { useInterfaceContext } from "./context"; 3 | 4 | export function ActionHistory() { 5 | const ctx = useInterfaceContext(); 6 | 7 | return ( 8 | 9 | 13 | No History 14 | 15 | } 16 | > 17 | {(entry, i) => ( 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | {(e) => ( 27 | 1, 31 | }} 32 | > 33 | {e.type} 34 | 35 | )} 36 | 37 | 38 | )} 39 | 40 | 41 | ); 42 | } 43 | -------------------------------------------------------------------------------- /interface/src/Sidebar/CommentBox/Info.tsx: -------------------------------------------------------------------------------- 1 | import type { CommentBox } from "@macrograph/runtime"; 2 | import { debounce } from "@solid-primitives/scheduled"; 3 | import { type ParentProps, runWithOwner } from "solid-js"; 4 | 5 | import { SidebarSection } from "../../components/Sidebar"; 6 | import { useInterfaceContext } from "../../context"; 7 | 8 | function Field(props: ParentProps<{ name: string }>) { 9 | return ( 10 | 11 | {props.name} 12 | {props.children} 13 | 14 | ); 15 | } 16 | 17 | export function Info(props: { box: CommentBox }) { 18 | const interfaceCtx = useInterfaceContext(); 19 | 20 | let color: string | undefined; 21 | const saveColour = runWithOwner(null, () => 22 | debounce((graphId: number, boxId: number, tint: string) => { 23 | interfaceCtx.execute("setCommentBoxTint", { 24 | graphId, 25 | boxId, 26 | tint, 27 | prev: color, 28 | }); 29 | color = undefined; 30 | }, 500), 31 | )!; 32 | 33 | return ( 34 | 35 | {props.box.text} 36 | 37 | { 41 | if (color === undefined) color = props.box.tint; 42 | 43 | interfaceCtx.execute( 44 | "setCommentBoxTint", 45 | { 46 | graphId: props.box.graph.id, 47 | boxId: props.box.id, 48 | tint: e.currentTarget.value, 49 | }, 50 | { ephemeral: true }, 51 | ); 52 | 53 | saveColour(props.box.graph.id, props.box.id, e.currentTarget.value); 54 | }} 55 | /> 56 | 57 | 58 | ); 59 | } 60 | -------------------------------------------------------------------------------- /interface/src/Sidebar/CommentBox/index.tsx: -------------------------------------------------------------------------------- 1 | import type { CommentBox } from "@macrograph/runtime"; 2 | 3 | import { Info } from "./Info"; 4 | 5 | export function Sidebar(props: { box: CommentBox }) { 6 | return ; 7 | } 8 | -------------------------------------------------------------------------------- /interface/src/Sidebar/Graph/Outline.tsx: -------------------------------------------------------------------------------- 1 | import type { Graph } from "@macrograph/runtime"; 2 | 3 | import { For } from "solid-js"; 4 | import { produce } from "solid-js/store"; 5 | import { SidebarSection } from "../../components/Sidebar"; 6 | import { useInterfaceContext } from "../../context"; 7 | 8 | export function Outline(props: { graph: Graph }) { 9 | const interfaceCtx = useInterfaceContext(); 10 | 11 | return ( 12 | 13 | 14 | Comment Boxes 15 | 16 | 17 | 18 | {(box) => ( 19 | 20 | { 24 | interfaceCtx.execute("setGraphSelection", { 25 | graphId: props.graph.id, 26 | selection: [{ type: "commentBox", id: box.id }], 27 | }); 28 | // interfaceCtx.setGraphStates( 29 | // produce((states) => { 30 | // const state = states.find((s) => s.id === props.graph.id); 31 | // if (!state) return; 32 | 33 | // state.translate = { 34 | // x: 35 | // box.position.x + 36 | // box.size.x / 2 - 37 | // interfaceCtx.graphBounds.width / 2, 38 | // y: 39 | // box.position.y + 40 | // box.size.y / 2 - 41 | // interfaceCtx.graphBounds.height / 2, 42 | // }; 43 | // }), 44 | // ); 45 | }} 46 | > 47 | {box.text} 48 | 49 | 50 | )} 51 | 52 | 53 | 54 | ); 55 | } 56 | -------------------------------------------------------------------------------- /interface/src/Sidebar/Graph/index.tsx: -------------------------------------------------------------------------------- 1 | import type { Graph } from "@macrograph/runtime"; 2 | 3 | import { Outline } from "./Outline"; 4 | import { Variables } from "./Variables"; 5 | 6 | export function Sidebar(props: { graph: Graph }) { 7 | return ( 8 | <> 9 | 10 | 11 | > 12 | ); 13 | } 14 | -------------------------------------------------------------------------------- /interface/src/Sidebar/Node/Info.tsx: -------------------------------------------------------------------------------- 1 | import type { Node } from "@macrograph/runtime"; 2 | import type { ParentProps } from "solid-js"; 3 | 4 | import { SidebarSection } from "../../components/Sidebar"; 5 | 6 | function Field(props: ParentProps<{ name: string }>) { 7 | return ( 8 | 9 | {props.name} 10 | {props.children} 11 | 12 | ); 13 | } 14 | 15 | export function NodeInfo(props: { node: Node }) { 16 | return ( 17 | 18 | {props.node.state.name} 19 | {props.node.schema.name} 20 | {props.node.schema.package.name} 21 | 22 | ); 23 | } 24 | -------------------------------------------------------------------------------- /interface/src/Sidebar/Node/index.tsx: -------------------------------------------------------------------------------- 1 | import type { Node } from "@macrograph/runtime"; 2 | import { Show } from "solid-js"; 3 | 4 | import { NodeInfo } from "./Info"; 5 | import { Properties } from "./Properties"; 6 | 7 | export function Sidebar(props: { node: Node }) { 8 | return ( 9 | <> 10 | 11 | 14 | {(properties) => ( 15 | 16 | )} 17 | 18 | > 19 | ); 20 | } 21 | -------------------------------------------------------------------------------- /interface/src/Sidebar/Project/Variables.tsx: -------------------------------------------------------------------------------- 1 | import type { Project } from "@macrograph/runtime"; 2 | import { useInterfaceContext } from "../../context"; 3 | import { Variables as VariablesRoot } from "../Variables"; 4 | 5 | export function Variables(props: { project: Project }) { 6 | const interfaceCtx = useInterfaceContext(); 7 | 8 | return ( 9 | { 13 | interfaceCtx.execute("createVariable", { location: "project" }); 14 | }} 15 | onRemoveVariable={(id) => { 16 | interfaceCtx.execute("deleteVariable", { 17 | location: "project", 18 | variableId: id, 19 | }); 20 | }} 21 | onSetVariableValue={(id, value) => { 22 | interfaceCtx.execute("setVariableValue", { 23 | location: "project", 24 | variableId: id, 25 | value, 26 | }); 27 | }} 28 | onSetVariableType={(id, type) => { 29 | interfaceCtx.execute("setVariableType", { 30 | location: "project", 31 | variableId: id, 32 | type, 33 | }); 34 | }} 35 | onVariableNameChanged={(id, name) => { 36 | interfaceCtx.execute("setVariableName", { 37 | location: "project", 38 | variableId: id, 39 | name, 40 | }); 41 | }} 42 | /> 43 | ); 44 | } 45 | -------------------------------------------------------------------------------- /interface/src/Sidebar/Project/index.tsx: -------------------------------------------------------------------------------- 1 | import type { Graph, Project } from "@macrograph/runtime"; 2 | 3 | import { CustomTypes } from "./CustomTypes"; 4 | import { Graphs } from "./Graphs"; 5 | import { PrintOutput } from "./PrintOutput"; 6 | import { Resources } from "./Resources"; 7 | import { Variables } from "./Variables"; 8 | 9 | export function Sidebar(props: { 10 | project: Project; 11 | currentGraph?: Graph; 12 | onGraphClicked(graph: Graph): void; 13 | }) { 14 | return ( 15 | <> 16 | 20 | 21 | 22 | 23 | 24 | > 25 | ); 26 | } 27 | -------------------------------------------------------------------------------- /interface/src/Sidebar/SearchInput.tsx: -------------------------------------------------------------------------------- 1 | import type { ComponentProps } from "solid-js"; 2 | 3 | export function SearchInput( 4 | props: Omit< 5 | ComponentProps<"input">, 6 | "onKeyDown" | "type" | "class" | "placeholder" 7 | >, 8 | ) { 9 | return ( 10 | e.stopPropagation()} 12 | onKeyUp={(e) => e.stopPropagation()} 13 | type="text" 14 | class="h-6 w-full flex-1 bg-neutral-900 border-none rounded-sm text-xs !pl-1.5 focus:outline-none focus:ring-1 focus:ring-mg-focus" 15 | placeholder="Search" 16 | {...props} 17 | /> 18 | ); 19 | } 20 | -------------------------------------------------------------------------------- /interface/src/Sidebar/index.tsx: -------------------------------------------------------------------------------- 1 | export { Sidebar as Graph } from "./Graph"; 2 | export { Sidebar as Project } from "./Project"; 3 | export { Sidebar as Node } from "./Node"; 4 | export { Sidebar as CommentBox } from "./CommentBox"; 5 | -------------------------------------------------------------------------------- /interface/src/components/Graph/Connection/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./ConnectionRenderer"; 2 | -------------------------------------------------------------------------------- /interface/src/components/Graph/Context.tsx: -------------------------------------------------------------------------------- 1 | import type { Graph as GraphModel, XY } from "@macrograph/runtime"; 2 | import { createContextProvider } from "@solid-primitives/context"; 3 | import type * as Solid from "solid-js"; 4 | 5 | export type SelectedItemID = 6 | | { type: "node"; id: number } 7 | | { type: "commentBox"; id: number }; 8 | 9 | export function toGraphSpace(clientXY: XY, bounds: XY, state: GraphState) { 10 | return { 11 | x: (clientXY.x - bounds.x) / state.scale + state.translate.x, 12 | y: (clientXY.y - bounds.y) / state.scale + state.translate.y, 13 | }; 14 | } 15 | 16 | export function toScreenSpace(graphXY: XY, bounds: XY, state: GraphState) { 17 | return { 18 | x: (graphXY.x - state.translate.x) * state.scale + bounds.x, 19 | y: (graphXY.y - state.translate.y) * state.scale + bounds.y, 20 | }; 21 | } 22 | 23 | export function makeGraphState(model: GraphModel) { 24 | return { 25 | id: model.id, 26 | translate: { 27 | x: 0, 28 | y: 0, 29 | } as XY, 30 | scale: 1, 31 | selectedItemIds: [] as SelectedItemID[], 32 | }; 33 | } 34 | 35 | export type GraphState = ReturnType; 36 | 37 | export type GraphContext = { 38 | model: Solid.Accessor; 39 | state: GraphState; 40 | offset: XY; 41 | toGraphSpace(pos: XY): XY; 42 | toScreenSpace(pos: XY): XY; 43 | }; 44 | 45 | export const [GraphContextProvider, useGraphContext] = createContextProvider( 46 | (props: { value: GraphContext }) => props.value, 47 | null!, 48 | ); 49 | -------------------------------------------------------------------------------- /interface/src/components/Graph/ContextMenu.tsx: -------------------------------------------------------------------------------- 1 | import { ContextMenu } from "@kobalte/core"; 2 | import type { ComponentProps } from "solid-js"; 3 | import clsx from "clsx"; 4 | 5 | import { tw } from "../../util"; 6 | import { useInlineTextEditorCtx } from "../../Sidebar/InlineTextEditor"; 7 | 8 | export const ContextMenuItem = tw( 9 | ContextMenu.Item, 10 | )`px-1.5 py-1.5 outline-none ui-highlighted:bg-white/10 rounded-sm flex flex-row items-center gap-2`; 11 | 12 | export function ContextMenuRenameItem() { 13 | const inlineEditorContext = useInlineTextEditorCtx()!; 14 | 15 | return ( 16 | { 18 | inlineEditorContext.setEditing(true); 19 | }} 20 | > 21 | Rename 22 | 23 | ); 24 | } 25 | 26 | export function ContextMenuContent( 27 | props: Omit>, "onKeyDown">, 28 | ) { 29 | return ( 30 | 31 | e.stopPropagation()} 34 | class={clsx( 35 | "border border-black rounded bg-neutral-900 min-w-32 text-sm ui-expanded:animate-in ui-expanded:fade-in ui-expanded:zoom-in-95 origin-top-left ui-closed:animate-out ui-closed:fade-out ui-closed:zoom-out-95 p-1 focus:outline-none select-none text-neutral-300", 36 | props.class, 37 | )} 38 | > 39 | {props.children} 40 | 41 | 42 | ); 43 | } 44 | -------------------------------------------------------------------------------- /interface/src/components/Graph/IO/DataOutput.tsx: -------------------------------------------------------------------------------- 1 | import type { DataOutput as DataOutputModel } from "@macrograph/runtime"; 2 | import { Show } from "solid-js"; 3 | import { DataPin } from "./DataPin"; 4 | 5 | interface Props { 6 | output: DataOutputModel; 7 | } 8 | 9 | export const DataOutput = (props: Props) => { 10 | return ( 11 | 12 | {(name) => {name()}} 13 | 14 | 15 | ); 16 | }; 17 | -------------------------------------------------------------------------------- /interface/src/components/Graph/IO/ExecInput.tsx: -------------------------------------------------------------------------------- 1 | import type { ExecInput as ExecInputModel } from "@macrograph/runtime"; 2 | import { Show } from "solid-js"; 3 | 4 | import { usePin } from "./usePin"; 5 | 6 | interface Props { 7 | input: ExecInputModel; 8 | } 9 | 10 | export const ExecInput = (props: Props) => { 11 | const { ref, highlight, dim } = usePin(() => props.input); 12 | 13 | return ( 14 | 15 | 20 | 0 || highlight() 27 | ? "white" 28 | : "currentColor" 29 | } 30 | xmlns="http://www.w3.org/2000/svg" 31 | > 32 | 37 | 38 | 39 | {(name) => {name()}} 40 | 41 | ); 42 | }; 43 | -------------------------------------------------------------------------------- /interface/src/components/Graph/IO/ExecOutput.tsx: -------------------------------------------------------------------------------- 1 | import type { ExecOutput as ExecOutputModel } from "@macrograph/runtime"; 2 | import { Show } from "solid-js"; 3 | 4 | import { usePin } from "./usePin"; 5 | 6 | interface Props { 7 | output: ExecOutputModel; 8 | } 9 | 10 | export const ExecOutput = (props: Props) => { 11 | const { ref, highlight, dim } = usePin(() => props.output); 12 | 13 | return ( 14 | 15 | {(name) => {name()}} 16 | 21 | 33 | 38 | 39 | 40 | 41 | ); 42 | }; 43 | -------------------------------------------------------------------------------- /interface/src/components/Graph/IO/ScopeInput.tsx: -------------------------------------------------------------------------------- 1 | import type { ScopeInput as ScopeInputModel } from "@macrograph/runtime"; 2 | import { Show } from "solid-js"; 3 | 4 | import { usePin } from "./usePin"; 5 | 6 | interface Props { 7 | input: ScopeInputModel; 8 | } 9 | 10 | export const ScopeInput = (props: Props) => { 11 | const { ref, highlight, dim } = usePin(() => props.input); 12 | 13 | return ( 14 | 15 | 20 | 32 | 37 | 38 | 39 | {(name) => {name()}} 40 | 41 | ); 42 | }; 43 | -------------------------------------------------------------------------------- /interface/src/components/Graph/IO/ScopeOutput.tsx: -------------------------------------------------------------------------------- 1 | import type { ScopeOutput as ScopeOutputModel } from "@macrograph/runtime"; 2 | import { Show } from "solid-js"; 3 | 4 | import { usePin } from "./usePin"; 5 | 6 | interface Props { 7 | output: ScopeOutputModel; 8 | } 9 | 10 | export const ScopeOutput = (props: Props) => { 11 | const { ref, highlight, dim } = usePin(() => props.output); 12 | 13 | return ( 14 | 15 | {(name) => {name()}} 16 | 21 | 33 | 38 | 39 | 40 | 41 | ); 42 | }; 43 | -------------------------------------------------------------------------------- /interface/src/components/Graph/IO/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./DataInput"; 2 | export * from "./DataPin"; 3 | export * from "./ExecInput"; 4 | export * from "./ExecOutput"; 5 | export * from "./DataOutput"; 6 | export * from "./ScopeInput"; 7 | export * from "./ScopeOutput"; 8 | -------------------------------------------------------------------------------- /interface/src/components/Graph/Node.css: -------------------------------------------------------------------------------- 1 | .active-fade-in { 2 | opacity: 0.5; 3 | } 4 | -------------------------------------------------------------------------------- /interface/src/components/Graph/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./IO"; 2 | export * from "./Node"; 3 | export * from "./Connection"; 4 | export * from "./Graph"; 5 | -------------------------------------------------------------------------------- /interface/src/components/SchemaMenu/state.ts: -------------------------------------------------------------------------------- 1 | import type { Pin, XY } from "@macrograph/runtime"; 2 | 3 | import type { GraphState } from "../Graph/Context"; 4 | 5 | export type SchemaMenuState = 6 | | { status: "closed" } 7 | | { 8 | status: "open"; 9 | position: XY; 10 | graph: GraphState; 11 | suggestion?: { pin: Pin }; 12 | }; 13 | 14 | export function createSchemaMenu(): SchemaMenuState { 15 | return { status: "closed" }; 16 | } 17 | -------------------------------------------------------------------------------- /interface/src/components/ui/CheckBox.tsx: -------------------------------------------------------------------------------- 1 | import clsx from "clsx"; 2 | 3 | interface Props { 4 | value: boolean; 5 | onChange(value: boolean): void; 6 | class?: string; 7 | } 8 | 9 | export const CheckBox = (props: Props) => { 10 | return ( 11 | 12 | props.onChange(e.target.checked)} 15 | checked={props.value} 16 | class={clsx("absolute opacity-0 w-0 h-0 peer")} 17 | /> 18 | 19 | 26 | 27 | 28 | 29 | ); 30 | }; 31 | -------------------------------------------------------------------------------- /interface/src/components/ui/FloatInput.tsx: -------------------------------------------------------------------------------- 1 | import { createEffect, createSignal } from "solid-js"; 2 | import { Input } from "./Input"; 3 | 4 | interface Props { 5 | initialValue: number; 6 | onChange(v: number): void; 7 | class?: string; 8 | value?: number; 9 | } 10 | 11 | export const FloatInput = (props: Props) => { 12 | // if NaN reset to 0 13 | // const initialValue = Number.isNaN(props.initialValue) 14 | // ? 0 15 | // : props.initialValue; 16 | 17 | const [value, setValue] = createSignal(props.initialValue.toString()); 18 | createEffect(() => { 19 | if (props.value !== undefined) setValue(props.value.toString()); 20 | }); 21 | 22 | return ( 23 | e.stopPropagation()} 27 | onKeyUp={(e) => e.stopPropagation()} 28 | onChange={(e) => { 29 | e.stopPropagation(); 30 | 31 | const value = e.target.value; 32 | setValue(value); 33 | 34 | const numValue = Number.parseFloat(value); 35 | if (!Number.isNaN(numValue)) props.onChange(numValue); 36 | }} 37 | onBlur={(e) => { 38 | const s = e.target.value; 39 | const num = Number.parseFloat(s); 40 | 41 | if (s.length === 0) { 42 | setValue("0"); 43 | props.onChange(0); 44 | } else if (Number.isNaN(num)) { 45 | setValue(props.initialValue.toString()); 46 | } else { 47 | setValue(num.toString()); 48 | props.onChange(num); 49 | } 50 | }} 51 | class={props.class} 52 | /> 53 | ); 54 | }; 55 | -------------------------------------------------------------------------------- /interface/src/components/ui/Input.tsx: -------------------------------------------------------------------------------- 1 | import { tw } from "../../util"; 2 | 3 | export const Input = tw.input`p-0 pl-1.5 h-6 w-full bg-neutral-900 border-neutral-700 rounded-sm text-xs appearance-none focus:ring-0 focus:border-mg-focus`; 4 | -------------------------------------------------------------------------------- /interface/src/components/ui/index.tsx: -------------------------------------------------------------------------------- 1 | import { tw } from "../../util"; 2 | 3 | export * from "./CheckBox"; 4 | export * from "./TextInput"; 5 | export * from "./IntInput"; 6 | export * from "./FloatInput"; 7 | export * from "./EnumInput"; 8 | 9 | export const IconButton = tw.button`hover:bg-white/10 rounded transition-colors`; 10 | -------------------------------------------------------------------------------- /interface/src/global.css: -------------------------------------------------------------------------------- 1 | // add the code bellow 2 | /* @layer utilities { 3 | @variants responsive { */ 4 | /* Hide scrollbar for Chrome, Safari and Opera */ 5 | .no-scrollbar::-webkit-scrollbar { 6 | display: none; 7 | } 8 | 9 | /* Hide scrollbar for IE, Edge and Firefox */ 10 | .no-scrollbar { 11 | -ms-overflow-style: none; /* IE and Edge */ 12 | scrollbar-width: none; /* Firefox */ 13 | } 14 | /* } 15 | } */ 16 | 17 | * { 18 | min-width: 0; 19 | } 20 | -------------------------------------------------------------------------------- /interface/src/platform.ts: -------------------------------------------------------------------------------- 1 | import { useContext } from "solid-js"; 2 | import { createContext } from "solid-js"; 3 | 4 | export interface Platform { 5 | projectPersistence?: { 6 | saveProject(saveAs?: boolean): Promise; 7 | loadProject(): Promise; 8 | url: string | null; 9 | }; 10 | clipboard: { 11 | readText(): Promise; 12 | writeText(text: string): Promise; 13 | }; 14 | } 15 | 16 | export const PlatformContext = createContext(null); 17 | 18 | export function usePlatform() { 19 | const ctx = useContext(PlatformContext); 20 | 21 | if (!ctx) throw new Error("PlatformContext not mounted"); 22 | 23 | return ctx; 24 | } 25 | -------------------------------------------------------------------------------- /interface/src/settings/ui.tsx: -------------------------------------------------------------------------------- 1 | import type { ComponentProps } from "solid-js"; 2 | 3 | export const Button = (props: ComponentProps<"button">) => ( 4 | 9 | {props.children} 10 | 11 | ); 12 | 13 | export const Input = (props: ComponentProps<"input">) => ( 14 | 19 | ); 20 | -------------------------------------------------------------------------------- /interface/tailwind.config.js: -------------------------------------------------------------------------------- 1 | // just exists to get lsp 2 | module.exports = { 3 | content: [ 4 | `${__dirname}/src/**/*.{tsx,ts,html}`, 5 | `${__dirname}/../{apps,packages}/*/src/**/*.{tsx,ts,html}`, 6 | ], 7 | theme: { 8 | extend: { 9 | colors: { 10 | mg: { 11 | bool: "#DC2626", 12 | event: "#C20000", 13 | string: "#DA5697", 14 | exec: "#2163EB", 15 | int: "#30F3DB", 16 | pure: "#008E62", 17 | float: "#00AE75", 18 | graph: "#262626", 19 | base: "#696969", 20 | enum: "#1B4DFF", 21 | struct: "#FACC15", 22 | current: "var(--mg-current)", 23 | focus: "#eab308", 24 | }, 25 | }, 26 | }, 27 | }, 28 | plugins: [ 29 | require("@tailwindcss/forms"), 30 | require("@kobalte/tailwindcss"), 31 | require("tailwind-scrollbar"), 32 | ], 33 | presets: [require("@macrograph/ui/tailwind.preset.js")], 34 | }; 35 | -------------------------------------------------------------------------------- /interface/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@macrograph/config/tsconfig", 3 | "compilerOptions": { 4 | "outDir": "./dist", 5 | "types": ["unplugin-icons/types/solid", "@macrograph/ui/types"] 6 | }, 7 | "include": ["src"], 8 | "references": [ 9 | { "path": "../packages/runtime" }, 10 | { "path": "../packages/packages" }, 11 | { "path": "../packages/typesystem" }, 12 | { "path": "../packages/clipboard" } 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "desktop": "pnpm --filter @macrograph/desktop --", 5 | "web": "pnpm --filter @macrograph/web --", 6 | "runtime": "pnpm --filter @macrograph/runtime --", 7 | "test": "vitest", 8 | "typecheck": "pnpm tsc -b", 9 | "lint": "turbo run lint", 10 | "lint:fix": "turbo run lint -- --fix", 11 | "dev:web": "pnpm web dev", 12 | "dev:desktop": "pnpm desktop tauri dev", 13 | "format": "biome format --write .", 14 | "fix": "biome lint --apply . && biome format --write . && biome check . --apply" 15 | }, 16 | "devDependencies": { 17 | "@testing-library/jest-dom": "^6.4.8", 18 | "@vitest/browser": "^0.32.4", 19 | "@vitest/ui": "^2.0.5", 20 | "jsdom": "^24.1.1", 21 | "safaridriver": "^0.0.5", 22 | "solid-js": "^1.9.7", 23 | "turbo": "^1.13.0", 24 | "turbo-ignore": "^1.13.0", 25 | "typescript": "^5.5.3", 26 | "vite-plugin-solid": "^2.10.2", 27 | "vitest": "^2.0.5", 28 | "vitest-github-actions-reporter": "^0.11.1", 29 | "webdriverio": "^8.35.1" 30 | }, 31 | "dependencies": { 32 | "@biomejs/biome": "^1.6.4" 33 | }, 34 | "pnpm": { 35 | "patchedDependencies": { 36 | "@kobalte/core@0.13.7": "patches/@kobalte__core@0.13.7.patch" 37 | } 38 | }, 39 | "packageManager": "pnpm@9.15.6+sha512.139cab068fdf0b751268179ac5f909b5be72afb4a75c513d1905d151befc8977b593d3cf8671ed83d4d6637c5c94b98ffbce108125de4a5a27a31233601a99de" 40 | } 41 | -------------------------------------------------------------------------------- /packages/action-history/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@macrograph/action-history", 3 | "private": true, 4 | "main": "./src/index.ts", 5 | "types": "./src/index.ts", 6 | "type": "module", 7 | "scripts": { 8 | "clean": "rm -rf .turbo && rm -rf node_modules && rm -rf dist", 9 | "test": "vitest", 10 | "typecheck": "tsc -b" 11 | }, 12 | "devDependencies": { 13 | "@macrograph/config": "workspace:*" 14 | }, 15 | "dependencies": { 16 | "solid-js": "^1.9.7" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /packages/action-history/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@macrograph/config/tsconfig", 3 | "compilerOptions": { 4 | "outDir": "dist" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /packages/api-contract/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@macrograph/api-contract", 3 | "private": true, 4 | "main": "./src/index.ts", 5 | "types": "./src/index.ts", 6 | "type": "module", 7 | "dependencies": { 8 | "@tanstack/solid-query": "^4.36.1", 9 | "@ts-rest/core": "^3.52.1", 10 | "zod": "^3.25.36" 11 | }, 12 | "devDependencies": { 13 | "@macrograph/config": "workspace:*", 14 | "@total-typescript/ts-reset": "^0.4.2" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /packages/api-contract/src/index.ts: -------------------------------------------------------------------------------- 1 | import { initContract } from "@ts-rest/core"; 2 | import { z } from "zod"; 3 | 4 | const c = initContract(); 5 | 6 | export const OAUTH_TOKEN = z.object({ 7 | access_token: z.string(), 8 | expires_in: z.number(), 9 | refresh_token: z.string().optional(), 10 | token_type: z.string(), 11 | }); 12 | 13 | export const CREDENTIAL = z.object({ 14 | provider: z.string(), 15 | id: z.string(), 16 | displayName: z.string().nullable(), 17 | token: OAUTH_TOKEN.and(z.object({ issuedAt: z.number() })), 18 | }); 19 | 20 | export type Credential = z.infer; 21 | 22 | export const contract = c.router({ 23 | getCredentials: { 24 | method: "GET", 25 | path: "/credentials", 26 | responses: { 27 | 200: z.array(CREDENTIAL), 28 | }, 29 | summary: "Gets all credentials linked to an account", 30 | }, 31 | refreshCredential: { 32 | method: "POST", 33 | path: "/credentials/:providerId/:providerUserId/refresh", 34 | pathParams: z.object({ 35 | providerId: z.string(), 36 | providerUserId: z.string(), 37 | }), 38 | responses: { 39 | 200: CREDENTIAL, 40 | }, 41 | body: null, 42 | summary: "Refreshes a credential if it hasn't been recently refreshed", 43 | }, 44 | getUser: { 45 | method: "GET", 46 | path: "/user", 47 | responses: { 48 | 200: z.object({ id: z.string(), email: z.string() }).nullable(), 49 | }, 50 | summary: "Gets the currently logged in user, if there is one", 51 | }, 52 | }); 53 | -------------------------------------------------------------------------------- /packages/api-contract/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@macrograph/config/tsconfig", 3 | "compilerOptions": { 4 | "outDir": "dist" 5 | }, 6 | "references": [{ "path": "../runtime" }, { "path": "../typesystem" }] 7 | } 8 | -------------------------------------------------------------------------------- /packages/clipboard/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@macrograph/clipboard", 3 | "private": true, 4 | "main": "./src/index.ts", 5 | "types": "./src/index.ts", 6 | "type": "module", 7 | "scripts": { 8 | "clean": "rm -rf .turbo && rm -rf node_modules && rm -rf dist", 9 | "test": "vitest", 10 | "typecheck": "tsc -b" 11 | }, 12 | "dependencies": { 13 | "@macrograph/option": "workspace:*", 14 | "@macrograph/runtime": "workspace:*", 15 | "@macrograph/runtime-serde": "workspace:*", 16 | "valibot": "^0.37.0" 17 | }, 18 | "devDependencies": { 19 | "@macrograph/config": "workspace:*" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /packages/clipboard/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@macrograph/config/tsconfig", 3 | "compilerOptions": { 4 | "outDir": "dist" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /packages/config/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@macrograph/config", 3 | "type": "module", 4 | "private": true, 5 | "exports": { 6 | "./tsconfig": "./tsconfig.json" 7 | }, 8 | "dependencies": { 9 | "@tanstack/solid-query": "^4.36.1" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /packages/config/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "incremental": true, 4 | "strict": true, 5 | "target": "ESNext", 6 | "module": "ESNext", 7 | "lib": ["dom", "dom.iterable", "ESNext"], 8 | "moduleResolution": "node", 9 | "jsx": "preserve", 10 | "jsxImportSource": "solid-js", 11 | "allowSyntheticDefaultImports": true, 12 | "esModuleInterop": true, 13 | "noUncheckedIndexedAccess": true, 14 | "emitDeclarationOnly": true, 15 | "isolatedModules": true, 16 | "composite": true, 17 | "skipLibCheck": true 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /packages/effect-server-fn/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "effect-server-fn", 3 | "main": "./src/index.ts", 4 | "types": "./src/index.ts", 5 | "devDependencies": { 6 | "@macrograph/config": "workspace:*" 7 | }, 8 | "dependencies": { 9 | "effect": "^3.14.2" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /packages/effect-server-fn/src/index.ts: -------------------------------------------------------------------------------- 1 | export * as ServerFn from "./ServerFn"; 2 | -------------------------------------------------------------------------------- /packages/effect-server-fn/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@macrograph/config/tsconfig", 3 | "compilerOptions": { 4 | "outDir": "dist" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /packages/http-client/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@macrograph/http-client", 3 | "private": true, 4 | "main": "./src/index.ts", 5 | "types": "./src/index.ts", 6 | "type": "module", 7 | "scripts": { 8 | "clean": "rm -rf .turbo && rm -rf node_modules && rm -rf dist", 9 | "test": "vitest", 10 | "typecheck": "tsc -b" 11 | }, 12 | "devDependencies": { 13 | "@macrograph/config": "workspace:*" 14 | }, 15 | "dependencies": { 16 | "@tanstack/solid-query": "^4.36.1" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /packages/http-client/src/index.ts: -------------------------------------------------------------------------------- 1 | export function createHTTPClient(args: { 2 | root: string; 3 | fetch: (ctx: TCtx, ...args: Parameters) => Promise; 4 | }) { 5 | return { 6 | call( 7 | restPath: TPath, 8 | ctx: TCtx, 9 | init?: Omit, 10 | ): Promise { 11 | const [method, path] = splitRESTPath(restPath as any); 12 | return args.fetch(ctx, `${args.root}${path}`, { 13 | method, 14 | ...init, 15 | }); 16 | }, 17 | }; 18 | } 19 | 20 | type HTTPMethod = "GET" | "POST" | "PUT" | "PATCH"; 21 | type RESTPath = `${HTTPMethod} ${string}`; 22 | type RESTDefinitions = { 23 | [K: RESTPath]: any; 24 | }; 25 | 26 | function splitRESTPath(restPath: T) { 27 | const [method, path]: [HTTPMethod, string] = restPath.split(" ") as any; 28 | return [method, path] as const; 29 | } 30 | -------------------------------------------------------------------------------- /packages/http-client/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@macrograph/config/tsconfig", 3 | "compilerOptions": { 4 | "outDir": "dist" 5 | }, 6 | "references": [{ "path": "../runtime" }, { "path": "../typesystem" }] 7 | } 8 | -------------------------------------------------------------------------------- /packages/json/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@macrograph/json", 3 | "private": true, 4 | "main": "./src/index.ts", 5 | "types": "./src/index.ts", 6 | "type": "module", 7 | "scripts": { 8 | "clean": "rm -rf .turbo && rm -rf node_modules && rm -rf dist", 9 | "test": "vitest", 10 | "typecheck": "tsc -b" 11 | }, 12 | "dependencies": { 13 | "@macrograph/option": "workspace:*", 14 | "@macrograph/runtime": "workspace:*", 15 | "@macrograph/runtime-serde": "workspace:*", 16 | "@macrograph/typesystem": "workspace:*", 17 | "@solid-primitives/deep": "^0.2.7", 18 | "@solid-primitives/map": "^0.4.11", 19 | "@solid-primitives/set": "^0.4.11", 20 | "@tanstack/solid-query": "^4.36.1", 21 | "zod": "^3.25.36" 22 | }, 23 | "devDependencies": { 24 | "@macrograph/config": "workspace:*", 25 | "@total-typescript/ts-reset": "^0.4.2", 26 | "safaridriver": "^0.0.5", 27 | "solid-js": "^1.9.7", 28 | "type-fest": "^2.19.0", 29 | "webdriverio": "^8.35.1" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /packages/json/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./type"; 2 | export * from "./pkg"; 3 | export * from "./conversion"; 4 | -------------------------------------------------------------------------------- /packages/json/src/type.ts: -------------------------------------------------------------------------------- 1 | import { createEnum } from "@macrograph/runtime"; 2 | import { 3 | type Enum, 4 | type EnumBuilder, 5 | type EnumVariant, 6 | type EnumVariants, 7 | type Field, 8 | type InferEnumVariant, 9 | t, 10 | } from "@macrograph/typesystem"; 11 | import type { ReactiveMap } from "@solid-primitives/map"; 12 | 13 | const JSONLiteralVariants = (e: EnumBuilder) => 14 | [ 15 | e.variant("Null"), 16 | e.variant("Bool", { value: t.bool() }), 17 | e.variant("Number", { value: t.float() }), 18 | e.variant("String", { value: t.string() }), 19 | ] satisfies EnumVariants; 20 | 21 | type JSONLiteralVariantTypes = ReturnType; 22 | type JSONLiteralValue = InferEnumVariant; 23 | export type JSONValue = 24 | | JSONLiteralValue 25 | | { variant: "List"; data: { value: JSONValue[] } } 26 | | { variant: "Map"; data: { value: ReactiveMap } }; 27 | 28 | type JSONVariantTypes = [ 29 | ...JSONLiteralVariantTypes, 30 | EnumVariant<"List", { value: Field>>> }>, 31 | EnumVariant<"Map", { value: Field>>> }>, 32 | ]; 33 | 34 | export const JSONEnum = createEnum("JSON", (e) => 35 | e.lazy(() => [ 36 | ...JSONLiteralVariants(e), 37 | e.variant("List", { value: t.list(t.enum(JSONEnum)) }), 38 | e.variant("Map", { value: t.map(t.enum(JSONEnum)) }), 39 | ]), 40 | ) as Enum; 41 | 42 | type Bruh = Extract< 43 | InferEnumVariant, 44 | { variant: "Map" } 45 | >; 46 | -------------------------------------------------------------------------------- /packages/json/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@macrograph/config/tsconfig", 3 | "compilerOptions": { 4 | "outDir": "dist" 5 | }, 6 | "references": [{ "path": "../runtime" }, { "path": "../typesystem" }] 7 | } 8 | -------------------------------------------------------------------------------- /packages/option/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@macrograph/option", 3 | "private": true, 4 | "main": "./src/index.ts", 5 | "types": "./src/index.ts", 6 | "type": "module", 7 | "scripts": { 8 | "clean": "rm -rf .turbo && rm -rf node_modules && rm -rf dist", 9 | "test": "vitest", 10 | "typecheck": "tsc -b" 11 | }, 12 | "devDependencies": { 13 | "@macrograph/config": "workspace:*", 14 | "solid-js": "^1.9.7" 15 | }, 16 | "dependencies": { 17 | "@tanstack/solid-query": "^4.36.1" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /packages/option/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@macrograph/config/tsconfig", 3 | "compilerOptions": { 4 | "outDir": "dist" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /packages/packages/src/discord/resource.ts: -------------------------------------------------------------------------------- 1 | import { type PropertyDef, createResourceType } from "@macrograph/runtime"; 2 | import type { Pkg } from "."; 3 | 4 | export const DiscordAccount = createResourceType({ 5 | name: "Discord Account", 6 | sources: (pkg: Pkg) => { 7 | const allAccounts = [...pkg.ctx!.auth.accounts]; 8 | 9 | return allAccounts 10 | .map(([id, data]) => { 11 | const d = data(); 12 | if (!d) return; 13 | return [id, d] as const; 14 | }) 15 | .filter(Boolean) 16 | .map(([userId, account]) => ({ 17 | id: userId, 18 | display: account.data.username, 19 | value: account, 20 | })); 21 | }, 22 | }); 23 | 24 | export const accountProperty = { 25 | name: "Discord Account", 26 | resource: DiscordAccount, 27 | } satisfies PropertyDef; 28 | 29 | export const defaultProperties = { account: accountProperty }; 30 | 31 | export const DiscordBot = createResourceType({ 32 | name: "Discord Bot", 33 | sources: (pkg: Pkg) => { 34 | const allBots = [...pkg.ctx!.auth.bots]; 35 | 36 | return allBots 37 | .map(([id, data]) => { 38 | const d = data(); 39 | if (!d) return; 40 | return [id, d] as const; 41 | }) 42 | .filter(Boolean) 43 | .map(([userId, account]) => ({ 44 | id: userId, 45 | display: account.data.username, 46 | value: account, 47 | })); 48 | }, 49 | }); 50 | 51 | export const botProperty = { 52 | name: "Discord Bot", 53 | resource: DiscordBot, 54 | } satisfies PropertyDef; 55 | -------------------------------------------------------------------------------- /packages/packages/src/discord/schemas.ts: -------------------------------------------------------------------------------- 1 | import * as v from "valibot"; 2 | 3 | const NullishString = v.nullish(v.string()); 4 | const NullishBoolean = v.nullish(v.boolean()); 5 | const NullishNumber = v.nullish(v.number()); 6 | 7 | export const USER_SCHEMA = v.object({ 8 | id: v.string(), 9 | username: v.string(), 10 | global_name: NullishString, 11 | discriminator: v.string(), 12 | avatar: NullishString, 13 | bot: NullishBoolean, 14 | system: NullishBoolean, 15 | mfa_enabled: NullishBoolean, 16 | banner: NullishString, 17 | accent_color: NullishNumber, 18 | locale: NullishString, 19 | verified: NullishBoolean, 20 | email: NullishString, 21 | flags: NullishNumber, 22 | premium_type: NullishNumber, 23 | public_flags: NullishNumber, 24 | }); 25 | 26 | export const GUILD_MEMBER_SCHEMA = v.object({ 27 | user: v.optional(USER_SCHEMA), 28 | nick: v.optional(v.string()), 29 | roles: v.array(v.string()), 30 | }); 31 | 32 | export const ROLE_SCHEMA = v.object({ 33 | color: v.number(), 34 | flags: v.number(), 35 | hoist: v.boolean(), 36 | id: v.string(), 37 | managed: v.boolean(), 38 | mentionable: v.boolean(), 39 | permissions: v.string(), 40 | position: v.number(), 41 | name: v.string(), 42 | }); 43 | -------------------------------------------------------------------------------- /packages/packages/src/elevenlabs/Settings.tsx: -------------------------------------------------------------------------------- 1 | import { Some } from "@macrograph/option"; 2 | import { Button, Input } from "@macrograph/ui"; 3 | import { createForm } from "@tanstack/solid-form"; 4 | import { Match, Switch, createSignal } from "solid-js"; 5 | 6 | import type { Ctx } from "./ctx"; 7 | 8 | export default function ({ setKey, key }: Ctx) { 9 | const [update, setUpdate] = createSignal(""); 10 | 11 | const form = createForm(() => ({ 12 | defaultValues: { key: key().unwrapOr("") }, 13 | onSubmit: ({ value }) => { 14 | setKey(Some(value.key)); 15 | }, 16 | })); 17 | 18 | return ( 19 | 20 | Eleven Labs API 21 | { 23 | e.preventDefault(); 24 | e.stopPropagation(); 25 | form.handleSubmit(); 26 | setUpdate("hello"); 27 | setTimeout(() => { 28 | setUpdate(""); 29 | }, 1000); 30 | }} 31 | class="flex flex-row space-x-4" 32 | > 33 | 34 | {(field) => ( 35 | field().handleChange(e.currentTarget.value)} 37 | onBlur={() => field().handleBlur()} 38 | value={field().state.value} 39 | type="password" 40 | placeholder="Elevenlabs AI Key" 41 | /> 42 | )} 43 | 44 | Submit 45 | 46 | 47 | 48 | Key Saved! 49 | 50 | 51 | 52 | ); 53 | } 54 | -------------------------------------------------------------------------------- /packages/packages/src/elevenlabs/ctx.ts: -------------------------------------------------------------------------------- 1 | import { 2 | None, 3 | type Option, 4 | Some, 5 | makePersistedOption, 6 | } from "@macrograph/option"; 7 | import { createEffect, createSignal, on } from "solid-js"; 8 | 9 | const ELEVEN_KEY = "ELEVENLABS_KEY"; 10 | 11 | export type Ctx = ReturnType; 12 | 13 | export function createCtx() { 14 | const [state, setState] = createSignal>(None); 15 | 16 | const [key, setKey] = makePersistedOption( 17 | createSignal>(None), 18 | ELEVEN_KEY, 19 | ); 20 | 21 | createEffect( 22 | on( 23 | () => key(), 24 | (key) => { 25 | key.map((key) => { 26 | const api = key; 27 | setState(Some(api)); 28 | }); 29 | }, 30 | ), 31 | ); 32 | 33 | return { key, setKey, state, setState }; 34 | } 35 | -------------------------------------------------------------------------------- /packages/packages/src/elevenlabs/index.ts: -------------------------------------------------------------------------------- 1 | import { Package } from "@macrograph/runtime"; 2 | 3 | import * as sends from "./sends"; 4 | 5 | import { createCtx } from "./ctx"; 6 | 7 | export type Pkg = ReturnType; 8 | 9 | export function pkg() { 10 | const ctx = createCtx(); 11 | 12 | const pkg = new Package({ 13 | name: "Elevenlabs", 14 | ctx, 15 | SettingsUI: () => import("./Settings"), 16 | }); 17 | 18 | sends.register(pkg, ctx); 19 | 20 | return pkg; 21 | } 22 | -------------------------------------------------------------------------------- /packages/packages/src/fs.ts: -------------------------------------------------------------------------------- 1 | import { Package } from "@macrograph/runtime"; 2 | import { t } from "@macrograph/typesystem"; 3 | 4 | type Entry = { Dir: string } | { File: string }; 5 | 6 | export function register(actions: { list(path: string): Promise }) { 7 | const pkg = new Package({ name: "FS" }); 8 | 9 | pkg.createSchema({ 10 | name: "List Files", 11 | type: "exec", 12 | createIO({ io }) { 13 | return { 14 | path: io.dataInput({ 15 | id: "path", 16 | name: "Folder Path", 17 | type: t.string(), 18 | }), 19 | files: io.dataOutput({ 20 | id: "files", 21 | name: "Files", 22 | type: t.list(t.string()), 23 | }), 24 | }; 25 | }, 26 | async run({ ctx, io }) { 27 | const files = await actions.list(ctx.getInput(io.path)); 28 | 29 | const array = files 30 | .map((f) => { 31 | if ("File" in f) return f.File; 32 | }) 33 | .filter(Boolean) as string[]; 34 | 35 | ctx.setOutput(io.files, array); 36 | }, 37 | }); 38 | 39 | pkg.createSchema({ 40 | name: "List Folders", 41 | type: "exec", 42 | createIO({ io }) { 43 | return { 44 | path: io.dataInput({ 45 | id: "path", 46 | name: "Folder Path", 47 | type: t.string(), 48 | }), 49 | files: io.dataOutput({ 50 | id: "folders", 51 | name: "Folders", 52 | type: t.list(t.string()), 53 | }), 54 | }; 55 | }, 56 | async run({ ctx, io }) { 57 | const files = await actions.list(ctx.getInput(io.path)); 58 | 59 | const array = files 60 | .map((f) => { 61 | if ("Dir" in f) return f.Dir; 62 | }) 63 | .filter(Boolean) as string[]; 64 | 65 | ctx.setOutput(io.files, array); 66 | }, 67 | }); 68 | 69 | return pkg; 70 | } 71 | -------------------------------------------------------------------------------- /packages/packages/src/github/Settings.tsx: -------------------------------------------------------------------------------- 1 | import { None, Some } from "@macrograph/option"; 2 | import { Button } from "@macrograph/ui"; 3 | import { Match, Show, Suspense, Switch, createSignal } from "solid-js"; 4 | 5 | import type { Ctx } from "./ctx"; 6 | 7 | export default function ({ core, setAuthToken, authToken, user }: Ctx) { 8 | const [loggingIn, setLoggingIn] = createSignal(false); 9 | 10 | return ( 11 | 12 | 13 | 14 | 15 | {(user) => ( 16 | 17 | Logged in as {user().login} 18 | setAuthToken(None)}>Log Out 19 | 20 | )} 21 | 22 | 23 | 24 | 25 | 26 | Logging in... 27 | setLoggingIn(false)}>Cancel 28 | 29 | 30 | 31 | { 33 | setLoggingIn(true); 34 | 35 | try { 36 | const token = await core.oauth.authorize("github"); 37 | 38 | if (!loggingIn()) return; 39 | 40 | setAuthToken(Some(token)); 41 | } finally { 42 | setLoggingIn(false); 43 | } 44 | }} 45 | > 46 | Login 47 | 48 | 49 | 50 | ); 51 | } 52 | -------------------------------------------------------------------------------- /packages/packages/src/github/ctx.ts: -------------------------------------------------------------------------------- 1 | import { None, Some, makePersistedOption } from "@macrograph/option"; 2 | import type { Core, OAuthToken } from "@macrograph/runtime"; 3 | import { Octokit } from "octokit"; 4 | import { createResource, createSignal } from "solid-js"; 5 | 6 | import { createCallbackAuth } from "./auth"; 7 | 8 | export const TOKEN_LOCALSTORAGE = "githubToken"; 9 | 10 | export function createCtx(core: Core) { 11 | const [authToken, setAuthToken] = makePersistedOption( 12 | createSignal(None), 13 | TOKEN_LOCALSTORAGE, 14 | ); 15 | 16 | let refreshPromise: Promise | null = null; 17 | 18 | const client = new Octokit({ 19 | authStrategy: createCallbackAuth, 20 | auth: { 21 | refresh: async () => { 22 | if (!refreshPromise) 23 | refreshPromise = (async () => { 24 | const newToken = await core.oauth.refresh( 25 | "github", 26 | authToken().unwrap().refresh_token, 27 | ); 28 | setAuthToken(Some(newToken as OAuthToken)); 29 | })(); 30 | 31 | await refreshPromise; 32 | }, 33 | callback: () => { 34 | return authToken().unwrap().access_token; 35 | }, 36 | }, 37 | }); 38 | 39 | const [user] = createResource( 40 | () => authToken().toNullable(), 41 | async () => { 42 | const resp = await client.rest.users.getAuthenticated(); 43 | 44 | return resp.data; 45 | }, 46 | ); 47 | 48 | return { 49 | core, 50 | authToken, 51 | user, 52 | setAuthToken, 53 | }; 54 | } 55 | 56 | export type Ctx = ReturnType; 57 | -------------------------------------------------------------------------------- /packages/packages/src/github/index.ts: -------------------------------------------------------------------------------- 1 | import { type Core, Package } from "@macrograph/runtime"; 2 | 3 | import { createCtx } from "./ctx"; 4 | 5 | export function pkg(core: Core) { 6 | const pkg = new Package({ 7 | name: "GitHub", 8 | ctx: createCtx(core), 9 | SettingsUI: () => import("./Settings"), 10 | }); 11 | 12 | return pkg; 13 | } 14 | -------------------------------------------------------------------------------- /packages/packages/src/google/Settings.tsx: -------------------------------------------------------------------------------- 1 | import { None, Some } from "@macrograph/option"; 2 | import { Button } from "@macrograph/ui"; 3 | import { Match, Show, Suspense, Switch, createSignal } from "solid-js"; 4 | 5 | import type { Ctx } from "./ctx"; 6 | 7 | export default function ({ core, setAuthToken, authToken, user }: Ctx) { 8 | const [loggingIn, setLoggingIn] = createSignal(false); 9 | 10 | return ( 11 | 12 | 13 | 14 | 15 | {(user) => ( 16 | 17 | Logged in as {user().name} 18 | setAuthToken(None)}>Log Out 19 | 20 | )} 21 | 22 | 23 | 24 | 25 | 26 | Logging in... 27 | setLoggingIn(false)}>Cancel 28 | 29 | 30 | 31 | { 33 | setLoggingIn(true); 34 | 35 | try { 36 | const token = await core.oauth.authorize("google"); 37 | 38 | if (!loggingIn()) return; 39 | 40 | setAuthToken(Some(token)); 41 | } finally { 42 | setLoggingIn(false); 43 | } 44 | }} 45 | > 46 | Login 47 | 48 | 49 | Login may not work without approval of project maintainer 50 | 51 | 52 | 53 | ); 54 | } 55 | -------------------------------------------------------------------------------- /packages/packages/src/google/index.ts: -------------------------------------------------------------------------------- 1 | import { type Core, Package } from "@macrograph/runtime"; 2 | 3 | import { createCtx } from "./ctx"; 4 | 5 | export function pkg(core: Core) { 6 | const pkg = new Package({ 7 | name: "Google", 8 | ctx: createCtx(core), 9 | SettingsUI: () => import("./Settings"), 10 | }); 11 | 12 | return pkg; 13 | } 14 | -------------------------------------------------------------------------------- /packages/packages/src/goxlr/Settings.tsx: -------------------------------------------------------------------------------- 1 | import { None, Some } from "@macrograph/option"; 2 | import { Button, Input } from "@macrograph/ui"; 3 | import { createForm } from "@tanstack/solid-form"; 4 | import { Match, Switch } from "solid-js"; 5 | 6 | import type { Ctx } from "./ctx"; 7 | 8 | export default function ({ state, setUrl }: Ctx) { 9 | return ( 10 | 11 | Socket API 12 | 13 | 14 | {(_) => { 15 | const form = createForm(() => ({ 16 | defaultValues: { url: "" }, 17 | onSubmit: ({ value }) => { 18 | setUrl(Some(value.url)); 19 | }, 20 | })); 21 | 22 | return ( 23 | { 25 | e.preventDefault(); 26 | e.stopPropagation(); 27 | form.handleSubmit(); 28 | }} 29 | class="flex flex-row space-x-4" 30 | > 31 | 32 | {(field) => ( 33 | 35 | field().handleChange(e.currentTarget.value) 36 | } 37 | onBlur={() => field().handleBlur()} 38 | value={field().state.value} 39 | placeholder="GoXLR WS Url" 40 | /> 41 | )} 42 | 43 | 44 | Submit 45 | 46 | 47 | ); 48 | }} 49 | 50 | 51 | 52 | setUrl(None)}>Disconnect 53 | 54 | 55 | 56 | 57 | ); 58 | } 59 | -------------------------------------------------------------------------------- /packages/packages/src/goxlr/index.ts: -------------------------------------------------------------------------------- 1 | import { Package } from "@macrograph/runtime"; 2 | 3 | import { createCtx } from "./ctx"; 4 | import * as events from "./events"; 5 | import * as sends from "./sends"; 6 | import { createTypes } from "./types"; 7 | 8 | export type Event = { 9 | levelsChange: { 10 | channel: string; 11 | value: number; 12 | }; 13 | buttonDown: { 14 | buttonName: string; 15 | state: boolean; 16 | }; 17 | faderStatus: { 18 | channel: string; 19 | state: boolean; 20 | }; 21 | effects: { 22 | dial: string; 23 | amount: number; 24 | }; 25 | }; 26 | 27 | export type Pkg = ReturnType; 28 | 29 | export function pkg() { 30 | const ctx = createCtx((e) => pkg.emitEvent(e)); 31 | 32 | const pkg = new Package({ 33 | name: "GoXLR", 34 | ctx, 35 | SettingsUI: () => import("./Settings"), 36 | }); 37 | 38 | const types = createTypes(pkg); 39 | 40 | events.register(pkg); 41 | sends.register(pkg, ctx, types); 42 | 43 | return pkg; 44 | } 45 | -------------------------------------------------------------------------------- /packages/packages/src/index.tsx: -------------------------------------------------------------------------------- 1 | import "@total-typescript/ts-reset"; 2 | export * as json from "@macrograph/json"; 3 | 4 | export * as audio from "./audio"; 5 | export * as customEvents from "./customEvents"; 6 | export * as discord from "./discord"; 7 | export * as elevenlabs from "./elevenlabs"; 8 | export * as fs from "./fs"; 9 | export * as github from "./github"; 10 | export * as globalKeyboardMouse from "./globalKeyboardMouse"; 11 | export * as google from "./google"; 12 | export * as goxlr from "./goxlr"; 13 | export * as http from "./http"; 14 | export * as keyboard from "./keyboard"; 15 | export * as list from "./list"; 16 | export * as localStorage from "./localStorage"; 17 | export * as logic from "./logic"; 18 | export * as map from "./map"; 19 | export * as midi from "./midi"; 20 | export * as obs from "./obs"; 21 | export * as openai from "./openai"; 22 | export * as patreon from "./patreon"; 23 | export * as shell from "./shell"; 24 | export * as speakerbot from "./speakerbot"; 25 | export * as spotify from "./spotify"; 26 | export * as streamdeck from "./streamdeck"; 27 | export * as streamlabs from "./streamlabs"; 28 | export * as twitch from "./twitch"; 29 | export * as utils from "./utils"; 30 | export * as vtubeStudio from "./vtubeStudio"; 31 | export * as websocket from "./websocketClient"; 32 | export * as websocketServer from "./websocketServer"; 33 | export * as variables from "./variables"; 34 | export * as voicemod from "./voicemod"; 35 | -------------------------------------------------------------------------------- /packages/packages/src/midi/Settings.tsx: -------------------------------------------------------------------------------- 1 | import { Button } from "@macrograph/ui"; 2 | import { For, Match, Suspense, Switch } from "solid-js"; 3 | 4 | import type { Ctx } from "./ctx"; 5 | 6 | export default ({ access, requestAccess, io }: Ctx) => { 7 | return ( 8 | 9 | 10 | 11 | 12 | MIDI Access Denied Retry 13 | 14 | 15 | Request MIDI Access 16 | 17 | 18 | 19 | {( 20 | [ 21 | ["Inputs", io.inputs], 22 | ["Outputs", io.outputs], 23 | ] as const 24 | ).map(([title, ports]) => ( 25 | 26 | {title} 27 | 28 | 29 | {(input) => ( 30 | 31 | {input.name} 32 | 33 | )} 34 | 35 | 36 | 37 | ))} 38 | 39 | 40 | 41 | 42 | 43 | ); 44 | }; 45 | -------------------------------------------------------------------------------- /packages/packages/src/midi/ctx.ts: -------------------------------------------------------------------------------- 1 | import { createEventListener } from "@solid-primitives/event-listener"; 2 | import { createEffect, createResource, on } from "solid-js"; 3 | import { createStore } from "solid-js/store"; 4 | 5 | export type Ctx = ReturnType; 6 | 7 | export function createCtx() { 8 | const [io, setIo] = createStore({ 9 | inputs: [] as MIDIInput[], 10 | outputs: [] as MIDIOutput[], 11 | }); 12 | 13 | const [access, accessActions] = createResource(async () => { 14 | try { 15 | return await navigator.requestMIDIAccess({ sysex: true }); 16 | } catch { 17 | return null; 18 | } 19 | }); 20 | 21 | createEffect( 22 | on(access, (access) => { 23 | if (!access) return; 24 | 25 | setIo({ 26 | inputs: Array.from(access.inputs.values()), 27 | outputs: Array.from(access.outputs.values()), 28 | }); 29 | 30 | createEventListener(access, "statechange", () => { 31 | setIo({ 32 | inputs: Array.from(access.inputs.values()), 33 | outputs: Array.from(access.outputs.values()), 34 | }); 35 | }); 36 | }), 37 | ); 38 | 39 | function requestAccess() { 40 | accessActions.refetch(); 41 | } 42 | 43 | return { access, requestAccess, io }; 44 | } 45 | -------------------------------------------------------------------------------- /packages/packages/src/midi/index.ts: -------------------------------------------------------------------------------- 1 | import { Package } from "@macrograph/runtime"; 2 | 3 | import { type Ctx, createCtx } from "./ctx"; 4 | 5 | import * as events from "./events"; 6 | import * as requests from "./requests"; 7 | import * as resources from "./resource"; 8 | 9 | export type Pkg = Package, Ctx>; 10 | 11 | export const STATUS_BYTES = { 12 | noteOff: 0x8, 13 | noteOn: 0x9, 14 | polyphonicAftertouch: 0xa, 15 | controlChange: 0xb, 16 | programChange: 0xc, 17 | channelAftertouch: 0xd, 18 | pitchBend: 0xe, 19 | }; 20 | 21 | export function pkg(): Pkg { 22 | const ctx = createCtx(); 23 | 24 | const pkg: Pkg = new Package({ 25 | name: "MIDI", 26 | ctx, 27 | SettingsUI: () => import("./Settings"), 28 | }); 29 | 30 | pkg.registerResourceType(resources.MIDIInput); 31 | pkg.registerResourceType(resources.MIDIOutput); 32 | 33 | events.register(pkg); 34 | requests.register(pkg); 35 | 36 | return pkg; 37 | } 38 | -------------------------------------------------------------------------------- /packages/packages/src/midi/resource.ts: -------------------------------------------------------------------------------- 1 | import { type PropertyDef, createResourceType } from "@macrograph/runtime"; 2 | import type { Pkg } from "."; 3 | 4 | export const MIDIInput = createResourceType({ 5 | name: "MIDI Input", 6 | sources: (pkg: Pkg) => 7 | [...pkg.ctx!.io.inputs].map((input) => ({ 8 | id: input.id, 9 | display: input.name ?? "", 10 | value: input, 11 | })), 12 | }); 13 | 14 | export const midiInputProperty = { 15 | name: "MIDI Input", 16 | resource: MIDIInput, 17 | } satisfies PropertyDef; 18 | 19 | export const MIDIOutput = createResourceType({ 20 | name: "MIDI Output", 21 | sources: (pkg: Pkg) => 22 | [...pkg.ctx!.io.outputs].map((output) => ({ 23 | id: output.id, 24 | display: output.name ?? "", 25 | value: output, 26 | })), 27 | }); 28 | 29 | export const midiOutputProperty = { 30 | name: "MIDI Output", 31 | resource: MIDIOutput, 32 | } satisfies PropertyDef; 33 | -------------------------------------------------------------------------------- /packages/packages/src/obs/index.ts: -------------------------------------------------------------------------------- 1 | import { Package } from "@macrograph/runtime"; 2 | import type { EventTypes } from "obs-websocket-js"; 3 | 4 | import * as events from "./events"; 5 | import * as requests from "./requests"; 6 | 7 | import { type Ctx, createCtx } from "./ctx"; 8 | import { OBSInstance } from "./resource"; 9 | import { createTypes } from "./types"; 10 | 11 | export type Pkg = Package; 12 | 13 | export function pkg(): Pkg { 14 | const ctx = createCtx(); 15 | 16 | const pkg = new Package({ 17 | name: "OBS Websocket", 18 | ctx, 19 | SettingsUI: () => import("./Settings"), 20 | }); 21 | 22 | const types = createTypes(pkg); 23 | 24 | events.register(pkg, types); 25 | requests.register(pkg, types); 26 | 27 | pkg.registerResourceType(OBSInstance); 28 | 29 | return pkg; 30 | } 31 | -------------------------------------------------------------------------------- /packages/packages/src/obs/resource.ts: -------------------------------------------------------------------------------- 1 | import { type PropertyDef, createResourceType } from "@macrograph/runtime"; 2 | 3 | import type { Pkg } from "."; 4 | 5 | export const OBSInstance = createResourceType({ 6 | name: "OBS Instance", 7 | sources: (pkg: Pkg) => 8 | [...pkg.ctx!.instances].map(([ip, instance]) => ({ 9 | id: ip, 10 | display: ip, 11 | value: instance, 12 | })), 13 | }); 14 | 15 | export const instanceProperty = { 16 | name: "OBS Instance", 17 | resource: OBSInstance, 18 | } satisfies PropertyDef; 19 | 20 | export const defaultProperties = { instance: instanceProperty }; 21 | -------------------------------------------------------------------------------- /packages/packages/src/obs/ws.ts: -------------------------------------------------------------------------------- 1 | import { Maybe } from "@macrograph/option"; 2 | import OBS, { EventSubscription } from "obs-websocket-js"; 3 | import { createSignal } from "solid-js"; 4 | import * as v from "valibot"; 5 | 6 | const OBS_WS = "obsWs"; 7 | 8 | export const AUTH_SCHEMA = v.object({ 9 | url: v.pipe(v.string(), v.url()), 10 | password: v.optional(v.string()), 11 | }); 12 | 13 | export function createWs() { 14 | const obs = new OBS(); 15 | 16 | const [state, setState] = createSignal< 17 | "disconnected" | "connecting" | "connected" 18 | >("disconnected"); 19 | 20 | const disconnect = async () => { 21 | setState("disconnected"); 22 | await obs.disconnect(); 23 | }; 24 | 25 | const connect = async (url: string, password?: string) => { 26 | await disconnect(); 27 | 28 | await obs.connect(url, password, { 29 | eventSubscriptions: 30 | EventSubscription.All | 31 | EventSubscription.SceneItemTransformChanged | 32 | EventSubscription.InputActiveStateChanged | 33 | EventSubscription.InputShowStateChanged, 34 | }); 35 | 36 | localStorage.setItem(OBS_WS, JSON.stringify({ url, password })); 37 | 38 | setState("connected"); 39 | }; 40 | 41 | obs.on("ConnectionClosed", () => setState("disconnected")); 42 | obs.on("ConnectionError", () => setState("disconnected")); 43 | 44 | Maybe(localStorage.getItem(OBS_WS)).mapAsync(async (jstr) => { 45 | const { url, password } = v.parse(AUTH_SCHEMA, JSON.parse(jstr)); 46 | 47 | try { 48 | await connect(url, password); 49 | } catch { 50 | localStorage.removeItem(OBS_WS); 51 | } 52 | }); 53 | 54 | return { connect, disconnect, state, obs }; 55 | } 56 | -------------------------------------------------------------------------------- /packages/packages/src/openai/ctx.ts: -------------------------------------------------------------------------------- 1 | import { 2 | None, 3 | type Option, 4 | Some, 5 | makePersistedOption, 6 | } from "@macrograph/option"; 7 | import OpenAI from "openai"; 8 | import { createEffect, createSignal, on } from "solid-js"; 9 | 10 | const GPT_KEY = "ChatGptKey"; 11 | 12 | export type Ctx = ReturnType; 13 | 14 | export function createCtx() { 15 | const [state, setState] = createSignal>(None); 16 | 17 | const [key, setKey] = makePersistedOption( 18 | createSignal>(None), 19 | GPT_KEY, 20 | ); 21 | 22 | createEffect( 23 | on( 24 | () => key(), 25 | (key) => { 26 | key.map((key) => { 27 | const api = new OpenAI({ 28 | apiKey: key, 29 | dangerouslyAllowBrowser: true, 30 | }); 31 | setState(Some(api)); 32 | }); 33 | }, 34 | ), 35 | ); 36 | 37 | return { key, setKey, state, setState }; 38 | } 39 | -------------------------------------------------------------------------------- /packages/packages/src/openai/index.ts: -------------------------------------------------------------------------------- 1 | import { Package } from "@macrograph/runtime"; 2 | 3 | import * as sends from "./sends"; 4 | 5 | import { createCtx } from "./ctx"; 6 | 7 | export type Pkg = ReturnType; 8 | 9 | export function pkg() { 10 | const ctx = createCtx(); 11 | 12 | const pkg = new Package({ 13 | name: "OpenAi", 14 | ctx, 15 | SettingsUI: () => import("./Settings"), 16 | }); 17 | 18 | sends.register(pkg, ctx); 19 | 20 | return pkg; 21 | } 22 | -------------------------------------------------------------------------------- /packages/packages/src/patreon/Settings.tsx: -------------------------------------------------------------------------------- 1 | import { None, Some } from "@macrograph/option"; 2 | import { Button } from "@macrograph/ui"; 3 | import { Match, Show, Suspense, Switch, createSignal } from "solid-js"; 4 | 5 | import type { Ctx } from "./ctx"; 6 | 7 | export default function ({ core, setAuthToken, authToken, user }: Ctx) { 8 | const [loggingIn, setLoggingIn] = createSignal(false); 9 | 10 | return ( 11 | 12 | 13 | 14 | 15 | {(user) => ( 16 | 17 | Logged in as {user().data.attributes.full_name} 18 | setAuthToken(None)}>Log Out 19 | 20 | )} 21 | 22 | 23 | 24 | 25 | 26 | Logging in... 27 | setLoggingIn(false)}>Cancel 28 | 29 | 30 | 31 | { 33 | setLoggingIn(true); 34 | 35 | try { 36 | const token = await core.oauth.authorize("patreon"); 37 | 38 | if (!loggingIn()) return; 39 | 40 | setAuthToken(Some(token)); 41 | } finally { 42 | setLoggingIn(false); 43 | } 44 | }} 45 | > 46 | Login 47 | 48 | 49 | 50 | ); 51 | } 52 | -------------------------------------------------------------------------------- /packages/packages/src/patreon/index.ts: -------------------------------------------------------------------------------- 1 | import { type Core, Package } from "@macrograph/runtime"; 2 | 3 | import { createCtx } from "./ctx"; 4 | 5 | export function pkg(core: Core) { 6 | const pkg = new Package({ 7 | name: "Patreon", 8 | ctx: createCtx(core), 9 | SettingsUI: () => import("./Settings"), 10 | }); 11 | 12 | return pkg; 13 | } 14 | -------------------------------------------------------------------------------- /packages/packages/src/shell.ts: -------------------------------------------------------------------------------- 1 | import { Package } from "@macrograph/runtime"; 2 | import { t } from "@macrograph/typesystem"; 3 | 4 | export function pkg(execute: (cmd: string) => Promise) { 5 | const pkg = new Package({ name: "Shell" }); 6 | 7 | pkg.createSchema({ 8 | type: "exec", 9 | name: "Execute Shell Command", 10 | createIO: ({ io }) => { 11 | return io.dataInput({ 12 | id: "command", 13 | name: "Command", 14 | type: t.string(), 15 | }); 16 | }, 17 | run: async ({ ctx, io }) => { 18 | await execute(ctx.getInput(io)); 19 | }, 20 | }); 21 | 22 | return pkg; 23 | } 24 | -------------------------------------------------------------------------------- /packages/packages/src/speakerbot/ctx.ts: -------------------------------------------------------------------------------- 1 | import { None, makePersistedOption } from "@macrograph/option"; 2 | import { createEffect, createSignal, on, onCleanup } from "solid-js"; 3 | 4 | const SPEAKER_BOT_PORT = "SpeakerBotPort"; 5 | 6 | export type Ctx = ReturnType; 7 | 8 | export function createCtx() { 9 | const [state, setState] = createSignal< 10 | | { 11 | type: "disconnected"; 12 | } 13 | | { type: "connecting" | "connected"; ws: WebSocket } 14 | >({ type: "disconnected" }); 15 | 16 | const [url, setUrl] = makePersistedOption( 17 | createSignal(None), 18 | SPEAKER_BOT_PORT, 19 | ); 20 | 21 | createEffect( 22 | on( 23 | () => url(), 24 | (url) => { 25 | url.map((url) => { 26 | const ws = new WebSocket(url); 27 | 28 | ws.addEventListener("open", () => { 29 | setState({ type: "connected", ws }); 30 | }); 31 | 32 | ws.addEventListener("message", (msg) => { 33 | console.log(msg); 34 | }); 35 | 36 | setState({ type: "connecting", ws }); 37 | 38 | onCleanup(() => { 39 | ws.close(); 40 | setState({ type: "disconnected" }); 41 | }); 42 | }); 43 | }, 44 | ), 45 | ); 46 | 47 | return { url, setUrl, state, setState }; 48 | } 49 | -------------------------------------------------------------------------------- /packages/packages/src/speakerbot/index.ts: -------------------------------------------------------------------------------- 1 | import { Package } from "@macrograph/runtime"; 2 | 3 | import { createCtx } from "./ctx"; 4 | import * as sends from "./sends"; 5 | 6 | export type Pkg = ReturnType; 7 | 8 | export function pkg() { 9 | const ctx = createCtx(); 10 | 11 | const pkg = new Package({ 12 | name: "SpeakerBot", 13 | ctx, 14 | SettingsUI: () => import("./Settings"), 15 | }); 16 | 17 | sends.register(pkg, ctx); 18 | 19 | return pkg; 20 | } 21 | -------------------------------------------------------------------------------- /packages/packages/src/spotify/Settings.tsx: -------------------------------------------------------------------------------- 1 | import { None, Some } from "@macrograph/option"; 2 | import { Button } from "@macrograph/ui"; 3 | import { Match, Show, Suspense, Switch, createSignal } from "solid-js"; 4 | 5 | import type { Ctx } from "./ctx"; 6 | 7 | export default function ({ core, setAuthToken, authToken, user }: Ctx) { 8 | const [loggingIn, setLoggingIn] = createSignal(false); 9 | 10 | return ( 11 | 12 | 13 | 14 | 15 | {(user) => ( 16 | 17 | Logged in as {user().display_name} 18 | setAuthToken(None)}>Log Out 19 | 20 | )} 21 | 22 | 23 | 24 | 25 | 26 | Logging in... 27 | setLoggingIn(false)}>Cancel 28 | 29 | 30 | 31 | { 33 | setLoggingIn(true); 34 | 35 | try { 36 | const token = await core.oauth.authorize("spotify"); 37 | 38 | if (!loggingIn()) return; 39 | 40 | setAuthToken(Some(token)); 41 | } finally { 42 | setLoggingIn(false); 43 | } 44 | }} 45 | > 46 | Login 47 | 48 | 49 | 50 | ); 51 | } 52 | -------------------------------------------------------------------------------- /packages/packages/src/spotify/index.ts: -------------------------------------------------------------------------------- 1 | import { type Core, Package } from "@macrograph/runtime"; 2 | 3 | import { createCtx } from "./ctx"; 4 | 5 | export function pkg(core: Core) { 6 | const pkg = new Package({ 7 | name: "Spotify", 8 | ctx: createCtx(core), 9 | SettingsUI: () => import("./Settings"), 10 | }); 11 | 12 | return pkg; 13 | } 14 | -------------------------------------------------------------------------------- /packages/packages/src/streamdeck/index.ts: -------------------------------------------------------------------------------- 1 | import { Package, type WsProvider } from "@macrograph/runtime"; 2 | import { t } from "@macrograph/typesystem"; 3 | 4 | import { type Events, createCtx } from "./ctx"; 5 | 6 | export function pkg(ws: WsProvider) { 7 | const ctx = createCtx(ws, (e) => pkg.emitEvent(e)); 8 | 9 | const pkg = new Package({ 10 | name: "Stream Deck WebSocket", 11 | ctx, 12 | SettingsUI: () => import("./Settings"), 13 | }); 14 | 15 | pkg.createEventSchema({ 16 | event: "keyDown", 17 | name: "Stream Deck Key Down", 18 | createIO: ({ io }) => ({ 19 | exec: io.execOutput({ 20 | id: "exec", 21 | name: "", 22 | }), 23 | id: io.dataOutput({ 24 | id: "id", 25 | name: "Key ID", 26 | type: t.string(), 27 | }), 28 | }), 29 | run({ ctx, data, io }) { 30 | ctx.setOutput(io.id, data.settings.id); 31 | ctx.exec(io.exec); 32 | }, 33 | }); 34 | 35 | pkg.createEventSchema({ 36 | event: "keyUp", 37 | name: "Stream Deck Key Up", 38 | createIO: ({ io }) => ({ 39 | exec: io.execOutput({ 40 | id: "exec", 41 | name: "", 42 | }), 43 | id: io.dataOutput({ 44 | id: "id", 45 | name: "Key ID", 46 | type: t.string(), 47 | }), 48 | }), 49 | run({ ctx, data, io }) { 50 | ctx.setOutput(io.id, data.settings.id); 51 | ctx.exec(io.exec); 52 | }, 53 | }); 54 | 55 | return pkg; 56 | } 57 | -------------------------------------------------------------------------------- /packages/packages/src/twitch/index.ts: -------------------------------------------------------------------------------- 1 | import { type Core, Package } from "@macrograph/runtime"; 2 | 3 | import * as chat from "./chat"; 4 | import * as eventsub from "./eventSub"; 5 | import * as helix from "./helix"; 6 | 7 | import { type Ctx, createCtx } from "./ctx"; 8 | import { TwitchAccount, TwitchChannel } from "./resource"; 9 | import { createTypes } from "./types"; 10 | 11 | export type Pkg = Package; 12 | 13 | export function pkg(core: Core) { 14 | const ctx = createCtx(core); 15 | 16 | const pkg = new Package({ 17 | name: "Twitch Events", 18 | ctx, 19 | SettingsUI: () => import("./Settings"), 20 | }); 21 | 22 | const types = createTypes(pkg); 23 | 24 | helix.register(pkg, ctx.helixClient, types); 25 | eventsub.register(pkg, ctx, types); 26 | chat.register(pkg, ctx); 27 | 28 | pkg.registerResourceType(TwitchAccount); 29 | pkg.registerResourceType(TwitchChannel); 30 | 31 | return pkg; 32 | } 33 | -------------------------------------------------------------------------------- /packages/packages/src/twitch/resource.ts: -------------------------------------------------------------------------------- 1 | import { type PropertyDef, createResourceType } from "@macrograph/runtime"; 2 | import { t } from "@macrograph/typesystem"; 3 | import type { Pkg } from "."; 4 | 5 | export const TwitchAccount = createResourceType({ 6 | name: "Twitch Account", 7 | sources: (pkg: Pkg) => { 8 | const allAccounts = [...pkg.ctx!.auth.accounts]; 9 | 10 | return allAccounts 11 | .map(([id, data]) => { 12 | const d = data(); 13 | if (!d) return; 14 | return [id, d] as const; 15 | }) 16 | .filter(Boolean) 17 | .map(([userId, account]) => ({ 18 | id: userId, 19 | display: account.data.display_name, 20 | value: account, 21 | })); 22 | }, 23 | }); 24 | 25 | export const accountProperty = { 26 | name: "Twitch Account", 27 | resource: TwitchAccount, 28 | } satisfies PropertyDef; 29 | 30 | export const defaultProperties = { account: accountProperty }; 31 | 32 | export const TwitchChannel = createResourceType({ 33 | name: "Twitch Channel", 34 | type: t.string(), 35 | }); 36 | -------------------------------------------------------------------------------- /packages/packages/src/utils.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, test } from "vitest"; 2 | import { parseFormatString } from "./utils"; 3 | 4 | describe("formatString", () => { 5 | test("Hello!", ({ task }) => { 6 | expect(parseFormatString(task.name)).toEqual([{ text: "Hello!" }]); 7 | }); 8 | 9 | test("Hello {Name}!", ({ task }) => { 10 | expect(parseFormatString(task.name)).toEqual([ 11 | { text: "Hello " }, 12 | { variable: "Name" }, 13 | { text: "!" }, 14 | ]); 15 | }); 16 | 17 | test("Hello {{Name}}!", ({ task }) => { 18 | expect(parseFormatString(task.name)).toEqual([{ text: "Hello {Name}!" }]); 19 | }); 20 | 21 | test("{{{Name}}}", ({ task }) => { 22 | expect(parseFormatString(task.name)).toEqual([ 23 | { text: "{" }, 24 | { variable: "Name" }, 25 | { text: "}" }, 26 | ]); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /packages/packages/src/voicemod/settings.tsx: -------------------------------------------------------------------------------- 1 | import type { Ctx } from "./ctx"; 2 | 3 | export default function (ctx: Ctx) { 4 | return {ctx.state().isSome() ? "Connected" : "Disconnected"}; 5 | } 6 | -------------------------------------------------------------------------------- /packages/packages/src/vtubeStudio/index.tsx: -------------------------------------------------------------------------------- 1 | import { Package } from "@macrograph/runtime"; 2 | 3 | import { createCtx } from "./ctx"; 4 | import { requests } from "./requests"; 5 | import { VTubeStudioInstance } from "./resource"; 6 | import { createTypes } from "./types"; 7 | 8 | export type Pkg = ReturnType; 9 | export function pkg() { 10 | const ctx = createCtx(); 11 | 12 | const pkg = new Package({ 13 | name: "VTube Studio", 14 | ctx, 15 | SettingsUI: () => import("./Settings"), 16 | }); 17 | 18 | pkg.registerResourceType(VTubeStudioInstance); 19 | 20 | const types = createTypes(pkg); 21 | 22 | requests(pkg, types); 23 | 24 | return pkg; 25 | } 26 | -------------------------------------------------------------------------------- /packages/packages/src/vtubeStudio/resource.ts: -------------------------------------------------------------------------------- 1 | import { type PropertyDef, createResourceType } from "@macrograph/runtime"; 2 | import type { Pkg } from "."; 3 | 4 | export const VTubeStudioInstance = createResourceType({ 5 | name: "VTube Studio Instance", 6 | sources: (pkg: Pkg) => 7 | [...pkg.ctx!.instances].map(([url, instance]) => ({ 8 | id: url, 9 | display: url, 10 | value: instance.client, 11 | })), 12 | }); 13 | 14 | export const instanceProperty = { 15 | name: "VTube Studio Instance", 16 | resource: VTubeStudioInstance, 17 | } satisfies PropertyDef; 18 | 19 | export const defaultProperties = { instance: instanceProperty }; 20 | -------------------------------------------------------------------------------- /packages/packages/src/vtubeStudio/types.ts: -------------------------------------------------------------------------------- 1 | import { t } from "@macrograph/typesystem"; 2 | 3 | import type { Pkg } from "."; 4 | 5 | export function createTypes(pkg: Pkg) { 6 | const Model = pkg.createStruct("Model", (s) => ({ 7 | modelLoaded: s.field("Loaded", t.bool()), 8 | modelName: s.field("Name", t.string()), 9 | modelID: s.field("ID", t.string()), 10 | vtsModelName: s.field("VTS Name", t.string()), 11 | vtsModelIconName: s.field("VTS Icon Name", t.string()), 12 | })); 13 | 14 | const Expression = pkg.createStruct("Expression", (s) => ({ 15 | name: s.field("Name", t.string()), 16 | file: s.field("File", t.string()), 17 | active: s.field("Active", t.bool()), 18 | })); 19 | 20 | const Hotkey = pkg.createStruct("Hotkey", (s) => ({ 21 | name: s.field("Name", t.string()), 22 | id: s.field("ID", t.string()), 23 | type: s.field("Type", t.string()), 24 | description: s.field("Description", t.string()), 25 | file: s.field("File", t.string()), 26 | })); 27 | 28 | return { 29 | Model, 30 | Expression, 31 | Hotkey, 32 | }; 33 | } 34 | 35 | export type Types = ReturnType; 36 | -------------------------------------------------------------------------------- /packages/packages/src/websocketClient/index.ts: -------------------------------------------------------------------------------- 1 | import { Package } from "@macrograph/runtime"; 2 | import { t } from "@macrograph/typesystem"; 3 | 4 | import { createCtx } from "./ctx"; 5 | 6 | export function pkg() { 7 | const sockets = createCtx((data) => pkg.emitEvent({ name: "wsEvent", data })); 8 | 9 | const getWebSocket = (ip: string) => { 10 | const ws = sockets.websockets.get(ip); 11 | if (ws?.state !== "connected") throw new Error(); 12 | return ws.socket; 13 | }; 14 | 15 | const pkg = new Package({ 16 | name: "Websocket", 17 | ctx: sockets, 18 | SettingsUI: () => import("./Settings"), 19 | }); 20 | 21 | pkg.createSchema({ 22 | name: "WS Emit", 23 | type: "exec", 24 | createIO({ io }) { 25 | return { 26 | ip: io.dataInput({ 27 | id: "ip", 28 | name: "WS IP", 29 | type: t.string(), 30 | }), 31 | data: io.dataInput({ 32 | id: "data", 33 | name: "Data", 34 | type: t.string(), 35 | }), 36 | }; 37 | }, 38 | run({ ctx, io }) { 39 | getWebSocket(ctx.getInput(io.ip)).send(ctx.getInput(io.data)); 40 | }, 41 | }); 42 | 43 | pkg.createEventSchema({ 44 | event: "wsEvent", 45 | name: "WS Event", 46 | createIO({ io }) { 47 | return { 48 | exec: io.execOutput({ 49 | id: "exec", 50 | }), 51 | ip: io.dataOutput({ 52 | id: "ip", 53 | name: "WS IP", 54 | type: t.string(), 55 | }), 56 | data: io.dataOutput({ 57 | id: "data", 58 | name: "Data", 59 | type: t.string(), 60 | }), 61 | }; 62 | }, 63 | run({ ctx, data, io }) { 64 | ctx.setOutput(io.ip, data.ip); 65 | ctx.setOutput(io.data, data.data); 66 | ctx.exec(io.exec); 67 | }, 68 | }); 69 | 70 | return pkg; 71 | } 72 | -------------------------------------------------------------------------------- /packages/packages/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@macrograph/config/tsconfig", 3 | "compilerOptions": { 4 | "outDir": "dist" 5 | }, 6 | "references": [ 7 | { "path": "../runtime" }, 8 | { "path": "../json" }, 9 | { "path": "../typesystem" }, 10 | { "path": "../ui" } 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /packages/runtime-rendering/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@macrograph/runtime-rendering", 3 | "private": true, 4 | "main": "./src/index.ts", 5 | "types": "./src/index.ts", 6 | "type": "module", 7 | "scripts": { 8 | "clean": "rm -rf .turbo && rm -rf node_modules && rm -rf dist", 9 | "test": "vitest", 10 | "typecheck": "tsc -b" 11 | }, 12 | "dependencies": { 13 | "@macrograph/option": "workspace:^", 14 | "@macrograph/runtime": "workspace:^", 15 | "@macrograph/typesystem": "workspace:^" 16 | }, 17 | "devDependencies": { 18 | "@macrograph/config": "workspace:*", 19 | "@macrograph/json": "workspace:*" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /packages/runtime-rendering/src/index.test.ts: -------------------------------------------------------------------------------- 1 | import { pkg } from "@macrograph/json"; 2 | import { describe, test } from "vitest"; 3 | 4 | import { renderSchema } from "."; 5 | 6 | const jsonPkg = pkg(); 7 | 8 | describe("render", () => { 9 | test("works", () => { 10 | console.log( 11 | JSON.stringify( 12 | renderSchema(jsonPkg.schemas.get("JSON Get String") as any), 13 | null, 14 | 4, 15 | ), 16 | ); 17 | console.log( 18 | JSON.stringify( 19 | renderSchema(jsonPkg.schemas.get("To JSON") as any), 20 | null, 21 | 4, 22 | ), 23 | ); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /packages/runtime-rendering/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@macrograph/config/tsconfig", 3 | "compilerOptions": { 4 | "outDir": "dist" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /packages/runtime-serde/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@macrograph/runtime-serde", 3 | "private": true, 4 | "main": "./src/index.ts", 5 | "types": "./src/index.ts", 6 | "type": "module", 7 | "scripts": { 8 | "clean": "rm -rf .turbo && rm -rf node_modules && rm -rf dist", 9 | "test": "vitest", 10 | "typecheck": "tsc -b" 11 | }, 12 | "dependencies": { 13 | "@macrograph/option": "workspace:*", 14 | "@macrograph/runtime": "workspace:*", 15 | "@macrograph/typesystem": "workspace:*", 16 | "@solid-primitives/map": "^0.4.11", 17 | "valibot": "^0.37.0" 18 | }, 19 | "devDependencies": { 20 | "@macrograph/config": "workspace:*" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /packages/runtime-serde/src/index.ts: -------------------------------------------------------------------------------- 1 | export * as serde from "./serde"; 2 | export * from "./serialize"; 3 | export * from "./deserialize"; 4 | -------------------------------------------------------------------------------- /packages/runtime-serde/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@macrograph/config/tsconfig", 3 | "compilerOptions": { 4 | "outDir": "dist" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /packages/runtime/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@macrograph/runtime", 3 | "private": true, 4 | "main": "./src/index.ts", 5 | "types": "./src/index.ts", 6 | "type": "module", 7 | "scripts": { 8 | "clean": "rm -rf .turbo && rm -rf node_modules && rm -rf dist", 9 | "test": "vitest", 10 | "typecheck": "tsc -b" 11 | }, 12 | "dependencies": { 13 | "@macrograph/api-contract": "workspace:^", 14 | "@macrograph/option": "workspace:^", 15 | "@macrograph/typesystem": "workspace:*", 16 | "@rspc/client": "0.0.0-main-6ed8cc98", 17 | "@rspc/tauri": "0.0.0-main-6ed8cc98", 18 | "@solid-primitives/deep": "^0.2.7", 19 | "@solid-primitives/event-bus": "^1.0.11", 20 | "@solid-primitives/map": "^0.4.11", 21 | "@solid-primitives/memo": "^1.3.8", 22 | "@solid-primitives/set": "^0.4.11", 23 | "@solidjs/router": "^0.14.3", 24 | "@tanstack/solid-query": "^4.36.1", 25 | "@tauri-apps/api": "^1.5.3", 26 | "@ts-rest/core": "^3.52.1", 27 | "@ts-rest/solid-query": "^3.41.1", 28 | "valibot": "^0.37.0", 29 | "zod": "^3.25.36" 30 | }, 31 | "devDependencies": { 32 | "@macrograph/config": "workspace:*", 33 | "@total-typescript/ts-reset": "^0.4.2", 34 | "@types/tmi.js": "^1.8.6", 35 | "safaridriver": "^0.0.5", 36 | "solid-js": "^1.9.7", 37 | "type-fest": "^2.19.0", 38 | "webdriverio": "^8.35.1" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /packages/runtime/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./models"; 2 | export * from "./utils"; 3 | -------------------------------------------------------------------------------- /packages/runtime/src/models/CommentBox.ts: -------------------------------------------------------------------------------- 1 | import { createMutable } from "solid-js/store"; 2 | 3 | import type { XY } from "../utils"; 4 | import type { Graph } from "./Graph"; 5 | 6 | export interface CommentBoxArgs { 7 | id: number; 8 | graph: Graph; 9 | position: XY; 10 | size: XY; 11 | text: string; 12 | tint?: string; 13 | } 14 | 15 | export class CommentBox { 16 | id: number; 17 | graph: Graph; 18 | position: XY; 19 | size: XY; 20 | text: string; 21 | tint: string; 22 | 23 | constructor(args: CommentBoxArgs) { 24 | this.id = args.id; 25 | this.graph = args.graph; 26 | this.position = args.position; 27 | this.size = args.size; 28 | this.text = args.text; 29 | this.tint = args.tint ?? "#FFF"; 30 | 31 | return createMutable(this); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /packages/runtime/src/models/CustomEvent.ts: -------------------------------------------------------------------------------- 1 | import { Field, type PrimitiveType, t } from "@macrograph/typesystem"; 2 | import { createMutable } from "solid-js/store"; 3 | 4 | import type { Project } from "./Project"; 5 | 6 | export interface EventArgs { 7 | id: number; 8 | name: string; 9 | project: Project; 10 | } 11 | 12 | // const Source = z.discriminatedUnion("variant", [ 13 | // z.object({ variant: z.literal("package"), package: z.string() }), 14 | // z.object({ variant: z.literal("custom") }), 15 | // ]); 16 | 17 | export class CustomEvent { 18 | id: number; 19 | name: string; 20 | project: Project; 21 | 22 | fields: Array = []; 23 | 24 | fieldIdCounter = 0; 25 | 26 | constructor(args: EventArgs) { 27 | this.id = args.id; 28 | this.name = args.name; 29 | this.project = args.project; 30 | 31 | this.createField(); 32 | return createMutable(this); 33 | } 34 | 35 | generateId() { 36 | return this.fieldIdCounter++; 37 | } 38 | 39 | createField(args?: { id?: string }) { 40 | const id = args?.id ?? this.generateId().toString(); 41 | this.fields.push(new Field(id, t.string(), "New Field")); 42 | } 43 | 44 | field(id: string) { 45 | return this.fields.find((f) => f.id === id.toString()); 46 | } 47 | 48 | editFieldName(id: string, name?: string) { 49 | const pin = this.fields.find((f) => f.id === id.toString()); 50 | if (!pin) return; 51 | pin.name = name; 52 | } 53 | 54 | editFieldType(id: string, type: PrimitiveType) { 55 | const pin = this.fields.find((f) => f.id === id.toString()); 56 | if (!pin) return; 57 | pin.type = type; 58 | } 59 | 60 | deleteField(id: string) { 61 | const index = this.fields.findIndex((f) => f.id === id.toString()); 62 | if (index === -1) return; 63 | this.fields.splice(index, 1); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /packages/runtime/src/models/CustomStruct.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Field, 3 | StructBase, 4 | type StructFields, 5 | t, 6 | } from "@macrograph/typesystem"; 7 | import { createMutable } from "solid-js/store"; 8 | 9 | import type { Project } from "./Project"; 10 | 11 | export class CustomStruct extends StructBase { 12 | id: number; 13 | name: string; 14 | project: Project; 15 | fields: StructFields; 16 | fieldOrder: Array = []; 17 | 18 | fieldIdCounter = 0; 19 | 20 | constructor(args: { 21 | id: number; 22 | project: Project; 23 | name?: string; 24 | fields?: Record; 25 | }) { 26 | super(); 27 | 28 | this.id = args.id; 29 | this.project = args.project; 30 | this.name = args?.name ?? ""; 31 | this.fields = createMutable(args?.fields ?? {}); 32 | this.source = { variant: "custom", id: this.id }; 33 | 34 | this.createField(); 35 | 36 | return createMutable(this); 37 | } 38 | 39 | createField(args?: { id?: string }) { 40 | const id = (args?.id ?? this.fieldIdCounter++).toString(); 41 | 42 | this.fields[id] = new Field(id, t.string(), "New Field"); 43 | this.fieldOrder.push(id); 44 | 45 | return id; 46 | } 47 | 48 | removeField(id: string) { 49 | delete this.fields[id]; 50 | this.fieldOrder.splice(this.fieldOrder.indexOf(id), 1); 51 | } 52 | 53 | editFieldType(id: string, type: t.Any) { 54 | const field = this.fields[id]; 55 | if (!field) return; 56 | field.type = type; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /packages/runtime/src/models/Variable.ts: -------------------------------------------------------------------------------- 1 | import { Disposable, type t } from "@macrograph/typesystem"; 2 | import { createMutable } from "solid-js/store"; 3 | 4 | import { trackDeep } from "@solid-primitives/deep"; 5 | import { createEffect, createRoot, getOwner, on, runWithOwner } from "solid-js"; 6 | import { Graph } from "./Graph"; 7 | import type { Project } from "./Project"; 8 | 9 | export type VariableArgs = { 10 | id: number; 11 | name: string; 12 | type: t.Any; 13 | value: any; 14 | owner: Graph | Project; 15 | }; 16 | 17 | export class Variable extends Disposable { 18 | id: number; 19 | name: string; 20 | type: t.Any; 21 | value: any; 22 | previous: any; 23 | owner: Graph | Project; 24 | 25 | constructor(args: VariableArgs) { 26 | super(); 27 | 28 | this.id = args.id; 29 | this.name = args.name; 30 | this.type = args.type; 31 | this.value = args.value; 32 | this.previous = args.value; 33 | this.owner = args.owner; 34 | 35 | const self = createMutable(this); 36 | 37 | const { owner, dispose } = createRoot((dispose) => ({ 38 | owner: getOwner(), 39 | dispose, 40 | })); 41 | 42 | this.addDisposeListener(dispose); 43 | 44 | runWithOwner(owner, () => { 45 | createEffect( 46 | on( 47 | () => trackDeep(self.value), 48 | () => { 49 | if (self.owner instanceof Graph) 50 | self.owner.project.emit("modified"); 51 | else self.owner.emit("modified"); 52 | }, 53 | ), 54 | ); 55 | createEffect( 56 | on( 57 | () => self.type, 58 | () => { 59 | if (self.owner instanceof Graph) 60 | self.owner.project.emit("modified"); 61 | else self.owner.emit("modified"); 62 | }, 63 | ), 64 | ); 65 | }); 66 | 67 | return self; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /packages/runtime/src/models/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./IO"; 2 | export * from "./CustomEnum"; 3 | export * from "./CustomEvent"; 4 | export * from "./CustomStruct"; 5 | export * from "./NodeSchema"; 6 | export * from "./Node"; 7 | export * from "./CommentBox"; 8 | export * from "./Graph"; 9 | export * from "./Core"; 10 | export * from "./Package"; 11 | export * from "./Project"; 12 | export * from "./Variable"; 13 | -------------------------------------------------------------------------------- /packages/runtime/src/reset.d.ts: -------------------------------------------------------------------------------- 1 | import "@total-typescript/ts-reset"; 2 | -------------------------------------------------------------------------------- /packages/runtime/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@macrograph/config/tsconfig", 3 | "compilerOptions": { 4 | "outDir": "dist" 5 | }, 6 | "references": [{ "path": "../ui" }, { "path": "../typesystem" }] 7 | } 8 | -------------------------------------------------------------------------------- /packages/typesystem/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@macrograph/typesystem", 3 | "private": true, 4 | "main": "./src/index.ts", 5 | "types": "./src/index.ts", 6 | "type": "module", 7 | "scripts": { 8 | "clean": "rm -rf .turbo && rm -rf node_modules && rm -rf dist", 9 | "test": "vitest", 10 | "typecheck": "tsc -b" 11 | }, 12 | "dependencies": { 13 | "@macrograph/option": "workspace:^", 14 | "@solid-primitives/deep": "^0.2.7", 15 | "@solid-primitives/map": "^0.4.11", 16 | "@solid-primitives/set": "^0.4.11", 17 | "@tanstack/solid-query": "^4.36.1", 18 | "valibot": "^0.37.0" 19 | }, 20 | "devDependencies": { 21 | "@macrograph/config": "workspace:*", 22 | "@total-typescript/ts-reset": "^0.4.2", 23 | "safaridriver": "^0.0.5", 24 | "solid-js": "^1.9.7", 25 | "type-fest": "^2.19.0", 26 | "webdriverio": "^8.35.1" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /packages/typesystem/src/base.ts: -------------------------------------------------------------------------------- 1 | import type { TypeVariant, Wildcard, t } from "."; 2 | 3 | export abstract class BaseType { 4 | _type(): TOut { 5 | throw new Error("don't actually call this!"); 6 | } 7 | 8 | abstract default(): TOut; 9 | abstract variant(): TypeVariant; 10 | abstract toString(): string; 11 | // abstract asZodType(): ZodType; 12 | abstract getWildcards(): Wildcard[]; 13 | abstract eq(other: t.Any): boolean; 14 | abstract serialize(): any; 15 | abstract hasUnconnectedWildcard(): boolean; 16 | } 17 | 18 | export type infer> = ReturnType; 19 | -------------------------------------------------------------------------------- /packages/typesystem/src/field.ts: -------------------------------------------------------------------------------- 1 | import { createMutable } from "solid-js/store"; 2 | import type { BaseType } from "."; 3 | 4 | export class Field { 5 | constructor( 6 | public id: string, 7 | public type: Type, 8 | public name?: string, 9 | ) { 10 | return createMutable(this); 11 | } 12 | 13 | default(): any { 14 | return this.type.default(); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /packages/typesystem/src/list.ts: -------------------------------------------------------------------------------- 1 | import { type TypeVariant, type Wildcard, t } from "."; 2 | import { BaseType } from "./base"; 3 | 4 | export class ListType extends BaseType[]> { 5 | constructor(public item: T) { 6 | super(); 7 | } 8 | 9 | default(): t.infer[] { 10 | return []; 11 | } 12 | 13 | variant(): TypeVariant { 14 | return "list"; 15 | } 16 | 17 | toString(): string { 18 | return `List<${this.item.toString()}>`; 19 | } 20 | 21 | // asZodType(): z.ZodType[]> { 22 | // return z.array(this.item.asZodType()); 23 | // } 24 | 25 | getWildcards(): Wildcard[] { 26 | return this.item.getWildcards(); 27 | } 28 | 29 | eq(other: t.Any): boolean { 30 | return other instanceof t.List && this.item.eq(other.item); 31 | } 32 | 33 | serialize() { 34 | return { variant: "list", item: this.item.serialize() }; 35 | } 36 | 37 | hasUnconnectedWildcard(): boolean { 38 | return this.item.hasUnconnectedWildcard(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /packages/typesystem/src/map.ts: -------------------------------------------------------------------------------- 1 | import { ReactiveMap } from "@solid-primitives/map"; 2 | 3 | import { type TypeVariant, type Wildcard, t } from "."; 4 | import { BaseType } from "./base"; 5 | 6 | export type MapValue = ReactiveMap; 7 | 8 | export class MapType> extends BaseType< 9 | MapValue> 10 | > { 11 | constructor(public value: TValue) { 12 | super(); 13 | } 14 | 15 | default() { 16 | return new ReactiveMap>(); 17 | } 18 | 19 | variant(): TypeVariant { 20 | return "map"; 21 | } 22 | 23 | toString(): string { 24 | return `Map<${this.value.toString()}>`; 25 | } 26 | 27 | // asZodType() { 28 | // return z.map(z.string(), this.value.asZodType()); 29 | // } 30 | 31 | getWildcards(): Wildcard[] { 32 | return this.getWildcards(); 33 | } 34 | 35 | eq(other: t.Any): boolean { 36 | return other instanceof t.Map && this.value.eq(other.value); 37 | } 38 | 39 | serialize() { 40 | return { variant: "map", value: this.value.serialize() }; 41 | } 42 | 43 | hasUnconnectedWildcard(): boolean { 44 | return this.value.hasUnconnectedWildcard(); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /packages/typesystem/src/option.ts: -------------------------------------------------------------------------------- 1 | import { None, Option } from "@macrograph/option"; 2 | import * as v from "valibot"; 3 | 4 | import { type TypeVariant, type Wildcard, t } from "."; 5 | import { BaseType } from "./base"; 6 | 7 | export class OptionType> extends BaseType< 8 | Option> 9 | > { 10 | constructor(public inner: T) { 11 | super(); 12 | } 13 | 14 | default(): Option> { 15 | return None; 16 | } 17 | 18 | variant(): TypeVariant { 19 | return this.inner.variant(); 20 | } 21 | 22 | getInner(): T { 23 | if (this.inner instanceof OptionType) return this.inner.getInner(); 24 | 25 | return this.inner; 26 | } 27 | 28 | toString(): string { 29 | return `Option<${this.inner.toString()}>`; 30 | } 31 | 32 | asZodType() { 33 | // TODO: needs to validate inner 34 | 35 | return v.instance(Option>); 36 | } 37 | 38 | getWildcards(): Wildcard[] { 39 | return this.getWildcards(); 40 | } 41 | 42 | eq(other: t.Any): boolean { 43 | return other instanceof t.Option && this.inner.eq(other.inner); 44 | } 45 | 46 | serialize() { 47 | return { variant: "option", inner: this.inner.serialize() }; 48 | } 49 | 50 | hasUnconnectedWildcard(): boolean { 51 | return this.inner.hasUnconnectedWildcard(); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /packages/typesystem/src/t.ts: -------------------------------------------------------------------------------- 1 | import { 2 | type AnyType, 3 | type EnumBase, 4 | EnumType, 5 | ListType, 6 | MapType, 7 | OptionType, 8 | type Wildcard, 9 | WildcardType, 10 | } from "."; 11 | import type { infer } from "./base"; 12 | import { 13 | BasePrimitiveType, 14 | BoolType, 15 | FloatType, 16 | IntType, 17 | StringType, 18 | } from "./primitive"; 19 | import { type StructBase, StructType } from "./struct"; 20 | 21 | const INT = new IntType(); 22 | const FLOAT = new FloatType(); 23 | const STRING = new StringType(); 24 | const BOOL = new BoolType(); 25 | 26 | const int = () => INT; 27 | const float = () => FLOAT; 28 | const string = () => STRING; 29 | const bool = () => BOOL; 30 | const list = (t: T) => new ListType(t); 31 | const map = (v: TValue) => new MapType(v); 32 | const option = (t: T) => new OptionType(t); 33 | const enm = >(t: T) => new EnumType(t); 34 | const struct = (s: T) => new StructType(s); 35 | const wildcard = (w: Wildcard) => new WildcardType(w); 36 | 37 | export { 38 | int, 39 | float, 40 | string, 41 | bool, 42 | list, 43 | option, 44 | enm as enum, 45 | wildcard, 46 | struct, 47 | map, 48 | }; 49 | 50 | export { 51 | ListType as List, 52 | EnumType as Enum, 53 | WildcardType as Wildcard, 54 | OptionType as Option, 55 | MapType as Map, 56 | StringType as String, 57 | IntType as Int, 58 | FloatType as Float, 59 | BoolType as Bool, 60 | StructType as Struct, 61 | BasePrimitiveType as Primitive, 62 | }; 63 | 64 | export type { AnyType as Any, infer }; 65 | -------------------------------------------------------------------------------- /packages/typesystem/src/utils.ts: -------------------------------------------------------------------------------- 1 | export abstract class Disposable { 2 | disposeListeners: Set<() => void> = new Set(); 3 | 4 | private disposed = false; 5 | dispose() { 6 | if (this.disposed) return; 7 | 8 | for (const cb of this.disposeListeners) { 9 | cb(); 10 | } 11 | 12 | this.disposed = true; 13 | } 14 | 15 | addDisposeListener(cb: () => void): () => void { 16 | this.disposeListeners.add(cb); 17 | 18 | return () => this.disposeListeners.delete(cb); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /packages/typesystem/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@macrograph/config/tsconfig", 3 | "compilerOptions": { 4 | "outDir": "dist" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /packages/ui/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@macrograph/ui", 3 | "type": "module", 4 | "private": true, 5 | "sideEffects": false, 6 | "main": "./src/index.ts", 7 | "types": "./src/index.ts", 8 | "exports": { 9 | ".": "./src/index.ts", 10 | "./tailwind.preset.js": "./tailwind.preset.js", 11 | "./vite": "./vite.js", 12 | "./global.css": "./src/global.css", 13 | "./types": "./src/auto-imports.d.ts" 14 | }, 15 | "typesVersions": { 16 | "*": { 17 | "types": [ 18 | "./src/auto-imports.d.ts" 19 | ] 20 | } 21 | }, 22 | "devDependencies": { 23 | "solid-js": "^1.9.7", 24 | "storybook-solidjs": "1.0.0-beta.2", 25 | "tailwindcss": "^3.4.3", 26 | "tailwindcss-animate": "^1.0.7", 27 | "unplugin-auto-import": "^0.17.5", 28 | "unplugin-icons": "^0.18.5", 29 | "vite": "^6.3.5" 30 | }, 31 | "peerDependencies": { 32 | "solid-js": "^1.7.11", 33 | "tailwindcss": "^3.4.3" 34 | }, 35 | "dependencies": { 36 | "@kobalte/core": "^0.13.7", 37 | "@macrograph/config": "workspace:*", 38 | "@tanstack/solid-query": "^4.36.1", 39 | "clsx": "^1.2.1", 40 | "cva": "npm:class-variance-authority@^0.7.0", 41 | "i": "^0.3.7", 42 | "tailwind-merge": "^2.2.2" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /packages/ui/src/Card.tsx: -------------------------------------------------------------------------------- 1 | import clsx from "clsx"; 2 | import type { ComponentProps } from "solid-js"; 3 | 4 | export function Card(props: ComponentProps<"div">) { 5 | return ; 6 | } 7 | -------------------------------------------------------------------------------- /packages/ui/src/badge.tsx: -------------------------------------------------------------------------------- 1 | import type { Component, ComponentProps } from "solid-js"; 2 | import { splitProps } from "solid-js"; 3 | 4 | import type { VariantProps } from "cva"; 5 | import { cva } from "cva"; 6 | 7 | import { cn } from "./lib/utils"; 8 | 9 | const badgeVariants = cva( 10 | "inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2", 11 | { 12 | variants: { 13 | variant: { 14 | default: 15 | "border-transparent bg-primary text-primary-foreground hover:bg-primary/80", 16 | secondary: 17 | "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80", 18 | destructive: 19 | "border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80", 20 | outline: "text-foreground", 21 | }, 22 | }, 23 | defaultVariants: { 24 | variant: "default", 25 | }, 26 | }, 27 | ); 28 | 29 | export interface BadgeProps 30 | extends ComponentProps<"div">, 31 | VariantProps {} 32 | 33 | const Badge: Component = (props) => { 34 | const [, rest] = splitProps(props, ["variant", "class"]); 35 | return ( 36 | 40 | ); 41 | }; 42 | 43 | export { Badge, badgeVariants }; 44 | -------------------------------------------------------------------------------- /packages/ui/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./Card"; 2 | export * from "./button"; 3 | export * from "./dropdown-menu"; 4 | export * from "./input"; 5 | export * from "./dialog"; 6 | export * from "./badge"; 7 | // export * from "./popover"; 8 | import "./global.css"; 9 | -------------------------------------------------------------------------------- /packages/ui/src/input.tsx: -------------------------------------------------------------------------------- 1 | import type { Component, ComponentProps } from "solid-js"; 2 | import { splitProps } from "solid-js"; 3 | 4 | import { cn } from "./lib/utils"; 5 | 6 | const Input: Component> = (props) => { 7 | const [, rest] = splitProps(props, ["type", "class"]); 8 | return ( 9 | 17 | ); 18 | }; 19 | 20 | export { Input }; 21 | -------------------------------------------------------------------------------- /packages/ui/src/lib/utils.ts: -------------------------------------------------------------------------------- 1 | import type { ClassValue } from "clsx"; 2 | import { clsx } from "clsx"; 3 | import { twMerge } from "tailwind-merge"; 4 | 5 | export function cn(...inputs: ClassValue[]) { 6 | return twMerge(clsx(inputs)); 7 | } 8 | -------------------------------------------------------------------------------- /packages/ui/src/popover.tsx: -------------------------------------------------------------------------------- 1 | import type { PolymorphicProps } from "@kobalte/core/polymorphic"; 2 | import * as PopoverPrimitive from "@kobalte/core/popover"; 3 | import type { Component, ValidComponent } from "solid-js"; 4 | import { splitProps } from "solid-js"; 5 | 6 | import { cn } from "./lib/utils"; 7 | 8 | const Popover: Component = (props) => { 9 | return ; 10 | }; 11 | 12 | const PopoverTrigger = PopoverPrimitive.Trigger; 13 | 14 | type PopoverContentProps = 15 | PopoverPrimitive.PopoverContentProps & { class?: string | undefined }; 16 | 17 | const PopoverContent = ( 18 | props: PolymorphicProps>, 19 | ) => { 20 | const [local, others] = splitProps(props as PopoverContentProps, ["class"]); 21 | return ( 22 | 23 | 30 | 31 | ); 32 | }; 33 | 34 | export { Popover, PopoverTrigger, PopoverContent }; 35 | -------------------------------------------------------------------------------- /packages/ui/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@macrograph/config/tsconfig", 3 | "compilerOptions": { 4 | "outDir": "dist" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /packages/ui/ui.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "tsx": true, 3 | "componentDir": "./src", 4 | "tailwind": { 5 | "config": "tailwind.preset.js", 6 | "css": "src/app.css" 7 | }, 8 | "aliases": { 9 | "path": "~/*" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /packages/ui/vite.js: -------------------------------------------------------------------------------- 1 | import { fileURLToPath } from "node:url"; 2 | import AutoImport from "unplugin-auto-import/vite"; 3 | import IconsResolver from "unplugin-icons/resolver"; 4 | import Icons from "unplugin-icons/vite"; 5 | 6 | // Workaround for https://github.com/solidjs/solid-start/issues/1374 7 | const VinxiAutoImport = (options) => { 8 | const autoimport = AutoImport(options); 9 | 10 | return { 11 | ...autoimport, 12 | transform(src, id) { 13 | let pathname = id; 14 | 15 | if (id.startsWith("/")) { 16 | pathname = new URL(`file://${id}`).pathname; 17 | } 18 | 19 | return autoimport.transform(src, pathname); 20 | }, 21 | }; 22 | }; 23 | 24 | export default [ 25 | VinxiAutoImport({ 26 | dts: fileURLToPath( 27 | new URL("../../packages/ui/src/auto-imports.d.ts", import.meta.url).href, 28 | ), 29 | resolvers: [ 30 | IconsResolver({ 31 | prefix: "Icon", 32 | extension: "jsx", 33 | }), 34 | ], 35 | }), 36 | Icons({ compiler: "solid", scale: 1 }), 37 | ]; 38 | -------------------------------------------------------------------------------- /packages/utils/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@macrograph/utils", 3 | "private": true, 4 | "main": "./src/index.ts", 5 | "types": "./src/index.ts", 6 | "type": "module", 7 | "scripts": { 8 | "clean": "rm -rf .turbo && rm -rf node_modules && rm -rf dist", 9 | "test": "vitest", 10 | "typecheck": "tsc -b" 11 | }, 12 | "devDependencies": { 13 | "@macrograph/config": "workspace:*", 14 | "solid-js": "^1.9.7" 15 | }, 16 | "dependencies": { 17 | "@tanstack/solid-query": "^4.36.1" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /packages/utils/src/index.ts: -------------------------------------------------------------------------------- 1 | export function makeCache(fn: () => Promise) { 2 | let value: Promise | undefined; 3 | 4 | return async () => { 5 | value ??= fn(); 6 | return await value; 7 | }; 8 | } 9 | -------------------------------------------------------------------------------- /packages/utils/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@macrograph/config/tsconfig", 3 | "compilerOptions": { 4 | "outDir": "dist" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /packages/web-api/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@macrograph/web-api", 3 | "private": true, 4 | "main": "./src/index.ts", 5 | "types": "./src/index.ts", 6 | "type": "module", 7 | "dependencies": { 8 | "@effect/platform": "^0.80.2", 9 | "effect": "^3.14.2" 10 | }, 11 | "devDependencies": { 12 | "@macrograph/config": "workspace:*" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /packages/web-api/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@macrograph/config/tsconfig", 3 | "compilerOptions": { 4 | "outDir": "dist" 5 | }, 6 | "references": [{ "path": "../runtime" }, { "path": "../typesystem" }] 7 | } 8 | -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | - "interface" 3 | - "apps/*" 4 | - "crates/*" 5 | - "packages/*" 6 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "files": [], 3 | "references": [ 4 | { "path": "./packages/typesystem" }, 5 | { "path": "./packages/ui" }, 6 | { "path": "./packages/runtime" }, 7 | { "path": "./packages/json" }, 8 | { "path": "./packages/packages" }, 9 | { "path": "./packages/api-contract" }, 10 | { "path": "./packages/clipboard" }, 11 | { "path": "./interface" }, 12 | { "path": "./apps/desktop" }, 13 | { "path": "./apps/web" } 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /turbo.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://turbo.build/schema.json", 3 | "pipeline": { 4 | "build": { 5 | "dependsOn": ["^build"], 6 | "outputs": ["dist/**", ".vercel/**"] 7 | }, 8 | "dev": { 9 | "cache": false 10 | }, 11 | "lint": {}, 12 | "typecheck": { 13 | "outputs": ["dist/**"] 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /vitest.config.ts: -------------------------------------------------------------------------------- 1 | import solid from "vite-plugin-solid"; 2 | import GithubActionsReporter from "vitest-github-actions-reporter"; 3 | import { defineConfig } from "vitest/config"; 4 | 5 | export default defineConfig({ 6 | plugins: [solid()], 7 | test: { 8 | reporters: process.env.GITHUB_ACTIONS 9 | ? ["default", new GithubActionsReporter()] 10 | : [], 11 | }, 12 | }); 13 | --------------------------------------------------------------------------------
Logged in as {user().login}
Logging in...
Logged in as {user().name}
49 | Login may not work without approval of project maintainer 50 |
Logged in as {user().data.attributes.full_name}
Logged in as {user().display_name}