### Run Minimal Zustand Example Application Source: https://github.com/goosewobbler/zubridge/blob/main/docs/developer.md Starts the example application using the minimal Zustand setup. ```bash pnpm --filter minimal-zustand dev ``` -------------------------------- ### Install and Run Development Server Source: https://github.com/goosewobbler/zubridge/blob/main/apps/electron/minimal-zustand-basic/README.md Install project dependencies and start the development server. ```bash pnpm install pnpm dev ``` -------------------------------- ### Run Electron Example Application Source: https://github.com/goosewobbler/zubridge/blob/main/docs/developer.md Starts the example application using the Electron platform. ```bash pnpm --filter electron-example dev ``` -------------------------------- ### Install and Run Development Server Source: https://github.com/goosewobbler/zubridge/blob/main/apps/electron/minimal-zustand-handlers/README.md Commands to install dependencies, start the development server, build for production, and create distributables. ```bash pnpm install pnpm dev pnpm build pnpm dist ``` -------------------------------- ### Run Electron Example with pnpm Source: https://github.com/goosewobbler/zubridge/blob/main/examples/README.md Instructions for navigating to an example, installing dependencies, and running the development server or build process using pnpm. ```bash # Navigate to any example cd examples/electron/zustand-basic # Install dependencies pnpm install # Start development server pnpm dev # Build for production pnpm build # Run tests pnpm test ``` -------------------------------- ### Install, Develop, Build, and Distribute Electron App Source: https://github.com/goosewobbler/zubridge/blob/main/apps/electron/minimal-zustand-reducers/README.md Standard commands for managing dependencies, starting the development server, building for production, and creating distributable packages for the Electron application. ```bash pnpm install ``` ```bash pnpm dev ``` ```bash pnpm build ``` ```bash pnpm dist ``` -------------------------------- ### Install and Run Test App Source: https://github.com/goosewobbler/zubridge/blob/main/apps/electron/minimal-context-isolation-false/README.md Commands to install dependencies, build packages, and run the minimal test application. ```bash pnpm install pnpm build:packages cd apps/electron/minimal-context-isolation-false pnpm dev ``` -------------------------------- ### Install Dependencies Source: https://github.com/goosewobbler/zubridge/blob/main/docs/developer.md Installs all project dependencies using pnpm. ```bash pnpm install ``` -------------------------------- ### Start Dev Mode with Per-Mode Shortcuts Source: https://github.com/goosewobbler/zubridge/blob/main/apps/tauri/e2e/README.md Utilize per-mode shortcuts to start the Tauri e2e development server. ```bash pnpm --filter tauri-e2e dev:zustand-basic pnpm --filter tauri-e2e dev:redux ``` -------------------------------- ### Install @zubridge/electron Source: https://github.com/goosewobbler/zubridge/blob/main/packages/electron/docs/getting-started.md Install the package using npm. Alternatively, use pnpm or yarn. ```bash npm install @zubridge/electron ``` -------------------------------- ### Full Zubridge Plugin Usage Example Source: https://github.com/goosewobbler/zubridge/blob/main/packages/tauri/docs/api-reference.md An example demonstrating how to define application state, implement the `StateManager` trait, and create/register the Zubridge plugin. ```rust use tauri_plugin_zubridge::{StateManager, ZubridgePlugin, ZubridgeAction}; use std::sync::Mutex; use serde::{Serialize, Deserialize}; // Define your state #[derive(Serialize, Deserialize, Clone, Debug)] pub struct AppState { counter: i32, } impl Default for AppState { fn default() -> Self { Self { counter: 0 } } } // Implement the StateManager trait struct AppStateManager { state: Mutex, } impl StateManager for AppStateManager { fn get_state(&self) -> serde_json::Value { let state = self.state.lock().unwrap(); serde_json::to_value(&*state).unwrap() } fn process_action(&self, action: &ZubridgeAction) -> Result<(), String> { let mut state = self.state.lock().unwrap(); match action.action_type.as_str() { "INCREMENT" => { state.counter += 1; Ok(()) }, "DECREMENT" => { state.counter -= 1; Ok(()) }, _ => Err(format!("Unknown action: {}", action.action_type)), } } } // Create and register the plugin pub fn zubridge() -> TauriPlugin { let state_manager = AppStateManager { state: Mutex::new(AppState::default()), }; ZubridgePlugin::new(state_manager) } ``` -------------------------------- ### Start Specific Development Modes Source: https://github.com/goosewobbler/zubridge/blob/main/apps/electron/e2e/README.md Commands to start the development server for individual Zubridge modes. Use these to test specific state management patterns. ```bash pnpm dev:basic pnpm dev:handlers pnpm dev:reducers pnpm dev:redux pnpm dev:custom ``` -------------------------------- ### Install Frontend Libraries Source: https://github.com/goosewobbler/zubridge/blob/main/packages/tauri/docs/getting-started.md Install the Zubridge frontend library and its peer dependencies for your JavaScript/TypeScript project. ```bash # Using npm npm install @zubridge/tauri zustand @tauri-apps/api # Using yarn yarn add @zubridge/tauri zustand @tauri-apps/api # Using pnpm pnpm add @zubridge/tauri zustand @tauri-apps/api ``` -------------------------------- ### Install Dependencies with pnpm Source: https://github.com/goosewobbler/zubridge/blob/main/apps/electron/minimal-custom/README.md Installs project dependencies using the pnpm package manager. Ensure pnpm is installed globally before running. ```bash pnpm install ``` -------------------------------- ### Install Linux Build Dependencies Source: https://github.com/goosewobbler/zubridge/blob/main/apps/tauri/e2e/README.md Install necessary GTK 3 and WebKit2GTK 4.1 development headers for Tauri on Ubuntu 24.04. ```bash sudo apt-get install \ libgtk-3-dev \ libwebkit2gtk-4.1-dev \ libsoup-3.0-dev \ libjavascriptcoregtk-4.1-dev ``` -------------------------------- ### Install Zubridge Electron Source: https://github.com/goosewobbler/zubridge/blob/main/packages/electron/README.md Install the Zubridge Electron package and Zustand using npm or your preferred package manager. ```bash npm install @zubridge/electron zustand ``` -------------------------------- ### Dispatch Examples Source: https://github.com/goosewobbler/zubridge/blob/main/packages/electron/docs/api-reference.md Demonstrates different ways to call the dispatch function with or without options. ```typescript // String dispatch — payload only, no options dispatch('INCREMENT'); dispatch('SET_VALUE', 42); // Action object dispatch — supports options dispatch({ type: 'URGENT_ACTION', payload: 42 }, { immediate: true }); dispatch({ type: 'ADMIN_UPDATE' }, { keys: ['admin'] }); // Thunk dispatch — supports options dispatch(myThunk, { immediate: true }); ``` -------------------------------- ### initializeBridge Example Source: https://github.com/goosewobbler/zubridge/blob/main/packages/tauri/docs/api-reference.md Example of initializing the Zubridge bridge using Tauri's core API functions for invoke and listen. ```typescript import { initializeBridge } from '@zubridge/tauri'; import { invoke } from '@tauri-apps/api/core'; // For Tauri v2 import { listen } from '@tauri-apps/api/event'; initializeBridge({ invoke, listen }); ``` -------------------------------- ### useZubridgeStore Example Source: https://github.com/goosewobbler/zubridge/blob/main/packages/tauri/docs/api-reference.md Example of using the `useZubridgeStore` hook to display a counter value and the internal bridge status. ```tsx import { useZubridgeStore } from '@zubridge/tauri'; function Counter() { const counter = useZubridgeStore((state) => state.counter); // Access internal bridge status const status = useZubridgeStore((state) => state.__bridge_status); return
Counter: {counter}
; } ``` -------------------------------- ### Start Development Server with pnpm Source: https://github.com/goosewobbler/zubridge/blob/main/apps/electron/minimal-custom/README.md Starts the development server for the Electron application. This command is typically used during development to enable hot-reloading and other development features. ```bash pnpm dev ``` -------------------------------- ### Complete Thunk Pattern for Data Fetching with Caching Source: https://github.com/goosewobbler/zubridge/blob/main/packages/electron/docs/thunks.md An example of a recommended pattern for data fetching using thunks, including loading states, error handling, and caching logic. It dispatches actions for fetch start, success, and error, and updates cache time. ```typescript // Define action types for better type safety const ActionTypes = { FETCH_STARTED: 'FETCH_STARTED', FETCH_SUCCESS: 'FETCH_SUCCESS', FETCH_ERROR: 'FETCH_ERROR', UPDATE_CACHE_TIME: 'UPDATE_CACHE_TIME', }; // Thunk for fetching data with caching const fetchData = (forceRefresh = false) => async (getState, dispatch) => { const state = getState(); const cacheTime = state.lastFetchTime || 0; const cacheExpired = Date.now() - cacheTime > 5 * 60 * 1000; // 5 minutes // Check if we can use cached data if (!forceRefresh && !cacheExpired && state.data) { console.log('Using cached data'); return state.data; } // Start fetching dispatch({ type: ActionTypes.FETCH_STARTED }); try { const response = await fetch('https://api.example.com/data'); // Check if response is ok if (!response.ok) { throw new Error(`HTTP error ${response.status}`); } const data = await response.json(); // Update the state with the fetched data dispatch({ type: ActionTypes.FETCH_SUCCESS, payload: data }); dispatch({ type: ActionTypes.UPDATE_CACHE_TIME, payload: Date.now() }); return data; } catch (error) { // Handle and log the error console.error('Error fetching data:', error); dispatch({ type: ActionTypes.FETCH_ERROR, payload: error.message }); // Re-throw the error for the caller to handle throw error; } }; ``` -------------------------------- ### useZubridgeDispatch Example Source: https://github.com/goosewobbler/zubridge/blob/main/packages/tauri/docs/api-reference.md Example of using the `useZubridgeDispatch` hook to create a button that dispatches an 'INCREMENT' action to the backend. ```tsx import { useZubridgeDispatch } from '@zubridge/tauri'; function Counter() { const dispatch = useZubridgeDispatch(); return ; } ``` -------------------------------- ### Install Zubridge Tauri Dependencies Source: https://github.com/goosewobbler/zubridge/blob/main/packages/tauri/README.md Install the necessary packages for Zubridge Tauri, including the core library, Zustand, and Tauri API. ```bash npm install @zubridge/tauri zustand @tauri-apps/api ``` -------------------------------- ### Start Dev Mode with Different Modes Source: https://github.com/goosewobbler/zubridge/blob/main/apps/tauri/e2e/README.md Start the Tauri e2e development server in one of the five available modes by setting the ZUBRIDGE_MODE environment variable. ```bash ZUBRIDGE_MODE=zustand-basic pnpm --filter tauri-e2e dev ZUBRIDGE_MODE=zustand-handlers pnpm --filter tauri-e2e dev ZUBRIDGE_MODE=zustand-reducers pnpm --filter tauri-e2e dev ZUBRIDGE_MODE=redux pnpm --filter tauri-e2e dev ZUBRIDGE_MODE=custom pnpm --filter tauri-e2e dev ``` -------------------------------- ### Action Dispatch Examples Source: https://github.com/goosewobbler/zubridge/blob/main/packages/electron/docs/how-it-works.md Demonstrates different ways to dispatch actions with Zubridge, including string types, object types, and nested path resolution. ```typescript dispatch('SET_COUNTER', 10); // Looks for a SET_COUNTER (or set_counter) function in your store/handlers / root reducer ``` ```typescript dispatch({ type: 'SET_COUNTER', payload: 10 }); // Looks for a SET_COUNTER (or set_counter) handler ``` ```typescript dispatch('ui.counter.increment'); // Can resolve nested handler functions ``` -------------------------------- ### Priority System Example: Renderer and Main Process Interaction Source: https://github.com/goosewobbler/zubridge/blob/main/packages/electron/docs/performance.md Illustrates how the priority system handles actions dispatched from different windows and with different flags. Immediate actions bypass the queue, while normal actions are queued based on priority. ```typescript // Window A - runs a thunk dispatch(async (getState, dispatch) => { dispatch({ type: 'STEP_1' }); // Priority: 70 (active root thunk) await delay(1000); dispatch({ type: 'STEP_2' }); // Priority: 70 (active root thunk) }); // Window B - during the thunk above dispatch({ type: 'NORMAL_UPDATE' }); // Priority: 0 → queued behind Window A's thunk dispatch({ type: 'URGENT' }, { immediate: true }); // Priority: 100 → executes immediately, bypassing queue ``` -------------------------------- ### Create Distributable with pnpm Source: https://github.com/goosewobbler/zubridge/blob/main/apps/electron/minimal-custom/README.md Creates a distributable package of the Electron application, ready for deployment or user installation. ```bash pnpm dist ``` -------------------------------- ### Cross-Window Thunk and Action Processing Example Source: https://github.com/goosewobbler/zubridge/blob/main/packages/electron/docs/thunks.md Demonstrates how Zubridge queues actions and thunks dispatched from different windows during a thunk's execution to maintain state consistency. ```typescript // In Window A: dispatch(async (getState, dispatch) => { console.log('Thunk started'); dispatch('SET_VALUE', 2); // First change // Artificial delay to simulate a long-running operation await new Promise((resolve) => setTimeout(resolve, 1000)); dispatch('SET_VALUE', 4); // Second change await new Promise((resolve) => setTimeout(resolve, 1000)); dispatch('SET_VALUE', 8); // Third change await new Promise((resolve) => setTimeout(resolve, 1000)); dispatch('SET_VALUE', 4); // Final change console.log('Thunk completed'); }); // In Window B (during thunk execution): dispatch('SET_VALUE', 10); // This action will be queued // Also in Window B (during thunk execution): dispatch(async (getState, dispatch) => { dispatch('SET_VALUE', 20); }); // This thunk will also be queued ``` -------------------------------- ### Create Redux Store in Main Process Source: https://github.com/goosewobbler/zubridge/blob/main/packages/electron/docs/getting-started.md Configure a Redux store in the main process using Redux Toolkit. This setup includes defining reducers and configuring middleware, with `serializableCheck: false` recommended for Electron IPC. ```typescript // `src/main/store.ts` import { configureStore } from '@reduxjs/toolkit'; import counterReducer from './features/counter/counterSlice.js'; // Create the Redux store export const store = configureStore({ reducer: { counter: counterReducer, }, // Optional middleware configuration middleware: (getDefaultMiddleware) => getDefaultMiddleware({ serializableCheck: false, // Helpful for Electron IPC integration }), }); // Type inference for your application state export type RootState = ReturnType; ``` -------------------------------- ### v2 vs v3 String Dispatch Examples Source: https://github.com/goosewobbler/zubridge/blob/main/packages/electron/docs/migration/v2-to-v3.md Illustrates the difference in dispatching with options between v2 (duck-typed) and v3 (explicit positional arguments). In v3, options must be passed as the third argument when a payload is not present. ```typescript dispatch('URGENT_ACTION', { immediate: true }); // options duck-typed from payload ``` ```typescript dispatch('ADMIN_UPDATE', data, { keys: ['admin'] }); // 3-arg also worked ``` ```typescript dispatch('URGENT_ACTION', undefined, { immediate: true }); // payload=undefined, options ``` ```typescript dispatch('ADMIN_UPDATE', data, { keys: ['admin'] }); // payload=data, options — unchanged ``` -------------------------------- ### Plugin Entry Points Source: https://github.com/goosewobbler/zubridge/blob/main/packages/tauri-plugin/README.md These functions are used to initialize the Zubridge plugin. Choose the one that best fits your application's setup, whether you need a default configuration, custom options, or to register the state manager later. ```APIDOC ## Plugin Entry Points | Function | When to use | | --- | --- | | `plugin_default(state_manager)` | One-shot setup with a state manager and the default `ZubridgeOptions`. | | `plugin(state_manager, options)` | Same as above, but with custom `ZubridgeOptions` (e.g. a different state-update event name). | | `init()` | Builds the plugin without a state manager — register one later with `app.zubridge().register_state_manager(...)`. | ``` -------------------------------- ### Quick Start: Zubridge Plugin Integration Source: https://github.com/goosewobbler/zubridge/blob/main/packages/tauri-plugin/README.md Implement the StateManager trait and register the zubridge plugin with your Tauri application. This example shows a simple counter state. ```rust use serde::{Deserialize, Serialize}; use std::sync::Mutex; use tauri::plugin::TauriPlugin; use tauri_plugin_zubridge::{plugin_default, JsonValue, StateManager}; #[derive(Default, Serialize, Deserialize, Clone, Debug)] struct AppState { counter: i32, } struct AppStateManager(Mutex); impl StateManager for AppStateManager { fn get_initial_state(&self) -> JsonValue { let s = self.0.lock().unwrap(); serde_json::to_value(&*s).unwrap() } fn dispatch_action(&mut self, action: JsonValue) -> JsonValue { let mut s = self.0.lock().unwrap(); if let Some(t) = action.get("type").and_then(|v| v.as_str()) { match t { "INCREMENT" => s.counter += 1, "DECREMENT" => s.counter -= 1, "SET_COUNTER" => { if let Some(v) = action.get("payload").and_then(|v| v.as_i64()) { s.counter = v as i32; } } _ => {} } } serde_json::to_value(&*s).unwrap() } } fn zubridge() -> TauriPlugin { plugin_default(AppStateManager(Mutex::new(AppState::default()))) } fn main() { tauri::Builder::default() .plugin(zubridge()) .run(tauri::generate_context!()) .expect("error while running tauri application"); } ``` -------------------------------- ### Vanilla JavaScript Renderer Process Usage Source: https://github.com/goosewobbler/zubridge/blob/main/packages/electron/docs/getting-started.md Example of using Zubridge hooks in vanilla JavaScript for non-React applications. Shows how to get state, subscribe to changes, and dispatch actions. ```js // Non-React example const { createUseStore, useDispatch } = window.zubridge; // Create store hook and dispatcher const useStore = createUseStore(); const dispatch = useDispatch(); // Get current state and subscribe to changes function updateUI() { const state = useStore.getState(); document.getElementById('counter').textContent = state.counter; } // Initial UI update updateUI(); // Subscribe to state changes const unsubscribe = useStore.subscribe(updateUI); // Add event listeners document.getElementById('increment-btn').addEventListener('click', () => { dispatch('INCREMENT'); }); document.getElementById('decrement-btn').addEventListener('click', () => { dispatch('DECREMENT'); }); // Clean up when needed function cleanup() { unsubscribe(); } ``` -------------------------------- ### Expose Zubridge Handlers in Preload Script Source: https://github.com/goosewobbler/zubridge/blob/main/packages/electron/docs/getting-started.md In your preload script (`src/preload/index.ts`), use `preloadBridge` to get the handlers and expose them to the renderer process via `contextBridge`. This setup requires `contextIsolation: true`. ```typescript // `src/preload/index.ts` import { contextBridge } from 'electron'; import { preloadBridge } from '@zubridge/electron/preload'; const { handlers } = preloadBridge(); // Expose the handlers to the renderer process contextBridge.exposeInMainWorld('zubridge', handlers); ``` -------------------------------- ### Initialize Zustand Bridge in Main Process Source: https://github.com/goosewobbler/zubridge/blob/main/packages/electron/docs/getting-started.md Set up the Zubridge Zustand adapter in the main process. This involves creating the bridge instance and subscribing your Electron windows to state updates. Ensure to unsubscribe on application quit. ```typescript // `src/main/index.ts` import { app, BrowserWindow } from 'electron'; import { createZustandBridge } from '@zubridge/electron/main'; import { store } from './store.js'; // create main window const mainWindow = new BrowserWindow({ // ... webPreferences: { preload: path.join(__dirname, 'path/to/preload/index.cjs'), // other options... }, }); // instantiate bridge const bridge = createZustandBridge(store); // subscribe the window to state updates const { unsubscribe } = bridge.subscribe([mainWindow]); // unsubscribe on quit app.on('quit', unsubscribe); ``` -------------------------------- ### Build All Packages Source: https://github.com/goosewobbler/zubridge/blob/main/docs/developer.md Builds all packages within the project. ```bash pnpm build ``` -------------------------------- ### Build and Distribute Electron App Source: https://github.com/goosewobbler/zubridge/blob/main/apps/electron/minimal-zustand-basic/README.md Build the Electron app for production and create distributable packages. ```bash pnpm build pnpm dist ``` -------------------------------- ### Build All and Specific Modes Source: https://github.com/goosewobbler/zubridge/blob/main/apps/electron/e2e/README.md Commands to build the application for all modes or for specific Zubridge configurations. Useful for creating production-ready builds. ```bash pnpm build pnpm build:zustand-basic pnpm build:zustand-handlers pnpm build:zustand-reducers pnpm build:redux pnpm build:custom ``` -------------------------------- ### Vite Production Build Source: https://github.com/goosewobbler/zubridge/blob/main/apps/tauri/e2e/README.md Create a production build of the application using Vite. ```bash pnpm --filter tauri-e2e exec vite build ``` -------------------------------- ### Build Commands Source: https://github.com/goosewobbler/zubridge/blob/main/AGENTS.md Commands for building packages, linting, formatting, and type checking the project. Use `pnpm build:packages` for core package builds. ```bash pnpm build # Build all packages pnpm build:packages # Build core packages only (types → utils → electron/tauri/ui) pnpm lint # Run Biome linter pnpm format # Format all files with Biome pnpm check # Biome lint + format check pnpm check:fix # Fix lint and format issues pnpm typecheck # TypeScript validation across all packages ``` -------------------------------- ### Build for Production with pnpm Source: https://github.com/goosewobbler/zubridge/blob/main/apps/electron/minimal-custom/README.md Builds the Electron application for production. This command compiles and bundles the application assets. ```bash pnpm build ``` -------------------------------- ### Context-Aware Priority in Main Process Source: https://github.com/goosewobbler/zubridge/blob/main/packages/electron/docs/performance.md Determines the priority of an action in the main process based on whether it belongs to the currently active root thunk. Actions from the active root thunk get priority 70, while others get 50. ```typescript // ActionScheduler checks if a thunk action belongs to the ACTIVE root thunk const rootThunkId = this.thunkManager.getRootThunkId(); if (rootThunkId && action.__thunkParentId === rootThunkId) { return PRIORITY_LEVELS.ROOT_THUNK_ACTION; // 70 } ``` -------------------------------- ### Run Electron Benchmarks Source: https://github.com/goosewobbler/zubridge/blob/main/packages/electron/docs/performance.md Execute this command in the 'packages/electron' directory to run all benchmark files in 'benchmarks/' using vitest. The runner reports metrics like ops/sec, p75, p99, and statistical variance. ```bash cd packages/electron pnpm bench ``` -------------------------------- ### Install Zubridge Tauri Plugin Source: https://github.com/goosewobbler/zubridge/blob/main/packages/tauri-plugin/README.md Add the tauri-plugin-zubridge dependency to your Cargo.toml file. Ensure you also have serde and serde_json. ```toml # Cargo.toml [dependencies] tauri-plugin-zubridge = "0.2" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" ``` -------------------------------- ### Basic React Integration with Zubridge Source: https://github.com/goosewobbler/zubridge/blob/main/packages/tauri/docs/getting-started.md A simplified example of using Zubridge hooks in a React component to display and increment a counter. ```tsx import { useZubridgeStore, useZubridgeDispatch } from '@zubridge/tauri'; function Counter() { const counter = useZubridgeStore((state) => state.counter); const dispatch = useZubridgeDispatch(); return (

Counter: {counter}

); } ``` -------------------------------- ### Initialize Zubridge Electron Window Source: https://github.com/goosewobbler/zubridge/blob/main/apps/electron/minimal-sandbox-true/src/renderer/index.html This script runs immediately to determine if the window is a runtime window or the main window and applies a corresponding CSS class to the body. It uses URL parameters to make this determination. ```javascript ( () => { const urlParams = new URLSearchParams(window.location.search); const isRuntimeWindow = urlParams.get('runtime') === 'true'; document.body.classList.add(isRuntimeWindow ? 'runtime-window' : 'main-window'); })(); ``` -------------------------------- ### Disable Renderer Validation Temporarily Source: https://github.com/goosewobbler/zubridge/blob/main/packages/electron/docs/migration/v2-to-v3.md Temporarily disable renderer-side validation by setting the `ZUBRIDGE_RENDERER_VALIDATION` environment variable to `off` when starting the application. ```bash ZUBRIDGE_RENDERER_VALIDATION=off npm start ``` -------------------------------- ### Create Zustand Store in Main Process Source: https://github.com/goosewobbler/zubridge/blob/main/packages/electron/docs/getting-started.md Initialize a Zustand store in the main process. This store will be the source of truth for your application's state and will be bridged to the renderer process. ```typescript // `src/main/store.ts` import { createStore } from 'zustand/vanilla'; import type { AppState } from '../types/index.js'; const initialState: AppState = { counter: 0, ui: { ... } }; // create app store export const store = createStore()(() => initialState); ``` -------------------------------- ### Thunk Usage Example Source: https://github.com/goosewobbler/zubridge/blob/main/packages/electron/docs/api-reference.md Illustrates how to use the extended dispatch functions within a thunk, including direct, batched, and flushed dispatches. ```typescript const myThunk = async (getState, dispatch) => { // Direct dispatch (default) await dispatch({ type: 'A' }); // Batched dispatch void dispatch.batch({ type: 'B' }); // Flush and wait for completion const result = await dispatch.flush(); }; ``` -------------------------------- ### Get Zubridge State Source: https://github.com/goosewobbler/zubridge/blob/main/packages/tauri/docs/api-reference.md Use `getState()` to retrieve the current state object from the Zubridge store. This is useful for accessing the entire state at once. ```typescript function getState(): any; ``` ```typescript import { useZubridgeStore } from '@zubridge/tauri'; const currentState = useZubridgeStore.getState(); console.log(currentState.counter); ``` -------------------------------- ### Vanilla JavaScript Usage Source: https://github.com/goosewobbler/zubridge/blob/main/packages/tauri/docs/api-reference.md Demonstrates how to use the Zubridge store and dispatch functions with vanilla JavaScript, outside of a specific framework. ```APIDOC ## Vanilla JavaScript Usage While the library uses hook naming conventions, it can be used with any JavaScript framework or vanilla JS: ```javascript import { useZubridgeStore, useZubridgeDispatch } from '@zubridge/tauri'; // Access state directly const counter = useZubridgeStore.getState().counter; // Subscribe to changes const unsubscribe = useZubridgeStore.subscribe((state) => { console.log('Counter:', state.counter); document.getElementById('counter').textContent = state.counter; }); // Dispatch actions const dispatch = useZubridgeDispatch(); document.getElementById('increment').addEventListener('click', () => { dispatch({ type: 'INCREMENT' }); }); // Clean up window.addEventListener('beforeunload', () => { unsubscribe(); }); ``` ``` -------------------------------- ### Basic Sync Test Spec for Tauri Source: https://github.com/goosewobbler/zubridge/blob/main/UNIFFI_REFACTOR_PLAN.md Example test spec for Tauri E2E, covering counter increment/decrement and bidirectional window synchronization. ```typescript import { expect } from '@wdio/globals'; import { Counter } from '../utils/counter'; import { Window } from '../utils/window'; describe('Basic Sync Tests', () => { it('should increment and decrement the counter', async () => { const counter = new Counter(); await counter.increment(); expect(await counter.getValue()).toBe(1); await counter.decrement(); expect(await counter.getValue()).toBe(0); }); it('should synchronize window state', async () => { const mainWindow = new Window('main'); const secondWindow = new Window('secondary'); await mainWindow.setState({ value: 'hello' }); expect(await secondWindow.getState()).toEqual({ value: 'hello' }); await secondWindow.setState({ value: 'world' }); expect(await mainWindow.getState()).toEqual({ value: 'world' }); }); }); ``` -------------------------------- ### Create Zustand Bridge in Main Process Source: https://github.com/goosewobbler/zubridge/blob/main/packages/electron/docs/main-process.md Instantiate the Zustand bridge with an initial window or subscribe windows later. Use `dispatch` to send actions and `unsubscribe` on application quit. ```typescript // `src/main/index.ts` import { app, BrowserWindow } from 'electron'; import { createZustandBridge } from '@zubridge/electron/main'; import { store } from './store.js'; // Your Zustand store // Create main window const mainWindow = new BrowserWindow({ /* config */ }); // Option A: Instantiate bridge with initial window const { unsubscribe, subscribe, dispatch, getSubscribedWindows } = createZustandBridge(store, [mainWindow]); // Option B: Instantiate bridge without windows, subscribe later const bridge = createZustandBridge(store); const subscription = bridge.subscribe([mainWindow]); // Use dispatch to send actions dispatch('COUNTER:INCREMENT'); // Unsubscribe on quit app.on('quit', unsubscribe); ``` -------------------------------- ### Dispatch Flush Result Example Source: https://github.com/goosewobbler/zubridge/blob/main/packages/electron/docs/api-reference.md Demonstrates how to use the result object returned by `dispatch.flush()`. It logs the batch ID and the number of actions sent. ```typescript const result = await dispatch.flush(); console.log(`Batch ${result.batchId} sent ${result.actionsSent} actions`); ``` -------------------------------- ### Dispatch Zubridge Actions Source: https://github.com/goosewobbler/zubridge/blob/main/packages/tauri/docs/api-reference.md Examples of dispatching actions to the Zubridge store. Actions can be simple with just a `type`, or include a `payload` for additional data, which can be any type. ```typescript // Simple action without payload dispatch({ type: 'INCREMENT' }); // Action with payload dispatch({ type: 'ADD_TODO', payload: { text: 'Buy milk', completed: false }, }); // Action with primitive payload dispatch({ type: 'SET_COUNTER', payload: 5, }); ``` -------------------------------- ### Add WDIO Default Capability in main.json Source: https://github.com/goosewobbler/zubridge/blob/main/UNIFFI_REFACTOR_PLAN.md Include 'wdio:default' in the capabilities for the main Tauri window. ```json { "version": "1.0.0", "capabilities": { "wdio:default": { "platformName": "tauri" } } } ```