vojo/CLAUDE.md
2026-04-16 01:57:06 +03:00

8 KiB

Vojo — Project Guide

Matrix chat client (React 18 + TypeScript), forked from Cinny and rebranded as Vojo.

Quick Start

npm start        # dev server on :8080
npm run build    # production build → dist/
npm run lint     # eslint + prettier
npm run typecheck # tsc --noEmit

Build: Vite 5.4 with vanilla-extract, WASM, PWA plugins.

Source Layout

src/
├── index.tsx                    # Entry point
├── client/
│   ├── initMatrix.ts            # Matrix SDK init (createClient, startClient, logout)
│   └── secretStorageKeys.js     # Crypto callbacks
├── types/matrix/                # Matrix protocol types (room.ts, accountData.ts, common.ts)
└── app/
    ├── i18n.ts                  # i18next config
    ├── pages/
    │   └── App.tsx              # Root component (providers, config loader)
    ├── features/                # Feature modules
    ├── components/              # Shared components
    ├── hooks/                   # ~117 custom hooks
    ├── state/                   # Jotai atoms
    ├── plugins/                 # Content plugins
    ├── utils/                   # Utilities
    └── styles/                  # Vanilla-extract global styles

Pages & Routing (src/app/pages/)

Router in Router.tsx — createBrowserRouter / createHashRouter.

  • Auth: auth/login/, auth/register/, auth/reset-password/
  • Client: client/ — main layout after login
    • home/ — Home timeline (path: /home/, root / redirects here)
    • direct/ — DMs (path: /direct/)
    • space/ — Space view (path: /:spaceIdOrAlias/)
    • explore/ — Public rooms (path: /explore/)
    • inbox/ — Notifications, invites (path: /inbox/)
    • create/ — New room/space (path: /create/)
    • sidebar/ — Tab components for sidebar nav
    • WelcomePage.tsx — Empty state
    • SyncStatus.tsx, SpecVersions.tsx — Connection status

Features (src/app/features/)

Dir Purpose
room/ Core room view — RoomTimeline.tsx (63KB), RoomInput.tsx (24KB), RoomViewHeader.tsx (18KB), MembersDrawer, MessageEditor, RoomTombstone
room-nav/ Room list navigation items & categories
room-settings/ Room-specific settings page
common-settings/ Shared settings: general, members, permissions, emojis-stickers, developer-tools
space-settings/ Space-specific settings
settings/ User settings (general, account, notifications, devices, emojis, about, dev-tools)
lobby/ Space/room lobby view
search/ Global search
message-search/ In-room message search
create-chat/ DM creation flow
create-room/ Room creation
create-space/ Space creation
add-existing/ Join existing rooms
join-before-navigate/ Pre-join navigation logic
call/ Element Call integration
call-status/ Call state display

Key Components (src/app/components/)

  • message/ — Message rendering (layout variants: compact, bubble, modern)
  • editor/ — Slate-based rich text editor
  • emoji-board/ — Emoji picker
  • image-pack-view/ — Custom emoji pack management
  • image-viewer/, Pdf-viewer/ — Media viewers
  • sidebar/ — Sidebar navigation
  • user-profile/ — User info, power chips, moderation
  • member-tile/ — Member list items
  • power/ — Power level UI
  • upload-card/ — Upload progress cards
  • url-preview/ — Link previews
  • page/ — Page layout wrapper (Page, PageHeader, PageContent)
  • setting-tile/ — Settings list item pattern
  • sequence-card/, cutout-card/ — Card layouts
  • uia-stages/ — User-interactive auth stages (email, captcha, token)
  • room-intro/ — Room introduction card
  • invite-user-prompt/, join-address-prompt/, leave-room-prompt/ — Dialogs

State Management

Jotai atoms in src/app/state/:

  • settings.ts — User preferences (MessageLayout, DateFormat, etc.)
  • sessions.ts — Active session
  • upload.ts — Upload progress
  • room/ — roomInputDrafts, roomToParents, roomToUnread
  • room-list/ — roomList, inviteList, sorting/filtering

Some atoms persist to localStorage (e.g. settings.ts, navToActivePath.ts), others are in-memory only (e.g. upload.ts, roomInputDrafts.ts). Access via hooks in state/hooks/.

Localisation (i18n)

Config: src/app/i18n.ts — i18next + HTTP backend + language detector, fallbackLng: 'ru'

Locale files: public/locales/en.json, public/locales/ru.json

Namespaces (top-level keys in JSON): Organisms, Auth, Settings, Search, Home, Direct, Room, Inbox, Explore, Create, RoomSettings

Pattern:

import { useTranslation } from 'react-i18next';

const { t } = useTranslation();
return <Text>{t('RoomSettings.some_key')}</Text>;

Conventions:

  • Each component gets its own useTranslation() call
  • In custom hooks with useMemo, add [t] to dependency array
  • react-i18next import goes after framework imports, before local imports
  • For dynamic values: t('key', { count: 5 })
  • For rare cases with HTML in translations: dangerouslySetInnerHTML (used sparingly, e.g. in RoomAddress.tsx)

Key Libraries

  • React 18.2 + React Router DOM 6
  • matrix-js-sdk 38.2 — Matrix protocol
  • folds 2.6 — UI component library
  • jotai 2.6 — State management
  • vanilla-extract — Type-safe CSS
  • slate 0.123 — Rich text editor
  • @tanstack/react-query 5 — Data fetching
  • @tanstack/react-virtual 3 — Virtual scrolling
  • i18next 23 + react-i18next 15 — Localisation
  • Capacitor 8.3 — Native Android wrapper
  • @capacitor/browser 8.0 — External link handling in native

Android (Capacitor)

Requirements: Node >=22, JDK 17+ (21 used), Android SDK with platform 36 + build-tools 36.0.0

Config: capacitor.config.tsappId: chat.vojo.app, webDir: dist

Android project: android/ — generated, targetSdkVersion 36, compileSdkVersion 36, minSdkVersion 24

Build scripts:

npm run build:android:debug    # full chain: build → sync → debug APK
npm run build:android:release  # full chain: build → sync → release APK
npm run build:android:aab      # full chain: build → sync → release AAB
npm run android:sync           # sync dist/ → android assets
npm run android:apk:debug      # gradle debug build only

APK output: android/app/build/outputs/apk/debug/app-debug.apk

Version: versionCode and versionName auto-derived from package.json version (major1000000 + minor1000 + patch)

Key architecture decisions:

  • Bundled build (dist/ copied into APK), not remote WebView
  • Service Worker kept active — critical for authenticated Matrix media (MSC3916 / spec v1.11+). Do NOT disable. resolveServiceWorkerRequests default true.
  • Edge-to-edge via EdgeToEdge.enable() in MainActivity.java + windowLayoutInDisplayCutoutMode: shortEdges
  • External links opened via @capacitor/browser plugin (see src/app/utils/capacitor.ts)
  • body background-color bound to folds theme variable var(--oq6d070) for consistent safe-area coloring
  • Safe-area insets applied on #root (not body) so theme background extends behind system bars

VSCode tasks (.vscode/tasks.json):

  • Deploy to vojo.chat (Ctrl+Shift+D) — web deploy
  • Deploy to Android (ADB) (Ctrl+Shift+A) — build + adb install

ADB wireless: pair via adb pair <ip>:<port> <code>, connect via adb connect <ip>:<port>. Ports for pair and connect are different.

SDK location: /usr/lib/android-sdk, also set in android/local.properties

Matrix SDK Patterns

const mx = useMatrixClient();           // Get SDK instance
const room = useRoom();                  // Current room
const stateEvent = useStateEvent(room, StateEvent.Type);  // Room state
const powerLevels = usePowerLevels(room); // Permissions

Git

  • Main branch: dev
  • Current work branch: vojo/dev
  • Semantic-release on dev branch
  • CI: GitHub Actions (build, deploy, docker, netlify)