This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
npm run start:dev- Start development with hot reload (macOS/Linux)npm run start:dev-win- Start development with hot reload (Windows)npm run build- Production buildnpm run build:dev- Development buildnpm run typecheck- Run TypeScript type checkingnpm run lint- Run ESLint
npm run precommit- Run pre-commit checks (lint-staged)- Always run
npm run typecheckandnpm run lintafter making changes
npm run dist:mac- Build macOS distributionnpm run dist:win- Build Windows distributionnpm run dist:linux- Build Linux distribution
To test a build on macOS without code signing:
ELECTRON_SKIP_NOTARIZE=1 npm run dist:macThe build output is in dist/mac-arm64 (or dist/mac for x64). You can run the app directly from terminal:
./dist/mac-arm64/Anytype.app/Contents/MacOS/AnytypeDependencies included in the packaged app are whitelisted. The npm run build:deps script auto-detects required dependencies, but if some are missing at runtime, explicitly add them to package.deps.json.
Before development, you need the anytype-heart middleware:
- Run
./update.sh <platform> <arch>to fetch middleware - Start anytypeHelper binary in background
- Use
SERVER_PORTenv var to specify gRPC port
Anytype is an Electron-based desktop application with TypeScript/React frontend communicating with a Go-based middleware (anytype-heart) via gRPC.
Key Components:
- Electron Main Process (
electron.js) - Window management, IPC, system integration - React Frontend (
src/ts/) - UI components and business logic - gRPC Middleware - Backend logic (separate anytype-heart repository)
- Block-based Editor - Document editing with composable blocks
Entry Points:
entry.tsx- Application entry pointapp.tsx- Main React application component
Core Libraries (lib/):
api/- gRPC communication (dispatcher, commands, mapper)keyboard.ts- Keyboard shortcuts and input handlingstorage.ts- Local storage managementrenderer.ts- Electron IPC communicationutil/- Utility functions (common, data, router, etc.)
State Management (store/):
- MobX-based stores for different domains:
common.ts- Global application stateauth.ts- Authentication stateblock.ts- Document block statedetail.ts- Object detail statemenu.ts,popup.ts- UI state
Component Structure (component/):
block/- Document block components (text, dataview, media, etc.)page/- Page-level components (auth, main, settings)menu/- Context menus and dropdownspopup/- Modal dialogssidebar/- Left/right sidebarsutil/- Reusable UI utilities
Block-Based Documents:
- Documents are composed of blocks (text, images, databases, etc.)
- Each block type has corresponding model, content, and component
- Block operations handled via gRPC commands
MobX State Management:
- Reactive state with MobX stores
- Components observe store changes automatically
- Stores organized by domain (auth, blocks, UI, etc.)
gRPC Communication:
- Frontend communicates with middleware via gRPC
- Commands in
lib/api/command.ts - Response mapping in
lib/api/mapper.ts - Real-time updates via gRPC streaming
Electron Integration:
- Main process handles system integration
- Renderer process handles UI
- IPC communication for file operations, updates, etc.
The graph view uses a Web Worker with PixiJS WebGL rendering for performance:
Files:
src/ts/component/graph/provider.tsx- React component, D3 zoom/drag, image loadingdist/workers/graph.js- Web Worker with D3 force simulation + PixiJS WebGL renderingdist/workers/lib/pixi.min.js- Bundled PixiJS for worker (built fromrspack.pixi.config.js)
Architecture:
- OffscreenCanvas transferred to worker for off-main-thread rendering
- PixiJS 8 with WebWorkerAdapter for GPU-accelerated WebGL rendering
- D3.js force simulation for physics (center, charge, link, collision, cluster forces)
- Communication via postMessage between provider and worker
Rendering Structure (PixiJS):
- Stage → edgesGraphics (PIXI.Graphics for all edges)
- Stage → nodesContainer (PIXI.Container with Sprites for nodes)
- Stage → labelsContainer (PIXI.Container with Text for labels)
- Stage → selectBoxGraphics (PIXI.Graphics for drag selection)
GraphProvider API:
- Props:
id,rootId,data: { nodes, edges },storageKey,load - Ref methods:
init(),resize(),addNewNode(),forceUpdate() - Window events:
updateGraphSettings.{id},updateGraphRoot.{id},updateGraphData.{id}
Usage locations:
src/ts/component/page/main/graph.tsx- Global graph pagesrc/ts/component/block/dataview/view/graph.tsx- Dataview graphsrc/ts/component/widget/view/graph/index.tsx- Widget graph
Worker message protocol:
- Provider → Worker:
init,updateSettings,image,onZoom,onClick,onSelect,onMouseMove,onDragStart/Move/End,setRootId,resize,updateTheme - Worker → Provider:
onClick,onSelect,onMouseMove,onContextMenu,onTransform,setRootId
Building the PixiJS worker bundle:
npx rspack --config rspack.pixi.config.js- Identify the relevant component in
src/ts/component/ - Check corresponding interfaces in
src/ts/interface/ - Look for related stores in
src/ts/store/ - Update models in
src/ts/model/if needed - Add gRPC commands in
src/ts/lib/api/if backend changes needed
- Components: UI components in
src/ts/component/ - Styles: SCSS files in
src/scss/(organized to match components) - Assets: Images and icons in
src/img/ - Configuration: Electron config in
electron/ - Build: Rspack configuration in
rspack.config.js
- Uses Rspack for bundling (faster Webpack alternative)
- TypeScript with React 17
- MobX for state management
- Custom block-based editor system
- gRPC for backend communication
- Electron for desktop app packaging
- CSS supports native nesting - use nested selectors instead of flat/inline selectors
- Do not use
cursor: pointerin CSS - the app does not use custom cursors
- Write
else ifwith a linebreak beforeif:if (condition) { // ... } else if (anotherCondition) { // ... }
- Wrap logical parts of compound conditions in parentheses for readability:
// Good const isValid = (x > 0) && (y > 0) && (x < maxWidth); if ((a > b) && (c < d)) { ... } // Bad const isValid = x > 0 && y > 0 && x < maxWidth; if (a > b && c < d) { ... }
- All UI text should use
translate()function for i18n - Translation keys are defined in
src/json/text.json(source of truth). Files indist/lib/json/lang/are generated — do not edit them directly - Block operations should go through the command system
- Use existing utility functions in
lib/util/before creating new ones - Follow existing component patterns in
component/directory - Store updates should trigger UI re-renders automatically via MobX
Detailed README files are available throughout the codebase for deeper context on each module:
src/ts/README.md- TypeScript source overview, entry points, import aliases, key patterns
src/ts/component/README.md- All 18 component subdirectories overviewsrc/ts/component/block/README.md- Block system: 19 block types (text, media, dataview, table, chat, embed, etc.)src/ts/component/page/README.md- Page routing: auth flow, main pages, settings hierarchysrc/ts/component/menu/README.md- Menu system: ~50 context menu types with positioning and keyboard navsrc/ts/component/popup/README.md- Popup system: ~27 modal dialog typessrc/ts/component/editor/README.md- Block-based document editor (page.tsx ~2600 lines)src/ts/component/graph/README.md- Graph visualization: D3 + PixiJS WebGL via Web Workersrc/ts/component/sidebar/README.md- Left/right sidebars with tree navigation and object viewssrc/ts/component/widget/README.md- Dashboard widgets: list, gallery, board, calendar, graph viewssrc/ts/component/cell/README.md- Data cells for dataview grid/board renderingsrc/ts/component/drag/README.md- Drag-and-drop system for blocks and listssrc/ts/component/form/README.md- Form controls: inputs, selects, phrases, pinssrc/ts/component/header/README.md- Page headers by context (editor, set, settings, auth)src/ts/component/footer/README.md- Page footers (auth, main editor)src/ts/component/list/README.md- Object list components with virtual scrollingsrc/ts/component/notification/README.md- Toast notification systemsrc/ts/component/preview/README.md- Preview cards and tooltipssrc/ts/component/selection/README.md- Block and text selection handlingsrc/ts/component/util/README.md- ~48 reusable utility components
src/ts/lib/README.md- Core libraries overview (api, util, services, keyboard, storage)src/ts/lib/api/README.md- gRPC communication: dispatcher, 100+ commands, protobuf mappingsrc/ts/lib/util/README.md- ~20 utility modules (common, data, menu, object, router, string, etc.)src/ts/lib/constant/README.md- Application constants and static configurationsrc/ts/lib/service/README.md- Singleton services (sidebar, analytics, focus, translation)
src/ts/store/README.md- MobX stores: 13 domain stores (auth, block, common, detail, record, menu, popup, chat, etc.)src/ts/model/README.md- Data models: Block, Content classes, View, Filter, Sortsrc/ts/interface/README.md- TypeScript interfaces and enums for all domain types
src/ts/hook/README.md- Custom React hookssrc/ts/docs/README.md- In-app documentation and help contentsrc/ts/workers/README.md- Web Workers (graph PixiJS worker)electron/README.md- Electron main process: window management, IPC, updates, menussrc/scss/README.md- SCSS stylesheets organized to mirror component structuresrc/img/README.md- Images, icons (SVG), and static assetssrc/json/README.md- JSON data: translations, constants, colors, keyboard shortcuts
Run in browser without Electron: npm run start:web (starts anytypeHelper + dev server). Use ANYTYPE_USE_SIDE_SERVER=http://... to skip helper start. See src/ts/lib/web/README.md for details.
Use the LINEAR_API_KEY environment variable to fetch issue details from Linear.
Fetch issue by ID:
curl -s -X POST "https://api.linear.app/graphql" \
--header "Content-Type: application/json" \
--header "Authorization: $(printenv LINEAR_API_KEY)" \
--data '{"query":"query{issue(id:\"JS-1234\"){title description state{name}priority labels{nodes{name}}comments{nodes{body createdAt}}}}"}' | jq .Important: Use $(printenv LINEAR_API_KEY) instead of $LINEAR_API_KEY directly in curl commands to avoid shell expansion issues.
Use the Figma MCP tools to fetch design context and screenshots from Figma files.
Available tools:
mcp__figma__get_design_context- Get UI code/design context for a Figma node (preferred)mcp__figma__get_screenshot- Get a screenshot of a Figma nodemcp__figma__get_metadata- Get metadata/structure of a Figma node
Extract parameters from Figma URLs:
- URL format:
https://www.figma.com/design/:fileKey/:fileName?node-id=:nodeId fileKeyis the ID after/design/nodeIdis in thenode-idquery parameter (convert-to:for the API)
Extract parameters from Figma URLs:
For URL https://www.figma.com/design/uWka9aJ7IOdvHch60rIRlb/MyFile?node-id=12769-19003:
fileKey:uWka9aJ7IOdvHch60rIRlbnodeId:12769:19003
Important - Icons and Images:
- All icons and images must be stored locally in
src/img/- do NOT use remote Figma asset URLs - When implementing designs from Figma, recreate icons as SVG files in the appropriate
src/img/icon/subdirectory - Follow existing icon patterns (e.g.,
src/img/icon/add/for editor control button icons) - Icons typically have two variants:
name0.svg(default state, #B6B6B6) andname1.svg(hover state, #252525)
After completing any task that adds, removes, or significantly modifies files in a component/abstraction folder, run the /update-docs skill to update the co-located README.md. Documentation is kept lean and delta-driven — only sections affected by the change are updated. Skip for trivial changes (typo fixes, minor logic tweaks).
After completing any task that edits SCSS files (src/scss/), SVG/image files (src/img/), or adds new UI components, run the /dark-mode-check skill to audit for dark mode issues. This catches:
- Hardcoded colors that should use CSS variables (
--color-text-*,--color-bg-*,--color-shape-*, etc.) - Missing dark icon variants in
src/img/theme/dark/ - Inline
html.themeDarkoverrides that belong insrc/scss/theme/dark/ - Dynamic icon paths missing
S.Common.getThemePath()
After completing any task that modifies user-facing behavior — especially in the editor (component/editor/, component/block/), menus (component/menu/), popups (component/popup/), sidebar (component/sidebar/), or widgets (component/widget/) — run the /qa-engineer skill to generate E2E test coverage.
The QA Engineer skill:
- Analyzes the git diff to identify user-facing changes
- Checks existing test coverage in
../anytype-desktop-suite/ - Creates a test plan in
../anytype-desktop-suite/specs/ - Generates Playwright test files in
../anytype-desktop-suite/tests/ - Creates page objects if needed in
../anytype-desktop-suite/src/pages/
Skip for changes that have no user-facing impact (type refactors, internal utilities, CSS-only tweaks, build config).
Test suite repo: ../anytype-desktop-suite — Playwright E2E tests with Page Object Model, translation-aware selectors, and gRPC server lifecycle management. See its CLAUDE.md for test architecture details.