├── .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 |
37 |
{children}
38 |
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 | --------------------------------------------------------------------------------