Files
healthchat-web/CLAUDE.md
T

8.9 KiB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Repository layout

This is a pnpm + Nx monorepo (not Yarn — the older single-package layout is gone). Workspaces are defined in pnpm-workspace.yaml:

  • apps/web (element-web) — the browser client. This is where almost all feature work happens. Source is apps/web/src/ (not a top-level /src/).
  • apps/desktop (element-desktop) — the Electron wrapper. Uses Vitest for unit tests and hak for native modules.
  • packages/module-api (@element-hq/element-web-module-api) — public API surface for runtime modules.
  • packages/shared-components (@element-hq/web-shared-components) — shared React components; home of Storybook.
  • packages/playwright-common, packages/vite-common — shared test/build helpers.

Build orchestration is Nx (nx.json); most apps/web scripts (build, start, test, lint:types) delegate to Nx targets defined in apps/web/project.json, which handle prebuild steps (prebuild:module_system, prebuild:rethemendex) automatically.

Prerequisites

  • Node.js: .node-version pins 24 (package.json engines allows >=22.18)
  • pnpm (the repo uses a pnpm-lock.yaml and a catalog for shared dependency versions)

Commands

Run package-scoped commands from inside the package dir (e.g. cd apps/web) or from the root with pnpm --filter element-web <script>. Root-level scripts (pnpm lint, pnpm i18n) fan out across all workspaces via pnpm -r.

Setup

  • pnpm install — install dependencies (runs scripts/pnpm-link.ts postinstall, see SDK linking below)
  • Copy apps/web/config.sample.json to apps/web/config.json and configure (see docs/config.md)

Development (from apps/web)

  • pnpm start — dev server on http://localhost:8080 (nx start)
  • pnpm build — production build, outputs to apps/web/webapp/
  • pnpm dist — distribution tarball (./scripts/package.sh)

Linux note: If builds fail silently or with EMFILE: too many open files, increase inotify limits:

sudo sysctl fs.inotify.max_user_watches=131072
sudo sysctl fs.inotify.max_user_instances=512

Testing (from apps/web)

  • pnpm test — Jest unit tests (nx test:unit)
  • pnpm test -- path/to/file.test.ts — run a single Jest test file (the -- forwards args to Jest; or cd apps/web && npx jest path/...)
  • pnpm test:playwright — Playwright e2e tests
  • pnpm test:playwright:open — Playwright UI
  • pnpm test:playwright:screenshots — update Playwright screenshot snapshots
  • pnpm coverage — unit tests with coverage

Note: apps/desktop unit tests use Vitest (pnpm --filter element-desktop test:unit), not Jest.

Code Quality

  • pnpm lint (root) — full lint across all workspaces: types, prettier, eslint, stylelint, GH workflows, knip
  • pnpm --filter element-web lint — package-local shorthand for lint:types && lint:js && lint:style
  • pnpm --filter element-web lint:types — TypeScript check only (via Nx)
  • pnpm --filter element-web lint:js — ESLint check (lint:js-fix auto-fixes)
  • pnpm --filter element-web lint:style — Stylelint on res/css/**/*.pcss
  • pnpm lint:knip (root) — unused deps/exports; pnpm lint:workflows — validate GH Actions YAML. Both run as part of pnpm lint and gate CI.

Utilities

  • pnpm i18n (root or per-package) — regenerate/sort/lint translation strings; chains i18n:sort, i18n:lint, and i18n:diff (run any one directly when only that step is needed)
  • pnpm --filter @element-hq/web-shared-components storybook — Storybook on port 6007

Architecture

Element Web is a Matrix client built with React + TypeScript, customized here as "HealthChat". The architecture below describes apps/web/src/.

Component Structure

Hierarchical layout:

  • MatrixChat — root component managing whole-application state
  • LeftPanel — room list, spaces, navigation
  • MainSplit — central room view with messages and composer
  • RightPanel — member list, room info, contextual panels

Components live in apps/web/src/components/:

  • structures/ — major layout components (these use React classes)
  • views/ — reusable UI grouped by function (auth, dialogs, messages, rooms, settings)

Other recurring apps/web/src/ layers worth knowing:

  • customisations/ — Element customization hooks (e.g. Media.ts, WidgetPermissions.ts) — the sanctioned override points
  • viewmodels/ — view logic separated from React components, consumed by structures/ classes
  • async-components/ — code-split / lazy-loaded mirrors of components (e.g. ErrorView.tsx)

State Management

Flux architecture:

  • Centralized dispatcher in apps/web/src/dispatcher/
  • Stores in apps/web/src/stores/ as singletons (access via FooStore.instance)
  • Dispatch: dis.dispatch({ action: Action.FOO, ...payload }) or dis.fire(Action.FOO) for action-only

Styling

  • PostCSS .pcss files, BEM-like naming mx_ComponentName_element
  • Styles co-located with components (e.g. _MessageTile.pcss)
  • res/css/_components.pcss is generated by the rethemendex prebuild step — don't hand-edit it
  • Theming via CSS custom properties

Matrix Integration

  • matrix-js-sdk for all Matrix protocol operations
  • Client accessed via MatrixClientPeg
  • Room/event handling through SDK abstractions

Module System

Runtime plugin architecture (apps/web/module_system/, public API in packages/module-api):

  • prebuild:module_system reads apps/web/build_config.yaml and generates apps/web/src/modules.js (empty by default in this fork — no committed build_config.yaml)
  • In production, modules are loaded at runtime from config.json's modules array, not via the build-time modules.js path
  • HealthChat ships the healthchat-workspace module (see release section); its version is pinned in .healthchat-workspace-version

HealthChat fork specifics

Conventions unique to this fork that span multiple files:

  • Branding defaults: hardcoded in apps/web/src/SdkConfig.ts (brand: "HealthChat", mobile app-store URLs); runtime config in apps/web/config.json (homeserver matrix.healthchat.ch, custom "HealthChat Light" theme). config.sample.json is the dev template — copy it to config.json.
  • Widget auto-approval: widgets served from api.healthchat.ch bypass permission dialogs and hide revoke/minimize controls. Logic lives in apps/web/src/stores/widgets/ElementWidgetDriver.ts, apps/web/src/components/views/elements/AppTile.tsx, and .../context_menus/WidgetContextMenu.tsx; base URL comes from the ch.healthchat.api_base_url config key.
  • Desktop deep-linking: scheme is healthchat:// (apps/desktop/src/protocol.ts), with the SSO session passed via the healthchat-desktop-ssoid param. Desktop variant config (and its build runbook) lives under apps/desktop/element.io/healthchat/ (config.json, build.json, README).

Testing Patterns

  • Web unit tests in apps/web/test/unit-tests/ (Jest)
  • E2E tests in apps/web/playwright/e2e/ (Playwright)
  • Mock the Matrix client/SDK extensively in tests
  • Component stories/visual tests via Storybook in packages/shared-components

Important Conventions

  • TypeScript strict mode — always use explicit types
  • Functional components with hooks preferred over class components (except structures/ which use classes)
  • Translations use _t() with hierarchical keys
  • 4-space indentation, 120-character line limit
  • Component files: UpperCamelCase; utilities: kebab-case
  • Use Optional<T> instead of truly optional params when callers should explicitly acknowledge missing values
  • CSS class names must be mx_-prefixed and match the component name (e.g. mx_RoomTile)
  • Import matrix-js-sdk via matrix-js-sdk/src/matrix only (not matrix-js-sdk or matrix-js-sdk/src)
  • Use jest-matrix-react instead of @testing-library/react for component tests
  • No React.forwardRef — use ref props directly
  • Use UIStore for window dimensions instead of window.innerHeight/window.innerWidth
  • Use the Media helper for MXC URL handling instead of direct mxcUrlToHttp calls

Linked Development with matrix-js-sdk

pnpm link is not used (it mutates package.json/lockfile). Instead, the postinstall hook runs scripts/pnpm-link.ts, which reads a .link-config file of DEPENDENCY=PATH lines and replaces those node_modules entries with symlinks. To develop against a local SDK, add a line like matrix-js-sdk=/abs/path/to/matrix-js-sdk to .link-config (not committed) and re-run pnpm install.

Releasing HealthChat

Releases are cut by pushing a healthchat-v<semver> tag against release/healthchat, which triggers .github/workflows/build-healthchat.yaml (web) and build-healthchat-desktop.yaml (desktop). Release notes come from HEALTHCHAT_CHANGES.md (kept separate from upstream CHANGELOG.md); the healthchat-workspace plugin ref is pinned in .healthchat-workspace-version. Full runbook: releasing-healthchat skill.