├── .nvmrc
├── .husky
├── pre-commit
└── pre-push
├── packages
├── electron
│ ├── .gitignore
│ ├── main
│ │ └── package.json
│ ├── preload
│ │ └── package.json
│ ├── CHANGELOG.md
│ ├── tsconfig.json
│ ├── src
│ │ ├── types
│ │ │ ├── thunkProcessor.ts
│ │ │ ├── preload.ts
│ │ │ └── errors.ts
│ │ ├── utils
│ │ │ ├── deepGet.ts
│ │ │ ├── preloadOptions.ts
│ │ │ ├── thunkProcessor.ts
│ │ │ └── environment.ts
│ │ ├── thunk
│ │ │ └── init.ts
│ │ └── renderer.ts
│ ├── vitest.config.ts
│ ├── test
│ │ ├── helpers.ts
│ │ └── setup.ts
│ ├── tsdown.win32.node.config.ts
│ ├── tsdown.win32.preload.config.ts
│ └── scripts
│ │ └── build.ts
├── core
│ ├── src
│ │ └── index.ts
│ ├── README.md
│ └── tsconfig.json
├── middleware
│ ├── node
│ │ ├── build.rs
│ │ └── Cargo.toml
│ └── Cargo.toml
├── apps-shared
│ ├── README.md
│ ├── src
│ │ ├── state
│ │ │ └── index.ts
│ │ ├── index.ts
│ │ └── types.ts
│ ├── tsconfig.json
│ └── package.json
├── tauri
│ ├── .gitignore
│ ├── src
│ │ └── vite-env.d.ts
│ ├── rollup.config.js
│ ├── tsconfig.json
│ ├── vitest.config.ts
│ ├── tauri.conf.json
│ ├── Cargo.toml
│ └── scripts
│ │ └── build.ts
├── ui
│ ├── postcss.config.cjs
│ ├── README.md
│ ├── tsconfig.json
│ ├── tsdown.config.ts
│ └── src
│ │ ├── tauri.ts
│ │ ├── electron.ts
│ │ ├── styles
│ │ ├── tailwind.css
│ │ └── theme.css
│ │ └── components
│ │ └── WindowDisplay
│ │ └── index.tsx
├── tauri-plugin
│ ├── tauri.conf.json
│ ├── permissions
│ │ └── default.toml
│ ├── package.json
│ ├── .gitignore
│ ├── .cargo
│ │ └── config.toml
│ ├── src
│ │ ├── commands.rs
│ │ ├── error.rs
│ │ ├── mobile.rs
│ │ └── models.rs
│ └── build.rs
└── types
│ ├── README.md
│ ├── tsconfig.json
│ ├── tsdown.config.ts
│ └── src
│ └── internal.ts
├── examples
└── electron
│ ├── custom
│ ├── redux
│ ├── sandbox-true
│ ├── zustand-basic
│ ├── zustand-immer
│ ├── zustand-handlers
│ └── zustand-reducers
├── apps
├── electron
│ ├── e2e
│ │ ├── src
│ │ │ ├── main
│ │ │ │ ├── env.d.ts
│ │ │ │ └── adapters
│ │ │ │ │ └── index.ts
│ │ │ ├── renderer
│ │ │ │ ├── hooks
│ │ │ │ │ └── useStore.ts
│ │ │ │ ├── styles
│ │ │ │ │ └── index.css
│ │ │ │ └── index.html
│ │ │ └── modes
│ │ │ │ ├── zustand-basic
│ │ │ │ ├── store.ts
│ │ │ │ ├── features
│ │ │ │ │ ├── error
│ │ │ │ │ │ └── index.ts
│ │ │ │ │ └── theme
│ │ │ │ │ │ └── index.ts
│ │ │ │ ├── main.ts
│ │ │ │ └── tray.ts
│ │ │ │ ├── custom
│ │ │ │ └── features
│ │ │ │ │ ├── error
│ │ │ │ │ └── index.ts
│ │ │ │ │ ├── theme
│ │ │ │ │ └── index.ts
│ │ │ │ │ └── state
│ │ │ │ │ └── index.ts
│ │ │ │ ├── zustand-handlers
│ │ │ │ ├── store.ts
│ │ │ │ └── features
│ │ │ │ │ └── error
│ │ │ │ │ └── index.ts
│ │ │ │ ├── zustand-reducers
│ │ │ │ ├── store.ts
│ │ │ │ └── features
│ │ │ │ │ ├── error
│ │ │ │ │ └── index.ts
│ │ │ │ │ └── theme
│ │ │ │ │ └── index.ts
│ │ │ │ └── redux
│ │ │ │ └── features
│ │ │ │ ├── error
│ │ │ │ └── index.ts
│ │ │ │ └── theme
│ │ │ │ └── index.ts
│ │ ├── resources
│ │ │ └── electron-logo.png
│ │ ├── postcss.config.js
│ │ ├── tsconfig.web.json
│ │ └── tsconfig.node.json
│ ├── minimal-custom
│ │ ├── src
│ │ │ ├── main
│ │ │ │ └── env.d.ts
│ │ │ ├── renderer
│ │ │ │ ├── hooks
│ │ │ │ │ └── useStore.ts
│ │ │ │ └── index.html
│ │ │ ├── features
│ │ │ │ ├── theme
│ │ │ │ │ └── index.ts
│ │ │ │ ├── counter
│ │ │ │ │ └── index.ts
│ │ │ │ └── index.ts
│ │ │ └── preload
│ │ │ │ └── index.ts
│ │ ├── postcss.config.js
│ │ ├── resources
│ │ │ └── electron-logo.png
│ │ ├── electron-builder.config.ts
│ │ ├── test
│ │ │ ├── tsconfig.json
│ │ │ ├── utils
│ │ │ │ └── constants.ts
│ │ │ └── wdio.conf.ts
│ │ ├── tsconfig.web.json
│ │ └── tsconfig.node.json
│ ├── minimal-redux
│ │ ├── src
│ │ │ ├── main
│ │ │ │ ├── env.d.ts
│ │ │ │ ├── bridge.ts
│ │ │ │ └── store.ts
│ │ │ ├── renderer
│ │ │ │ ├── hooks
│ │ │ │ │ └── useStore.ts
│ │ │ │ └── index.html
│ │ │ ├── features
│ │ │ │ ├── index.ts
│ │ │ │ ├── theme
│ │ │ │ │ └── index.ts
│ │ │ │ └── counter
│ │ │ │ │ └── index.ts
│ │ │ └── preload
│ │ │ │ └── index.ts
│ │ ├── postcss.config.js
│ │ ├── resources
│ │ │ └── electron-logo.png
│ │ ├── electron-builder.config.ts
│ │ ├── test
│ │ │ ├── tsconfig.json
│ │ │ ├── utils
│ │ │ │ └── constants.ts
│ │ │ └── wdio.conf.ts
│ │ ├── tsconfig.web.json
│ │ └── tsconfig.node.json
│ ├── minimal-sandbox-true
│ │ ├── src
│ │ │ ├── main
│ │ │ │ ├── env.d.ts
│ │ │ │ ├── bridge.ts
│ │ │ │ └── store.ts
│ │ │ ├── renderer
│ │ │ │ ├── hooks
│ │ │ │ │ └── useStore.ts
│ │ │ │ └── index.html
│ │ │ ├── features
│ │ │ │ ├── theme
│ │ │ │ │ └── index.ts
│ │ │ │ ├── index.ts
│ │ │ │ └── counter
│ │ │ │ │ └── index.ts
│ │ │ └── preload
│ │ │ │ └── index.ts
│ │ ├── postcss.config.js
│ │ ├── resources
│ │ │ └── electron-logo.png
│ │ ├── electron-builder.config.ts
│ │ ├── test
│ │ │ ├── tsconfig.json
│ │ │ ├── utils
│ │ │ │ └── constants.ts
│ │ │ └── wdio.conf.ts
│ │ ├── tsconfig.node.json
│ │ ├── tsconfig.json
│ │ └── tsconfig.web.json
│ ├── minimal-zustand-basic
│ │ ├── src
│ │ │ ├── main
│ │ │ │ ├── env.d.ts
│ │ │ │ ├── bridge.ts
│ │ │ │ └── store.ts
│ │ │ ├── renderer
│ │ │ │ ├── hooks
│ │ │ │ │ └── useStore.ts
│ │ │ │ └── index.html
│ │ │ ├── features
│ │ │ │ ├── theme
│ │ │ │ │ └── index.ts
│ │ │ │ ├── index.ts
│ │ │ │ └── counter
│ │ │ │ │ └── index.ts
│ │ │ └── preload
│ │ │ │ └── index.ts
│ │ ├── postcss.config.js
│ │ ├── resources
│ │ │ └── electron-logo.png
│ │ ├── electron-builder.config.ts
│ │ ├── test
│ │ │ ├── tsconfig.json
│ │ │ ├── utils
│ │ │ │ └── constants.ts
│ │ │ └── wdio.conf.ts
│ │ ├── tsconfig.node.json
│ │ ├── tsconfig.json
│ │ └── tsconfig.web.json
│ ├── minimal-zustand-handlers
│ │ ├── src
│ │ │ ├── main
│ │ │ │ ├── env.d.ts
│ │ │ │ └── store.ts
│ │ │ ├── renderer
│ │ │ │ ├── hooks
│ │ │ │ │ └── useStore.ts
│ │ │ │ └── index.html
│ │ │ ├── features
│ │ │ │ └── theme
│ │ │ │ │ └── index.ts
│ │ │ └── preload
│ │ │ │ └── index.ts
│ │ ├── postcss.config.js
│ │ ├── resources
│ │ │ └── electron-logo.png
│ │ ├── electron-builder.config.ts
│ │ ├── test
│ │ │ ├── tsconfig.json
│ │ │ ├── utils
│ │ │ │ └── constants.ts
│ │ │ └── wdio.conf.ts
│ │ ├── tsconfig.web.json
│ │ └── tsconfig.node.json
│ ├── minimal-zustand-immer
│ │ ├── src
│ │ │ ├── main
│ │ │ │ ├── env.d.ts
│ │ │ │ ├── bridge.ts
│ │ │ │ └── store.ts
│ │ │ ├── renderer
│ │ │ │ ├── hooks
│ │ │ │ │ └── useStore.ts
│ │ │ │ └── index.html
│ │ │ ├── features
│ │ │ │ ├── theme
│ │ │ │ │ └── index.ts
│ │ │ │ ├── index.ts
│ │ │ │ └── counter
│ │ │ │ │ └── index.ts
│ │ │ └── preload
│ │ │ │ └── index.ts
│ │ ├── postcss.config.js
│ │ ├── resources
│ │ │ └── electron-logo.png
│ │ ├── electron-builder.config.ts
│ │ ├── test
│ │ │ ├── tsconfig.json
│ │ │ ├── utils
│ │ │ │ └── constants.ts
│ │ │ └── wdio.conf.ts
│ │ ├── tsconfig.node.json
│ │ ├── tsconfig.json
│ │ └── tsconfig.web.json
│ ├── minimal-zustand-reducers
│ │ ├── src
│ │ │ ├── main
│ │ │ │ ├── env.d.ts
│ │ │ │ └── store.ts
│ │ │ ├── renderer
│ │ │ │ ├── hooks
│ │ │ │ │ └── useStore.ts
│ │ │ │ └── index.html
│ │ │ ├── features
│ │ │ │ ├── theme
│ │ │ │ │ └── index.ts
│ │ │ │ ├── counter
│ │ │ │ │ └── index.ts
│ │ │ │ └── index.ts
│ │ │ └── preload
│ │ │ │ └── index.ts
│ │ ├── postcss.config.js
│ │ ├── resources
│ │ │ └── electron-logo.png
│ │ ├── electron-builder.config.ts
│ │ ├── test
│ │ │ ├── tsconfig.json
│ │ │ ├── utils
│ │ │ │ └── constants.ts
│ │ │ └── wdio.conf.ts
│ │ ├── tsconfig.web.json
│ │ └── tsconfig.node.json
│ └── minimal-context-isolation-false
│ │ ├── src
│ │ ├── main
│ │ │ ├── env.d.ts
│ │ │ ├── bridge.ts
│ │ │ └── store.ts
│ │ ├── renderer
│ │ │ ├── hooks
│ │ │ │ └── useStore.ts
│ │ │ └── index.html
│ │ └── features
│ │ │ ├── theme
│ │ │ └── index.ts
│ │ │ ├── index.ts
│ │ │ └── counter
│ │ │ └── index.ts
│ │ ├── postcss.config.js
│ │ ├── resources
│ │ └── electron-logo.png
│ │ ├── electron-builder.config.ts
│ │ ├── tsconfig.node.json
│ │ ├── tsconfig.json
│ │ └── tsconfig.web.json
├── tauri-v1
│ └── e2e
│ │ ├── src-tauri
│ │ ├── build.rs
│ │ ├── .gitignore
│ │ ├── icons
│ │ │ ├── 32x32.png
│ │ │ ├── icon.icns
│ │ │ ├── icon.ico
│ │ │ ├── icon.png
│ │ │ ├── 128x128.png
│ │ │ ├── 128x128@2x.png
│ │ │ ├── StoreLogo.png
│ │ │ ├── Square30x30Logo.png
│ │ │ ├── Square44x44Logo.png
│ │ │ ├── Square71x71Logo.png
│ │ │ ├── Square89x89Logo.png
│ │ │ ├── Square107x107Logo.png
│ │ │ ├── Square142x142Logo.png
│ │ │ ├── Square150x150Logo.png
│ │ │ ├── Square284x284Logo.png
│ │ │ └── Square310x310Logo.png
│ │ ├── capabilities
│ │ │ ├── default.json
│ │ │ └── main.json
│ │ ├── src
│ │ │ └── main.rs
│ │ └── Cargo.toml
│ │ ├── src
│ │ ├── types
│ │ │ ├── index.ts
│ │ │ └── state.ts
│ │ ├── types.ts
│ │ ├── index.css
│ │ └── renderer
│ │ │ ├── styles
│ │ │ └── index.css
│ │ │ ├── index.html
│ │ │ └── store.ts
│ │ ├── tailwind.config.js
│ │ └── tsconfig.json
└── tauri
│ └── e2e
│ ├── src-tauri
│ ├── .gitignore
│ ├── icons
│ │ ├── 32x32.png
│ │ ├── icon.icns
│ │ ├── icon.ico
│ │ ├── icon.png
│ │ ├── 128x128.png
│ │ ├── 128x128@2x.png
│ │ ├── StoreLogo.png
│ │ ├── Square30x30Logo.png
│ │ ├── Square44x44Logo.png
│ │ ├── Square71x71Logo.png
│ │ ├── Square89x89Logo.png
│ │ ├── Square107x107Logo.png
│ │ ├── Square142x142Logo.png
│ │ ├── Square150x150Logo.png
│ │ ├── Square284x284Logo.png
│ │ └── Square310x310Logo.png
│ ├── src
│ │ └── main.rs
│ ├── capabilities
│ │ ├── default.json
│ │ └── main.json
│ ├── build.rs
│ └── Cargo.toml
│ ├── src
│ ├── types
│ │ ├── index.ts
│ │ └── state.ts
│ └── renderer
│ │ ├── styles
│ │ └── index.css
│ │ └── index.html
│ ├── tailwind.config.js
│ └── tsconfig.json
├── vercel.json
├── resources
├── build
│ ├── icon.icns
│ ├── icon.ico
│ ├── icon.png
│ └── entitlements.mac.plist
├── zubridge-hero.png
├── zubridge-electron-app-architecture.png
├── zubridge-tauri-direct-architecture.png
└── zubridge-tauri-plugin-architecture.png
├── agent-os
├── config.yml
├── specs
│ └── 2025-10-05-zubridge-core-rust-crate
│ │ └── spec-lite.md
├── product
│ └── mission-lite.md
└── standards
│ ├── frontend
│ ├── css.md
│ ├── accessibility.md
│ ├── components.md
│ └── responsive.md
│ ├── backend
│ ├── migrations.md
│ ├── queries.md
│ ├── models.md
│ └── api.md
│ └── testing
│ └── test-writing.md
├── .editorconfig
├── .cursor
└── rules
│ ├── execute-tasks.mdc
│ ├── plan-product.mdc
│ ├── analyze-product.mdc
│ ├── create-tasks.mdc
│ ├── create-spec.mdc
│ └── coding-patterns.mdc
├── tsconfig.base.json
├── version.config.json
├── scripts
├── create-task-graph.ts
└── clean-cache.ts
├── e2e
└── tsconfig.json
├── .github
├── workflows
│ └── actions
│ │ ├── cargo-setup
│ │ └── action.yml
│ │ └── setup-workspace
│ │ └── action.yml
└── dependabot.yml
├── pnpm-workspace.yaml
├── .gitignore
└── LICENSE
/.nvmrc:
--------------------------------------------------------------------------------
1 | v20.18.0
2 |
--------------------------------------------------------------------------------
/.husky/pre-commit:
--------------------------------------------------------------------------------
1 | pnpx lint-staged
2 |
--------------------------------------------------------------------------------
/.husky/pre-push:
--------------------------------------------------------------------------------
1 | turbo run test:unit
2 |
--------------------------------------------------------------------------------
/packages/electron/.gitignore:
--------------------------------------------------------------------------------
1 | coverage
2 | *.tgz
3 |
--------------------------------------------------------------------------------
/examples/electron/custom:
--------------------------------------------------------------------------------
1 | ../../apps/electron/minimal-custom
--------------------------------------------------------------------------------
/examples/electron/redux:
--------------------------------------------------------------------------------
1 | ../../apps/electron/minimal-redux
--------------------------------------------------------------------------------
/packages/core/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from './utils/debug.js';
2 |
--------------------------------------------------------------------------------
/examples/electron/sandbox-true:
--------------------------------------------------------------------------------
1 | ../../apps/electron/minimal-sandbox-true
--------------------------------------------------------------------------------
/examples/electron/zustand-basic:
--------------------------------------------------------------------------------
1 | ../../apps/electron/minimal-zustand-basic
--------------------------------------------------------------------------------
/examples/electron/zustand-immer:
--------------------------------------------------------------------------------
1 | ../../apps/electron/minimal-zustand-immer
--------------------------------------------------------------------------------
/apps/electron/e2e/src/main/env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/examples/electron/zustand-handlers:
--------------------------------------------------------------------------------
1 | ../../apps/electron/minimal-zustand-handlers
--------------------------------------------------------------------------------
/examples/electron/zustand-reducers:
--------------------------------------------------------------------------------
1 | ../../apps/electron/minimal-zustand-reducers
--------------------------------------------------------------------------------
/packages/middleware/node/build.rs:
--------------------------------------------------------------------------------
1 | fn main() {
2 | napi_build::setup();
3 | }
4 |
--------------------------------------------------------------------------------
/vercel.json:
--------------------------------------------------------------------------------
1 | {
2 | "git": {
3 | "deploymentEnabled": false
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/apps/electron/minimal-custom/src/main/env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/apps/electron/minimal-redux/src/main/env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/apps/tauri-v1/e2e/src-tauri/build.rs:
--------------------------------------------------------------------------------
1 | fn main() {
2 | tauri_build::build()
3 | }
4 |
--------------------------------------------------------------------------------
/apps/electron/minimal-sandbox-true/src/main/env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/apps/electron/minimal-zustand-basic/src/main/env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/apps/electron/minimal-zustand-handlers/src/main/env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/apps/electron/minimal-zustand-immer/src/main/env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/apps/electron/minimal-zustand-reducers/src/main/env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/apps/electron/minimal-context-isolation-false/src/main/env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/resources/build/icon.icns:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/goosewobbler/zubridge/HEAD/resources/build/icon.icns
--------------------------------------------------------------------------------
/resources/build/icon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/goosewobbler/zubridge/HEAD/resources/build/icon.ico
--------------------------------------------------------------------------------
/resources/build/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/goosewobbler/zubridge/HEAD/resources/build/icon.png
--------------------------------------------------------------------------------
/resources/zubridge-hero.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/goosewobbler/zubridge/HEAD/resources/zubridge-hero.png
--------------------------------------------------------------------------------
/packages/core/README.md:
--------------------------------------------------------------------------------
1 | # @zubridge/core
2 |
3 | Core utilities and shared functionality for Zubridge packages.
4 |
--------------------------------------------------------------------------------
/packages/electron/main/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "main": "../dist/main.cjs",
3 | "types": "../dist/main.d.cts"
4 | }
5 |
--------------------------------------------------------------------------------
/packages/apps-shared/README.md:
--------------------------------------------------------------------------------
1 | # @zubridge/apps-shared
2 |
3 | Core utilities and shared functionality for Zubridge apps.
4 |
--------------------------------------------------------------------------------
/packages/electron/preload/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "main": "../dist/preload.cjs",
3 | "types": "../dist/preload.d.cts"
4 | }
5 |
--------------------------------------------------------------------------------
/apps/tauri/e2e/src-tauri/.gitignore:
--------------------------------------------------------------------------------
1 | # Generated by Cargo
2 | # will have compiled files and executables
3 | /target/
4 | /gen/schemas
5 |
--------------------------------------------------------------------------------
/apps/tauri/e2e/src-tauri/icons/32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/goosewobbler/zubridge/HEAD/apps/tauri/e2e/src-tauri/icons/32x32.png
--------------------------------------------------------------------------------
/apps/tauri/e2e/src-tauri/icons/icon.icns:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/goosewobbler/zubridge/HEAD/apps/tauri/e2e/src-tauri/icons/icon.icns
--------------------------------------------------------------------------------
/apps/tauri/e2e/src-tauri/icons/icon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/goosewobbler/zubridge/HEAD/apps/tauri/e2e/src-tauri/icons/icon.ico
--------------------------------------------------------------------------------
/apps/tauri/e2e/src-tauri/icons/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/goosewobbler/zubridge/HEAD/apps/tauri/e2e/src-tauri/icons/icon.png
--------------------------------------------------------------------------------
/apps/tauri-v1/e2e/src-tauri/.gitignore:
--------------------------------------------------------------------------------
1 | # Generated by Cargo
2 | # will have compiled files and executables
3 | /target/
4 | /gen/schemas
5 |
--------------------------------------------------------------------------------
/apps/tauri-v1/e2e/src-tauri/icons/32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/goosewobbler/zubridge/HEAD/apps/tauri-v1/e2e/src-tauri/icons/32x32.png
--------------------------------------------------------------------------------
/apps/tauri-v1/e2e/src-tauri/icons/icon.icns:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/goosewobbler/zubridge/HEAD/apps/tauri-v1/e2e/src-tauri/icons/icon.icns
--------------------------------------------------------------------------------
/apps/tauri-v1/e2e/src-tauri/icons/icon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/goosewobbler/zubridge/HEAD/apps/tauri-v1/e2e/src-tauri/icons/icon.ico
--------------------------------------------------------------------------------
/apps/tauri-v1/e2e/src-tauri/icons/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/goosewobbler/zubridge/HEAD/apps/tauri-v1/e2e/src-tauri/icons/icon.png
--------------------------------------------------------------------------------
/apps/tauri/e2e/src-tauri/icons/128x128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/goosewobbler/zubridge/HEAD/apps/tauri/e2e/src-tauri/icons/128x128.png
--------------------------------------------------------------------------------
/apps/electron/e2e/resources/electron-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/goosewobbler/zubridge/HEAD/apps/electron/e2e/resources/electron-logo.png
--------------------------------------------------------------------------------
/apps/tauri-v1/e2e/src-tauri/icons/128x128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/goosewobbler/zubridge/HEAD/apps/tauri-v1/e2e/src-tauri/icons/128x128.png
--------------------------------------------------------------------------------
/apps/tauri/e2e/src-tauri/icons/128x128@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/goosewobbler/zubridge/HEAD/apps/tauri/e2e/src-tauri/icons/128x128@2x.png
--------------------------------------------------------------------------------
/apps/tauri/e2e/src-tauri/icons/StoreLogo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/goosewobbler/zubridge/HEAD/apps/tauri/e2e/src-tauri/icons/StoreLogo.png
--------------------------------------------------------------------------------
/apps/electron/e2e/postcss.config.js:
--------------------------------------------------------------------------------
1 | export default {
2 | plugins: {
3 | '@tailwindcss/postcss': {},
4 | autoprefixer: {},
5 | },
6 | };
7 |
--------------------------------------------------------------------------------
/apps/tauri-v1/e2e/src-tauri/icons/128x128@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/goosewobbler/zubridge/HEAD/apps/tauri-v1/e2e/src-tauri/icons/128x128@2x.png
--------------------------------------------------------------------------------
/apps/tauri-v1/e2e/src-tauri/icons/StoreLogo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/goosewobbler/zubridge/HEAD/apps/tauri-v1/e2e/src-tauri/icons/StoreLogo.png
--------------------------------------------------------------------------------
/resources/zubridge-electron-app-architecture.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/goosewobbler/zubridge/HEAD/resources/zubridge-electron-app-architecture.png
--------------------------------------------------------------------------------
/resources/zubridge-tauri-direct-architecture.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/goosewobbler/zubridge/HEAD/resources/zubridge-tauri-direct-architecture.png
--------------------------------------------------------------------------------
/resources/zubridge-tauri-plugin-architecture.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/goosewobbler/zubridge/HEAD/resources/zubridge-tauri-plugin-architecture.png
--------------------------------------------------------------------------------
/apps/tauri/e2e/src-tauri/icons/Square30x30Logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/goosewobbler/zubridge/HEAD/apps/tauri/e2e/src-tauri/icons/Square30x30Logo.png
--------------------------------------------------------------------------------
/apps/tauri/e2e/src-tauri/icons/Square44x44Logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/goosewobbler/zubridge/HEAD/apps/tauri/e2e/src-tauri/icons/Square44x44Logo.png
--------------------------------------------------------------------------------
/apps/tauri/e2e/src-tauri/icons/Square71x71Logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/goosewobbler/zubridge/HEAD/apps/tauri/e2e/src-tauri/icons/Square71x71Logo.png
--------------------------------------------------------------------------------
/apps/tauri/e2e/src-tauri/icons/Square89x89Logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/goosewobbler/zubridge/HEAD/apps/tauri/e2e/src-tauri/icons/Square89x89Logo.png
--------------------------------------------------------------------------------
/apps/tauri/e2e/src/types/index.ts:
--------------------------------------------------------------------------------
1 | // Re-export types from the state definition file
2 | export { type BaseState, isBaseState, type State } from './state';
3 |
--------------------------------------------------------------------------------
/packages/tauri/.gitignore:
--------------------------------------------------------------------------------
1 | coverage
2 | *.tgz
3 |
4 | # Tauri bridge generated bindings
5 | **/src-rust/gen
6 |
7 | # Rust build artifacts
8 | **/target/
9 |
--------------------------------------------------------------------------------
/apps/electron/minimal-custom/postcss.config.js:
--------------------------------------------------------------------------------
1 | export default {
2 | plugins: {
3 | '@tailwindcss/postcss': {},
4 | autoprefixer: {},
5 | },
6 | };
7 |
--------------------------------------------------------------------------------
/apps/electron/minimal-redux/postcss.config.js:
--------------------------------------------------------------------------------
1 | export default {
2 | plugins: {
3 | '@tailwindcss/postcss': {},
4 | autoprefixer: {},
5 | },
6 | };
7 |
--------------------------------------------------------------------------------
/apps/tauri-v1/e2e/src-tauri/icons/Square30x30Logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/goosewobbler/zubridge/HEAD/apps/tauri-v1/e2e/src-tauri/icons/Square30x30Logo.png
--------------------------------------------------------------------------------
/apps/tauri-v1/e2e/src-tauri/icons/Square44x44Logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/goosewobbler/zubridge/HEAD/apps/tauri-v1/e2e/src-tauri/icons/Square44x44Logo.png
--------------------------------------------------------------------------------
/apps/tauri-v1/e2e/src-tauri/icons/Square71x71Logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/goosewobbler/zubridge/HEAD/apps/tauri-v1/e2e/src-tauri/icons/Square71x71Logo.png
--------------------------------------------------------------------------------
/apps/tauri-v1/e2e/src-tauri/icons/Square89x89Logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/goosewobbler/zubridge/HEAD/apps/tauri-v1/e2e/src-tauri/icons/Square89x89Logo.png
--------------------------------------------------------------------------------
/apps/tauri-v1/e2e/src/types/index.ts:
--------------------------------------------------------------------------------
1 | // Re-export types from the state definition file
2 | export { type BaseState, isBaseState, type State } from './state';
3 |
--------------------------------------------------------------------------------
/apps/tauri/e2e/src-tauri/icons/Square107x107Logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/goosewobbler/zubridge/HEAD/apps/tauri/e2e/src-tauri/icons/Square107x107Logo.png
--------------------------------------------------------------------------------
/apps/tauri/e2e/src-tauri/icons/Square142x142Logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/goosewobbler/zubridge/HEAD/apps/tauri/e2e/src-tauri/icons/Square142x142Logo.png
--------------------------------------------------------------------------------
/apps/tauri/e2e/src-tauri/icons/Square150x150Logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/goosewobbler/zubridge/HEAD/apps/tauri/e2e/src-tauri/icons/Square150x150Logo.png
--------------------------------------------------------------------------------
/apps/tauri/e2e/src-tauri/icons/Square284x284Logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/goosewobbler/zubridge/HEAD/apps/tauri/e2e/src-tauri/icons/Square284x284Logo.png
--------------------------------------------------------------------------------
/apps/tauri/e2e/src-tauri/icons/Square310x310Logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/goosewobbler/zubridge/HEAD/apps/tauri/e2e/src-tauri/icons/Square310x310Logo.png
--------------------------------------------------------------------------------
/apps/electron/minimal-redux/resources/electron-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/goosewobbler/zubridge/HEAD/apps/electron/minimal-redux/resources/electron-logo.png
--------------------------------------------------------------------------------
/apps/electron/minimal-sandbox-true/postcss.config.js:
--------------------------------------------------------------------------------
1 | export default {
2 | plugins: {
3 | '@tailwindcss/postcss': {},
4 | autoprefixer: {},
5 | },
6 | };
7 |
--------------------------------------------------------------------------------
/apps/tauri-v1/e2e/src-tauri/icons/Square107x107Logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/goosewobbler/zubridge/HEAD/apps/tauri-v1/e2e/src-tauri/icons/Square107x107Logo.png
--------------------------------------------------------------------------------
/apps/tauri-v1/e2e/src-tauri/icons/Square142x142Logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/goosewobbler/zubridge/HEAD/apps/tauri-v1/e2e/src-tauri/icons/Square142x142Logo.png
--------------------------------------------------------------------------------
/apps/tauri-v1/e2e/src-tauri/icons/Square150x150Logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/goosewobbler/zubridge/HEAD/apps/tauri-v1/e2e/src-tauri/icons/Square150x150Logo.png
--------------------------------------------------------------------------------
/apps/tauri-v1/e2e/src-tauri/icons/Square284x284Logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/goosewobbler/zubridge/HEAD/apps/tauri-v1/e2e/src-tauri/icons/Square284x284Logo.png
--------------------------------------------------------------------------------
/apps/tauri-v1/e2e/src-tauri/icons/Square310x310Logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/goosewobbler/zubridge/HEAD/apps/tauri-v1/e2e/src-tauri/icons/Square310x310Logo.png
--------------------------------------------------------------------------------
/apps/electron/minimal-custom/resources/electron-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/goosewobbler/zubridge/HEAD/apps/electron/minimal-custom/resources/electron-logo.png
--------------------------------------------------------------------------------
/apps/electron/minimal-zustand-basic/postcss.config.js:
--------------------------------------------------------------------------------
1 | export default {
2 | plugins: {
3 | '@tailwindcss/postcss': {},
4 | autoprefixer: {},
5 | },
6 | };
7 |
--------------------------------------------------------------------------------
/apps/electron/minimal-zustand-handlers/postcss.config.js:
--------------------------------------------------------------------------------
1 | export default {
2 | plugins: {
3 | '@tailwindcss/postcss': {},
4 | autoprefixer: {},
5 | },
6 | };
7 |
--------------------------------------------------------------------------------
/apps/electron/minimal-zustand-immer/postcss.config.js:
--------------------------------------------------------------------------------
1 | export default {
2 | plugins: {
3 | '@tailwindcss/postcss': {},
4 | autoprefixer: {},
5 | },
6 | };
7 |
--------------------------------------------------------------------------------
/apps/electron/minimal-zustand-reducers/postcss.config.js:
--------------------------------------------------------------------------------
1 | export default {
2 | plugins: {
3 | '@tailwindcss/postcss': {},
4 | autoprefixer: {},
5 | },
6 | };
7 |
--------------------------------------------------------------------------------
/packages/ui/postcss.config.cjs:
--------------------------------------------------------------------------------
1 | // postcss.config.cjs - CommonJS format for PostCSS
2 | module.exports = {
3 | plugins: {
4 | '@tailwindcss/postcss': {},
5 | },
6 | };
7 |
--------------------------------------------------------------------------------
/apps/electron/minimal-context-isolation-false/postcss.config.js:
--------------------------------------------------------------------------------
1 | export default {
2 | plugins: {
3 | '@tailwindcss/postcss': {},
4 | autoprefixer: {},
5 | },
6 | };
7 |
--------------------------------------------------------------------------------
/apps/electron/minimal-sandbox-true/resources/electron-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/goosewobbler/zubridge/HEAD/apps/electron/minimal-sandbox-true/resources/electron-logo.png
--------------------------------------------------------------------------------
/apps/electron/minimal-zustand-basic/resources/electron-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/goosewobbler/zubridge/HEAD/apps/electron/minimal-zustand-basic/resources/electron-logo.png
--------------------------------------------------------------------------------
/apps/electron/minimal-zustand-immer/resources/electron-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/goosewobbler/zubridge/HEAD/apps/electron/minimal-zustand-immer/resources/electron-logo.png
--------------------------------------------------------------------------------
/agent-os/config.yml:
--------------------------------------------------------------------------------
1 | version: 2.0.5
2 | last_compiled: 2025-10-19 17:57:51
3 | profile: default
4 | multi_agent_mode: true
5 | multi_agent_tool: claude-code
6 | single_agent_mode: false
7 |
--------------------------------------------------------------------------------
/apps/electron/minimal-zustand-handlers/resources/electron-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/goosewobbler/zubridge/HEAD/apps/electron/minimal-zustand-handlers/resources/electron-logo.png
--------------------------------------------------------------------------------
/apps/electron/minimal-zustand-reducers/resources/electron-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/goosewobbler/zubridge/HEAD/apps/electron/minimal-zustand-reducers/resources/electron-logo.png
--------------------------------------------------------------------------------
/packages/tauri-plugin/tauri.conf.json:
--------------------------------------------------------------------------------
1 | {
2 | "identifier": "com.zubridge.plugin",
3 | "build": {
4 | "devUrl": "http://localhost:5173",
5 | "frontendDist": "../dist"
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/apps/electron/minimal-context-isolation-false/resources/electron-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/goosewobbler/zubridge/HEAD/apps/electron/minimal-context-isolation-false/resources/electron-logo.png
--------------------------------------------------------------------------------
/apps/tauri-v1/e2e/src/types.ts:
--------------------------------------------------------------------------------
1 | export interface AppState {
2 | counter?: number;
3 | theme?: {
4 | is_dark: boolean;
5 | };
6 | __bridge_status?: 'ready' | 'error' | 'initializing';
7 | }
8 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | indent_style = space
6 | indent_size = 2
7 | end_of_line = lf
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
--------------------------------------------------------------------------------
/apps/tauri/e2e/src-tauri/src/main.rs:
--------------------------------------------------------------------------------
1 | // Prevents additional console window on Windows in release, DO NOT REMOVE!!
2 | #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
3 |
4 | fn main() {
5 | app_lib::run();
6 | }
7 |
--------------------------------------------------------------------------------
/packages/ui/README.md:
--------------------------------------------------------------------------------
1 | # @zubridge/ui
2 |
3 | Shared UI components and styles for Zubridge applications. This package provides reusable components, styling utilities, and themes that can be used across both Electron and Tauri applications.
4 |
--------------------------------------------------------------------------------
/.cursor/rules/execute-tasks.mdc:
--------------------------------------------------------------------------------
1 | ---
2 | alwaysApply: false
3 | ---
4 |
5 | # Execute Task
6 |
7 | Execute the next task.
8 |
9 | Refer to the instructions located in this file:
10 | @.agent-os/instructions/core/execute-tasks.md
11 |
--------------------------------------------------------------------------------
/packages/apps-shared/src/state/index.ts:
--------------------------------------------------------------------------------
1 | import type { BaseState } from '../types.js';
2 |
3 | /**
4 | * Initial state for all apps
5 | */
6 | export const initialState: BaseState = {
7 | counter: 0,
8 | theme: 'dark',
9 | };
10 |
--------------------------------------------------------------------------------
/packages/tauri-plugin/permissions/default.toml:
--------------------------------------------------------------------------------
1 | "$schema" = "schemas/schema.json"
2 |
3 | [default]
4 | description = "Allows state management through the Zubridge plugin"
5 | permissions = ["allow-get-initial-state", "allow-dispatch-action"]
6 |
--------------------------------------------------------------------------------
/packages/tauri/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
3 | interface ImportMeta {
4 | readonly env: ImportMetaEnv;
5 | readonly vitest?: typeof import('vitest'); // Add this line
6 | }
7 |
8 | type ImportMetaEnv = {};
9 |
--------------------------------------------------------------------------------
/apps/electron/e2e/src/renderer/hooks/useStore.ts:
--------------------------------------------------------------------------------
1 | import { createUseStore } from '@zubridge/electron';
2 | import type { State } from '../../types.js';
3 |
4 | // Create a shared store hook for the entire application
5 | export const useStore = createUseStore();
6 |
--------------------------------------------------------------------------------
/apps/tauri-v1/e2e/src-tauri/capabilities/default.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "../gen/schemas/desktop-schema.json",
3 | "identifier": "default",
4 | "description": "enables the default permissions",
5 | "windows": ["main"],
6 | "permissions": ["core:default"]
7 | }
8 |
--------------------------------------------------------------------------------
/apps/tauri/e2e/src-tauri/capabilities/default.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "../gen/schemas/desktop-schema.json",
3 | "identifier": "default",
4 | "description": "enables the default permissions",
5 | "windows": ["main"],
6 | "permissions": ["core:default"]
7 | }
8 |
--------------------------------------------------------------------------------
/.cursor/rules/plan-product.mdc:
--------------------------------------------------------------------------------
1 | ---
2 | alwaysApply: false
3 | ---
4 |
5 | # Plan Product
6 |
7 | Plan a new product and install Agent OS in its codebase.
8 |
9 | Refer to the instructions located in this file:
10 | @.agent-os/instructions/core/plan-product.md
11 |
--------------------------------------------------------------------------------
/.cursor/rules/analyze-product.mdc:
--------------------------------------------------------------------------------
1 | ---
2 | alwaysApply: false
3 | ---
4 |
5 | # Analyze Product
6 |
7 | Analyze your product's codebase and install Agent OS
8 |
9 | Refer to the instructions located in this file:
10 | @.agent-os/instructions/core/analyze-product.md
11 |
--------------------------------------------------------------------------------
/apps/tauri-v1/e2e/tailwind.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('tailwindcss').Config} */
2 | export default {
3 | content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}', '../../packages/ui/**/*.{js,ts,jsx,tsx}'],
4 | theme: {
5 | extend: {},
6 | },
7 | plugins: [],
8 | };
9 |
--------------------------------------------------------------------------------
/packages/tauri-plugin/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@zubridge/tauri-plugin",
3 | "version": "0.1.1-next.1",
4 | "description": "Tauri plugin providing a backend API for `@zubridge/tauri`",
5 | "private": true,
6 | "files": [
7 | "README.md"
8 | ]
9 | }
10 |
--------------------------------------------------------------------------------
/packages/electron/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # [1.0.0](https://github.com/goosewobbler/zubridge/compare/v0.0.1-next.2...v1.0.0) (2025-03-12)
2 |
3 | ## [0.0.1-next.2](https://github.com/goosewobbler/zubridge/compare/v0.0.1-next.1...v0.0.1-next.2) (2025-03-12)
4 |
5 | ## 0.0.1-next.1 (2025-03-12)
6 |
--------------------------------------------------------------------------------
/packages/tauri-plugin/.gitignore:
--------------------------------------------------------------------------------
1 | /.tauri
2 | /target
3 | Cargo.lock
4 |
5 | # Explicitly ignore generated files
6 | /gen
7 | **/gen/
8 | **/target/package/**/gen/
9 |
10 | # But exclude them from gitignore during Cargo publish
11 | !target/package/tauri-plugin-zubridge-*/gen/
12 |
--------------------------------------------------------------------------------
/.cursor/rules/create-tasks.mdc:
--------------------------------------------------------------------------------
1 | ---
2 | alwaysApply: false
3 | ---
4 |
5 | # Create Tasks
6 |
7 | Create a tasks list with sub-tasks to execute a feature based on its spec.
8 |
9 | Refer to the instructions located in this file:
10 | @.agent-os/instructions/core/create-tasks.md
11 |
--------------------------------------------------------------------------------
/apps/electron/minimal-zustand-handlers/src/renderer/hooks/useStore.ts:
--------------------------------------------------------------------------------
1 | import { createUseStore } from '@zubridge/electron';
2 | import type { State } from '../../features/index.js';
3 |
4 | // Create a shared store hook for the entire application
5 | export const useStore = createUseStore();
6 |
--------------------------------------------------------------------------------
/apps/electron/minimal-zustand-reducers/src/renderer/hooks/useStore.ts:
--------------------------------------------------------------------------------
1 | import { createUseStore } from '@zubridge/electron';
2 | import type { State } from '../../features/index.js';
3 |
4 | // Create a shared store hook for the entire application
5 | export const useStore = createUseStore();
6 |
--------------------------------------------------------------------------------
/packages/apps-shared/src/index.ts:
--------------------------------------------------------------------------------
1 | // Export types
2 |
3 | // Export state
4 | export * from './state/index.js';
5 | // Export state generation utilities
6 | export * from './stateGeneration.js';
7 | // Export thunks
8 | export * from './thunks/index.js';
9 | export * from './types.js';
10 |
--------------------------------------------------------------------------------
/.cursor/rules/create-spec.mdc:
--------------------------------------------------------------------------------
1 | ---
2 | alwaysApply: false
3 | ---
4 |
5 | # Create Spec
6 |
7 | Create a detailed spec for a new feature with technical specifications and task breakdown
8 |
9 | Refer to the instructions located in this file:
10 | @.agent-os/instructions/core/create-spec.md
11 |
--------------------------------------------------------------------------------
/packages/tauri-plugin/.cargo/config.toml:
--------------------------------------------------------------------------------
1 | [env]
2 | # Ensure generated files are placed in OUT_DIR during build
3 | TAURI_BUILD_GEN_DIR = { value = "${OUT_DIR}", relative = false }
4 |
5 | [build]
6 | # Ensure rustc emits helpful messages during the build
7 | rustflags = ["--cfg", "tauri_plugin_zubridge"]
8 |
--------------------------------------------------------------------------------
/packages/types/README.md:
--------------------------------------------------------------------------------
1 | # @zubridge/types
2 |
3 | > Shared type definitions for Zubridge packages
4 |
5 | This package contains the shared TypeScript type definitions used across all Zubridge packages. It provides a consistent type system for state management across different cross-platform frameworks.
6 |
--------------------------------------------------------------------------------
/tsconfig.base.json:
--------------------------------------------------------------------------------
1 | {
2 | "include": ["packages/electron/src/**/*.ts"],
3 | "exclude": ["node_modules/**/*"],
4 | "compilerOptions": {
5 | "declaration": true,
6 | "moduleResolution": "Bundler",
7 | "strict": true,
8 | "module": "ESNext",
9 | "target": "ESNext"
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/packages/apps-shared/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.base.json",
3 | "compilerOptions": {
4 | "outDir": "./dist",
5 | "rootDir": "./src",
6 | "baseUrl": "./src",
7 | "declaration": true
8 | },
9 | "include": ["src/**/*"],
10 | "exclude": ["node_modules", "dist"]
11 | }
12 |
--------------------------------------------------------------------------------
/apps/tauri/e2e/src-tauri/build.rs:
--------------------------------------------------------------------------------
1 | fn main() {
2 | tauri_build::try_build(
3 | tauri_build::Attributes::new()
4 | .plugin(
5 | "zubridge",
6 | tauri_build::InlinedPlugin::new().commands(&["get_initial_state", "dispatch_action"]),
7 | )
8 | )
9 | .expect("failed to run tauri-build");
10 | }
11 |
--------------------------------------------------------------------------------
/packages/tauri/rollup.config.js:
--------------------------------------------------------------------------------
1 | const sharedConfig = {
2 | plugins: [],
3 | external: ['zustand', 'zustand/vanilla'],
4 | };
5 |
6 | export default [
7 | {
8 | input: './dist/index.js',
9 | output: {
10 | file: './dist/index.cjs',
11 | format: 'cjs',
12 | },
13 | ...sharedConfig,
14 | },
15 | ];
16 |
--------------------------------------------------------------------------------
/apps/tauri-v1/e2e/src-tauri/src/main.rs:
--------------------------------------------------------------------------------
1 | // Prevents additional console window on Windows in release, DO NOT REMOVE!!
2 | #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
3 |
4 | // // Removed module declaration for lib again
5 | // mod lib;
6 |
7 | fn main() {
8 | // Call run function via crate name
9 | app_lib::run();
10 | }
11 |
--------------------------------------------------------------------------------
/version.config.json:
--------------------------------------------------------------------------------
1 | {
2 | "versionPrefix": "v",
3 | "preset": "angular",
4 | "baseBranch": "main",
5 | "sync": false,
6 | "commitMessage": "chore: release ${packageName}@${version} [skip-ci]",
7 | "tagTemplate": "${packageName}@${prefix}${version}",
8 | "skip": ["@zubridge/apps-shared", "e2e-*", "minimal-*", "zubridge-e2e"],
9 | "prereleaseIdentifier": "next"
10 | }
11 |
--------------------------------------------------------------------------------
/scripts/create-task-graph.ts:
--------------------------------------------------------------------------------
1 | // Create a task graph for the specified tasks and open it in the default image viewer.
2 | // Usage: tsx scripts/create-task-graph.ts [graph-file] [tasks...]
3 | import shell from 'shelljs';
4 |
5 | const graphFile = process.argv[2] || 'task-graph.png';
6 | const tasks = process.argv.slice(3).join(' ');
7 |
8 | shell.exec(`turbo run ${tasks} --graph=${graphFile} && open ${graphFile}`);
9 |
--------------------------------------------------------------------------------
/packages/core/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.base.json",
3 | "compilerOptions": {
4 | "declaration": true,
5 | "outDir": "./dist",
6 | "rootDir": "./src",
7 | "moduleResolution": "Bundler",
8 | "skipLibCheck": true,
9 | "strict": true,
10 | "module": "ESNext",
11 | "target": "ESNext"
12 | },
13 | "include": ["src/**/*"],
14 | "exclude": ["node_modules", "dist"]
15 | }
16 |
--------------------------------------------------------------------------------
/apps/electron/minimal-redux/src/renderer/hooks/useStore.ts:
--------------------------------------------------------------------------------
1 | import type { State } from '../../features/index.js';
2 |
3 | /**
4 | * Hook to access the Zubridge store in the renderer process
5 | */
6 | export const useStore = () => {
7 | // Access the store through the window.zubridge object
8 | return (window as { zubridge?: { useStore?: () => State } }).zubridge?.useStore?.() as
9 | | State
10 | | undefined;
11 | };
12 |
--------------------------------------------------------------------------------
/packages/electron/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.base.json",
3 | "include": ["./src/**/*.ts"],
4 | "exclude": ["node_modules/**/*"],
5 | "compilerOptions": {
6 | "declaration": true,
7 | "outDir": "./dist",
8 | "rootDir": "./src",
9 | "moduleResolution": "Bundler",
10 | "skipLibCheck": true,
11 | "strict": true,
12 | "module": "ESNext",
13 | "target": "ESNext"
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/packages/tauri/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.base.json",
3 | "include": ["./src/**/*.ts"],
4 | "exclude": ["node_modules/**/*"],
5 | "compilerOptions": {
6 | "declaration": true,
7 | "outDir": "./dist",
8 | "rootDir": "./src",
9 | "moduleResolution": "Bundler",
10 | "skipLibCheck": true,
11 | "strict": true,
12 | "module": "ESNext",
13 | "target": "ESNext"
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/apps/electron/minimal-custom/src/renderer/hooks/useStore.ts:
--------------------------------------------------------------------------------
1 | import type { State } from '../../features/index.js';
2 |
3 | /**
4 | * Hook to access the Zubridge store in the renderer process
5 | */
6 | export const useStore = () => {
7 | // Access the store through the window.zubridge object
8 | return (window as { zubridge?: { useStore?: () => State } }).zubridge?.useStore?.() as
9 | | State
10 | | undefined;
11 | };
12 |
--------------------------------------------------------------------------------
/e2e/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.base.json",
3 | "compilerOptions": {
4 | "declaration": false,
5 | "declarationMap": false,
6 | "sourceMap": false,
7 | "inlineSources": false,
8 | "noEmit": true,
9 | "types": ["node", "@wdio/globals/types", "mocha"],
10 | "typeRoots": ["./node_modules", "./node_modules/@types"]
11 | },
12 | "include": ["./**/*.spec.ts"],
13 | "exclude": []
14 | }
15 |
--------------------------------------------------------------------------------
/apps/electron/minimal-sandbox-true/src/renderer/hooks/useStore.ts:
--------------------------------------------------------------------------------
1 | import type { State } from '../../features/index.js';
2 |
3 | /**
4 | * Hook to access the Zubridge store in the renderer process
5 | */
6 | export const useStore = () => {
7 | // Access the store through the window.zubridge object
8 | return (window as { zubridge?: { useStore?: () => State } }).zubridge?.useStore?.() as
9 | | State
10 | | undefined;
11 | };
12 |
--------------------------------------------------------------------------------
/apps/electron/minimal-zustand-basic/src/renderer/hooks/useStore.ts:
--------------------------------------------------------------------------------
1 | import type { State } from '../../features/index.js';
2 |
3 | /**
4 | * Hook to access the Zubridge store in the renderer process
5 | */
6 | export const useStore = () => {
7 | // Access the store through the window.zubridge object
8 | return (window as { zubridge?: { useStore?: () => State } }).zubridge?.useStore?.() as
9 | | State
10 | | undefined;
11 | };
12 |
--------------------------------------------------------------------------------
/apps/electron/minimal-zustand-immer/src/renderer/hooks/useStore.ts:
--------------------------------------------------------------------------------
1 | import type { State } from '../../features/index.js';
2 |
3 | /**
4 | * Hook to access the Zubridge store in the renderer process
5 | */
6 | export const useStore = () => {
7 | // Access the store through the window.zubridge object
8 | return (window as { zubridge?: { useStore?: () => State } }).zubridge?.useStore?.() as
9 | | State
10 | | undefined;
11 | };
12 |
--------------------------------------------------------------------------------
/apps/electron/minimal-context-isolation-false/src/renderer/hooks/useStore.ts:
--------------------------------------------------------------------------------
1 | import type { State } from '../../features/index.js';
2 |
3 | /**
4 | * Hook to access the Zubridge store in the renderer process
5 | */
6 | export const useStore = () => {
7 | // Access the store through the window.zubridge object
8 | return (window as { zubridge?: { useStore?: () => State } }).zubridge?.useStore?.() as
9 | | State
10 | | undefined;
11 | };
12 |
--------------------------------------------------------------------------------
/packages/types/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ES2020",
4 | "module": "ESNext",
5 | "moduleResolution": "Node",
6 | "declaration": true,
7 | "outDir": "./dist",
8 | "strict": true,
9 | "esModuleInterop": true,
10 | "skipLibCheck": true,
11 | "forceConsistentCasingInFileNames": true
12 | },
13 | "include": ["src/**/*"],
14 | "exclude": ["node_modules", "dist", "**/*.spec.ts"]
15 | }
16 |
--------------------------------------------------------------------------------
/apps/electron/e2e/src/modes/zustand-basic/store.ts:
--------------------------------------------------------------------------------
1 | import { create, type StoreApi } from 'zustand';
2 | import type { State } from '../../types.js';
3 |
4 | /**
5 | * Gets or creates the basic store
6 | * Uses Zustand with a simple state object
7 | */
8 | export function getBasicStore(initialState?: Partial): StoreApi {
9 | console.log('[Basic Mode] Creating Zustand store');
10 |
11 | return create()(() => initialState as State);
12 | }
13 |
--------------------------------------------------------------------------------
/apps/electron/e2e/src/modes/custom/features/error/index.ts:
--------------------------------------------------------------------------------
1 | import { debug } from '@zubridge/core';
2 |
3 | /**
4 | * Creates a handler that intentionally throws an error for testing error handling
5 | */
6 | export const triggerMainProcessError = () => {
7 | return () => {
8 | debug('main:error', 'Intentionally throwing error in main process for testing');
9 | throw new Error('Intentional error thrown in main process for testing purposes');
10 | };
11 | };
12 |
--------------------------------------------------------------------------------
/apps/electron/e2e/src/modes/zustand-handlers/store.ts:
--------------------------------------------------------------------------------
1 | import { create, type StoreApi } from 'zustand';
2 | import type { State } from '../../types.js';
3 |
4 | /**
5 | * Gets or creates the handlers store
6 | * Uses Zustand with a simple state object
7 | */
8 | export function getHandlersStore(initialState?: Partial): StoreApi {
9 | console.log('[Handlers Mode] Creating Zustand store');
10 |
11 | return create()(() => initialState as State);
12 | }
13 |
--------------------------------------------------------------------------------
/apps/electron/e2e/src/modes/zustand-handlers/features/error/index.ts:
--------------------------------------------------------------------------------
1 | import { debug } from '@zubridge/core';
2 |
3 | /**
4 | * Creates a handler that intentionally throws an error for testing error handling
5 | */
6 | export const triggerMainProcessError = () => {
7 | return () => {
8 | debug('main:error', 'Intentionally throwing error in main process for testing');
9 | throw new Error('Intentional error thrown in main process for testing purposes');
10 | };
11 | };
12 |
--------------------------------------------------------------------------------
/packages/electron/src/types/thunkProcessor.ts:
--------------------------------------------------------------------------------
1 | export interface ThunkProcessorOptions {
2 | /**
3 | * Maximum number of pending actions allowed in the queue (default: 100)
4 | * When this limit is exceeded, new actions will throw a QueueOverflowError
5 | */
6 | maxQueueSize?: number;
7 | /**
8 | * Timeout for action completion in milliseconds
9 | * Platform-specific defaults: Linux=60000ms, others=30000ms
10 | */
11 | actionCompletionTimeoutMs?: number;
12 | }
13 |
--------------------------------------------------------------------------------
/.github/workflows/actions/cargo-setup/action.yml:
--------------------------------------------------------------------------------
1 | name: 'Setup Cargo Configuration'
2 | description: 'Configures Rust/Cargo for better performance in CI environments'
3 |
4 | runs:
5 | using: "composite"
6 | steps:
7 | - name: Setup Cargo Cache
8 | shell: bash
9 | run: |
10 | mkdir -p ~/.cargo/{registry,git}
11 | echo '[net]
12 | git-fetch-with-cli = true
13 | retry = 3
14 | [build]
15 | jobs = 2' > ~/.cargo/config.toml
16 |
--------------------------------------------------------------------------------
/apps/electron/minimal-redux/src/features/index.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Types for the Redux mode state
3 | * In Redux mode, we use Redux Toolkit to manage state
4 | */
5 | export interface State {
6 | counter: number;
7 | theme: 'light' | 'dark';
8 |
9 | // Index signature to satisfy AnyState requirement
10 | [key: string]: unknown;
11 | }
12 |
13 | /**
14 | * Initial state for Redux mode
15 | */
16 | export const initialState: State = {
17 | counter: 0,
18 | theme: 'dark',
19 | };
20 |
--------------------------------------------------------------------------------
/resources/build/entitlements.mac.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | com.apple.security.cs.allow-jit
6 |
7 | com.apple.security.cs.allow-unsigned-executable-memory
8 |
9 | com.apple.security.cs.allow-dyld-environment-variables
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/agent-os/specs/2025-10-05-zubridge-core-rust-crate/spec-lite.md:
--------------------------------------------------------------------------------
1 | # Spec Summary (Lite)
2 |
3 | Establish the foundational `zubridge-core` Rust crate with conditional compilation for UniFFI, NAPI-RS, and Tauri targets, enabling a unified core for all framework integrations. Implement minimal working example (`create_store()` function) that compiles and exports correctly for all three platforms, define middleware architecture, and set up testing/CI infrastructure to validate the architecture before full implementation.
4 |
--------------------------------------------------------------------------------
/packages/tauri/vitest.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vitest/config';
2 |
3 | export default defineConfig({
4 | test: {
5 | include: ['test/**/*.spec.ts{,x}'],
6 | environment: 'jsdom',
7 | coverage: {
8 | enabled: true,
9 | include: ['src/**/*'],
10 | exclude: ['src/types.ts', '**/*.d.ts'],
11 | thresholds: {
12 | lines: 15,
13 | functions: 15,
14 | branches: 15,
15 | statements: 15,
16 | },
17 | },
18 | },
19 | });
20 |
--------------------------------------------------------------------------------
/apps/electron/minimal-zustand-handlers/src/main/store.ts:
--------------------------------------------------------------------------------
1 | import { create, type StoreApi } from 'zustand';
2 | import type { State } from '../features/index.js';
3 | import { initialState } from '../features/index.js';
4 |
5 | /**
6 | * Creates a Zustand store for the handlers mode
7 | * Uses Zustand with a simple state object
8 | */
9 | export function createStore(): StoreApi {
10 | console.log('[Handlers Mode] Creating Zustand store');
11 |
12 | return create()(() => initialState);
13 | }
14 |
--------------------------------------------------------------------------------
/apps/electron/minimal-zustand-reducers/src/main/store.ts:
--------------------------------------------------------------------------------
1 | import { create, type StoreApi } from 'zustand';
2 | import type { State } from '../features/index.js';
3 | import { initialState } from '../features/index.js';
4 |
5 | /**
6 | * Creates a Zustand store for the reducers mode
7 | * Uses Zustand with a simple state object
8 | */
9 | export function createStore(): StoreApi {
10 | console.log('[Reducers Mode] Creating Zustand store');
11 |
12 | return create()(() => initialState);
13 | }
14 |
--------------------------------------------------------------------------------
/apps/electron/e2e/src/modes/zustand-reducers/store.ts:
--------------------------------------------------------------------------------
1 | import { debug } from '@zubridge/core';
2 | import { create, type StoreApi } from 'zustand';
3 | import type { State } from '../../types.js';
4 |
5 | /**
6 | * Gets or creates the reducers store
7 | * Uses Zustand with a simple state object
8 | */
9 | export function getReducersStore(initialState?: Partial): StoreApi {
10 | debug('store', '[Reducers Mode] Creating Zustand store');
11 |
12 | return create()(() => initialState as State);
13 | }
14 |
--------------------------------------------------------------------------------
/apps/tauri/e2e/tailwind.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('tailwindcss').Config} */
2 | export default {
3 | content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}', '../../packages/ui/**/*.{js,ts,jsx,tsx}'],
4 | theme: {
5 | extend: {
6 | colors: {
7 | 'light-bg': '#f3f0ff',
8 | 'light-text': '#1e293b',
9 | 'dark-bg': '#0f172a',
10 | 'dark-text': '#f1f5f9',
11 | 'status-ready': '#10b981',
12 | 'status-error': '#ef4444',
13 | },
14 | },
15 | },
16 | plugins: [],
17 | };
18 |
--------------------------------------------------------------------------------
/agent-os/product/mission-lite.md:
--------------------------------------------------------------------------------
1 | # Zubridge Mission (Lite)
2 |
3 | Zubridge is a cross-platform state management library for multi-process desktop and mobile applications.
4 |
5 | Zubridge serves developers building desktop and mobile applications who need Zustand's familiar API with automatic state synchronization across process boundaries. Built on a unified Rust core, Zubridge provides a consistent API across Electron, Tauri, and upcoming support for Flutter, Neutralino, and Blazor - designed to scale from simple apps to performance-critical use cases.
6 |
--------------------------------------------------------------------------------
/packages/ui/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.base.json",
3 | "compilerOptions": {
4 | "outDir": "./dist",
5 | "rootDir": ".",
6 | "module": "ESNext",
7 | "moduleResolution": "bundler",
8 | "target": "ESNext",
9 | "lib": ["DOM", "DOM.Iterable", "ESNext"],
10 | "jsx": "react-jsx",
11 | "esModuleInterop": true,
12 | "allowJs": true,
13 | "declaration": true,
14 | "emitDeclarationOnly": false
15 | },
16 | "include": ["src/**/*", "scripts/**/*"],
17 | "exclude": ["node_modules", "dist"]
18 | }
19 |
--------------------------------------------------------------------------------
/packages/types/tsdown.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'tsdown';
2 |
3 | export default defineConfig({
4 | entry: {
5 | index: 'src/index.ts',
6 | internal: 'src/internal.ts',
7 | app: 'src/app.ts',
8 | },
9 | format: ['esm', 'cjs'],
10 | dts: true,
11 | clean: true,
12 | sourcemap: false,
13 | minify: false,
14 | external: ['zustand', 'electron'],
15 | outExtensions({ format }) {
16 | return {
17 | js: format === 'cjs' ? '.cjs' : '.js',
18 | dts: format === 'cjs' ? '.d.cts' : '.d.ts',
19 | };
20 | },
21 | });
22 |
--------------------------------------------------------------------------------
/packages/tauri/tauri.conf.json:
--------------------------------------------------------------------------------
1 | {
2 | "build": {
3 | "beforeDevCommand": "",
4 | "beforeBuildCommand": "",
5 | "devUrl": "http://localhost:1420",
6 | "frontendDist": "../dist"
7 | },
8 | "productName": "zubridge-tauri",
9 | "version": "0.1.0",
10 | "identifier": "com.zubridge-tauri",
11 | "app": {
12 | "security": {
13 | "csp": null
14 | },
15 | "windows": [
16 | {
17 | "fullscreen": false,
18 | "width": 800,
19 | "height": 600,
20 | "label": "main"
21 | }
22 | ]
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/apps/electron/minimal-redux/src/features/theme/index.ts:
--------------------------------------------------------------------------------
1 | import { createSlice } from '@reduxjs/toolkit';
2 |
3 | /**
4 | * Theme slice for Redux mode
5 | * In Redux mode, theme logic is handled by Redux Toolkit slices
6 | */
7 | export const themeSlice = createSlice({
8 | name: 'theme',
9 | initialState: 'dark' as 'dark' | 'light',
10 | reducers: {
11 | toggleTheme: (state) => {
12 | console.log('[Redux Theme] Toggling theme');
13 | return state === 'dark' ? 'light' : 'dark';
14 | },
15 | },
16 | });
17 |
18 | export const themeActions = themeSlice.actions;
19 |
--------------------------------------------------------------------------------
/apps/tauri-v1/e2e/src/index.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
5 | @layer base {
6 | :root {
7 | @apply bg-white text-black;
8 | }
9 |
10 | :root[data-theme="dark"] {
11 | @apply bg-black text-white;
12 | }
13 | }
14 |
15 | @layer components {
16 | .app-container {
17 | @apply flex flex-col h-screen;
18 | }
19 |
20 | .content {
21 | @apply flex-1 p-4;
22 | }
23 |
24 | .window-content {
25 | @apply flex flex-col gap-4;
26 | }
27 |
28 | .window-actions {
29 | @apply flex flex-col gap-2;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/packages/electron/src/utils/deepGet.ts:
--------------------------------------------------------------------------------
1 | export function deepGet(
2 | obj: Record,
3 | key: string | string[],
4 | def?: unknown,
5 | ): unknown {
6 | let p: number;
7 | let undef: unknown;
8 | const path = typeof key === 'string' ? key.split('.') : key;
9 | let currentObj: unknown = obj;
10 | for (p = 0; p < path.length; p++) {
11 | currentObj =
12 | currentObj && typeof currentObj === 'object'
13 | ? (currentObj as Record)[path[p]]
14 | : undef;
15 | }
16 | return currentObj === undef ? def : currentObj;
17 | }
18 |
--------------------------------------------------------------------------------
/packages/tauri/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "zubridge-tauri"
3 | version = "1.1.1-next.1"
4 | edition = "2021"
5 |
6 | [dependencies]
7 | serde_json = "1.0"
8 |
9 | [dependencies.tauri]
10 | version = "2.0.0"
11 | features = []
12 |
13 | [dependencies.serde]
14 | version = "1.0"
15 | features = [ "derive" ]
16 |
17 | [build-dependencies]
18 | [build-dependencies.tauri-build]
19 | version = "2.0.0"
20 | features = []
21 |
22 | [lib]
23 | name = "zubridge_tauri"
24 | path = "src-rust/lib.rs"
25 |
26 | [dev-dependencies]
27 | [dev-dependencies.tokio]
28 | version = "1.36"
29 | features = [ "full" ]
--------------------------------------------------------------------------------
/apps/electron/minimal-custom/electron-builder.config.ts:
--------------------------------------------------------------------------------
1 | import type { Configuration } from 'electron-builder';
2 |
3 | const config: Configuration = {
4 | appId: 'com.zubridge.basic-minimal',
5 | productName: 'Zubridge Basic Minimal',
6 | directories: {
7 | output: 'dist',
8 | },
9 | files: ['out/**/*', 'node_modules/**/*', 'package.json'],
10 | mac: {
11 | icon: 'resources/electron-logo.png',
12 | },
13 | win: {
14 | icon: 'resources/electron-logo.png',
15 | },
16 | linux: {
17 | icon: 'resources/electron-logo.png',
18 | },
19 | };
20 |
21 | export default config;
22 |
--------------------------------------------------------------------------------
/apps/electron/minimal-redux/electron-builder.config.ts:
--------------------------------------------------------------------------------
1 | import type { Configuration } from 'electron-builder';
2 |
3 | const config: Configuration = {
4 | appId: 'com.zubridge.basic-minimal',
5 | productName: 'Zubridge Basic Minimal',
6 | directories: {
7 | output: 'dist',
8 | },
9 | files: ['out/**/*', 'node_modules/**/*', 'package.json'],
10 | mac: {
11 | icon: 'resources/electron-logo.png',
12 | },
13 | win: {
14 | icon: 'resources/electron-logo.png',
15 | },
16 | linux: {
17 | icon: 'resources/electron-logo.png',
18 | },
19 | };
20 |
21 | export default config;
22 |
--------------------------------------------------------------------------------
/apps/electron/e2e/src/renderer/styles/index.css:
--------------------------------------------------------------------------------
1 | @import "tailwindcss";
2 |
3 | /*
4 | * Most styles are now provided by @zubridge/ui
5 | * Only app-specific overrides are kept here
6 | */
7 |
8 | /* App container */
9 | .app-container {
10 | display: flex;
11 | flex-direction: column;
12 | min-height: 100vh;
13 | }
14 |
15 | /* Content area */
16 | .content {
17 | padding: 20px;
18 | margin-top: 20px;
19 | }
20 |
21 | /* Ensure proper spacing between component sections */
22 | .window-content {
23 | display: flex;
24 | flex-direction: column;
25 | gap: 20px;
26 | padding: 20px 0;
27 | }
28 |
--------------------------------------------------------------------------------
/apps/electron/minimal-redux/test/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "declaration": false,
4 | "declarationMap": false,
5 | "sourceMap": false,
6 | "inlineSources": false,
7 | "noEmit": true,
8 | "types": ["node", "@wdio/globals/types", "mocha"],
9 | "typeRoots": ["../node_modules", "../node_modules/@types"],
10 | "moduleResolution": "bundler",
11 | "allowImportingTsExtensions": true,
12 | "strict": true,
13 | "module": "ESNext",
14 | "target": "ESNext"
15 | },
16 | "include": ["./**/*.ts", "./**/*.js"],
17 | "exclude": ["node_modules/**/*"]
18 | }
19 |
--------------------------------------------------------------------------------
/apps/electron/minimal-sandbox-true/electron-builder.config.ts:
--------------------------------------------------------------------------------
1 | import type { Configuration } from 'electron-builder';
2 |
3 | const config: Configuration = {
4 | appId: 'com.zubridge.basic-minimal',
5 | productName: 'Zubridge Basic Minimal',
6 | directories: {
7 | output: 'dist',
8 | },
9 | files: ['out/**/*', 'node_modules/**/*', 'package.json'],
10 | mac: {
11 | icon: 'resources/electron-logo.png',
12 | },
13 | win: {
14 | icon: 'resources/electron-logo.png',
15 | },
16 | linux: {
17 | icon: 'resources/electron-logo.png',
18 | },
19 | };
20 |
21 | export default config;
22 |
--------------------------------------------------------------------------------
/apps/electron/minimal-zustand-basic/electron-builder.config.ts:
--------------------------------------------------------------------------------
1 | import type { Configuration } from 'electron-builder';
2 |
3 | const config: Configuration = {
4 | appId: 'com.zubridge.basic-minimal',
5 | productName: 'Zubridge Basic Minimal',
6 | directories: {
7 | output: 'dist',
8 | },
9 | files: ['out/**/*', 'node_modules/**/*', 'package.json'],
10 | mac: {
11 | icon: 'resources/electron-logo.png',
12 | },
13 | win: {
14 | icon: 'resources/electron-logo.png',
15 | },
16 | linux: {
17 | icon: 'resources/electron-logo.png',
18 | },
19 | };
20 |
21 | export default config;
22 |
--------------------------------------------------------------------------------
/apps/electron/minimal-zustand-immer/electron-builder.config.ts:
--------------------------------------------------------------------------------
1 | import type { Configuration } from 'electron-builder';
2 |
3 | const config: Configuration = {
4 | appId: 'com.zubridge.basic-minimal',
5 | productName: 'Zubridge Basic Minimal',
6 | directories: {
7 | output: 'dist',
8 | },
9 | files: ['out/**/*', 'node_modules/**/*', 'package.json'],
10 | mac: {
11 | icon: 'resources/electron-logo.png',
12 | },
13 | win: {
14 | icon: 'resources/electron-logo.png',
15 | },
16 | linux: {
17 | icon: 'resources/electron-logo.png',
18 | },
19 | };
20 |
21 | export default config;
22 |
--------------------------------------------------------------------------------
/apps/tauri-v1/e2e/src/renderer/styles/index.css:
--------------------------------------------------------------------------------
1 | @import "tailwindcss";
2 |
3 | /*
4 | * Most styles are now provided by @zubridge/ui
5 | * Only app-specific overrides are kept here
6 | */
7 |
8 | /* App container */
9 | .app-container {
10 | display: flex;
11 | flex-direction: column;
12 | min-height: 100vh;
13 | }
14 |
15 | /* Content area */
16 | .content {
17 | padding: 20px;
18 | margin-top: 20px;
19 | }
20 |
21 | /* Ensure proper spacing between component sections */
22 | .window-content {
23 | display: flex;
24 | flex-direction: column;
25 | gap: 20px;
26 | padding: 20px 0;
27 | }
28 |
--------------------------------------------------------------------------------
/apps/tauri/e2e/src/renderer/styles/index.css:
--------------------------------------------------------------------------------
1 | @import "tailwindcss";
2 |
3 | /*
4 | * Most styles are now provided by @zubridge/ui
5 | * Only app-specific overrides are kept here
6 | */
7 |
8 | /* App container */
9 | .app-container {
10 | display: flex;
11 | flex-direction: column;
12 | min-height: 100vh;
13 | }
14 |
15 | /* Content area */
16 | .content {
17 | padding: 20px;
18 | margin-top: 20px;
19 | }
20 |
21 | /* Ensure proper spacing between component sections */
22 | .window-content {
23 | display: flex;
24 | flex-direction: column;
25 | gap: 20px;
26 | padding: 20px 0;
27 | }
28 |
--------------------------------------------------------------------------------
/packages/electron/src/types/preload.ts:
--------------------------------------------------------------------------------
1 | import type { ThunkProcessorOptions } from './thunkProcessor.js';
2 |
3 | /**
4 | * Configuration options for preload bridge
5 | *
6 | * @example
7 | * ```typescript
8 | * // Configure with custom queue size and timeout
9 | * const options: PreloadOptions = {
10 | * maxQueueSize: 50, // Allow up to 50 pending actions
11 | * actionCompletionTimeoutMs: 15000 // 15 second timeout
12 | * };
13 | *
14 | * // Use in preload
15 | * const bridge = preloadBridge(options);
16 | * ```
17 | */
18 | export type PreloadOptions = ThunkProcessorOptions;
19 |
--------------------------------------------------------------------------------
/apps/electron/e2e/src/modes/zustand-reducers/features/error/index.ts:
--------------------------------------------------------------------------------
1 | import { debug } from '@zubridge/core';
2 | import type { Reducer } from 'redux';
3 |
4 | /**
5 | * Creates a handler that intentionally throws an error for testing error handling
6 | */
7 | const triggerMainProcessError = () => {
8 | debug('main:error', 'Intentionally throwing error in main process for testing');
9 | throw new Error('Intentional error thrown in main process for testing purposes');
10 | };
11 |
12 | export const reducer: Reducer = (state, _action) => {
13 | triggerMainProcessError();
14 | return state;
15 | };
16 |
--------------------------------------------------------------------------------
/apps/electron/minimal-custom/test/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "declaration": false,
4 | "declarationMap": false,
5 | "sourceMap": false,
6 | "inlineSources": false,
7 | "noEmit": true,
8 | "types": ["node", "@wdio/globals/types", "mocha"],
9 | "typeRoots": ["../node_modules", "../node_modules/@types"],
10 | "moduleResolution": "bundler",
11 | "allowImportingTsExtensions": true,
12 | "strict": true,
13 | "module": "ESNext",
14 | "target": "ESNext"
15 | },
16 | "include": ["./**/*.ts", "./**/*.js"],
17 | "exclude": ["node_modules/**/*"]
18 | }
19 |
--------------------------------------------------------------------------------
/apps/electron/minimal-zustand-handlers/electron-builder.config.ts:
--------------------------------------------------------------------------------
1 | import type { Configuration } from 'electron-builder';
2 |
3 | const config: Configuration = {
4 | appId: 'com.zubridge.basic-minimal',
5 | productName: 'Zubridge Basic Minimal',
6 | directories: {
7 | output: 'dist',
8 | },
9 | files: ['out/**/*', 'node_modules/**/*', 'package.json'],
10 | mac: {
11 | icon: 'resources/electron-logo.png',
12 | },
13 | win: {
14 | icon: 'resources/electron-logo.png',
15 | },
16 | linux: {
17 | icon: 'resources/electron-logo.png',
18 | },
19 | };
20 |
21 | export default config;
22 |
--------------------------------------------------------------------------------
/apps/electron/minimal-zustand-reducers/electron-builder.config.ts:
--------------------------------------------------------------------------------
1 | import type { Configuration } from 'electron-builder';
2 |
3 | const config: Configuration = {
4 | appId: 'com.zubridge.basic-minimal',
5 | productName: 'Zubridge Basic Minimal',
6 | directories: {
7 | output: 'dist',
8 | },
9 | files: ['out/**/*', 'node_modules/**/*', 'package.json'],
10 | mac: {
11 | icon: 'resources/electron-logo.png',
12 | },
13 | win: {
14 | icon: 'resources/electron-logo.png',
15 | },
16 | linux: {
17 | icon: 'resources/electron-logo.png',
18 | },
19 | };
20 |
21 | export default config;
22 |
--------------------------------------------------------------------------------
/apps/electron/minimal-context-isolation-false/electron-builder.config.ts:
--------------------------------------------------------------------------------
1 | import type { Configuration } from 'electron-builder';
2 |
3 | const config: Configuration = {
4 | appId: 'com.zubridge.basic-minimal',
5 | productName: 'Zubridge Basic Minimal',
6 | directories: {
7 | output: 'dist',
8 | },
9 | files: ['out/**/*', 'node_modules/**/*', 'package.json'],
10 | mac: {
11 | icon: 'resources/electron-logo.png',
12 | },
13 | win: {
14 | icon: 'resources/electron-logo.png',
15 | },
16 | linux: {
17 | icon: 'resources/electron-logo.png',
18 | },
19 | };
20 |
21 | export default config;
22 |
--------------------------------------------------------------------------------
/apps/electron/minimal-sandbox-true/test/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "declaration": false,
4 | "declarationMap": false,
5 | "sourceMap": false,
6 | "inlineSources": false,
7 | "noEmit": true,
8 | "types": ["node", "@wdio/globals/types", "mocha"],
9 | "typeRoots": ["../node_modules", "../node_modules/@types"],
10 | "moduleResolution": "bundler",
11 | "allowImportingTsExtensions": true,
12 | "strict": true,
13 | "module": "ESNext",
14 | "target": "ESNext"
15 | },
16 | "include": ["./**/*.ts", "./**/*.js"],
17 | "exclude": ["node_modules/**/*"]
18 | }
19 |
--------------------------------------------------------------------------------
/apps/electron/minimal-zustand-basic/test/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "declaration": false,
4 | "declarationMap": false,
5 | "sourceMap": false,
6 | "inlineSources": false,
7 | "noEmit": true,
8 | "types": ["node", "@wdio/globals/types", "mocha"],
9 | "typeRoots": ["../node_modules", "../node_modules/@types"],
10 | "moduleResolution": "bundler",
11 | "allowImportingTsExtensions": true,
12 | "strict": true,
13 | "module": "ESNext",
14 | "target": "ESNext"
15 | },
16 | "include": ["./**/*.ts", "./**/*.js"],
17 | "exclude": ["node_modules/**/*"]
18 | }
19 |
--------------------------------------------------------------------------------
/apps/electron/minimal-zustand-immer/test/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "declaration": false,
4 | "declarationMap": false,
5 | "sourceMap": false,
6 | "inlineSources": false,
7 | "noEmit": true,
8 | "types": ["node", "@wdio/globals/types", "mocha"],
9 | "typeRoots": ["../node_modules", "../node_modules/@types"],
10 | "moduleResolution": "bundler",
11 | "allowImportingTsExtensions": true,
12 | "strict": true,
13 | "module": "ESNext",
14 | "target": "ESNext"
15 | },
16 | "include": ["./**/*.ts", "./**/*.js"],
17 | "exclude": ["node_modules/**/*"]
18 | }
19 |
--------------------------------------------------------------------------------
/apps/electron/minimal-zustand-handlers/test/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "declaration": false,
4 | "declarationMap": false,
5 | "sourceMap": false,
6 | "inlineSources": false,
7 | "noEmit": true,
8 | "types": ["node", "@wdio/globals/types", "mocha"],
9 | "typeRoots": ["../node_modules", "../node_modules/@types"],
10 | "moduleResolution": "bundler",
11 | "allowImportingTsExtensions": true,
12 | "strict": true,
13 | "module": "ESNext",
14 | "target": "ESNext"
15 | },
16 | "include": ["./**/*.ts", "./**/*.js"],
17 | "exclude": ["node_modules/**/*"]
18 | }
19 |
--------------------------------------------------------------------------------
/apps/electron/minimal-zustand-reducers/test/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "declaration": false,
4 | "declarationMap": false,
5 | "sourceMap": false,
6 | "inlineSources": false,
7 | "noEmit": true,
8 | "types": ["node", "@wdio/globals/types", "mocha"],
9 | "typeRoots": ["../node_modules", "../node_modules/@types"],
10 | "moduleResolution": "bundler",
11 | "allowImportingTsExtensions": true,
12 | "strict": true,
13 | "module": "ESNext",
14 | "target": "ESNext"
15 | },
16 | "include": ["./**/*.ts", "./**/*.js"],
17 | "exclude": ["node_modules/**/*"]
18 | }
19 |
--------------------------------------------------------------------------------
/packages/tauri-plugin/src/commands.rs:
--------------------------------------------------------------------------------
1 | use tauri::{AppHandle, command, Runtime};
2 |
3 | use crate::models::*;
4 | use crate::Result;
5 | use crate::ZubridgeExt;
6 |
7 | #[command(rename = "zubridge.get-initial-state")]
8 | pub(crate) async fn get_initial_state(
9 | app: AppHandle,
10 | ) -> Result {
11 | app.zubridge().get_initial_state()
12 | }
13 |
14 | #[command(rename = "zubridge.dispatch-action")]
15 | pub(crate) async fn dispatch_action(
16 | app: AppHandle,
17 | action: ZubridgeAction,
18 | ) -> Result {
19 | app.zubridge().dispatch_action(action)
20 | }
21 |
--------------------------------------------------------------------------------
/apps/electron/minimal-custom/src/features/theme/index.ts:
--------------------------------------------------------------------------------
1 | import type { AnyState } from '@zubridge/types';
2 |
3 | /**
4 | * Theme feature for custom mode
5 | * In custom mode, theme logic is handled by the custom state manager
6 | */
7 |
8 | export const themeHandlers = {
9 | 'THEME:TOGGLE': (state: AnyState) => {
10 | const currentTheme = state.theme as 'light' | 'dark' | undefined;
11 | const newTheme = currentTheme === 'dark' ? 'light' : 'dark';
12 | console.log(`[Custom Theme] Toggling theme from ${currentTheme || 'unknown'} to ${newTheme}`);
13 | return {
14 | theme: newTheme,
15 | };
16 | },
17 | };
18 |
--------------------------------------------------------------------------------
/apps/electron/minimal-sandbox-true/src/features/theme/index.ts:
--------------------------------------------------------------------------------
1 | import type { StoreApi } from 'zustand';
2 | import type { State } from '../index.js';
3 |
4 | /**
5 | * Creates action handlers for theme operations in basic mode
6 | * In basic mode, these handlers are attached directly to the store state
7 | */
8 | export const createThemeHandlers = (store: StoreApi) => {
9 | return {
10 | 'THEME:TOGGLE': () => {
11 | console.log('[Basic] Toggling theme');
12 | store.setState((state) => ({
13 | ...state,
14 | theme: state.theme === 'dark' ? 'light' : 'dark',
15 | }));
16 | },
17 | };
18 | };
19 |
--------------------------------------------------------------------------------
/apps/electron/minimal-zustand-basic/src/features/theme/index.ts:
--------------------------------------------------------------------------------
1 | import type { StoreApi } from 'zustand';
2 | import type { State } from '../index.js';
3 |
4 | /**
5 | * Creates action handlers for theme operations in basic mode
6 | * In basic mode, these handlers are attached directly to the store state
7 | */
8 | export const createThemeHandlers = (store: StoreApi) => {
9 | return {
10 | 'THEME:TOGGLE': () => {
11 | console.log('[Basic] Toggling theme');
12 | store.setState((state) => ({
13 | ...state,
14 | theme: state.theme === 'dark' ? 'light' : 'dark',
15 | }));
16 | },
17 | };
18 | };
19 |
--------------------------------------------------------------------------------
/packages/ui/tsdown.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'tsdown';
2 |
3 | export default defineConfig({
4 | entry: ['src/index.ts', 'src/electron.ts', 'src/tauri.ts'],
5 | format: ['esm', 'cjs'],
6 | dts: true,
7 | external: [
8 | 'react',
9 | 'react-dom',
10 | '@zubridge/electron',
11 | '@zubridge/tauri',
12 | '@zubridge/core',
13 | '@tauri-apps/api/webviewWindow',
14 | ],
15 | noExternal: ['clsx'],
16 | clean: true,
17 | outExtensions({ format }) {
18 | return {
19 | js: format === 'cjs' ? '.cjs' : '.js',
20 | dts: format === 'cjs' ? '.d.cts' : '.d.ts',
21 | };
22 | },
23 | });
24 |
--------------------------------------------------------------------------------
/apps/electron/minimal-context-isolation-false/src/features/theme/index.ts:
--------------------------------------------------------------------------------
1 | import type { StoreApi } from 'zustand';
2 | import type { State } from '../index.js';
3 |
4 | /**
5 | * Creates action handlers for theme operations in basic mode
6 | * In basic mode, these handlers are attached directly to the store state
7 | */
8 | export const createThemeHandlers = (store: StoreApi) => {
9 | return {
10 | 'THEME:TOGGLE': () => {
11 | console.log('[Basic] Toggling theme');
12 | store.setState((state) => ({
13 | ...state,
14 | theme: state.theme === 'dark' ? 'light' : 'dark',
15 | }));
16 | },
17 | };
18 | };
19 |
--------------------------------------------------------------------------------
/apps/electron/minimal-sandbox-true/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "target": "ES2022",
5 | "lib": ["ES2022"],
6 | "module": "ESNext",
7 | "skipLibCheck": true,
8 | "moduleResolution": "bundler",
9 | "allowImportingTsExtensions": true,
10 | "resolveJsonModule": true,
11 | "isolatedModules": true,
12 | "noEmit": true,
13 | "strict": true,
14 | "esModuleInterop": true,
15 | "allowSyntheticDefaultImports": true,
16 | "forceConsistentCasingInFileNames": true,
17 | "types": ["node"]
18 | },
19 | "include": ["electron.vite.config.*", "wdio.conf.*"]
20 | }
21 |
--------------------------------------------------------------------------------
/apps/electron/minimal-zustand-basic/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "target": "ES2022",
5 | "lib": ["ES2022"],
6 | "module": "ESNext",
7 | "skipLibCheck": true,
8 | "moduleResolution": "bundler",
9 | "allowImportingTsExtensions": true,
10 | "resolveJsonModule": true,
11 | "isolatedModules": true,
12 | "noEmit": true,
13 | "strict": true,
14 | "esModuleInterop": true,
15 | "allowSyntheticDefaultImports": true,
16 | "forceConsistentCasingInFileNames": true,
17 | "types": ["node"]
18 | },
19 | "include": ["electron.vite.config.*", "wdio.conf.*"]
20 | }
21 |
--------------------------------------------------------------------------------
/apps/electron/minimal-zustand-immer/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "target": "ES2022",
5 | "lib": ["ES2022"],
6 | "module": "ESNext",
7 | "skipLibCheck": true,
8 | "moduleResolution": "bundler",
9 | "allowImportingTsExtensions": true,
10 | "resolveJsonModule": true,
11 | "isolatedModules": true,
12 | "noEmit": true,
13 | "strict": true,
14 | "esModuleInterop": true,
15 | "allowSyntheticDefaultImports": true,
16 | "forceConsistentCasingInFileNames": true,
17 | "types": ["node"]
18 | },
19 | "include": ["electron.vite.config.*", "wdio.conf.*"]
20 | }
21 |
--------------------------------------------------------------------------------
/apps/electron/e2e/src/main/adapters/index.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Unified store interface that can handle both Zustand and Redux stores
3 | * Provides a common contract for what a store must provide
4 | */
5 | export interface UnifiedStore {
6 | getState: () => T;
7 | getInitialState: () => T;
8 | setState: (partial: Partial | ((state: T) => Partial), replace?: boolean) => void;
9 | subscribe: (listener: (state: T, prevState: T) => void) => () => void;
10 | destroy: () => void;
11 | }
12 |
13 | export { createCustomAdapter } from './custom.js';
14 | export { createReduxAdapter } from './redux.js';
15 | export { createZustandAdapter } from './zustand.js';
16 |
--------------------------------------------------------------------------------
/apps/electron/minimal-context-isolation-false/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "target": "ES2022",
5 | "lib": ["ES2022"],
6 | "module": "ESNext",
7 | "skipLibCheck": true,
8 | "moduleResolution": "bundler",
9 | "allowImportingTsExtensions": true,
10 | "resolveJsonModule": true,
11 | "isolatedModules": true,
12 | "noEmit": true,
13 | "strict": true,
14 | "esModuleInterop": true,
15 | "allowSyntheticDefaultImports": true,
16 | "forceConsistentCasingInFileNames": true,
17 | "types": ["node"]
18 | },
19 | "include": ["electron.vite.config.*", "wdio.conf.*"]
20 | }
21 |
--------------------------------------------------------------------------------
/apps/electron/minimal-custom/src/features/counter/index.ts:
--------------------------------------------------------------------------------
1 | import type { AnyState } from '@zubridge/types';
2 |
3 | /**
4 | * Counter feature for custom mode
5 | * In custom mode, counter logic is handled by the custom state manager
6 | */
7 |
8 | export const counterHandlers = {
9 | 'COUNTER:INCREMENT': (state: AnyState) => {
10 | console.log('[Custom Counter] Incrementing counter');
11 | return {
12 | counter: (state.counter as number) + 1,
13 | };
14 | },
15 | 'COUNTER:DECREMENT': (state: AnyState) => {
16 | console.log('[Custom Counter] Decrementing counter');
17 | return {
18 | counter: (state.counter as number) - 1,
19 | };
20 | },
21 | };
22 |
--------------------------------------------------------------------------------
/apps/electron/minimal-redux/src/features/counter/index.ts:
--------------------------------------------------------------------------------
1 | import { createSlice } from '@reduxjs/toolkit';
2 |
3 | /**
4 | * Counter slice for Redux mode
5 | * In Redux mode, counter logic is handled by Redux Toolkit slices
6 | */
7 | export const counterSlice = createSlice({
8 | name: 'counter',
9 | initialState: 0,
10 | reducers: {
11 | increment: (state) => {
12 | console.log('[Redux Counter] Incrementing counter');
13 | return state + 1;
14 | },
15 | decrement: (state) => {
16 | console.log('[Redux Counter] Decrementing counter');
17 | return state - 1;
18 | },
19 | },
20 | });
21 |
22 | export const counterActions = counterSlice.actions;
23 |
--------------------------------------------------------------------------------
/agent-os/standards/frontend/css.md:
--------------------------------------------------------------------------------
1 | ## CSS best practices
2 |
3 | - **Consistent Methodology**: Apply and stick to the project's consistent CSS methodology (Tailwind, BEM, utility classes, CSS modules, etc.) across the entire project
4 | - **Avoid Overriding Framework Styles**: Work with your framework's patterns rather than fighting against them with excessive overrides
5 | - **Maintain Design System**: Establish and document design tokens (colors, spacing, typography) for consistency
6 | - **Minimize Custom CSS**: Leverage framework utilities and components to reduce custom CSS maintenance burden
7 | - **Performance Considerations**: Optimize for production with CSS purging/tree-shaking to remove unused styles
8 |
--------------------------------------------------------------------------------
/packages/electron/vitest.config.ts:
--------------------------------------------------------------------------------
1 | import { resolve } from 'node:path';
2 | import { defineConfig } from 'vitest/config';
3 |
4 | export default defineConfig({
5 | test: {
6 | include: ['test/**/*.spec.ts{,x}'],
7 | environment: 'jsdom',
8 | setupFiles: ['./test/setup.ts'],
9 | coverage: {
10 | enabled: true,
11 | include: ['src/**/*'],
12 | exclude: ['src/types/*.ts'],
13 | thresholds: {
14 | lines: 50,
15 | functions: 70,
16 | branches: 70,
17 | statements: 50,
18 | },
19 | },
20 | },
21 | resolve: {
22 | alias: {
23 | '@zubridge/types': resolve(__dirname, '../types/dist/index.js'),
24 | },
25 | },
26 | });
27 |
--------------------------------------------------------------------------------
/packages/ui/src/tauri.ts:
--------------------------------------------------------------------------------
1 | // Export Tauri-specific components and utilities
2 |
3 | export type { TauriAppProps } from './components/AppBase/hoc/withTauri';
4 | export { withTauri } from './components/AppBase/hoc/withTauri';
5 | // Export shared hooks
6 | export { useBridgeStatus } from './components/AppBase/hooks/useBridgeStatus';
7 | // Re-export shared components
8 | export * from './components/Button';
9 | export * from './components/CounterActions';
10 | export * from './components/Header';
11 | export * from './components/ThemeToggle';
12 | export * from './components/WindowActions';
13 | export * from './components/WindowDisplay';
14 |
15 | // Import types to augment Window interface
16 | import './types.js';
17 |
--------------------------------------------------------------------------------
/pnpm-workspace.yaml:
--------------------------------------------------------------------------------
1 | packages:
2 | - 'apps/electron/e2e'
3 | - 'apps/electron/minimal-context-isolation-false'
4 | - 'apps/electron/minimal-sandbox-true'
5 | - 'apps/electron/minimal-custom'
6 | - 'apps/electron/minimal-redux'
7 | - 'apps/electron/minimal-zustand-basic'
8 | - 'apps/electron/minimal-zustand-handlers'
9 | - 'apps/electron/minimal-zustand-immer'
10 | - 'apps/electron/minimal-zustand-reducers'
11 | - 'apps/tauri/e2e'
12 | - 'apps/tauri-v1/e2e'
13 | - 'e2e'
14 | - 'packages/apps-shared'
15 | - 'packages/core'
16 | - 'packages/electron'
17 | - 'packages/middleware/node'
18 | - 'packages/tauri'
19 | - 'packages/tauri-plugin'
20 | - 'packages/types'
21 | - 'packages/ui'
22 |
--------------------------------------------------------------------------------
/apps/electron/minimal-zustand-immer/src/features/theme/index.ts:
--------------------------------------------------------------------------------
1 | import type { StoreApi } from 'zustand';
2 | import type { State } from '../index.js';
3 |
4 | /**
5 | * Creates action handlers for theme operations using Immer middleware
6 | * With immer middleware, setState automatically uses produce() internally
7 | * This allows direct mutation syntax which is cleaner than manual produce() calls
8 | */
9 | export const createThemeHandlers = (store: StoreApi) => {
10 | return {
11 | 'THEME:TOGGLE': () => {
12 | console.log('[Immer] Toggling theme');
13 | store.setState((state) => {
14 | state.theme = state.theme === 'dark' ? 'light' : 'dark';
15 | });
16 | },
17 | };
18 | };
19 |
--------------------------------------------------------------------------------
/packages/electron/test/helpers.ts:
--------------------------------------------------------------------------------
1 | import { vi } from 'vitest';
2 |
3 | export function createIpcRendererMock() {
4 | return {
5 | addListener: vi.fn(),
6 | removeListener: vi.fn(),
7 | send: vi.fn(),
8 | invoke: vi.fn(),
9 | on: vi.fn(),
10 | once: vi.fn(),
11 | off: vi.fn(),
12 | postMessage: vi.fn(),
13 | removeAllListeners: vi.fn(),
14 | sendSync: vi.fn(),
15 | sendToHost: vi.fn(),
16 | setMaxListeners: vi.fn(),
17 | getMaxListeners: vi.fn(),
18 | emit: vi.fn(),
19 | prependListener: vi.fn(),
20 | listenerCount: vi.fn(),
21 | listeners: vi.fn(),
22 | prependOnceListener: vi.fn(),
23 | rawListeners: vi.fn(),
24 | eventNames: vi.fn(),
25 | };
26 | }
27 |
--------------------------------------------------------------------------------
/packages/ui/src/electron.ts:
--------------------------------------------------------------------------------
1 | // Export Electron-specific components and utilities
2 |
3 | export type { ElectronAppProps } from './components/AppBase/hoc/withElectron';
4 | export { withElectron } from './components/AppBase/hoc/withElectron';
5 | // Export shared hooks
6 | export { useBridgeStatus } from './components/AppBase/hooks/useBridgeStatus';
7 | // Re-export shared components
8 | export * from './components/Button';
9 | export * from './components/CounterActions';
10 | export * from './components/Header';
11 | export * from './components/ThemeToggle';
12 | export * from './components/WindowActions';
13 | export * from './components/WindowDisplay';
14 |
15 | // Import types to augment Window interface
16 | import './types.js';
17 |
--------------------------------------------------------------------------------
/apps/electron/e2e/src/modes/redux/features/error/index.ts:
--------------------------------------------------------------------------------
1 | import { createSlice } from '@reduxjs/toolkit';
2 |
3 | // Initial state
4 | const initialState = undefined; // Will start with dark theme
5 |
6 | /**
7 | * Theme slice using Redux Toolkit
8 | */
9 | export const errorSlice = createSlice({
10 | name: 'error',
11 | initialState,
12 | reducers: {
13 | triggerMainProcessError: () => {
14 | console.log('[Redux Slice] Triggering main process error');
15 | throw new Error('Intentional error thrown in main process for testing purposes');
16 | },
17 | },
18 | });
19 |
20 | // Export actions and reducer
21 | export const { triggerMainProcessError } = errorSlice.actions;
22 | export const { reducer } = errorSlice;
23 |
--------------------------------------------------------------------------------
/apps/electron/minimal-sandbox-true/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ES2022",
4 | "lib": ["ES2022", "DOM", "DOM.Iterable"],
5 | "module": "ESNext",
6 | "skipLibCheck": true,
7 | "moduleResolution": "bundler",
8 | "allowImportingTsExtensions": true,
9 | "resolveJsonModule": true,
10 | "isolatedModules": true,
11 | "noEmit": true,
12 | "jsx": "react-jsx",
13 | "strict": true,
14 | "noUnusedLocals": true,
15 | "noUnusedParameters": true,
16 | "noFallthroughCasesInSwitch": true,
17 | "esModuleInterop": true,
18 | "allowSyntheticDefaultImports": true,
19 | "forceConsistentCasingInFileNames": true
20 | },
21 | "include": ["src/**/*", "test/**/*"]
22 | }
23 |
--------------------------------------------------------------------------------
/apps/electron/minimal-zustand-basic/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ES2022",
4 | "lib": ["ES2022", "DOM", "DOM.Iterable"],
5 | "module": "ESNext",
6 | "skipLibCheck": true,
7 | "moduleResolution": "bundler",
8 | "allowImportingTsExtensions": true,
9 | "resolveJsonModule": true,
10 | "isolatedModules": true,
11 | "noEmit": true,
12 | "jsx": "react-jsx",
13 | "strict": true,
14 | "noUnusedLocals": true,
15 | "noUnusedParameters": true,
16 | "noFallthroughCasesInSwitch": true,
17 | "esModuleInterop": true,
18 | "allowSyntheticDefaultImports": true,
19 | "forceConsistentCasingInFileNames": true
20 | },
21 | "include": ["src/**/*", "test/**/*"]
22 | }
23 |
--------------------------------------------------------------------------------
/apps/electron/minimal-zustand-immer/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ES2022",
4 | "lib": ["ES2022", "DOM", "DOM.Iterable"],
5 | "module": "ESNext",
6 | "skipLibCheck": true,
7 | "moduleResolution": "bundler",
8 | "allowImportingTsExtensions": true,
9 | "resolveJsonModule": true,
10 | "isolatedModules": true,
11 | "noEmit": true,
12 | "jsx": "react-jsx",
13 | "strict": true,
14 | "noUnusedLocals": true,
15 | "noUnusedParameters": true,
16 | "noFallthroughCasesInSwitch": true,
17 | "esModuleInterop": true,
18 | "allowSyntheticDefaultImports": true,
19 | "forceConsistentCasingInFileNames": true
20 | },
21 | "include": ["src/**/*", "test/**/*"]
22 | }
23 |
--------------------------------------------------------------------------------
/apps/electron/minimal-zustand-reducers/src/features/theme/index.ts:
--------------------------------------------------------------------------------
1 | import type { Action, Reducer } from '@zubridge/electron';
2 |
3 | /**
4 | * Reducer for theme state
5 | * In the reducers pattern, the reducer function handles
6 | * all the theme-related actions
7 | */
8 | export const reducer: Reducer<'light' | 'dark'> = (state, action: Action) => {
9 | // Get type from action, handling both string and object actions
10 | const actionType = typeof action === 'string' ? action : action.type;
11 |
12 | switch (actionType) {
13 | case 'THEME:TOGGLE':
14 | console.log('[Reducer] Handling THEME:TOGGLE');
15 | return state === 'dark' ? 'light' : 'dark';
16 |
17 | default:
18 | return state;
19 | }
20 | };
21 |
--------------------------------------------------------------------------------
/apps/electron/minimal-context-isolation-false/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ES2022",
4 | "lib": ["ES2022", "DOM", "DOM.Iterable"],
5 | "module": "ESNext",
6 | "skipLibCheck": true,
7 | "moduleResolution": "bundler",
8 | "allowImportingTsExtensions": true,
9 | "resolveJsonModule": true,
10 | "isolatedModules": true,
11 | "noEmit": true,
12 | "jsx": "react-jsx",
13 | "strict": true,
14 | "noUnusedLocals": true,
15 | "noUnusedParameters": true,
16 | "noFallthroughCasesInSwitch": true,
17 | "esModuleInterop": true,
18 | "allowSyntheticDefaultImports": true,
19 | "forceConsistentCasingInFileNames": true
20 | },
21 | "include": ["src/**/*", "test/**/*"]
22 | }
23 |
--------------------------------------------------------------------------------
/apps/electron/e2e/tsconfig.web.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "esnext",
4 | "module": "esnext",
5 | "sourceMap": false,
6 | "strict": true,
7 | "jsx": "react-jsx",
8 | "esModuleInterop": true,
9 | "moduleResolution": "Bundler",
10 | "resolveJsonModule": true,
11 | "isolatedModules": true,
12 | "forceConsistentCasingInFileNames": true,
13 | "skipLibCheck": true,
14 | "noUnusedLocals": true,
15 | "noUnusedParameters": true,
16 | "noImplicitAny": false,
17 | "noImplicitReturns": true,
18 | "lib": ["ESNext", "DOM", "DOM.Iterable"],
19 | "composite": true,
20 | "baseUrl": "."
21 | },
22 | "include": ["src/features/**/*", "src/renderer/**/*", "src/preload/*", "src/types/**/*"]
23 | }
24 |
--------------------------------------------------------------------------------
/packages/electron/tsdown.win32.node.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'tsdown';
2 |
3 | // Windows-specific config for main and preload (node context)
4 | // Entry point is specified via CLI argument
5 | export default defineConfig({
6 | format: ['esm', 'cjs'],
7 | dts: true,
8 | external: ['electron', 'zustand', 'zustand/vanilla'],
9 | noExternal: ['@zubridge/core', 'weald', '@wdio/logger', 'tty', 'util', 'fs', 'os', 'process'],
10 | outDir: 'dist',
11 | clean: false,
12 | sourcemap: false,
13 | treeshake: true,
14 | platform: 'node',
15 | target: 'node18',
16 | outExtensions({ format }) {
17 | return {
18 | js: format === 'cjs' ? '.cjs' : '.js',
19 | dts: format === 'cjs' ? '.d.cts' : '.d.ts',
20 | };
21 | },
22 | });
23 |
--------------------------------------------------------------------------------
/apps/tauri-v1/e2e/src/renderer/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Zubridge Tauri V1 Example
7 |
8 |
9 |
10 |
11 |
12 |
13 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/apps/electron/minimal-redux/tsconfig.web.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "esnext",
4 | "module": "esnext",
5 | "sourceMap": false,
6 | "strict": true,
7 | "jsx": "react-jsx",
8 | "esModuleInterop": true,
9 | "moduleResolution": "Bundler",
10 | "resolveJsonModule": true,
11 | "isolatedModules": true,
12 | "forceConsistentCasingInFileNames": true,
13 | "skipLibCheck": true,
14 | "noUnusedLocals": true,
15 | "noUnusedParameters": true,
16 | "noImplicitAny": false,
17 | "noImplicitReturns": true,
18 | "lib": ["ESNext", "DOM", "DOM.Iterable"],
19 | "composite": true,
20 | "baseUrl": "."
21 | },
22 | "include": ["src/features/**/*", "src/renderer/**/*", "src/preload/*", "src/types/**/*"]
23 | }
24 |
--------------------------------------------------------------------------------
/apps/electron/minimal-sandbox-true/src/features/index.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Types for the basic mode state
3 | * In basic mode, action handlers are properties of the state object
4 | */
5 | export interface State {
6 | counter: number;
7 | theme: 'light' | 'dark';
8 |
9 | // Action handlers for basic mode
10 | 'COUNTER:INCREMENT': () => void;
11 | 'COUNTER:DECREMENT': () => void;
12 | 'THEME:TOGGLE': () => void;
13 |
14 | // Index signature to satisfy AnyState requirement
15 | [key: string]: unknown;
16 | }
17 |
18 | /**
19 | * Initial state for basic mode
20 | */
21 | export const initialState: State = {
22 | counter: 0,
23 | theme: 'dark',
24 | 'COUNTER:INCREMENT': () => {},
25 | 'COUNTER:DECREMENT': () => {},
26 | 'THEME:TOGGLE': () => {},
27 | };
28 |
--------------------------------------------------------------------------------
/apps/electron/minimal-zustand-basic/src/features/index.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Types for the basic mode state
3 | * In basic mode, action handlers are properties of the state object
4 | */
5 | export interface State {
6 | counter: number;
7 | theme: 'light' | 'dark';
8 |
9 | // Action handlers for basic mode
10 | 'COUNTER:INCREMENT': () => void;
11 | 'COUNTER:DECREMENT': () => void;
12 | 'THEME:TOGGLE': () => void;
13 |
14 | // Index signature to satisfy AnyState requirement
15 | [key: string]: unknown;
16 | }
17 |
18 | /**
19 | * Initial state for basic mode
20 | */
21 | export const initialState: State = {
22 | counter: 0,
23 | theme: 'dark',
24 | 'COUNTER:INCREMENT': () => {},
25 | 'COUNTER:DECREMENT': () => {},
26 | 'THEME:TOGGLE': () => {},
27 | };
28 |
--------------------------------------------------------------------------------
/apps/electron/minimal-zustand-immer/src/features/index.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Types for the basic mode state
3 | * In basic mode, action handlers are properties of the state object
4 | */
5 | export interface State {
6 | counter: number;
7 | theme: 'light' | 'dark';
8 |
9 | // Action handlers for basic mode
10 | 'COUNTER:INCREMENT': () => void;
11 | 'COUNTER:DECREMENT': () => void;
12 | 'THEME:TOGGLE': () => void;
13 |
14 | // Index signature to satisfy AnyState requirement
15 | [key: string]: unknown;
16 | }
17 |
18 | /**
19 | * Initial state for basic mode
20 | */
21 | export const initialState: State = {
22 | counter: 0,
23 | theme: 'dark',
24 | 'COUNTER:INCREMENT': () => {},
25 | 'COUNTER:DECREMENT': () => {},
26 | 'THEME:TOGGLE': () => {},
27 | };
28 |
--------------------------------------------------------------------------------
/apps/electron/minimal-custom/tsconfig.web.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "esnext",
4 | "module": "esnext",
5 | "sourceMap": false,
6 | "strict": true,
7 | "jsx": "react-jsx",
8 | "esModuleInterop": true,
9 | "moduleResolution": "Bundler",
10 | "resolveJsonModule": true,
11 | "isolatedModules": true,
12 | "forceConsistentCasingInFileNames": true,
13 | "skipLibCheck": true,
14 | "noUnusedLocals": true,
15 | "noUnusedParameters": true,
16 | "noImplicitAny": false,
17 | "noImplicitReturns": true,
18 | "lib": ["ESNext", "DOM", "DOM.Iterable"],
19 | "composite": true,
20 | "baseUrl": "."
21 | },
22 | "include": ["src/features/**/*", "src/renderer/**/*", "src/preload/*", "src/types/**/*"]
23 | }
24 |
--------------------------------------------------------------------------------
/packages/middleware/node/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "zubridge-middleware-node"
3 | version = "0.1.1-next.1"
4 | edition = "2021"
5 | authors = [ "Zubridge Contributors" ]
6 |
7 | [lib]
8 | crate-type = [ "cdylib" ]
9 |
10 | [dependencies]
11 | napi-derive = "3.2.5"
12 | fern = "0.6"
13 | log = "0.4"
14 | chrono = "0.4"
15 | serde_json = "1.0"
16 | tokio = { version = "1.0", features = ["full"] }
17 |
18 | [dependencies.napi]
19 | version = "3.3.0"
20 | features = [ "async" ]
21 |
22 | [dependencies.zubridge-middleware]
23 | path = "../"
24 |
25 | [dependencies.serde]
26 | version = "1.0"
27 | features = [ "derive" ]
28 |
29 | [build-dependencies]
30 | napi-build = "2.2.3"
31 |
32 | [profile]
33 | [profile.release]
34 | lto = true
35 | opt-level = 3
36 | codegen-units = 1
37 |
--------------------------------------------------------------------------------
/apps/electron/minimal-context-isolation-false/src/features/index.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Types for the basic mode state
3 | * In basic mode, action handlers are properties of the state object
4 | */
5 | export interface State {
6 | counter: number;
7 | theme: 'light' | 'dark';
8 |
9 | // Action handlers for basic mode
10 | 'COUNTER:INCREMENT': () => void;
11 | 'COUNTER:DECREMENT': () => void;
12 | 'THEME:TOGGLE': () => void;
13 |
14 | // Index signature to satisfy AnyState requirement
15 | [key: string]: unknown;
16 | }
17 |
18 | /**
19 | * Initial state for basic mode
20 | */
21 | export const initialState: State = {
22 | counter: 0,
23 | theme: 'dark',
24 | 'COUNTER:INCREMENT': () => {},
25 | 'COUNTER:DECREMENT': () => {},
26 | 'THEME:TOGGLE': () => {},
27 | };
28 |
--------------------------------------------------------------------------------
/apps/electron/minimal-sandbox-true/tsconfig.web.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "esnext",
4 | "module": "esnext",
5 | "sourceMap": false,
6 | "strict": true,
7 | "jsx": "react-jsx",
8 | "esModuleInterop": true,
9 | "moduleResolution": "Bundler",
10 | "resolveJsonModule": true,
11 | "isolatedModules": true,
12 | "forceConsistentCasingInFileNames": true,
13 | "skipLibCheck": true,
14 | "noUnusedLocals": true,
15 | "noUnusedParameters": true,
16 | "noImplicitAny": false,
17 | "noImplicitReturns": true,
18 | "lib": ["ESNext", "DOM", "DOM.Iterable"],
19 | "composite": true,
20 | "baseUrl": "."
21 | },
22 | "include": ["src/features/**/*", "src/renderer/**/*", "src/preload/*", "src/types/**/*"]
23 | }
24 |
--------------------------------------------------------------------------------
/apps/electron/minimal-zustand-basic/tsconfig.web.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "esnext",
4 | "module": "esnext",
5 | "sourceMap": false,
6 | "strict": true,
7 | "jsx": "react-jsx",
8 | "esModuleInterop": true,
9 | "moduleResolution": "Bundler",
10 | "resolveJsonModule": true,
11 | "isolatedModules": true,
12 | "forceConsistentCasingInFileNames": true,
13 | "skipLibCheck": true,
14 | "noUnusedLocals": true,
15 | "noUnusedParameters": true,
16 | "noImplicitAny": false,
17 | "noImplicitReturns": true,
18 | "lib": ["ESNext", "DOM", "DOM.Iterable"],
19 | "composite": true,
20 | "baseUrl": "."
21 | },
22 | "include": ["src/features/**/*", "src/renderer/**/*", "src/preload/*", "src/types/**/*"]
23 | }
24 |
--------------------------------------------------------------------------------
/apps/electron/minimal-zustand-immer/tsconfig.web.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "esnext",
4 | "module": "esnext",
5 | "sourceMap": false,
6 | "strict": true,
7 | "jsx": "react-jsx",
8 | "esModuleInterop": true,
9 | "moduleResolution": "Bundler",
10 | "resolveJsonModule": true,
11 | "isolatedModules": true,
12 | "forceConsistentCasingInFileNames": true,
13 | "skipLibCheck": true,
14 | "noUnusedLocals": true,
15 | "noUnusedParameters": true,
16 | "noImplicitAny": false,
17 | "noImplicitReturns": true,
18 | "lib": ["ESNext", "DOM", "DOM.Iterable"],
19 | "composite": true,
20 | "baseUrl": "."
21 | },
22 | "include": ["src/features/**/*", "src/renderer/**/*", "src/preload/*", "src/types/**/*"]
23 | }
24 |
--------------------------------------------------------------------------------
/apps/electron/minimal-sandbox-true/src/main/bridge.ts:
--------------------------------------------------------------------------------
1 | import type { ZubridgeMiddleware, ZustandBridge } from '@zubridge/electron/main';
2 | import { createZustandBridge } from '@zubridge/electron/main';
3 | import type { StoreApi } from 'zustand';
4 | import type { State } from '../features/index.js';
5 |
6 | /**
7 | * Creates a bridge using the basic approach
8 | * In basic mode, action handlers are attached directly to the store state
9 | */
10 | export function createBridge(
11 | store: StoreApi,
12 | middleware?: ZubridgeMiddleware,
13 | ): ZustandBridge {
14 | console.log('[Basic Mode] Creating bridge with attached handlers');
15 |
16 | // Create bridge with the store that has handlers attached
17 | return createZustandBridge(store, { middleware });
18 | }
19 |
--------------------------------------------------------------------------------
/apps/electron/minimal-zustand-basic/src/main/bridge.ts:
--------------------------------------------------------------------------------
1 | import type { ZubridgeMiddleware, ZustandBridge } from '@zubridge/electron/main';
2 | import { createZustandBridge } from '@zubridge/electron/main';
3 | import type { StoreApi } from 'zustand';
4 | import type { State } from '../features/index.js';
5 |
6 | /**
7 | * Creates a bridge using the basic approach
8 | * In basic mode, action handlers are attached directly to the store state
9 | */
10 | export function createBridge(
11 | store: StoreApi,
12 | middleware?: ZubridgeMiddleware,
13 | ): ZustandBridge {
14 | console.log('[Basic Mode] Creating bridge with attached handlers');
15 |
16 | // Create bridge with the store that has handlers attached
17 | return createZustandBridge(store, { middleware });
18 | }
19 |
--------------------------------------------------------------------------------
/apps/electron/minimal-zustand-handlers/tsconfig.web.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "esnext",
4 | "module": "esnext",
5 | "sourceMap": false,
6 | "strict": true,
7 | "jsx": "react-jsx",
8 | "esModuleInterop": true,
9 | "moduleResolution": "Bundler",
10 | "resolveJsonModule": true,
11 | "isolatedModules": true,
12 | "forceConsistentCasingInFileNames": true,
13 | "skipLibCheck": true,
14 | "noUnusedLocals": true,
15 | "noUnusedParameters": true,
16 | "noImplicitAny": false,
17 | "noImplicitReturns": true,
18 | "lib": ["ESNext", "DOM", "DOM.Iterable"],
19 | "composite": true,
20 | "baseUrl": "."
21 | },
22 | "include": ["src/features/**/*", "src/renderer/**/*", "src/preload/*", "src/types/**/*"]
23 | }
24 |
--------------------------------------------------------------------------------
/apps/electron/minimal-zustand-immer/src/main/bridge.ts:
--------------------------------------------------------------------------------
1 | import type { ZubridgeMiddleware, ZustandBridge } from '@zubridge/electron/main';
2 | import { createZustandBridge } from '@zubridge/electron/main';
3 | import type { StoreApi } from 'zustand';
4 | import type { State } from '../features/index.js';
5 |
6 | /**
7 | * Creates a bridge using the basic approach
8 | * In basic mode, action handlers are attached directly to the store state
9 | */
10 | export function createBridge(
11 | store: StoreApi,
12 | middleware?: ZubridgeMiddleware,
13 | ): ZustandBridge {
14 | console.log('[Basic Mode] Creating bridge with attached handlers');
15 |
16 | // Create bridge with the store that has handlers attached
17 | return createZustandBridge(store, { middleware });
18 | }
19 |
--------------------------------------------------------------------------------
/apps/electron/minimal-zustand-reducers/tsconfig.web.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "esnext",
4 | "module": "esnext",
5 | "sourceMap": false,
6 | "strict": true,
7 | "jsx": "react-jsx",
8 | "esModuleInterop": true,
9 | "moduleResolution": "Bundler",
10 | "resolveJsonModule": true,
11 | "isolatedModules": true,
12 | "forceConsistentCasingInFileNames": true,
13 | "skipLibCheck": true,
14 | "noUnusedLocals": true,
15 | "noUnusedParameters": true,
16 | "noImplicitAny": false,
17 | "noImplicitReturns": true,
18 | "lib": ["ESNext", "DOM", "DOM.Iterable"],
19 | "composite": true,
20 | "baseUrl": "."
21 | },
22 | "include": ["src/features/**/*", "src/renderer/**/*", "src/preload/*", "src/types/**/*"]
23 | }
24 |
--------------------------------------------------------------------------------
/apps/electron/minimal-context-isolation-false/tsconfig.web.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "esnext",
4 | "module": "esnext",
5 | "sourceMap": false,
6 | "strict": true,
7 | "jsx": "react-jsx",
8 | "esModuleInterop": true,
9 | "moduleResolution": "Bundler",
10 | "resolveJsonModule": true,
11 | "isolatedModules": true,
12 | "forceConsistentCasingInFileNames": true,
13 | "skipLibCheck": true,
14 | "noUnusedLocals": true,
15 | "noUnusedParameters": true,
16 | "noImplicitAny": false,
17 | "noImplicitReturns": true,
18 | "lib": ["ESNext", "DOM", "DOM.Iterable"],
19 | "composite": true,
20 | "baseUrl": "."
21 | },
22 | "include": ["src/features/**/*", "src/renderer/**/*", "src/preload/*", "src/types/**/*"]
23 | }
24 |
--------------------------------------------------------------------------------
/apps/tauri/e2e/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "esnext",
4 | "module": "esnext",
5 | "sourceMap": false,
6 | "strict": true,
7 | "jsx": "react-jsx",
8 | "esModuleInterop": true,
9 | "moduleResolution": "Bundler",
10 | "resolveJsonModule": true,
11 | "isolatedModules": true,
12 | "forceConsistentCasingInFileNames": true,
13 | "skipLibCheck": true,
14 | "noUnusedLocals": true,
15 | "noUnusedParameters": true,
16 | "noImplicitAny": false,
17 | "noImplicitReturns": true,
18 | "lib": ["ESNext", "DOM", "DOM.Iterable"],
19 | "composite": true,
20 | "baseUrl": ".",
21 | "types": ["vite/client"]
22 | },
23 | "include": ["src/features/**/*", "src/renderer/**/*", "src/preload/*", "src/types/**/*"]
24 | }
25 |
--------------------------------------------------------------------------------
/apps/electron/minimal-context-isolation-false/src/main/bridge.ts:
--------------------------------------------------------------------------------
1 | import type { ZubridgeMiddleware, ZustandBridge } from '@zubridge/electron/main';
2 | import { createZustandBridge } from '@zubridge/electron/main';
3 | import type { StoreApi } from 'zustand';
4 | import type { State } from '../features/index.js';
5 |
6 | /**
7 | * Creates a bridge using the basic approach
8 | * In basic mode, action handlers are attached directly to the store state
9 | */
10 | export function createBridge(
11 | store: StoreApi,
12 | middleware?: ZubridgeMiddleware,
13 | ): ZustandBridge {
14 | console.log('[Basic Mode] Creating bridge with attached handlers');
15 |
16 | // Create bridge with the store that has handlers attached
17 | return createZustandBridge(store, { middleware });
18 | }
19 |
--------------------------------------------------------------------------------
/apps/tauri-v1/e2e/src-tauri/capabilities/main.json:
--------------------------------------------------------------------------------
1 | {
2 | "identifier": "main-capability",
3 | "description": "Main application capabilities",
4 | "windows": ["*"],
5 | "permissions": [
6 | {
7 | "identifier": "core:webview:allow-create-webview-window",
8 | "allow": [
9 | {
10 | "label": "*",
11 | "url": "*"
12 | }
13 | ]
14 | },
15 | {
16 | "identifier": "core:window:allow-close",
17 | "allow": [{ "label": "*" }]
18 | },
19 | {
20 | "identifier": "core:event:allow-emit",
21 | "allow": [{ "event": "*" }]
22 | },
23 | {
24 | "identifier": "core:event:allow-listen",
25 | "allow": [{ "event": "__zubridge_state_update" }]
26 | },
27 | "core:window:allow-get-all-windows"
28 | ]
29 | }
30 |
--------------------------------------------------------------------------------
/apps/electron/e2e/src/modes/zustand-basic/features/error/index.ts:
--------------------------------------------------------------------------------
1 | import { debug } from '@zubridge/core';
2 | import type { StoreApi } from 'zustand';
3 | import type { BaseState } from '../../../../types.js';
4 |
5 | /**
6 | * Creates a handler that intentionally throws an error for testing error handling
7 | */
8 | export const triggerMainProcessError = () => {
9 | return () => {
10 | debug('main:error', 'Intentionally throwing error in main process for testing');
11 | throw new Error('Intentional error thrown in main process for testing purposes');
12 | };
13 | };
14 |
15 | export const attachErrorHandlers = (store: StoreApi) => {
16 | store.setState((state) => ({
17 | ...state,
18 | 'ERROR:TRIGGER_MAIN_PROCESS_ERROR': triggerMainProcessError(),
19 | }));
20 | };
21 |
--------------------------------------------------------------------------------
/apps/electron/minimal-custom/src/preload/index.ts:
--------------------------------------------------------------------------------
1 | import { preloadBridge } from '@zubridge/electron/preload';
2 | import { contextBridge, ipcRenderer } from 'electron';
3 | import type { State } from '../features/index.js';
4 |
5 | console.log('[Preload] Script initializing');
6 |
7 | // Get handlers from the preload bridge
8 | const { handlers } = preloadBridge();
9 |
10 | // Expose Zubridge handlers directly without wrapping
11 | contextBridge.exposeInMainWorld('zubridge', handlers);
12 |
13 | // Expose simple Electron API
14 | contextBridge.exposeInMainWorld('electronAPI', {
15 | getWindowInfo: () => {
16 | console.log('[Preload] Invoking get-window-info');
17 | return ipcRenderer.invoke('get-window-info');
18 | },
19 | });
20 |
21 | console.log('[Preload] Script initialized successfully');
22 |
--------------------------------------------------------------------------------
/apps/electron/minimal-redux/src/preload/index.ts:
--------------------------------------------------------------------------------
1 | import { preloadBridge } from '@zubridge/electron/preload';
2 | import { contextBridge, ipcRenderer } from 'electron';
3 | import type { State } from '../features/index.js';
4 |
5 | console.log('[Preload] Script initializing');
6 |
7 | // Get handlers from the preload bridge
8 | const { handlers } = preloadBridge();
9 |
10 | // Expose Zubridge handlers directly without wrapping
11 | contextBridge.exposeInMainWorld('zubridge', handlers);
12 |
13 | // Expose simple Electron API
14 | contextBridge.exposeInMainWorld('electronAPI', {
15 | getWindowInfo: () => {
16 | console.log('[Preload] Invoking get-window-info');
17 | return ipcRenderer.invoke('get-window-info');
18 | },
19 | });
20 |
21 | console.log('[Preload] Script initialized successfully');
22 |
--------------------------------------------------------------------------------
/apps/electron/minimal-zustand-handlers/src/features/theme/index.ts:
--------------------------------------------------------------------------------
1 | import type { StoreApi } from 'zustand';
2 |
3 | /**
4 | * Creates a theme toggle handler for the handlers mode
5 | * In handlers mode, each action has a dedicated handler function
6 | */
7 | export const toggleTheme =
8 | (store: StoreApi) =>
9 | () => {
10 | console.log('[Handler] Toggling theme');
11 |
12 | store.setState((state) => {
13 | const currentTheme = state.theme || 'light';
14 | const newTheme = currentTheme === 'dark' ? 'light' : 'dark';
15 |
16 | console.log(`[Handler] Changing theme from ${currentTheme} to ${newTheme}`);
17 |
18 | return {
19 | ...state,
20 | theme: newTheme,
21 | };
22 | });
23 | };
24 |
--------------------------------------------------------------------------------
/apps/electron/minimal-sandbox-true/src/preload/index.ts:
--------------------------------------------------------------------------------
1 | import { preloadBridge } from '@zubridge/electron/preload';
2 | import { contextBridge, ipcRenderer } from 'electron';
3 | import type { State } from '../features/index.js';
4 |
5 | console.log('[Preload] Script initializing');
6 |
7 | // Get handlers from the preload bridge
8 | const { handlers } = preloadBridge();
9 |
10 | // Expose Zubridge handlers directly without wrapping
11 | contextBridge.exposeInMainWorld('zubridge', handlers);
12 |
13 | // Expose simple Electron API
14 | contextBridge.exposeInMainWorld('electronAPI', {
15 | getWindowInfo: () => {
16 | console.log('[Preload] Invoking get-window-info');
17 | return ipcRenderer.invoke('get-window-info');
18 | },
19 | });
20 |
21 | console.log('[Preload] Script initialized successfully');
22 |
--------------------------------------------------------------------------------
/apps/electron/minimal-zustand-basic/src/preload/index.ts:
--------------------------------------------------------------------------------
1 | import { preloadBridge } from '@zubridge/electron/preload';
2 | import { contextBridge, ipcRenderer } from 'electron';
3 | import type { State } from '../features/index.js';
4 |
5 | console.log('[Preload] Script initializing');
6 |
7 | // Get handlers from the preload bridge
8 | const { handlers } = preloadBridge();
9 |
10 | // Expose Zubridge handlers directly without wrapping
11 | contextBridge.exposeInMainWorld('zubridge', handlers);
12 |
13 | // Expose simple Electron API
14 | contextBridge.exposeInMainWorld('electronAPI', {
15 | getWindowInfo: () => {
16 | console.log('[Preload] Invoking get-window-info');
17 | return ipcRenderer.invoke('get-window-info');
18 | },
19 | });
20 |
21 | console.log('[Preload] Script initialized successfully');
22 |
--------------------------------------------------------------------------------
/apps/electron/minimal-zustand-immer/src/preload/index.ts:
--------------------------------------------------------------------------------
1 | import { preloadBridge } from '@zubridge/electron/preload';
2 | import { contextBridge, ipcRenderer } from 'electron';
3 | import type { State } from '../features/index.js';
4 |
5 | console.log('[Preload] Script initializing');
6 |
7 | // Get handlers from the preload bridge
8 | const { handlers } = preloadBridge();
9 |
10 | // Expose Zubridge handlers directly without wrapping
11 | contextBridge.exposeInMainWorld('zubridge', handlers);
12 |
13 | // Expose simple Electron API
14 | contextBridge.exposeInMainWorld('electronAPI', {
15 | getWindowInfo: () => {
16 | console.log('[Preload] Invoking get-window-info');
17 | return ipcRenderer.invoke('get-window-info');
18 | },
19 | });
20 |
21 | console.log('[Preload] Script initialized successfully');
22 |
--------------------------------------------------------------------------------
/apps/tauri-v1/e2e/src/renderer/store.ts:
--------------------------------------------------------------------------------
1 | import { create } from 'zustand';
2 |
3 | // Define the state structure (matching Rust backend)
4 | interface CounterStoreState {
5 | counter: number;
6 | // Add methods to update state if needed internally by the store
7 | // Or rely purely on external updates via the bridge
8 | setCounter: (count: number) => void;
9 | }
10 |
11 | // Create the Zustand store
12 | export const useStore = create((set) => ({
13 | counter: 0, // Initial state
14 | setCounter: (count) => set({ counter: count }),
15 | }));
16 |
17 | // Function to initialize the store (can be called by the bridge)
18 | export const initializeStore = async () => {
19 | // Potentially fetch initial state here, or let the bridge do it
20 | console.log('Zustand store initialized');
21 | };
22 |
--------------------------------------------------------------------------------
/apps/tauri-v1/e2e/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "esnext",
4 | "module": "esnext",
5 | "sourceMap": false,
6 | "strict": true,
7 | "jsx": "react-jsx",
8 | "esModuleInterop": true,
9 | "moduleResolution": "Bundler",
10 | "resolveJsonModule": true,
11 | "isolatedModules": true,
12 | "forceConsistentCasingInFileNames": true,
13 | "skipLibCheck": true,
14 | "noUnusedLocals": true,
15 | "noUnusedParameters": true,
16 | "noImplicitAny": false,
17 | "noImplicitReturns": true,
18 | "lib": ["ESNext", "DOM", "DOM.Iterable"],
19 | "baseUrl": ".",
20 | "types": ["vite/client"]
21 | },
22 | "include": ["src/features/**/*", "src/renderer/**/*", "src/preload/*", "src/types/**/*"],
23 | "exclude": ["node_modules", "dist", "src-tauri"]
24 | }
25 |
--------------------------------------------------------------------------------
/packages/electron/src/thunk/init.ts:
--------------------------------------------------------------------------------
1 | import { debug } from '@zubridge/core';
2 | import { initActionScheduler } from '../action/ActionScheduler.js';
3 | import { initThunkManager as initManager } from './ThunkManager.js';
4 | import { ThunkScheduler } from './scheduling/ThunkScheduler.js';
5 |
6 | // Initialize the ThunkManager immediately when this file is imported
7 | const scheduler = new ThunkScheduler();
8 | const thunkManager = initManager(scheduler);
9 |
10 | debug('thunk', 'ThunkManager initialized with scheduler');
11 |
12 | // Initialize the ActionScheduler with the ThunkManager
13 | const actionScheduler = initActionScheduler(thunkManager);
14 |
15 | debug('scheduler', 'ActionScheduler initialized with ThunkManager');
16 |
17 | // Export the initialized components
18 | export { thunkManager, actionScheduler };
19 |
--------------------------------------------------------------------------------
/packages/ui/src/styles/tailwind.css:
--------------------------------------------------------------------------------
1 | @import "./theme.css";
2 | @import "tailwindcss";
3 |
4 | @layer components {
5 | /* Base theme styles */
6 | body {
7 | @apply transition-colors duration-300;
8 | }
9 |
10 | body.light-theme {
11 | @apply bg-light-bg text-light-text;
12 | }
13 |
14 | body.dark-theme {
15 | @apply bg-dark-bg text-dark-text;
16 | }
17 |
18 | /* Header Component */
19 | .app-header,
20 | .window-header {
21 | @apply bg-black/80 text-white py-2 px-4 flex justify-between items-center z-10;
22 | }
23 |
24 | .status-indicator {
25 | @apply inline-block w-2 h-2 rounded-full;
26 | }
27 |
28 | .status-ready .status-indicator {
29 | @apply bg-status-ready;
30 | }
31 |
32 | .status-error .status-indicator {
33 | @apply bg-status-error;
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | - package-ecosystem: npm
4 | directory: '/'
5 | schedule:
6 | interval: weekly
7 | open-pull-requests-limit: 10
8 | groups:
9 | production-dependencies:
10 | dependency-type: 'production'
11 | patterns:
12 | - '*'
13 | development-dependencies:
14 | dependency-type: 'development'
15 | patterns:
16 | - '*'
17 | ignore:
18 | # For all packages, ignore major updates
19 | - dependency-name: '*'
20 | update-types: ['version-update:semver-major']
21 | # For `electron-builder`, ignore all updates (they break things)
22 | - dependency-name: 'electron-builder'
23 | update-types: ['version-update:semver-major', 'version-update:semver-minor', 'version-update:semver-patch']
24 |
--------------------------------------------------------------------------------
/apps/electron/minimal-custom/src/features/index.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Types for the custom mode state
3 | * In custom mode, we use a custom state manager implementation
4 | */
5 | export interface State {
6 | counter: number;
7 | theme: 'light' | 'dark';
8 |
9 | // Index signature to satisfy AnyState requirement
10 | [key: string]: unknown;
11 | }
12 |
13 | /**
14 | * Initial state for custom mode
15 | */
16 | export const initialState: State = {
17 | counter: 0,
18 | theme: 'dark',
19 | };
20 |
21 | export * from './counter/index.js';
22 | export * from './theme/index.js';
23 |
24 | // Combine all handlers for use in the store
25 | import { counterHandlers } from './counter/index.js';
26 | import { themeHandlers } from './theme/index.js';
27 |
28 | export const handlers = {
29 | ...counterHandlers,
30 | ...themeHandlers,
31 | };
32 |
--------------------------------------------------------------------------------
/packages/tauri-plugin/src/error.rs:
--------------------------------------------------------------------------------
1 | use serde::{ser::Serializer, Serialize};
2 |
3 | pub type Result = std::result::Result;
4 |
5 | #[derive(Debug, thiserror::Error)]
6 | pub enum Error {
7 | #[error(transparent)]
8 | Io(#[from] std::io::Error),
9 | #[cfg(mobile)]
10 | #[error(transparent)]
11 | PluginInvoke(#[from] tauri::plugin::mobile::PluginInvokeError),
12 |
13 | #[error("State error: {0}")]
14 | StateError(String),
15 |
16 | #[error("Event emission error: {0}")]
17 | EmitError(String),
18 |
19 | #[error("Serialization error: {0}")]
20 | SerializationError(String),
21 | }
22 |
23 | impl Serialize for Error {
24 | fn serialize(&self, serializer: S) -> std::result::Result
25 | where
26 | S: Serializer,
27 | {
28 | serializer.serialize_str(self.to_string().as_ref())
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/apps/electron/minimal-sandbox-true/src/features/counter/index.ts:
--------------------------------------------------------------------------------
1 | import type { StoreApi } from 'zustand';
2 | import type { State } from '../index.js';
3 |
4 | /**
5 | * Creates action handlers for counter operations in basic mode
6 | * In basic mode, these handlers are attached directly to the store state
7 | */
8 | export const createCounterHandlers = (store: StoreApi) => {
9 | return {
10 | 'COUNTER:INCREMENT': () => {
11 | console.log('[Basic] Incrementing counter');
12 | store.setState((state) => ({
13 | ...state,
14 | counter: state.counter + 1,
15 | }));
16 | },
17 | 'COUNTER:DECREMENT': () => {
18 | console.log('[Basic] Decrementing counter');
19 | store.setState((state) => ({
20 | ...state,
21 | counter: state.counter - 1,
22 | }));
23 | },
24 | };
25 | };
26 |
--------------------------------------------------------------------------------
/apps/electron/minimal-zustand-basic/src/features/counter/index.ts:
--------------------------------------------------------------------------------
1 | import type { StoreApi } from 'zustand';
2 | import type { State } from '../index.js';
3 |
4 | /**
5 | * Creates action handlers for counter operations in basic mode
6 | * In basic mode, these handlers are attached directly to the store state
7 | */
8 | export const createCounterHandlers = (store: StoreApi) => {
9 | return {
10 | 'COUNTER:INCREMENT': () => {
11 | console.log('[Basic] Incrementing counter');
12 | store.setState((state) => ({
13 | ...state,
14 | counter: state.counter + 1,
15 | }));
16 | },
17 | 'COUNTER:DECREMENT': () => {
18 | console.log('[Basic] Decrementing counter');
19 | store.setState((state) => ({
20 | ...state,
21 | counter: state.counter - 1,
22 | }));
23 | },
24 | };
25 | };
26 |
--------------------------------------------------------------------------------
/apps/electron/minimal-zustand-reducers/src/features/counter/index.ts:
--------------------------------------------------------------------------------
1 | import type { Reducer } from '@zubridge/electron';
2 | import type { Action } from '@zubridge/types';
3 |
4 | export type CounterAction = { type: 'COUNTER:INCREMENT' } | { type: 'COUNTER:DECREMENT' };
5 |
6 | /**
7 | * Reducer for the counter state
8 | * In the reducers pattern, we implement pure functions that
9 | * receive the current state and an action, and return a new state
10 | */
11 | export const reducer: Reducer = (counter, action: Action) => {
12 | switch (action.type) {
13 | case 'COUNTER:INCREMENT':
14 | console.log('[Reducer] Incrementing counter');
15 | return counter + 1;
16 | case 'COUNTER:DECREMENT':
17 | console.log('[Reducer] Decrementing counter');
18 | return counter - 1;
19 | default:
20 | return counter;
21 | }
22 | };
23 |
--------------------------------------------------------------------------------
/apps/electron/minimal-context-isolation-false/src/features/counter/index.ts:
--------------------------------------------------------------------------------
1 | import type { StoreApi } from 'zustand';
2 | import type { State } from '../index.js';
3 |
4 | /**
5 | * Creates action handlers for counter operations in basic mode
6 | * In basic mode, these handlers are attached directly to the store state
7 | */
8 | export const createCounterHandlers = (store: StoreApi) => {
9 | return {
10 | 'COUNTER:INCREMENT': () => {
11 | console.log('[Basic] Incrementing counter');
12 | store.setState((state) => ({
13 | ...state,
14 | counter: state.counter + 1,
15 | }));
16 | },
17 | 'COUNTER:DECREMENT': () => {
18 | console.log('[Basic] Decrementing counter');
19 | store.setState((state) => ({
20 | ...state,
21 | counter: state.counter - 1,
22 | }));
23 | },
24 | };
25 | };
26 |
--------------------------------------------------------------------------------
/agent-os/standards/backend/migrations.md:
--------------------------------------------------------------------------------
1 | ## Database migration best practices
2 |
3 | - **Reversible Migrations**: Always implement rollback/down methods to enable safe migration reversals
4 | - **Small, Focused Changes**: Keep each migration focused on a single logical change for clarity and easier troubleshooting
5 | - **Zero-Downtime Deployments**: Consider deployment order and backwards compatibility for high-availability systems
6 | - **Separate Schema and Data**: Keep schema changes separate from data migrations for better rollback safety
7 | - **Index Management**: Create indexes on large tables carefully, using concurrent options when available to avoid locks
8 | - **Naming Conventions**: Use clear, descriptive names that indicate what the migration does
9 | - **Version Control**: Always commit migrations to version control and never modify existing migrations after deployment
10 |
--------------------------------------------------------------------------------
/agent-os/standards/backend/queries.md:
--------------------------------------------------------------------------------
1 | ## Database query best practices
2 |
3 | - **Prevent SQL Injection**: Always use parameterized queries or ORM methods; never interpolate user input into SQL strings
4 | - **Avoid N+1 Queries**: Use eager loading or joins to fetch related data in a single query instead of multiple queries
5 | - **Select Only Needed Data**: Request only the columns you need rather than using SELECT * for better performance
6 | - **Index Strategic Columns**: Index columns used in WHERE, JOIN, and ORDER BY clauses for query optimization
7 | - **Use Transactions for Related Changes**: Wrap related database operations in transactions to maintain data consistency
8 | - **Set Query Timeouts**: Implement timeouts to prevent runaway queries from impacting system performance
9 | - **Cache Expensive Queries**: Cache results of complex or frequently-run queries when appropriate
10 |
--------------------------------------------------------------------------------
/packages/apps-shared/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@zubridge/apps-shared",
3 | "version": "0.1.0",
4 | "private": true,
5 | "description": "Shared utilities and logic for Zubridge example apps",
6 | "main": "./dist/index.js",
7 | "module": "./dist/index.js",
8 | "types": "./dist/index.d.ts",
9 | "type": "module",
10 | "scripts": {
11 | "build": "tsc",
12 | "clean": "pnpm run clean:output && pnpm run clean:cache && shx rm -rf node_modules",
13 | "clean:output": "shx rm -rf dist",
14 | "clean:cache": "shx rm -rf .turbo",
15 | "dev": "tsc --watch",
16 | "typecheck": "tsc --noEmit"
17 | },
18 | "dependencies": {
19 | "@zubridge/core": "workspace:*",
20 | "@zubridge/types": "workspace:*"
21 | },
22 | "peerDependencies": {
23 | "zustand": "^4.3.0"
24 | },
25 | "devDependencies": {
26 | "typescript": "^5.9.3"
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/scripts/clean-cache.ts:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env tsx
2 |
3 | import { execSync } from 'node:child_process';
4 | import { existsSync } from 'node:fs';
5 |
6 | const isWindows = process.platform === 'win32';
7 | const cacheDir = '.turbo';
8 |
9 | if (!existsSync(cacheDir)) {
10 | console.log('No cache directory found, skipping clean');
11 | process.exit(0);
12 | }
13 |
14 | try {
15 | if (isWindows) {
16 | // Use Windows native rmdir which handles file locks better
17 | execSync(`rmdir /s /q "${cacheDir}"`, { stdio: 'inherit' });
18 | } else {
19 | // Use Unix rm
20 | execSync(`rm -rf "${cacheDir}"`, { stdio: 'inherit' });
21 | }
22 | console.log('Cache cleaned successfully');
23 | } catch (error) {
24 | console.warn('Cache clean failed (this is usually safe to ignore):', error.message);
25 | // Don't fail the build if cache clean fails
26 | process.exit(0);
27 | }
28 |
--------------------------------------------------------------------------------
/apps/electron/minimal-zustand-immer/src/features/counter/index.ts:
--------------------------------------------------------------------------------
1 | import type { StoreApi } from 'zustand';
2 | import type { State } from '../index.js';
3 |
4 | /**
5 | * Creates action handlers for counter operations using Immer middleware
6 | * With immer middleware, setState automatically uses produce() internally
7 | * This allows direct mutation syntax which is cleaner than manual produce() calls
8 | */
9 | export const createCounterHandlers = (store: StoreApi) => {
10 | return {
11 | 'COUNTER:INCREMENT': () => {
12 | console.log('[Immer] Incrementing counter');
13 | store.setState((state) => {
14 | state.counter += 1;
15 | });
16 | },
17 | 'COUNTER:DECREMENT': () => {
18 | console.log('[Immer] Decrementing counter');
19 | store.setState((state) => {
20 | state.counter -= 1;
21 | });
22 | },
23 | };
24 | };
25 |
--------------------------------------------------------------------------------
/apps/electron/minimal-zustand-handlers/src/preload/index.ts:
--------------------------------------------------------------------------------
1 | import { preloadBridge } from '@zubridge/electron/preload';
2 | import { contextBridge, ipcRenderer } from 'electron';
3 | import type { State } from '../features/index.js';
4 |
5 | console.log('[Preload] Script initializing');
6 |
7 | // Get handlers from the preload bridge
8 | const { handlers } = preloadBridge();
9 |
10 | // Expose Zubridge handlers directly without wrapping
11 | contextBridge.exposeInMainWorld('zubridge', handlers);
12 |
13 | // Expose protected methods that allow the renderer process to use
14 | // the ipcRenderer without exposing the entire object
15 | contextBridge.exposeInMainWorld('electronAPI', {
16 | getWindowInfo: () => {
17 | console.log('[Preload] Invoking get-window-info');
18 | return ipcRenderer.invoke('get-window-info');
19 | },
20 | });
21 |
22 | console.log('[Preload] Script initialized successfully');
23 |
--------------------------------------------------------------------------------
/apps/electron/minimal-zustand-reducers/src/preload/index.ts:
--------------------------------------------------------------------------------
1 | import { preloadBridge } from '@zubridge/electron/preload';
2 | import { contextBridge, ipcRenderer } from 'electron';
3 | import type { State } from '../features/index.js';
4 |
5 | console.log('[Preload] Script initializing');
6 |
7 | // Get handlers from the preload bridge
8 | const { handlers } = preloadBridge();
9 |
10 | // Expose Zubridge handlers directly without wrapping
11 | contextBridge.exposeInMainWorld('zubridge', handlers);
12 |
13 | // Expose protected methods that allow the renderer process to use
14 | // the ipcRenderer without exposing the entire object
15 | contextBridge.exposeInMainWorld('electronAPI', {
16 | getWindowInfo: () => {
17 | console.log('[Preload] Invoking get-window-info');
18 | return ipcRenderer.invoke('get-window-info');
19 | },
20 | });
21 |
22 | console.log('[Preload] Script initialized successfully');
23 |
--------------------------------------------------------------------------------
/.github/workflows/actions/setup-workspace/action.yml:
--------------------------------------------------------------------------------
1 | description: 'Sets up Node.js environment with PNPM for CI/CD workflows'
2 | inputs:
3 | node-version:
4 | description: 'Node.js version to use (e.g., "20")'
5 | required: true
6 |
7 | runs:
8 | using: composite
9 | steps:
10 | - name: 🧰 Setup PNPM
11 | uses: pnpm/action-setup@v4
12 | with:
13 | run_install: false
14 |
15 | - name: 🛠️ Setup Node.js ${{ inputs.node-version }}
16 | uses: actions/setup-node@v4
17 | with:
18 | node-version: ${{ inputs.node-version }}
19 | cache: 'pnpm'
20 |
21 | - name: ⚙️ Install Dependencies
22 | shell: bash
23 | run: pnpm install --frozen-lockfile
24 |
25 | - name: 🔧 Configure Git identity
26 | shell: bash
27 | run: |
28 | git config --global user.email "actions@github.com"
29 | git config --global user.name "GitHub Actions"
30 |
--------------------------------------------------------------------------------
/apps/electron/e2e/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "esnext",
4 | "module": "esnext",
5 | "sourceMap": false,
6 | "strict": true,
7 | "jsx": "preserve",
8 | "esModuleInterop": true,
9 | "moduleResolution": "Bundler",
10 | "resolveJsonModule": true,
11 | "isolatedModules": true,
12 | "forceConsistentCasingInFileNames": true,
13 | "skipLibCheck": true,
14 | "noUnusedLocals": true,
15 | "noUnusedParameters": true,
16 | "noImplicitAny": false,
17 | "noImplicitReturns": true,
18 | "composite": true,
19 | "baseUrl": ".",
20 | "types": ["electron-vite/node"]
21 | },
22 | "include": [
23 | "electron.vite.config.*",
24 | "src/shared/**/*",
25 | "src/main/**/*",
26 | "src/preload/**/*",
27 | "src/features/**/*",
28 | "src/utils/**/*",
29 | "src/modes/**/*",
30 | "src/types/**/*",
31 | "src/tray/**/*"
32 | ]
33 | }
34 |
--------------------------------------------------------------------------------
/packages/electron/src/renderer.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Renderer-safe entry point for @zubridge/electron
3 | * This excludes Node.js-specific functionality like debug logging
4 | */
5 |
6 | export type * from '@zubridge/types';
7 | // Re-export core renderer functionality
8 | export { createHandlers, createUseStore, useDispatch } from './index.js';
9 | export {
10 | canDispatchAction,
11 | getAffectedStateKeys,
12 | registerActionMapping,
13 | registerActionMappings,
14 | validateActionDispatch,
15 | } from './renderer/actionValidator.js';
16 | // Re-export validation functions (these don't use Node.js APIs)
17 | export {
18 | getWindowSubscriptions,
19 | isSubscribedToKey,
20 | stateKeyExists,
21 | validateStateAccess,
22 | validateStateAccessWithExistence,
23 | } from './renderer/subscriptionValidator.js';
24 |
25 | // Re-export environment utility (uses dynamic import for app)
26 | export { isDev } from './utils/environment.js';
27 |
--------------------------------------------------------------------------------
/apps/tauri-v1/e2e/src-tauri/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "app"
3 | version = "0.1.0"
4 | description = "A Tauri App"
5 | authors = ["you"]
6 | license = ""
7 | repository = ""
8 | edition = "2021"
9 | rust-version = "1.77.2"
10 |
11 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
12 |
13 | [lib]
14 | name = "app_lib"
15 | crate-type = ["staticlib", "cdylib", "rlib"]
16 |
17 | [build-dependencies]
18 | tauri-build = { version = "1.5.6", features = [] }
19 |
20 | [features]
21 | custom-protocol = ["tauri/custom-protocol"]
22 |
23 | [dependencies]
24 | serde_json = "1.0"
25 | serde = { version = "1.0", features = ["derive"] }
26 | log = "0.4"
27 | tauri = { version = "1.5.3", features = [ "custom-protocol", "protocol-asset", "process-exit", "window-all", "shell-open", "system-tray", "devtools"] }
28 | tauri-plugin-log = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v1" }
29 |
--------------------------------------------------------------------------------
/packages/electron/src/utils/preloadOptions.ts:
--------------------------------------------------------------------------------
1 | import type { PreloadOptions } from '../types/preload.js';
2 | import { getThunkProcessorOptions, THUNK_PROCESSOR_DEFAULTS } from './thunkProcessor.js';
3 |
4 | /**
5 | * Default configuration values for preload bridge
6 | * Extends base thunk processor defaults
7 | */
8 | export const PRELOAD_DEFAULTS: Required = {
9 | ...THUNK_PROCESSOR_DEFAULTS,
10 | // Add any preload-specific defaults here in the future
11 | };
12 |
13 | /**
14 | * Merge user options with preload defaults
15 | */
16 | export function getPreloadOptions(userOptions?: PreloadOptions): Required {
17 | // Use base thunk processor option merging, then add preload-specific handling
18 | const thunkProcessorOptions = getThunkProcessorOptions(userOptions);
19 | return {
20 | ...thunkProcessorOptions,
21 | // Add any preload-specific option handling here in the future
22 | };
23 | }
24 |
--------------------------------------------------------------------------------
/apps/electron/minimal-custom/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "esnext",
4 | "module": "esnext",
5 | "sourceMap": false,
6 | "strict": true,
7 | "jsx": "preserve",
8 | "esModuleInterop": true,
9 | "moduleResolution": "Bundler",
10 | "resolveJsonModule": true,
11 | "isolatedModules": true,
12 | "forceConsistentCasingInFileNames": true,
13 | "skipLibCheck": true,
14 | "noUnusedLocals": true,
15 | "noUnusedParameters": true,
16 | "noImplicitAny": false,
17 | "noImplicitReturns": true,
18 | "composite": true,
19 | "baseUrl": ".",
20 | "types": ["electron-vite/node"]
21 | },
22 | "include": [
23 | "electron.vite.config.*",
24 | "src/shared/**/*",
25 | "src/main/**/*",
26 | "src/preload/**/*",
27 | "src/features/**/*",
28 | "src/utils/**/*",
29 | "src/modes/**/*",
30 | "src/types/**/*",
31 | "src/tray/**/*"
32 | ]
33 | }
34 |
--------------------------------------------------------------------------------
/apps/electron/minimal-redux/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "esnext",
4 | "module": "esnext",
5 | "sourceMap": false,
6 | "strict": true,
7 | "jsx": "preserve",
8 | "esModuleInterop": true,
9 | "moduleResolution": "Bundler",
10 | "resolveJsonModule": true,
11 | "isolatedModules": true,
12 | "forceConsistentCasingInFileNames": true,
13 | "skipLibCheck": true,
14 | "noUnusedLocals": true,
15 | "noUnusedParameters": true,
16 | "noImplicitAny": false,
17 | "noImplicitReturns": true,
18 | "composite": true,
19 | "baseUrl": ".",
20 | "types": ["electron-vite/node"]
21 | },
22 | "include": [
23 | "electron.vite.config.*",
24 | "src/shared/**/*",
25 | "src/main/**/*",
26 | "src/preload/**/*",
27 | "src/features/**/*",
28 | "src/utils/**/*",
29 | "src/modes/**/*",
30 | "src/types/**/*",
31 | "src/tray/**/*"
32 | ]
33 | }
34 |
--------------------------------------------------------------------------------
/agent-os/standards/backend/models.md:
--------------------------------------------------------------------------------
1 | ## Database model best practices
2 |
3 | - **Clear Naming**: Use singular names for models and plural for tables following your framework's conventions
4 | - **Timestamps**: Include created and updated timestamps on all tables for auditing and debugging
5 | - **Data Integrity**: Use database constraints (NOT NULL, UNIQUE, foreign keys) to enforce data rules at the database level
6 | - **Appropriate Data Types**: Choose data types that match the data's purpose and size requirements
7 | - **Indexes on Foreign Keys**: Index foreign key columns and other frequently queried fields for performance
8 | - **Validation at Multiple Layers**: Implement validation at both model and database levels for defense in depth
9 | - **Relationship Clarity**: Define relationships clearly with appropriate cascade behaviors and naming conventions
10 | - **Avoid Over-Normalization**: Balance normalization with practical query performance needs
11 |
--------------------------------------------------------------------------------
/packages/types/src/internal.ts:
--------------------------------------------------------------------------------
1 | import type { AnyState, Handlers } from './index';
2 |
3 | /**
4 | * Internal global augmentations for Zubridge
5 | * These are used by the core Zubridge packages (electron, tauri)
6 | */
7 | export interface ZubridgeInternalWindow {
8 | /**
9 | * Internal subscription validator exposed by preload
10 | */
11 | __zubridge_subscriptionValidator?: {
12 | getWindowSubscriptions: () => Promise;
13 | isSubscribedToKey: (key: string) => Promise;
14 | stateKeyExists: (state: unknown, key: string) => boolean;
15 | };
16 |
17 | /**
18 | * Window ID tracking (internal)
19 | */
20 | __zubridge_windowId?: string;
21 |
22 | /**
23 | * Public zubridge API exposed to renderer processes
24 | */
25 | zubridge?: Handlers;
26 | }
27 |
28 | // Only apply internal properties to Window
29 | declare global {
30 | interface Window extends ZubridgeInternalWindow {}
31 | }
32 |
--------------------------------------------------------------------------------
/apps/electron/minimal-zustand-handlers/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "esnext",
4 | "module": "esnext",
5 | "sourceMap": false,
6 | "strict": true,
7 | "jsx": "preserve",
8 | "esModuleInterop": true,
9 | "moduleResolution": "Bundler",
10 | "resolveJsonModule": true,
11 | "isolatedModules": true,
12 | "forceConsistentCasingInFileNames": true,
13 | "skipLibCheck": true,
14 | "noUnusedLocals": true,
15 | "noUnusedParameters": true,
16 | "noImplicitAny": false,
17 | "noImplicitReturns": true,
18 | "composite": true,
19 | "baseUrl": ".",
20 | "types": ["electron-vite/node"]
21 | },
22 | "include": [
23 | "electron.vite.config.*",
24 | "src/shared/**/*",
25 | "src/main/**/*",
26 | "src/preload/**/*",
27 | "src/features/**/*",
28 | "src/utils/**/*",
29 | "src/modes/**/*",
30 | "src/types/**/*",
31 | "src/tray/**/*"
32 | ]
33 | }
34 |
--------------------------------------------------------------------------------
/apps/electron/minimal-zustand-reducers/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "esnext",
4 | "module": "esnext",
5 | "sourceMap": false,
6 | "strict": true,
7 | "jsx": "preserve",
8 | "esModuleInterop": true,
9 | "moduleResolution": "Bundler",
10 | "resolveJsonModule": true,
11 | "isolatedModules": true,
12 | "forceConsistentCasingInFileNames": true,
13 | "skipLibCheck": true,
14 | "noUnusedLocals": true,
15 | "noUnusedParameters": true,
16 | "noImplicitAny": false,
17 | "noImplicitReturns": true,
18 | "composite": true,
19 | "baseUrl": ".",
20 | "types": ["electron-vite/node"]
21 | },
22 | "include": [
23 | "electron.vite.config.*",
24 | "src/shared/**/*",
25 | "src/main/**/*",
26 | "src/preload/**/*",
27 | "src/features/**/*",
28 | "src/utils/**/*",
29 | "src/modes/**/*",
30 | "src/types/**/*",
31 | "src/tray/**/*"
32 | ]
33 | }
34 |
--------------------------------------------------------------------------------
/packages/tauri-plugin/build.rs:
--------------------------------------------------------------------------------
1 | const COMMANDS: &[&str] = &["get_initial_state", "dispatch_action"];
2 |
3 | fn main() {
4 | // During cargo publish, ensure we use a proper directory that's included in the package
5 | // If this is a publish build, the environment variable CARGO_FEATURE_BEING_PACKAGED is set
6 | if std::env::var("CARGO_FEATURE_BEING_PACKAGED").is_ok() {
7 | // Set the generation directory to a standard location during packaging
8 | let out_dir = std::env::var("OUT_DIR").unwrap_or_else(|_| "target/package".to_string());
9 | std::env::set_var("TAURI_BUILD_GEN_DIR", out_dir);
10 | }
11 |
12 | tauri_build::try_build(
13 | tauri_build::Attributes::new()
14 | .plugin(
15 | "zubridge",
16 | tauri_build::InlinedPlugin::new().commands(&COMMANDS),
17 | )
18 | )
19 | .unwrap_or_else(|_| {
20 | println!("cargo:warning=Failed to build with tauri.conf.json, skipping config verification");
21 | });
22 | }
23 |
--------------------------------------------------------------------------------
/packages/electron/src/utils/thunkProcessor.ts:
--------------------------------------------------------------------------------
1 | import type { ThunkProcessorOptions } from '../types/thunkProcessor.js';
2 |
3 | /**
4 | * Base default configuration values for thunk processors
5 | */
6 | export const THUNK_PROCESSOR_DEFAULTS: Required = {
7 | /** Default maximum queue size */
8 | maxQueueSize: 100,
9 | /** Platform-specific timeout - Linux gets longer timeout due to slower IPC */
10 | actionCompletionTimeoutMs: process.platform === 'linux' ? 60000 : 30000,
11 | };
12 |
13 | /**
14 | * Merge user options with thunk processor defaults
15 | */
16 | export function getThunkProcessorOptions(
17 | userOptions?: ThunkProcessorOptions,
18 | ): Required {
19 | return {
20 | maxQueueSize: userOptions?.maxQueueSize ?? THUNK_PROCESSOR_DEFAULTS.maxQueueSize,
21 | actionCompletionTimeoutMs:
22 | userOptions?.actionCompletionTimeoutMs ?? THUNK_PROCESSOR_DEFAULTS.actionCompletionTimeoutMs,
23 | };
24 | }
25 |
--------------------------------------------------------------------------------
/apps/tauri/e2e/src-tauri/capabilities/main.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "../gen/schemas/desktop-schema.json",
3 | "identifier": "main-capability",
4 | "description": "Capability for all application windows, including Zubridge access",
5 | "windows": ["*"],
6 | "permissions": [
7 | {
8 | "identifier": "core:webview:allow-create-webview-window",
9 | "allow": [
10 | {
11 | "label": "*",
12 | "url": "*"
13 | }
14 | ]
15 | },
16 | {
17 | "identifier": "core:window:allow-close",
18 | "allow": [{ "label": "*" }]
19 | },
20 | {
21 | "identifier": "core:event:allow-emit",
22 | "allow": [{ "event": "*" }]
23 | },
24 | {
25 | "identifier": "core:event:allow-listen",
26 | "allow": [{ "event": "*" }]
27 | },
28 | "zubridge:allow-get-initial-state",
29 | "zubridge:allow-dispatch-action",
30 | "core:default",
31 | "core:window:allow-get-all-windows"
32 | ]
33 | }
34 |
--------------------------------------------------------------------------------
/apps/electron/e2e/src/modes/custom/features/theme/index.ts:
--------------------------------------------------------------------------------
1 | import type { AnyState } from '@zubridge/types';
2 |
3 | /**
4 | * Toggle theme action handler for custom mode
5 | */
6 | export const toggle = (state: AnyState): Partial => {
7 | const currentTheme = state.theme as 'light' | 'dark' | undefined;
8 | const newTheme = currentTheme === 'dark' ? 'light' : 'dark';
9 |
10 | console.log(`[Custom Theme] Toggling theme from ${currentTheme || 'unknown'} to ${newTheme}`);
11 |
12 | return {
13 | theme: newTheme,
14 | };
15 | };
16 |
17 | /**
18 | * Set theme action handler for custom mode
19 | * @param isDark Whether dark theme should be enabled
20 | */
21 | export const setValue = (isDark: boolean): Partial => {
22 | const theme = isDark ? 'dark' : 'light';
23 | console.log(`[Custom Theme] Setting theme to ${theme}`);
24 |
25 | return {
26 | theme,
27 | };
28 | };
29 |
30 | // Export default initial state
31 | export const initialState = 'dark';
32 |
--------------------------------------------------------------------------------
/apps/electron/minimal-custom/test/utils/constants.ts:
--------------------------------------------------------------------------------
1 | // Test timing constants for minimal app tests
2 | // These are shorter than main E2E tests since minimal apps are simpler
3 |
4 | export const TIMING = {
5 | // Button click pause - time to wait after clicking a button
6 | BUTTON_CLICK_PAUSE: process.platform === 'darwin' ? 500 : 300,
7 |
8 | // State sync pause - time to wait for state to sync between processes
9 | STATE_SYNC_PAUSE: process.platform === 'darwin' ? 1000 : 750,
10 |
11 | // Window change pause - time to wait when switching between windows
12 | WINDOW_CHANGE_PAUSE: process.platform === 'darwin' ? 800 : 600,
13 |
14 | // UI interaction pause - general pause for UI interactions
15 | UI_INTERACTION_PAUSE: 250,
16 |
17 | // Tray interaction pause - time to wait after tray interactions
18 | TRAY_INTERACTION_PAUSE: process.platform === 'darwin' ? 1500 : 1000,
19 | } as const;
20 |
21 | console.log(`Using timing configuration for platform: ${process.platform}`);
22 |
--------------------------------------------------------------------------------
/apps/electron/minimal-redux/test/utils/constants.ts:
--------------------------------------------------------------------------------
1 | // Test timing constants for minimal app tests
2 | // These are shorter than main E2E tests since minimal apps are simpler
3 |
4 | export const TIMING = {
5 | // Button click pause - time to wait after clicking a button
6 | BUTTON_CLICK_PAUSE: process.platform === 'darwin' ? 500 : 300,
7 |
8 | // State sync pause - time to wait for state to sync between processes
9 | STATE_SYNC_PAUSE: process.platform === 'darwin' ? 1000 : 750,
10 |
11 | // Window change pause - time to wait when switching between windows
12 | WINDOW_CHANGE_PAUSE: process.platform === 'darwin' ? 800 : 600,
13 |
14 | // UI interaction pause - general pause for UI interactions
15 | UI_INTERACTION_PAUSE: 250,
16 |
17 | // Tray interaction pause - time to wait after tray interactions
18 | TRAY_INTERACTION_PAUSE: process.platform === 'darwin' ? 1500 : 1000,
19 | } as const;
20 |
21 | console.log(`Using timing configuration for platform: ${process.platform}`);
22 |
--------------------------------------------------------------------------------
/apps/electron/e2e/src/modes/zustand-basic/main.ts:
--------------------------------------------------------------------------------
1 | import type { ZubridgeMiddleware, ZustandBridge } from '@zubridge/electron/main';
2 | import { createZustandBridge } from '@zubridge/electron/main';
3 | import type { StoreApi } from 'zustand';
4 |
5 | import type { BaseState } from '../../types.js';
6 | import { attachFeatureHandlers } from './features/index.js';
7 |
8 | /**
9 | * Creates a bridge using the basic approach
10 | * In this approach, handlers are attached to the store object
11 | */
12 | export const createBasicBridge = >(
13 | store: Store,
14 | middleware?: ZubridgeMiddleware,
15 | ): ZustandBridge => {
16 | console.log('[Basic Mode] Creating bridge with store-based handlers');
17 |
18 | // Attach handlers to the store with generic type parameter
19 | attachFeatureHandlers(store);
20 |
21 | // Create bridge with middleware if provided
22 | return createZustandBridge(store, {
23 | middleware,
24 | });
25 | };
26 |
--------------------------------------------------------------------------------
/apps/electron/minimal-sandbox-true/test/utils/constants.ts:
--------------------------------------------------------------------------------
1 | // Test timing constants for minimal app tests
2 | // These are shorter than main E2E tests since minimal apps are simpler
3 |
4 | export const TIMING = {
5 | // Button click pause - time to wait after clicking a button
6 | BUTTON_CLICK_PAUSE: process.platform === 'darwin' ? 500 : 300,
7 |
8 | // State sync pause - time to wait for state to sync between processes
9 | STATE_SYNC_PAUSE: process.platform === 'darwin' ? 1000 : 750,
10 |
11 | // Window change pause - time to wait when switching between windows
12 | WINDOW_CHANGE_PAUSE: process.platform === 'darwin' ? 800 : 600,
13 |
14 | // UI interaction pause - general pause for UI interactions
15 | UI_INTERACTION_PAUSE: 250,
16 |
17 | // Tray interaction pause - time to wait after tray interactions
18 | TRAY_INTERACTION_PAUSE: process.platform === 'darwin' ? 1500 : 1000,
19 | } as const;
20 |
21 | console.log(`Using timing configuration for platform: ${process.platform}`);
22 |
--------------------------------------------------------------------------------
/packages/tauri/scripts/build.ts:
--------------------------------------------------------------------------------
1 | // Build script for the project
2 | // Usage: tsx scripts/build.ts
3 | import fs from 'node:fs';
4 | import path from 'node:path';
5 | import { fileURLToPath } from 'node:url';
6 | import shell from 'shelljs';
7 |
8 | const __filename = fileURLToPath(import.meta.url);
9 | const __dirname = path.dirname(__filename);
10 |
11 | // compile and bundle
12 | shell.exec('tsc --project tsconfig.json');
13 | shell.exec('rollup --config rollup.config.js');
14 |
15 | // ensure dist dir exists
16 | if (!fs.existsSync('dist')) {
17 | shell.mkdir('dist');
18 | }
19 |
20 | // Find all d.ts files and create d.cts counterparts
21 | // The find command will find all d.ts files in the dist directory and its subdirectories
22 | const result = shell.find('dist').filter((file) => file.endsWith('.d.ts'));
23 |
24 | // Create .d.cts versions for the found files
25 | result.forEach((file) => {
26 | const ctsFile = file.replace('.d.ts', '.d.cts');
27 | shell.cp(file, ctsFile);
28 | });
29 |
--------------------------------------------------------------------------------
/apps/electron/minimal-zustand-basic/test/utils/constants.ts:
--------------------------------------------------------------------------------
1 | // Test timing constants for minimal app tests
2 | // These are shorter than main E2E tests since minimal apps are simpler
3 |
4 | export const TIMING = {
5 | // Button click pause - time to wait after clicking a button
6 | BUTTON_CLICK_PAUSE: process.platform === 'darwin' ? 500 : 300,
7 |
8 | // State sync pause - time to wait for state to sync between processes
9 | STATE_SYNC_PAUSE: process.platform === 'darwin' ? 1000 : 750,
10 |
11 | // Window change pause - time to wait when switching between windows
12 | WINDOW_CHANGE_PAUSE: process.platform === 'darwin' ? 800 : 600,
13 |
14 | // UI interaction pause - general pause for UI interactions
15 | UI_INTERACTION_PAUSE: 250,
16 |
17 | // Tray interaction pause - time to wait after tray interactions
18 | TRAY_INTERACTION_PAUSE: process.platform === 'darwin' ? 1500 : 1000,
19 | } as const;
20 |
21 | console.log(`Using timing configuration for platform: ${process.platform}`);
22 |
--------------------------------------------------------------------------------
/apps/electron/minimal-zustand-handlers/test/utils/constants.ts:
--------------------------------------------------------------------------------
1 | // Test timing constants for minimal app tests
2 | // These are shorter than main E2E tests since minimal apps are simpler
3 |
4 | export const TIMING = {
5 | // Button click pause - time to wait after clicking a button
6 | BUTTON_CLICK_PAUSE: process.platform === 'darwin' ? 500 : 300,
7 |
8 | // State sync pause - time to wait for state to sync between processes
9 | STATE_SYNC_PAUSE: process.platform === 'darwin' ? 1000 : 750,
10 |
11 | // Window change pause - time to wait when switching between windows
12 | WINDOW_CHANGE_PAUSE: process.platform === 'darwin' ? 800 : 600,
13 |
14 | // UI interaction pause - general pause for UI interactions
15 | UI_INTERACTION_PAUSE: 250,
16 |
17 | // Tray interaction pause - time to wait after tray interactions
18 | TRAY_INTERACTION_PAUSE: process.platform === 'darwin' ? 1500 : 1000,
19 | } as const;
20 |
21 | console.log(`Using timing configuration for platform: ${process.platform}`);
22 |
--------------------------------------------------------------------------------
/apps/electron/minimal-zustand-immer/test/utils/constants.ts:
--------------------------------------------------------------------------------
1 | // Test timing constants for minimal app tests
2 | // These are shorter than main E2E tests since minimal apps are simpler
3 |
4 | export const TIMING = {
5 | // Button click pause - time to wait after clicking a button
6 | BUTTON_CLICK_PAUSE: process.platform === 'darwin' ? 500 : 300,
7 |
8 | // State sync pause - time to wait for state to sync between processes
9 | STATE_SYNC_PAUSE: process.platform === 'darwin' ? 1000 : 750,
10 |
11 | // Window change pause - time to wait when switching between windows
12 | WINDOW_CHANGE_PAUSE: process.platform === 'darwin' ? 800 : 600,
13 |
14 | // UI interaction pause - general pause for UI interactions
15 | UI_INTERACTION_PAUSE: 250,
16 |
17 | // Tray interaction pause - time to wait after tray interactions
18 | TRAY_INTERACTION_PAUSE: process.platform === 'darwin' ? 1500 : 1000,
19 | } as const;
20 |
21 | console.log(`Using timing configuration for platform: ${process.platform}`);
22 |
--------------------------------------------------------------------------------
/apps/electron/minimal-zustand-reducers/test/utils/constants.ts:
--------------------------------------------------------------------------------
1 | // Test timing constants for minimal app tests
2 | // These are shorter than main E2E tests since minimal apps are simpler
3 |
4 | export const TIMING = {
5 | // Button click pause - time to wait after clicking a button
6 | BUTTON_CLICK_PAUSE: process.platform === 'darwin' ? 500 : 300,
7 |
8 | // State sync pause - time to wait for state to sync between processes
9 | STATE_SYNC_PAUSE: process.platform === 'darwin' ? 1000 : 750,
10 |
11 | // Window change pause - time to wait when switching between windows
12 | WINDOW_CHANGE_PAUSE: process.platform === 'darwin' ? 800 : 600,
13 |
14 | // UI interaction pause - general pause for UI interactions
15 | UI_INTERACTION_PAUSE: 250,
16 |
17 | // Tray interaction pause - time to wait after tray interactions
18 | TRAY_INTERACTION_PAUSE: process.platform === 'darwin' ? 1500 : 1000,
19 | } as const;
20 |
21 | console.log(`Using timing configuration for platform: ${process.platform}`);
22 |
--------------------------------------------------------------------------------
/agent-os/standards/frontend/accessibility.md:
--------------------------------------------------------------------------------
1 | ## UI accessibility best practices
2 |
3 | - **Semantic HTML**: Use appropriate HTML elements (nav, main, button, etc.) that convey meaning to assistive technologies
4 | - **Keyboard Navigation**: Ensure all interactive elements are accessible via keyboard with visible focus indicators
5 | - **Color Contrast**: Maintain sufficient contrast ratios (4.5:1 for normal text) and don't rely solely on color to convey information
6 | - **Alternative Text**: Provide descriptive alt text for images and meaningful labels for all form inputs
7 | - **Screen Reader Testing**: Test and verify that all views are accessible on screen reading devices.
8 | - **ARIA When Needed**: Use ARIA attributes to enhance complex components when semantic HTML isn't sufficient
9 | - **Logical Heading Structure**: Use heading levels (h1-h6) in proper order to create a clear document outline
10 | - **Focus Management**: Manage focus appropriately in dynamic content, modals, and single-page applications
11 |
--------------------------------------------------------------------------------
/packages/ui/src/styles/theme.css:
--------------------------------------------------------------------------------
1 | @theme {
2 | /* Colors - Core palette */
3 | --color-primary-light: #a78bfa;
4 | --color-primary: #8b5cf6;
5 | --color-primary-dark: #7c3aed;
6 | --color-primary-darker: #6d28d9;
7 |
8 | /* Colors - UI elements */
9 | --color-reset: #e6b800;
10 | --color-reset-hover: #cc9900;
11 | --color-reset-active: #b38600;
12 |
13 | --color-create: #22c55e;
14 | --color-create-hover: #16a34a;
15 | --color-create-active: #15803d;
16 |
17 | --color-close: #ef4444;
18 | --color-close-hover: #dc2626;
19 | --color-close-active: #b91c1c;
20 |
21 | --color-status-ready: #10b981;
22 | --color-status-error: #ef4444;
23 |
24 | /* Colors - Themes */
25 | --color-light-bg: #f3f0ff;
26 | --color-light-text: #1e293b;
27 | --color-light-runtime: #ede9fe;
28 |
29 | --color-dark-bg: #1e1b4b;
30 | --color-dark-text: #f1f5f9;
31 |
32 | /* Sizes and spacing */
33 | --button-height: 38px;
34 | --button-width: 140px;
35 | --container-width: 300px;
36 | }
37 |
--------------------------------------------------------------------------------
/apps/electron/minimal-zustand-reducers/src/features/index.ts:
--------------------------------------------------------------------------------
1 | import type { Reducer } from '@zubridge/electron';
2 |
3 | import { reducer as counterReducer } from './counter/index.js';
4 | import { reducer as themeReducer } from './theme/index.js';
5 |
6 | // Define the root state type for the reducers mode
7 | export interface State {
8 | counter: number;
9 | theme: 'light' | 'dark';
10 | [key: string]: unknown; // Index signature to satisfy AnyState requirement
11 | }
12 |
13 | /**
14 | * Initial state for reducers mode
15 | */
16 | export const initialState: State = {
17 | counter: 0,
18 | theme: 'dark',
19 | };
20 |
21 | /**
22 | * Root reducer that combines all feature reducers
23 | */
24 | export const rootReducer: Reducer = (state, action) => {
25 | // Apply individual feature reducers
26 | return {
27 | counter: counterReducer(state.counter, action),
28 | theme: themeReducer(state.theme, action),
29 | };
30 | };
31 |
32 | export type RootState = ReturnType;
33 |
--------------------------------------------------------------------------------
/apps/electron/e2e/src/modes/redux/features/theme/index.ts:
--------------------------------------------------------------------------------
1 | import { createSlice, type PayloadAction } from '@reduxjs/toolkit';
2 |
3 | // Define the theme type to match BaseState
4 | type Theme = 'light' | 'dark';
5 |
6 | // Initial state with explicit typing
7 | const initialState = 'dark' satisfies Theme;
8 |
9 | /**
10 | * Theme slice using Redux Toolkit
11 | */
12 | export const themeSlice = createSlice({
13 | name: 'theme',
14 | initialState,
15 | reducers: {
16 | toggleTheme: (state) => {
17 | console.log('[Redux Slice] Toggling theme');
18 | return state === 'dark' ? 'light' : 'dark';
19 | },
20 | setTheme: (_state, action: PayloadAction) => {
21 | const theme: Theme = action.payload ? 'dark' : 'light';
22 | console.log(`[Redux Slice] Setting theme to ${theme}`);
23 | return theme;
24 | },
25 | },
26 | });
27 |
28 | // Export actions and reducer
29 | export const { toggleTheme, setTheme } = themeSlice.actions;
30 | export const { reducer } = themeSlice;
31 |
--------------------------------------------------------------------------------
/apps/electron/e2e/src/renderer/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Zubridge Electron Example
7 |
8 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules
3 | dist
4 | out
5 | dist-*
6 | out-*
7 | *.log*
8 | *.tsbuildinfo
9 | .vscode
10 | .turbo
11 |
12 | # Keep project-specific VSCode settings
13 | !.vscode/settings.json
14 | !.vscode/extensions.json
15 | !.vscode/launch.json
16 | !.vscode/tasks.json
17 |
18 | # Ignore other VSCode files
19 | .vscode/*
20 |
21 | # Ignore VSCode local history
22 | .history/
23 | .ionide/
24 |
25 | # Ignore VSCode workspace files (usually personal)
26 | *.code-workspace
27 |
28 | # Ignore Aider files
29 | .aider*
30 |
31 | # Ignore Tauri files
32 | .tauri/
33 | target
34 | *.node
35 |
36 | # Rust / Cargo specific for libraries
37 | # Note: Cargo.lock is committed for middleware package to ensure deterministic NAPI-RS builds
38 | /packages/middleware/Cargo.lock
39 | !/packages/middleware/node/Cargo.lock
40 |
41 | # Ignore package tarballs
42 | *.tgz
43 |
44 | # Ignore Electron user data
45 | .electron-user-data*
46 |
47 | # Ignore napi-rs build artifacts
48 | **/node/index.js
49 | **/node/index.d.ts
50 | **/node/*.node
51 |
--------------------------------------------------------------------------------
/apps/electron/minimal-custom/test/wdio.conf.ts:
--------------------------------------------------------------------------------
1 | import path from 'node:path';
2 | import url from 'node:url';
3 |
4 | const __dirname = path.dirname(url.fileURLToPath(import.meta.url));
5 |
6 | const testSpecs = [path.join(__dirname, 'specs', '**/*.spec.ts')];
7 | const appEntryPoint = './out/main/index.js';
8 |
9 | const config: Record = {
10 | runner: 'local',
11 | specs: testSpecs,
12 | exclude: [],
13 | maxInstances: 1,
14 | capabilities: [
15 | {
16 | browserName: 'electron',
17 | 'wdio:electronServiceOptions': {
18 | appEntryPoint: appEntryPoint,
19 | },
20 | },
21 | ],
22 | logLevel: 'info',
23 | bail: 0,
24 | baseUrl: '',
25 | waitforTimeout: 10000,
26 | connectionRetryTimeout: 120000,
27 | connectionRetryCount: 3,
28 | services: ['electron'],
29 | framework: 'mocha',
30 | reporters: ['spec'],
31 | mochaOpts: {
32 | ui: 'bdd',
33 | timeout: 60000,
34 | },
35 | tsConfigPath: path.join(__dirname, 'tsconfig.json'),
36 | };
37 |
38 | export { config };
39 |
--------------------------------------------------------------------------------
/apps/electron/minimal-redux/src/renderer/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Zubridge Electron Example
7 |
8 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/apps/electron/minimal-redux/test/wdio.conf.ts:
--------------------------------------------------------------------------------
1 | import path from 'node:path';
2 | import url from 'node:url';
3 |
4 | const __dirname = path.dirname(url.fileURLToPath(import.meta.url));
5 |
6 | const testSpecs = [path.join(__dirname, 'specs', '**/*.spec.ts')];
7 | const appEntryPoint = './out/main/index.js';
8 |
9 | const config: Record = {
10 | runner: 'local',
11 | specs: testSpecs,
12 | exclude: [],
13 | maxInstances: 1,
14 | capabilities: [
15 | {
16 | browserName: 'electron',
17 | 'wdio:electronServiceOptions': {
18 | appEntryPoint: appEntryPoint,
19 | },
20 | },
21 | ],
22 | logLevel: 'info',
23 | bail: 0,
24 | baseUrl: '',
25 | waitforTimeout: 10000,
26 | connectionRetryTimeout: 120000,
27 | connectionRetryCount: 3,
28 | services: ['electron'],
29 | framework: 'mocha',
30 | reporters: ['spec'],
31 | mochaOpts: {
32 | ui: 'bdd',
33 | timeout: 60000,
34 | },
35 | tsConfigPath: path.join(__dirname, 'tsconfig.json'),
36 | };
37 |
38 | export { config };
39 |
--------------------------------------------------------------------------------
/apps/electron/minimal-custom/src/renderer/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Zubridge Electron Example
7 |
8 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/apps/electron/minimal-sandbox-true/test/wdio.conf.ts:
--------------------------------------------------------------------------------
1 | import path from 'node:path';
2 | import url from 'node:url';
3 |
4 | const __dirname = path.dirname(url.fileURLToPath(import.meta.url));
5 |
6 | const testSpecs = [path.join(__dirname, 'specs', '**/*.spec.ts')];
7 | const appEntryPoint = './out/main/index.js';
8 |
9 | const config: Record = {
10 | runner: 'local',
11 | specs: testSpecs,
12 | exclude: [],
13 | maxInstances: 1,
14 | capabilities: [
15 | {
16 | browserName: 'electron',
17 | 'wdio:electronServiceOptions': {
18 | appEntryPoint: appEntryPoint,
19 | },
20 | },
21 | ],
22 | logLevel: 'info',
23 | bail: 0,
24 | baseUrl: '',
25 | waitforTimeout: 10000,
26 | connectionRetryTimeout: 120000,
27 | connectionRetryCount: 3,
28 | services: ['electron'],
29 | framework: 'mocha',
30 | reporters: ['spec'],
31 | mochaOpts: {
32 | ui: 'bdd',
33 | timeout: 60000,
34 | },
35 | tsConfigPath: path.join(__dirname, 'tsconfig.json'),
36 | };
37 |
38 | export { config };
39 |
--------------------------------------------------------------------------------
/apps/electron/minimal-sandbox-true/src/renderer/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Zubridge Electron Example
7 |
8 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/apps/electron/minimal-zustand-basic/src/renderer/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Zubridge Electron Example
7 |
8 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/apps/electron/minimal-zustand-basic/test/wdio.conf.ts:
--------------------------------------------------------------------------------
1 | import path from 'node:path';
2 | import url from 'node:url';
3 |
4 | const __dirname = path.dirname(url.fileURLToPath(import.meta.url));
5 |
6 | const testSpecs = [path.join(__dirname, 'specs', '**/*.spec.ts')];
7 | const appEntryPoint = './out/main/index.js';
8 |
9 | const config: Record = {
10 | runner: 'local',
11 | specs: testSpecs,
12 | exclude: [],
13 | maxInstances: 1,
14 | capabilities: [
15 | {
16 | browserName: 'electron',
17 | 'wdio:electronServiceOptions': {
18 | appEntryPoint: appEntryPoint,
19 | },
20 | },
21 | ],
22 | logLevel: 'info',
23 | bail: 0,
24 | baseUrl: '',
25 | waitforTimeout: 10000,
26 | connectionRetryTimeout: 120000,
27 | connectionRetryCount: 3,
28 | services: ['electron'],
29 | framework: 'mocha',
30 | reporters: ['spec'],
31 | mochaOpts: {
32 | ui: 'bdd',
33 | timeout: 60000,
34 | },
35 | tsConfigPath: path.join(__dirname, 'tsconfig.json'),
36 | };
37 |
38 | export { config };
39 |
--------------------------------------------------------------------------------
/apps/electron/minimal-zustand-handlers/test/wdio.conf.ts:
--------------------------------------------------------------------------------
1 | import path from 'node:path';
2 | import url from 'node:url';
3 |
4 | const __dirname = path.dirname(url.fileURLToPath(import.meta.url));
5 |
6 | const testSpecs = [path.join(__dirname, 'specs', '**/*.spec.ts')];
7 | const appEntryPoint = './out/main/index.js';
8 |
9 | const config: Record = {
10 | runner: 'local',
11 | specs: testSpecs,
12 | exclude: [],
13 | maxInstances: 1,
14 | capabilities: [
15 | {
16 | browserName: 'electron',
17 | 'wdio:electronServiceOptions': {
18 | appEntryPoint: appEntryPoint,
19 | },
20 | },
21 | ],
22 | logLevel: 'info',
23 | bail: 0,
24 | baseUrl: '',
25 | waitforTimeout: 10000,
26 | connectionRetryTimeout: 120000,
27 | connectionRetryCount: 3,
28 | services: ['electron'],
29 | framework: 'mocha',
30 | reporters: ['spec'],
31 | mochaOpts: {
32 | ui: 'bdd',
33 | timeout: 60000,
34 | },
35 | tsConfigPath: path.join(__dirname, 'tsconfig.json'),
36 | };
37 |
38 | export { config };
39 |
--------------------------------------------------------------------------------
/apps/electron/minimal-zustand-immer/src/renderer/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Zubridge Electron Example
7 |
8 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/apps/electron/minimal-zustand-immer/test/wdio.conf.ts:
--------------------------------------------------------------------------------
1 | import path from 'node:path';
2 | import url from 'node:url';
3 |
4 | const __dirname = path.dirname(url.fileURLToPath(import.meta.url));
5 |
6 | const testSpecs = [path.join(__dirname, 'specs', '**/*.spec.ts')];
7 | const appEntryPoint = './out/main/index.js';
8 |
9 | const config: Record = {
10 | runner: 'local',
11 | specs: testSpecs,
12 | exclude: [],
13 | maxInstances: 1,
14 | capabilities: [
15 | {
16 | browserName: 'electron',
17 | 'wdio:electronServiceOptions': {
18 | appEntryPoint: appEntryPoint,
19 | },
20 | },
21 | ],
22 | logLevel: 'info',
23 | bail: 0,
24 | baseUrl: '',
25 | waitforTimeout: 10000,
26 | connectionRetryTimeout: 120000,
27 | connectionRetryCount: 3,
28 | services: ['electron'],
29 | framework: 'mocha',
30 | reporters: ['spec'],
31 | mochaOpts: {
32 | ui: 'bdd',
33 | timeout: 60000,
34 | },
35 | tsConfigPath: path.join(__dirname, 'tsconfig.json'),
36 | };
37 |
38 | export { config };
39 |
--------------------------------------------------------------------------------
/apps/electron/minimal-zustand-reducers/test/wdio.conf.ts:
--------------------------------------------------------------------------------
1 | import path from 'node:path';
2 | import url from 'node:url';
3 |
4 | const __dirname = path.dirname(url.fileURLToPath(import.meta.url));
5 |
6 | const testSpecs = [path.join(__dirname, 'specs', '**/*.spec.ts')];
7 | const appEntryPoint = './out/main/index.js';
8 |
9 | const config: Record = {
10 | runner: 'local',
11 | specs: testSpecs,
12 | exclude: [],
13 | maxInstances: 1,
14 | capabilities: [
15 | {
16 | browserName: 'electron',
17 | 'wdio:electronServiceOptions': {
18 | appEntryPoint: appEntryPoint,
19 | },
20 | },
21 | ],
22 | logLevel: 'info',
23 | bail: 0,
24 | baseUrl: '',
25 | waitforTimeout: 10000,
26 | connectionRetryTimeout: 120000,
27 | connectionRetryCount: 3,
28 | services: ['electron'],
29 | framework: 'mocha',
30 | reporters: ['spec'],
31 | mochaOpts: {
32 | ui: 'bdd',
33 | timeout: 60000,
34 | },
35 | tsConfigPath: path.join(__dirname, 'tsconfig.json'),
36 | };
37 |
38 | export { config };
39 |
--------------------------------------------------------------------------------
/packages/tauri-plugin/src/mobile.rs:
--------------------------------------------------------------------------------
1 | use serde::de::DeserializeOwned;
2 | use tauri::{
3 | plugin::{PluginApi, PluginHandle},
4 | AppHandle, Runtime,
5 | };
6 |
7 | use crate::models::*;
8 |
9 | #[cfg(target_os = "ios")]
10 | tauri::ios_plugin_binding!(init_plugin_zubridge);
11 |
12 | // initializes the Kotlin or Swift plugin classes
13 | pub fn init(
14 | _app: &AppHandle,
15 | api: PluginApi,
16 | ) -> crate::Result> {
17 | #[cfg(target_os = "android")]
18 | let handle = api.register_android_plugin("", "ExamplePlugin")?;
19 | #[cfg(target_os = "ios")]
20 | let handle = api.register_ios_plugin(init_plugin_zubridge)?;
21 | Ok(Zubridge(handle))
22 | }
23 |
24 | /// Access to the zubridge APIs.
25 | pub struct Zubridge(PluginHandle);
26 |
27 | impl Zubridge {
28 | pub fn ping(&self, payload: PingRequest) -> crate::Result {
29 | self
30 | .0
31 | .run_mobile_plugin("ping", payload)
32 | .map_err(Into::into)
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/agent-os/standards/frontend/components.md:
--------------------------------------------------------------------------------
1 | ## UI component best practices
2 |
3 | - **Single Responsibility**: Each component should have one clear purpose and do it well
4 | - **Reusability**: Design components to be reused across different contexts with configurable props
5 | - **Composability**: Build complex UIs by combining smaller, simpler components rather than monolithic structures
6 | - **Clear Interface**: Define explicit, well-documented props with sensible defaults for ease of use
7 | - **Encapsulation**: Keep internal implementation details private and expose only necessary APIs
8 | - **Consistent Naming**: Use clear, descriptive names that indicate the component's purpose and follow team conventions
9 | - **State Management**: Keep state as local as possible; lift it up only when needed by multiple components
10 | - **Minimal Props**: Keep the number of props manageable; if a component needs many props, consider composition or splitting it
11 | - **Documentation**: Document component usage, props, and provide examples for easier adoption by team members
12 |
--------------------------------------------------------------------------------
/apps/electron/minimal-zustand-handlers/src/renderer/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Zubridge Electron Example
7 |
8 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/apps/electron/minimal-zustand-reducers/src/renderer/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Zubridge Electron Example
7 |
8 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/apps/electron/minimal-context-isolation-false/src/renderer/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Zubridge Electron Example
7 |
8 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/apps/electron/minimal-redux/src/main/bridge.ts:
--------------------------------------------------------------------------------
1 | import {
2 | createReduxBridge,
3 | type ZubridgeMiddleware,
4 | type ZustandBridge,
5 | } from '@zubridge/electron/main';
6 | import type { Store } from 'redux';
7 | import { actions } from './store.js';
8 |
9 | /**
10 | * Creates a bridge using a Redux store
11 | * In this approach, we use Redux with Redux Toolkit to manage state
12 | */
13 | export function createBridge(store: Store, middleware?: ZubridgeMiddleware): ZustandBridge {
14 | console.log('[Redux Mode] Creating bridge with Redux store');
15 |
16 | // Create bridge with Redux store and action mapping
17 | const bridge = createReduxBridge(store, {
18 | middleware,
19 | handlers: {
20 | // Map string actions to Redux action creators
21 | 'COUNTER:INCREMENT': () => store.dispatch(actions['COUNTER:INCREMENT']()),
22 | 'COUNTER:DECREMENT': () => store.dispatch(actions['COUNTER:DECREMENT']()),
23 | 'THEME:TOGGLE': () => store.dispatch(actions['THEME:TOGGLE']()),
24 | },
25 | });
26 |
27 | return bridge;
28 | }
29 |
--------------------------------------------------------------------------------
/apps/tauri/e2e/src/renderer/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Zubridge Tauri Example
7 |
8 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/apps/electron/minimal-sandbox-true/src/main/store.ts:
--------------------------------------------------------------------------------
1 | import { create, type StoreApi } from 'zustand';
2 | import { createCounterHandlers } from '../features/counter/index.js';
3 | import type { State } from '../features/index.js';
4 | import { initialState } from '../features/index.js';
5 | import { createThemeHandlers } from '../features/theme/index.js';
6 |
7 | /**
8 | * Creates a Zustand store for the basic mode
9 | * In basic mode, action handlers are attached directly to the store state
10 | */
11 | export function createStore(): StoreApi {
12 | console.log('[Basic Mode] Creating Zustand store');
13 |
14 | const store = create()(() => initialState);
15 |
16 | // Create action handlers using the features pattern
17 | const counterHandlers = createCounterHandlers(store);
18 | const themeHandlers = createThemeHandlers(store);
19 |
20 | // Attach action handlers to the store (basic mode pattern)
21 | store.setState((state) => ({
22 | ...state,
23 | ...counterHandlers,
24 | ...themeHandlers,
25 | }));
26 |
27 | return store;
28 | }
29 |
--------------------------------------------------------------------------------
/apps/electron/minimal-zustand-basic/src/main/store.ts:
--------------------------------------------------------------------------------
1 | import { create, type StoreApi } from 'zustand';
2 | import { createCounterHandlers } from '../features/counter/index.js';
3 | import type { State } from '../features/index.js';
4 | import { initialState } from '../features/index.js';
5 | import { createThemeHandlers } from '../features/theme/index.js';
6 |
7 | /**
8 | * Creates a Zustand store for the basic mode
9 | * In basic mode, action handlers are attached directly to the store state
10 | */
11 | export function createStore(): StoreApi {
12 | console.log('[Basic Mode] Creating Zustand store');
13 |
14 | const store = create()(() => initialState);
15 |
16 | // Create action handlers using the features pattern
17 | const counterHandlers = createCounterHandlers(store);
18 | const themeHandlers = createThemeHandlers(store);
19 |
20 | // Attach action handlers to the store (basic mode pattern)
21 | store.setState((state) => ({
22 | ...state,
23 | ...counterHandlers,
24 | ...themeHandlers,
25 | }));
26 |
27 | return store;
28 | }
29 |
--------------------------------------------------------------------------------
/agent-os/standards/backend/api.md:
--------------------------------------------------------------------------------
1 | ## API endpoint standards and conventions
2 |
3 | - **RESTful Design**: Follow REST principles with clear resource-based URLs and appropriate HTTP methods (GET, POST, PUT, PATCH, DELETE)
4 | - **Consistent Naming**: Use consistent, lowercase, hyphenated or underscored naming conventions for endpoints across the API
5 | - **Versioning**: Implement API versioning strategy (URL path or headers) to manage breaking changes without disrupting existing clients
6 | - **Plural Nouns**: Use plural nouns for resource endpoints (e.g., `/users`, `/products`) for consistency
7 | - **Nested Resources**: Limit nesting depth to 2-3 levels maximum to keep URLs readable and maintainable
8 | - **Query Parameters**: Use query parameters for filtering, sorting, pagination, and search rather than creating separate endpoints
9 | - **HTTP Status Codes**: Return appropriate, consistent HTTP status codes that accurately reflect the response (200, 201, 400, 404, 500, etc.)
10 | - **Rate Limiting Headers**: Include rate limit information in response headers to help clients manage their usage
11 |
--------------------------------------------------------------------------------
/apps/electron/minimal-context-isolation-false/src/main/store.ts:
--------------------------------------------------------------------------------
1 | import { create, type StoreApi } from 'zustand';
2 | import { createCounterHandlers } from '../features/counter/index.js';
3 | import type { State } from '../features/index.js';
4 | import { initialState } from '../features/index.js';
5 | import { createThemeHandlers } from '../features/theme/index.js';
6 |
7 | /**
8 | * Creates a Zustand store for the basic mode
9 | * In basic mode, action handlers are attached directly to the store state
10 | */
11 | export function createStore(): StoreApi {
12 | console.log('[Basic Mode] Creating Zustand store');
13 |
14 | const store = create()(() => initialState);
15 |
16 | // Create action handlers using the features pattern
17 | const counterHandlers = createCounterHandlers(store);
18 | const themeHandlers = createThemeHandlers(store);
19 |
20 | // Attach action handlers to the store (basic mode pattern)
21 | store.setState((state) => ({
22 | ...state,
23 | ...counterHandlers,
24 | ...themeHandlers,
25 | }));
26 |
27 | return store;
28 | }
29 |
--------------------------------------------------------------------------------
/apps/tauri/e2e/src/types/state.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Common base state interface that all mode-specific states share.
3 | * This defines the minimal structure expected across all modes.
4 | */
5 | export interface BaseState {
6 | counter: number;
7 | window: {
8 | isOpen: boolean;
9 | };
10 | [key: string]: unknown; // Add index signature to satisfy AnyState constraint
11 | }
12 |
13 | /**
14 | * Type guard to check if a state object conforms to the BaseState interface
15 | */
16 | export function isBaseState(state: unknown): state is BaseState {
17 | if (!state || typeof state !== 'object') return false;
18 |
19 | const s = state as Record;
20 | const window = s.window;
21 | return (
22 | typeof s.counter === 'number' &&
23 | Boolean(window) &&
24 | typeof window === 'object' &&
25 | window !== null &&
26 | typeof (window as Record).isOpen === 'boolean'
27 | );
28 | }
29 |
30 | /**
31 | * Shared State type that all modes can use
32 | * This is the common state structure across all modes
33 | */
34 | export type State = BaseState;
35 |
--------------------------------------------------------------------------------
/apps/tauri-v1/e2e/src/types/state.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Common base state interface that all mode-specific states share.
3 | * This defines the minimal structure expected across all modes.
4 | */
5 | export interface BaseState {
6 | counter: number;
7 | window: {
8 | isOpen: boolean;
9 | };
10 | [key: string]: unknown; // Add index signature to satisfy AnyState constraint
11 | }
12 |
13 | /**
14 | * Type guard to check if a state object conforms to the BaseState interface
15 | */
16 | export function isBaseState(state: unknown): state is BaseState {
17 | if (!state || typeof state !== 'object') return false;
18 |
19 | const s = state as Record;
20 | const window = s.window;
21 | return (
22 | typeof s.counter === 'number' &&
23 | Boolean(window) &&
24 | typeof window === 'object' &&
25 | window !== null &&
26 | typeof (window as Record).isOpen === 'boolean'
27 | );
28 | }
29 |
30 | /**
31 | * Shared State type that all modes can use
32 | * This is the common state structure across all modes
33 | */
34 | export type State = BaseState;
35 |
--------------------------------------------------------------------------------
/packages/electron/test/setup.ts:
--------------------------------------------------------------------------------
1 | import type {} from '@zubridge/types/internal'; // Import internal window augmentations
2 | import { vi } from 'vitest';
3 |
4 | // Set up mocks for the window object
5 | const mockZubridge = {
6 | dispatch: vi.fn(),
7 | getState: vi.fn(),
8 | subscribe: vi.fn(),
9 | };
10 |
11 | // Add properties to global object in a type-safe way
12 | Object.defineProperty(global, 'window', {
13 | value: {
14 | zubridge: mockZubridge,
15 | __zubridge_windowId: undefined,
16 | dispatchEvent: vi.fn(),
17 | addEventListener: vi.fn(),
18 | removeEventListener: vi.fn(),
19 | },
20 | writable: true,
21 | });
22 |
23 | // Mock Electron IPC modules
24 | vi.mock('electron', () => ({
25 | ipcRenderer: {
26 | send: vi.fn(),
27 | invoke: vi.fn(),
28 | on: vi.fn(),
29 | removeListener: vi.fn(),
30 | },
31 | ipcMain: {
32 | on: vi.fn(),
33 | handle: vi.fn(),
34 | emit: vi.fn(),
35 | removeHandler: vi.fn(),
36 | removeAllListeners: vi.fn(),
37 | },
38 | contextBridge: {
39 | exposeInMainWorld: vi.fn(),
40 | },
41 | }));
42 |
--------------------------------------------------------------------------------
/apps/electron/e2e/src/modes/custom/features/state/index.ts:
--------------------------------------------------------------------------------
1 | import { generateTestState, initialState } from '@zubridge/apps-shared';
2 | import type { AnyState } from '@zubridge/types';
3 | import type { StoreApi } from 'zustand';
4 |
5 | let store: StoreApi;
6 |
7 | export const init = (s: StoreApi) => {
8 | store = s;
9 | };
10 |
11 | export const reset = () => {
12 | console.log('[Custom] Resetting state to defaults');
13 | store.setState(() => initialState as unknown as AnyState);
14 | };
15 |
16 | export const generateLargeState = async (options?: {
17 | variant?: 'small' | 'medium' | 'large' | 'xl';
18 | }) => {
19 | const variant = options?.variant || 'medium';
20 | console.log(`[Custom] Generating ${variant} test state`);
21 |
22 | // Use the shared generateTestState function
23 | const filler = generateTestState(variant);
24 |
25 | store.setState((state) => ({
26 | ...state,
27 | filler,
28 | }));
29 |
30 | console.log(
31 | `[Custom] ${variant} test state generated (${(filler.meta as { estimatedSize: string }).estimatedSize})`,
32 | );
33 | };
34 |
--------------------------------------------------------------------------------
/apps/electron/minimal-redux/src/main/store.ts:
--------------------------------------------------------------------------------
1 | import { configureStore } from '@reduxjs/toolkit';
2 | import { counterSlice } from '../features/counter/index.js';
3 | import { themeSlice } from '../features/theme/index.js';
4 |
5 | // Root reducer
6 | const rootReducer = {
7 | counter: counterSlice.reducer,
8 | theme: themeSlice.reducer,
9 | };
10 |
11 | // Create the Redux store
12 | export function createStore() {
13 | console.log('[Redux Store] Creating Redux store with Redux Toolkit');
14 | const store = configureStore({
15 | reducer: rootReducer,
16 | middleware: (getDefaultMiddleware) =>
17 | getDefaultMiddleware({
18 | serializableCheck: false, // For better interop with Electron
19 | }),
20 | });
21 |
22 | return store;
23 | }
24 |
25 | // Export action creators
26 | export const actions = {
27 | 'COUNTER:INCREMENT': counterSlice.actions.increment,
28 | 'COUNTER:DECREMENT': counterSlice.actions.decrement,
29 | 'THEME:TOGGLE': themeSlice.actions.toggleTheme,
30 | };
31 |
32 | // Export types
33 | export type RootState = ReturnType['getState'];
34 |
--------------------------------------------------------------------------------
/packages/electron/src/types/errors.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Error thrown when the action queue exceeds its maximum size
3 | *
4 | * This error indicates that the application is generating actions faster than
5 | * they can be processed, which could lead to memory issues or performance degradation.
6 | *
7 | * @example
8 | * ```typescript
9 | * import { QueueOverflowError } from '@zubridge/electron';
10 | *
11 | * try {
12 | * await dispatch(someAction);
13 | * } catch (error) {
14 | * if (error instanceof QueueOverflowError) {
15 | * debug('queue:error', 'Action queue is full:', error.message);
16 | * // Handle overflow - maybe wait and retry, or warn user
17 | * }
18 | * }
19 | * ```
20 | */
21 | export class QueueOverflowError extends Error {
22 | public readonly queueSize: number;
23 | public readonly maxSize: number;
24 |
25 | constructor(queueSize: number, maxSize: number) {
26 | super(`Action queue overflow: ${queueSize} actions pending, maximum allowed is ${maxSize}`);
27 | this.name = 'QueueOverflowError';
28 | this.queueSize = queueSize;
29 | this.maxSize = maxSize;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 WebdriverIO Community
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/agent-os/standards/frontend/responsive.md:
--------------------------------------------------------------------------------
1 | ## Responsive design best practices
2 |
3 | - **Mobile-First Development**: Start with mobile layout and progressively enhance for larger screens
4 | - **Standard Breakpoints**: Consistently use standard breakpoints across the application (e.g., mobile, tablet, desktop)
5 | - **Fluid Layouts**: Use percentage-based widths and flexible containers that adapt to screen size
6 | - **Relative Units**: Prefer rem/em units over fixed pixels for better scalability and accessibility
7 | - **Test Across Devices**: Test and verify UI changes across multiple screen sizes from mobile to tablet to desktop screen sizes and ensure a balanced, user-friendly viewing and reading experience on all
8 | - **Touch-Friendly Design**: Ensure tap targets are appropriately sized (minimum 44x44px) for mobile users
9 | - **Performance on Mobile**: Optimize images and assets for mobile network conditions and smaller screens
10 | - **Readable Typography**: Maintain readable font sizes across all breakpoints without requiring zoom
11 | - **Content Priority**: Show the most important content first on smaller screens through thoughtful layout decisions
12 |
--------------------------------------------------------------------------------
/.cursor/rules/coding-patterns.mdc:
--------------------------------------------------------------------------------
1 | ---
2 | description:
3 | globs:
4 | alwaysApply: true
5 | ---
6 | # Zubridge Coding Patterns
7 |
8 | ## Rules
9 | - Develop functions with a test-driven development mindset, ensuring each low-level function or method intended for reuse performs a single, atomic task, but avoid adding unnecessary abstration layers.
10 |
11 | ## Promise Handling
12 | - Always await Promises; use `void` prefix if intentionally not awaiting
13 | - Avoid floating promises to prevent unhandled rejections
14 |
15 | ## React Components
16 | - Avoid default React import; use named imports only
17 | - Prefer functional components over class components
18 | - Follow React hooks rules (dependencies array, call only at top level)
19 |
20 | ## Restricted Patterns
21 | - Avoid "barrel" files at the root of package subdirectories (like `packages/ui/src/components/Logger/index.ts`). Barrel files at the package root level (like `packages/ui/src/index.ts`) are acceptable.
22 | - Prefer named export over export default
23 |
24 | ## Error Handling
25 | - Use try/catch blocks for async operations that might fail
26 | - Provide appropriate error messages and fallbacks
27 |
28 |
--------------------------------------------------------------------------------
/agent-os/standards/testing/test-writing.md:
--------------------------------------------------------------------------------
1 | ## Test coverage best practices
2 |
3 | - **Write Minimal Tests During Development**: Do NOT write tests for every change or intermediate step. Focus on completing the feature implementation first, then add strategic tests only at logical completion points
4 | - **Test Only Core User Flows**: Write tests exclusively for critical paths and primary user workflows. Skip writing tests for non-critical utilities and secondary workflows until if/when you're instructed to do so.
5 | - **Defer Edge Case Testing**: Do NOT test edge cases, error states, or validation logic unless they are business-critical. These can be addressed in dedicated testing phases, not during feature development.
6 | - **Test Behavior, Not Implementation**: Focus tests on what the code does, not how it does it, to reduce brittleness
7 | - **Clear Test Names**: Use descriptive names that explain what's being tested and the expected outcome
8 | - **Mock External Dependencies**: Isolate units by mocking databases, APIs, file systems, and other external services
9 | - **Fast Execution**: Keep unit tests fast (milliseconds) so developers run them frequently during development
10 |
--------------------------------------------------------------------------------
/apps/electron/e2e/src/modes/zustand-basic/features/theme/index.ts:
--------------------------------------------------------------------------------
1 | import type { StoreApi } from 'zustand';
2 | import type { BaseState } from '../../../../types.js';
3 |
4 | /**
5 | * Attaches the theme handlers to the state object
6 | * In the basic mode, handlers are part of the state object itself
7 | */
8 | export const attachThemeHandlers = (store: StoreApi) => {
9 | const { setState } = store;
10 |
11 | // Set up theme initial state
12 | setState((state) => ({
13 | ...state,
14 | theme: 'dark', // Initialize to dark mode using string union
15 |
16 | // Implement the toggle theme handler
17 | 'THEME:TOGGLE': () => {
18 | console.log('[Basic] Toggling theme');
19 | setState((state) => ({
20 | ...state,
21 | theme: state.theme === 'dark' ? 'light' : 'dark',
22 | }));
23 | },
24 |
25 | // Implement the set theme handler
26 | 'THEME:SET': (isDark: boolean) => {
27 | console.log(`[Basic] Setting theme to ${isDark ? 'dark' : 'light'}`);
28 | setState((state) => ({
29 | ...state,
30 | theme: isDark ? 'dark' : 'light',
31 | }));
32 | },
33 | }));
34 | };
35 |
--------------------------------------------------------------------------------
/packages/apps-shared/src/types.ts:
--------------------------------------------------------------------------------
1 | import type { AnyState, Thunk } from '@zubridge/types';
2 |
3 | /**
4 | * Base state interface shared across example apps
5 | * All properties are optional to ensure compatibility with dispatch functions
6 | */
7 | export interface BaseState extends AnyState {
8 | counter: number;
9 | theme: 'light' | 'dark';
10 | filler?: Record;
11 | }
12 |
13 | /**
14 | * Thunk context information to personalize log messages
15 | */
16 | export interface ThunkContext {
17 | /** Where the thunk is executing (main process, renderer, tauri) */
18 | environment: 'main' | 'renderer' | 'tauri';
19 | /** Custom prefix for log messages */
20 | logPrefix?: string;
21 | }
22 |
23 | /**
24 | * Counter operation methods
25 | */
26 | export type CounterMethod = 'action' | 'thunk' | 'main-thunk' | 'slow-thunk' | 'slow-main-thunk';
27 |
28 | /**
29 | * Thunk creator function type that includes context
30 | * Uses a generic state type S that must extend BaseState (which now has optional properties)
31 | */
32 | export type ThunkCreator = (
33 | counter: number,
34 | context: ThunkContext,
35 | ) => Thunk;
36 |
--------------------------------------------------------------------------------
/packages/ui/src/components/WindowDisplay/index.tsx:
--------------------------------------------------------------------------------
1 | import clsx from 'clsx';
2 | import type React from 'react';
3 |
4 | interface WindowDisplayProps {
5 | windowId: number | string;
6 | windowTitle: string;
7 | mode?: string;
8 | bridgeStatus?: 'ready' | 'error' | 'initializing';
9 | isMainWindow?: boolean;
10 | isRuntimeWindow?: boolean;
11 | className?: string;
12 | children?: React.ReactNode;
13 | }
14 |
15 | /**
16 | * WindowDisplay component that shows information about the current window
17 | */
18 | export const WindowDisplay: React.FC = ({
19 | windowId: _windowId,
20 | windowTitle: _windowTitle,
21 | mode: _mode,
22 | bridgeStatus: _bridgeStatus = 'ready',
23 | isMainWindow = false,
24 | isRuntimeWindow = false,
25 | className = '',
26 | children,
27 | }) => {
28 | const displayClasses = clsx(
29 | 'window-display',
30 | isMainWindow && 'main-window',
31 | isRuntimeWindow && 'runtime-window',
32 | className,
33 | );
34 |
35 | return (
36 |
39 | );
40 | };
41 |
42 | export default WindowDisplay;
43 |
--------------------------------------------------------------------------------
/apps/electron/e2e/src/modes/zustand-basic/tray.ts:
--------------------------------------------------------------------------------
1 | import { createDispatch } from '@zubridge/electron/main';
2 | import type { BrowserWindow } from 'electron';
3 | import type { StoreApi } from 'zustand';
4 | import { BaseSystemTray } from '../../main/tray/base.js';
5 | import type { State } from '../../types.js';
6 |
7 | /**
8 | * Basic mode tray implementation
9 | * In basic mode, we use createDispatch directly with the store, which
10 | * automatically creates the appropriate adapter internally
11 | */
12 | export class BasicSystemTray extends BaseSystemTray {
13 | private unsubscribe?: () => void;
14 |
15 | public init(store: StoreApi, window: BrowserWindow) {
16 | this.window = window;
17 |
18 | // Unsubscribe from previous subscription if it exists
19 | if (this.unsubscribe) {
20 | this.unsubscribe();
21 | }
22 |
23 | // Create dispatch directly from the store
24 | this.dispatch = createDispatch(store);
25 |
26 | // Initialize immediately with current state
27 | this.update(store.getState());
28 |
29 | // Subscribe to state changes to update the tray UI
30 | this.unsubscribe = store.subscribe((state) => this.update(state));
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/packages/tauri-plugin/src/models.rs:
--------------------------------------------------------------------------------
1 | use serde::{Deserialize};
2 | use std::fmt::Debug;
3 |
4 | pub use serde_json::Value as JsonValue;
5 |
6 | /// An action to be dispatched to the state manager.
7 | #[derive(Deserialize, Debug)]
8 | pub struct ZubridgeAction {
9 | /// A string label for the action
10 | pub action_type: String,
11 | /// An optional payload for the action
12 | pub payload: Option,
13 | }
14 |
15 | /// Options for the Zubridge plugin.
16 | #[derive(Clone)]
17 | pub struct ZubridgeOptions {
18 | /// The event name to use for state updates. Defaults to "zubridge://state-update".
19 | pub event_name: String,
20 | }
21 |
22 | impl Default for ZubridgeOptions {
23 | fn default() -> Self {
24 | Self {
25 | event_name: "zubridge://state-update".to_string(),
26 | }
27 | }
28 | }
29 |
30 | /// A trait that manages state for the app.
31 | pub trait StateManager: Send + Sync + 'static {
32 | /// Get the initial state of the app.
33 | fn get_initial_state(&self) -> JsonValue;
34 |
35 | /// Apply an action to the state and return the new state.
36 | fn dispatch_action(&mut self, action: JsonValue) -> JsonValue;
37 | }
38 |
--------------------------------------------------------------------------------
/packages/electron/tsdown.win32.preload.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'tsdown';
2 | import { defineEnv } from 'unenv';
3 | import { createUnenvExternalPlugin, externalizeUnenvRuntime } from './scripts/build-utils.js';
4 |
5 | const { env } = defineEnv({
6 | nodeCompat: true,
7 | npmShims: true,
8 | resolve: false,
9 | overrides: {},
10 | presets: [],
11 | });
12 |
13 | const { alias } = env;
14 | // Windows-specific config for preload (sandboxed context, needs polyfills)
15 | export default defineConfig({
16 | entry: ['src/preload.ts'],
17 | format: ['esm', 'cjs'],
18 | dts: true,
19 | external: (id) => {
20 | if (externalizeUnenvRuntime(id)) return true;
21 | return ['electron', 'zustand', 'zustand/vanilla', 'weald', '@wdio/logger'].includes(id);
22 | },
23 | noExternal: ['@zubridge/core'],
24 | outDir: 'dist',
25 | clean: false,
26 | sourcemap: false,
27 | treeshake: true,
28 | platform: 'node',
29 | target: 'node18',
30 | alias,
31 | plugins: [createUnenvExternalPlugin()],
32 | outExtensions({ format }) {
33 | return {
34 | js: format === 'cjs' ? '.cjs' : '.js',
35 | dts: format === 'cjs' ? '.d.cts' : '.d.ts',
36 | };
37 | },
38 | });
39 |
--------------------------------------------------------------------------------
/packages/electron/src/utils/environment.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Determines if the application is running in development mode
3 | *
4 | * Uses a combination of checks to ensure consistent behavior:
5 | * 1. Checks if app is packaged (production builds are packaged)
6 | * 2. Checks NODE_ENV environment variable
7 | * 3. Checks ELECTRON_IS_DEV environment variable (set by electron-is-dev or similar utilities)
8 | *
9 | * @returns {boolean} True if running in development mode, false otherwise
10 | */
11 | export const isDev = async (): Promise => {
12 | // Ensure we have access to the app object (should be in the main process)
13 | if (process.type !== 'browser') {
14 | // Not in main process, use environment variables only
15 | if (process.env.NODE_ENV === 'production' || process.env.ELECTRON_IS_DEV === '0') {
16 | return false;
17 | }
18 | return (
19 | process.env.NODE_ENV === 'development' ||
20 | process.env.ELECTRON_IS_DEV === '1' ||
21 | !process.env.VITE_DEV_SERVER_URL
22 | );
23 | }
24 | const { app } = await import('electron');
25 |
26 | return (
27 | !app.isPackaged || process.env.NODE_ENV === 'development' || process.env.ELECTRON_IS_DEV === '1'
28 | );
29 | };
30 |
--------------------------------------------------------------------------------
/packages/middleware/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "zubridge-middleware"
3 | version = "0.1.0"
4 | edition = "2021"
5 | authors = ["Zubridge Contributors"]
6 | description = "Middleware framework for Zubridge state management"
7 | license = "MIT"
8 | repository = "https://github.com/zubridge/zubridge"
9 | readme = "README.md"
10 | keywords = ["zubridge", "middleware", "state-management", "tauri", "electron"]
11 | categories = ["development-tools", "web-programming"]
12 |
13 | [dependencies]
14 | # Async runtime
15 | tokio = { version = "1.32", features = ["full"] }
16 |
17 | # Serialization
18 | serde = { version = "1.0", features = ["derive"] }
19 | serde_json = "1.0"
20 | rmp-serde = "1.1" # MessagePack serialization
21 |
22 | # WebSocket server
23 | tokio-tungstenite = "0.20"
24 | futures-util = "0.3"
25 |
26 | # Utilities
27 | log = "0.4"
28 | fern = "0.6"
29 | thiserror = "1.0"
30 | async-trait = "0.1"
31 | uuid = { version = "1.4", features = ["v4"] }
32 | chrono = { version = "0.4", features = ["serde"] }
33 |
34 | # Optional Tauri v2 integration
35 | tauri = { version = "2.0.0-beta", optional = true }
36 |
37 | [features]
38 | default = []
39 | tauri = ["dep:tauri"]
40 |
41 | [dev-dependencies]
42 | tokio-test = "0.4"
43 |
--------------------------------------------------------------------------------
/apps/electron/minimal-zustand-immer/src/main/store.ts:
--------------------------------------------------------------------------------
1 | import { create, type StoreApi } from 'zustand';
2 | import { immer } from 'zustand/middleware/immer';
3 | import { createCounterHandlers } from '../features/counter/index.js';
4 | import type { State } from '../features/index.js';
5 | import { initialState } from '../features/index.js';
6 | import { createThemeHandlers } from '../features/theme/index.js';
7 |
8 | /**
9 | * Creates a Zustand store using Immer middleware
10 | * The immer middleware wraps the store, allowing mutable-style updates with setState
11 | */
12 | export function createStore(): StoreApi {
13 | console.log('[Immer Mode] Creating Zustand store with Immer middleware');
14 |
15 | // Wrap the store creator with immer middleware
16 | const store = create()(immer(() => initialState));
17 |
18 | // Create action handlers - they will use the immer-wrapped setState
19 | const counterHandlers = createCounterHandlers(store);
20 | const themeHandlers = createThemeHandlers(store);
21 |
22 | // Attach action handlers to the store (basic mode pattern)
23 | store.setState((state) => ({
24 | ...state,
25 | ...counterHandlers,
26 | ...themeHandlers,
27 | }));
28 |
29 | return store;
30 | }
31 |
--------------------------------------------------------------------------------
/apps/tauri/e2e/src-tauri/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "app"
3 | version = "0.1.0"
4 | description = "A Tauri App"
5 | authors = ["Zubridge team"]
6 | license = ""
7 | repository = ""
8 | default-run = "app"
9 | edition = "2021"
10 | rust-version = "1.77.2"
11 |
12 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
13 |
14 | [lib]
15 | name = "app_lib"
16 | path = "src/lib.rs"
17 | crate-type = ["staticlib", "cdylib", "rlib"]
18 |
19 | [build-dependencies]
20 | tauri-build = { version = "2.1.1", features = [] }
21 |
22 | [features]
23 | # this feature is used for production builds or when `devPath` points to the filesystem and the built-in dev server is disabled.
24 | # If you use cargo directly instead of tauri's cli you can use this feature flag to switch between tauri's `dev` and `build` modes.
25 | # DO NOT REMOVE!!
26 | custom-protocol = ["tauri/custom-protocol"]
27 |
28 | [dependencies]
29 | serde_json = "1"
30 | serde = { version = "1", features = ["derive"] }
31 | log = "0.4"
32 | tauri = { version = "2.4.1", features = ["tray-icon"] }
33 | tauri-plugin-log = "2.0.0-rc"
34 | tokio = { version = "1", features = ["time"] }
35 | tauri-plugin-zubridge = { path = "../../../packages/tauri-plugin" }
36 |
--------------------------------------------------------------------------------
/apps/electron/e2e/src/modes/zustand-reducers/features/theme/index.ts:
--------------------------------------------------------------------------------
1 | import { debug } from '@zubridge/core';
2 | import type { Action, Reducer } from '@zubridge/electron';
3 |
4 | /**
5 | * Reducer for theme state
6 | * In the reducers pattern, the reducer function handles
7 | * all the theme-related actions
8 | */
9 | export const reducer: Reducer<'light' | 'dark'> = (state, action: Action) => {
10 | // Get type from action, handling both string and object actions
11 | const actionType = typeof action === 'string' ? action : action.type;
12 |
13 | switch (actionType) {
14 | case 'THEME:TOGGLE':
15 | debug('store', '[Reducer] Handling THEME:TOGGLE');
16 | return state === 'dark' ? 'light' : 'dark';
17 |
18 | case 'THEME:SET': {
19 | debug('store', '[Reducer] Handling THEME:SET');
20 | // Only proceed if action is an object with payload
21 | if (typeof action === 'object' && 'payload' in action) {
22 | const isDark = action.payload as boolean;
23 | const theme = isDark ? 'dark' : 'light';
24 | debug('store', `[Reducer] Setting theme to ${theme}`);
25 | return theme;
26 | }
27 | return state;
28 | }
29 |
30 | default:
31 | return state;
32 | }
33 | };
34 |
--------------------------------------------------------------------------------
/packages/electron/scripts/build.ts:
--------------------------------------------------------------------------------
1 | // Build script for the project
2 | // Usage: tsx scripts/build.ts
3 | import fs from 'node:fs';
4 | import shell from 'shelljs';
5 |
6 | // compile and bundle
7 | shell.exec('tsc --project tsconfig.json');
8 | shell.exec('rollup --config rollup.config.js');
9 |
10 | // ensure dist dir exists
11 | if (!fs.existsSync('dist')) {
12 | shell.mkdir('dist');
13 | }
14 |
15 | // Find all d.ts files and create d.cts counterparts
16 | // The find command will find all d.ts files in the dist directory and its subdirectories
17 | const result = shell.find('dist').filter((file) => file.endsWith('.d.ts'));
18 |
19 | // Create .d.cts versions for the found files and strip .js extensions for CJS compatibility
20 | result.forEach((file) => {
21 | const ctsFile = file.replace('.d.ts', '.d.cts');
22 | shell.cp(file, ctsFile);
23 |
24 | // Strip .js extensions from relative imports in .d.cts files for proper CJS resolution
25 | const content = fs.readFileSync(ctsFile, 'utf8');
26 | const fixedContent = content.replace(/from ['"](\.[^'"]+)\.js['"]/g, "from '$1'");
27 | const fixedImportContent = fixedContent.replace(/import ['"](\.[^'"]+)\.js['"]/g, "import '$1'");
28 | fs.writeFileSync(ctsFile, fixedImportContent);
29 | });
30 |
--------------------------------------------------------------------------------