From d7d2861a7928cecd5b2648c060b6236624b10b5e Mon Sep 17 00:00:00 2001 From: Naiyuan Qing <145280634+NevilleQingNY@users.noreply.github.com> Date: Fri, 30 Jan 2026 10:52:54 +0800 Subject: [PATCH] feat(store): add shared Zustand store package with counter example - Create packages/store with @multica/store package - Add zustand to pnpm catalog for version consistency - Add counter store as cross-platform state example - Integrate counter into ComponentExample for verification - Add tsconfig path mappings for web and desktop - Add @multica/store to Next.js transpilePackages - Add @multica/store dependency to packages/ui Co-Authored-By: Claude Opus 4.5 --- apps/desktop/tsconfig.json | 3 +- apps/web/next.config.ts | 2 +- apps/web/tsconfig.json | 3 +- packages/store/package.json | 15 +++++++ packages/store/src/counter.ts | 15 +++++++ packages/store/src/index.ts | 1 + packages/store/tsconfig.json | 19 +++++++++ packages/ui/package.json | 1 + .../ui/src/components/component-example.tsx | 36 +++++++++++++++++ pnpm-lock.yaml | 40 +++++++++++++++++++ pnpm-workspace.yaml | 1 + 11 files changed, 133 insertions(+), 3 deletions(-) create mode 100644 packages/store/package.json create mode 100644 packages/store/src/counter.ts create mode 100644 packages/store/src/index.ts create mode 100644 packages/store/tsconfig.json diff --git a/apps/desktop/tsconfig.json b/apps/desktop/tsconfig.json index ab38ca18..cda7ec8f 100644 --- a/apps/desktop/tsconfig.json +++ b/apps/desktop/tsconfig.json @@ -16,7 +16,8 @@ "baseUrl": ".", "paths": { "@/*": ["./src/*"], - "@multica/ui/*": ["../../packages/ui/src/*"] + "@multica/ui/*": ["../../packages/ui/src/*"], + "@multica/store/*": ["../../packages/store/src/*"] }, /* Linting */ diff --git a/apps/web/next.config.ts b/apps/web/next.config.ts index a68f4a73..1e258c7f 100644 --- a/apps/web/next.config.ts +++ b/apps/web/next.config.ts @@ -1,7 +1,7 @@ import type { NextConfig } from "next"; const nextConfig: NextConfig = { - transpilePackages: ["@multica/ui"], + transpilePackages: ["@multica/ui", "@multica/store"], }; export default nextConfig; diff --git a/apps/web/tsconfig.json b/apps/web/tsconfig.json index a5a8a540..7c326cd3 100644 --- a/apps/web/tsconfig.json +++ b/apps/web/tsconfig.json @@ -20,7 +20,8 @@ ], "paths": { "@/*": ["./*"], - "@multica/ui/*": ["../../packages/ui/src/*"] + "@multica/ui/*": ["../../packages/ui/src/*"], + "@multica/store/*": ["../../packages/store/src/*"] } }, "include": [ diff --git a/packages/store/package.json b/packages/store/package.json new file mode 100644 index 00000000..2e9a547d --- /dev/null +++ b/packages/store/package.json @@ -0,0 +1,15 @@ +{ + "name": "@multica/store", + "version": "0.1.0", + "private": true, + "type": "module", + "exports": { + "./*": "./src/*.ts" + }, + "dependencies": { + "zustand": "catalog:" + }, + "devDependencies": { + "typescript": "catalog:" + } +} diff --git a/packages/store/src/counter.ts b/packages/store/src/counter.ts new file mode 100644 index 00000000..30ffc733 --- /dev/null +++ b/packages/store/src/counter.ts @@ -0,0 +1,15 @@ +import { create } from 'zustand' + +interface CounterState { + count: number + increment: () => void + decrement: () => void + reset: () => void +} + +export const useCounterStore = create((set) => ({ + count: 0, + increment: () => set((state) => ({ count: state.count + 1 })), + decrement: () => set((state) => ({ count: state.count - 1 })), + reset: () => set({ count: 0 }), +})) diff --git a/packages/store/src/index.ts b/packages/store/src/index.ts new file mode 100644 index 00000000..778a5959 --- /dev/null +++ b/packages/store/src/index.ts @@ -0,0 +1 @@ +export { useCounterStore } from './counter' diff --git a/packages/store/tsconfig.json b/packages/store/tsconfig.json new file mode 100644 index 00000000..2fa65e5c --- /dev/null +++ b/packages/store/tsconfig.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "target": "ESNext", + "module": "ESNext", + "moduleResolution": "bundler", + "jsx": "react-jsx", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "declaration": true, + "outDir": "./dist", + "rootDir": "./src", + "baseUrl": ".", + "paths": { + "@multica/store/*": ["./src/*"] + } + }, + "include": ["src"] +} diff --git a/packages/ui/package.json b/packages/ui/package.json index 2bd86e54..17628c39 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -12,6 +12,7 @@ "./hooks/*": "./src/hooks/*.ts" }, "dependencies": { + "@multica/store": "workspace:*", "@base-ui/react": "^1.1.0", "@hugeicons/core-free-icons": "^3.1.1", "@hugeicons/react": "^1.1.4", diff --git a/packages/ui/src/components/component-example.tsx b/packages/ui/src/components/component-example.tsx index 2b4a96ee..7d6fe331 100644 --- a/packages/ui/src/components/component-example.tsx +++ b/packages/ui/src/components/component-example.tsx @@ -65,16 +65,52 @@ import { import { Textarea } from "@multica/ui/components/ui/textarea" import { HugeiconsIcon } from "@hugeicons/react" import { PlusSignIcon, BluetoothIcon, MoreVerticalCircle01Icon, FileIcon, FolderIcon, FolderOpenIcon, CodeIcon, MoreHorizontalCircle01Icon, SearchIcon, FloppyDiskIcon, DownloadIcon, EyeIcon, LayoutIcon, PaintBoardIcon, SunIcon, MoonIcon, ComputerIcon, UserIcon, CreditCardIcon, SettingsIcon, KeyboardIcon, LanguageCircleIcon, NotificationIcon, MailIcon, ShieldIcon, HelpCircleIcon, File01Icon, LogoutIcon } from "@hugeicons/core-free-icons" +import { useCounterStore } from "@multica/store/counter" export function ComponentExample() { return ( + ) } +function CounterExample() { + const { count, increment, decrement, reset } = useCounterStore() + + return ( + + + + Shared Counter + + This counter uses Zustand from @multica/store, shared across web and desktop. + + + +
+ + {count} + +
+
+ + + Count: {count} + +
+
+ ) +} + function CardExample() { return ( diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 530ce78f..ca1c875c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -24,6 +24,9 @@ catalogs: typescript: specifier: ^5.9.3 version: 5.9.3 + zustand: + specifier: ^5.0.0 + version: 5.0.10 importers: @@ -241,6 +244,16 @@ importers: specifier: ^5.9.3 version: 5.9.3 + packages/store: + dependencies: + zustand: + specifier: 'catalog:' + version: 5.0.10(@types/react@19.2.10)(react@19.2.3)(use-sync-external-store@1.6.0(react@19.2.3)) + devDependencies: + typescript: + specifier: 'catalog:' + version: 5.9.3 + packages/ui: dependencies: '@base-ui/react': @@ -252,6 +265,9 @@ importers: '@hugeicons/react': specifier: ^1.1.4 version: 1.1.4(react@19.2.3) + '@multica/store': + specifier: workspace:* + version: link:../store class-variance-authority: specifier: ^0.7.1 version: 0.7.1 @@ -5712,6 +5728,24 @@ packages: zod@4.3.6: resolution: {integrity: sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==} + zustand@5.0.10: + resolution: {integrity: sha512-U1AiltS1O9hSy3rul+Ub82ut2fqIAefiSuwECWt6jlMVUGejvf+5omLcRBSzqbRagSM3hQZbtzdeRc6QVScXTg==} + engines: {node: '>=12.20.0'} + peerDependencies: + '@types/react': '>=18.0.0' + immer: '>=9.0.6' + react: '>=18.0.0' + use-sync-external-store: '>=1.2.0' + peerDependenciesMeta: + '@types/react': + optional: true + immer: + optional: true + react: + optional: true + use-sync-external-store: + optional: true + snapshots: 7zip-bin@5.2.0: {} @@ -12059,3 +12093,9 @@ snapshots: zod@3.25.76: {} zod@4.3.6: {} + + zustand@5.0.10(@types/react@19.2.10)(react@19.2.3)(use-sync-external-store@1.6.0(react@19.2.3)): + optionalDependencies: + '@types/react': 19.2.10 + react: 19.2.3 + use-sync-external-store: 1.6.0(react@19.2.3) diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index c8686a2b..852ac082 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -9,3 +9,4 @@ catalog: "@types/react-dom": "^19" "@types/node": "^25.0.10" typescript: "^5.9.3" + zustand: "^5.0.0"