Offline-first sticky notes and idea-management canvas with real-time collaboration sync.
- Frontend: React 19, TypeScript, Vite 7, React Router 7
- Styling: Tailwind CSS v4 + app-level CSS
- Canvas/UI engine: Konva + react-konva
- State management: Zustand stores (
auth,project,board) - Local persistence: Dexie on IndexedDB (
sticky-db) - Backend services: Supabase (Auth, PostgreSQL, Realtime, Storage)
- Sync strategy: Queue-based sync engine with online flush + realtime subscriptions
- PWA:
vite-plugin-pwa+ service worker auto-update registration - Tooling: ESLint 9, TypeScript 5, npm scripts
- Deployment: Multi-stage Docker build (Node 22 + Nginx 1.27), Traefik reverse proxy
- UI Layer
src/pages/BoardPage.tsxsrc/components/canvas/BoardCanvas.tsx
- State Layer
src/store/useAuthStore.tssrc/store/useProjectStore.tssrc/store/useBoardStore.ts
- Data Layer
- Local:
src/lib/db/appDb.ts - Remote:
src/lib/api/supabaseApi.ts
- Sync Layer
src/lib/sync/syncEngine.tssrc/hooks/useRealtimeSync.ts
- Node.js 22+
- npm 10+
- Supabase project (for auth/realtime/cloud sync)
- Docker + Docker Compose (for container deployment)
Create local env:
cp .env.example .envRequired values in .env:
VITE_SUPABASE_URL=https://YOUR_PROJECT_ID.supabase.co
VITE_SUPABASE_ANON_KEY=YOUR_SUPABASE_ANON_KEYOptional:
VITE_APP_BASE_PATH(default/) for subpath hosting like/sticky/
If Supabase variables are missing, the app still runs locally with offline mode, but online sync is disabled.
Install dependencies:
npm installRun development server:
npm run devOther scripts:
npm run build
npm run preview
npm run lintApply SQL migrations in order:
supabase/sql/001_init.sqlsupabase/sql/002_profiles_and_superadmin.sql
Bootstrap superadmin account:
npm run bootstrap:superadminFor each mutation:
- Update Zustand state
- Persist to Dexie immediately
- Enqueue a sync item in
syncQueue - Flush queue when online/focused/visible
- Merge remote updates through Supabase Realtime channels
Conflict behavior follows last-write-wins using newer updated_at values.
This repo includes docker-compose.traefik.yml that starts only the Sticky frontend and attaches it to external Traefik network shop-online_web.
- Create deployment env file:
cp .env.traefik.example .env.traefik- Configure
.env.traefik:
VITE_SUPABASE_URLVITE_SUPABASE_ANON_KEYVITE_APP_BASE_PATH(example:/sticky/)TRAEFIK_HOST(example:projects.doimih.net)TRAEFIK_PATH_PREFIX(example:/sticky)
- Validate compose:
docker compose --env-file .env.traefik -f docker-compose.traefik.yml config- Build and run:
docker compose --env-file .env.traefik -f docker-compose.traefik.yml up -d --buildDefault URL:
https://projects.doimih.net/sticky
- Public route:
https://projects.doimih.net/sticky/api/health - Container route:
/api/health - Method:
GET - Response: JSON
Example:
{
"status": "ok",
"service": "sticky",
"timestamp": "2026-02-23T12:34:56+00:00"
}Access is restricted to requests with Origin: https://projects.doimih.net.
- Code sync (Git)
- Push from machine A
- Run
git pullon machine B
- Data sync (Supabase)
- Verify
.envon both machines - Sign in with the same account
- Select the same project
- Offline cache note
- Unsynced changes remain in IndexedDB (
sticky-db) until sync succeeds