davidmcarati / element-capacitor · fork of element-hq/element-web

Capacitor ⊣⊢

An Element fork tuned for daily use — the same end-to-end-encrypted Matrix client underneath, with the everyday experience reworked to feel like the chat apps people already know.

v1.12.23Element base
+1,712lines added
50files touched
7features shipped

Element is solid. It just doesn't feel familiar.

Matrix gets the hard parts right — federation, encryption, self-hosting. But for people arriving from Slack or Discord, Element's navigation asks them to learn a new mental model before they can send a message. Capacitor keeps the engine and rebuilds the cabin.

A room list that reads like Slack

Channels and Contacts, not "All Chats"

Upstream Element pools everything into one list. Capacitor splits it into two synthetic sections — Channels for group rooms and Contacts for direct messages — so the sidebar maps onto how people actually think about their conversations. Implemented in the room list store (RoomListStoreV3) with new section definitions, so filtering, sorting, and virtualization all keep working.

Compact, denser rows

Avatars drop to 24 px and row heights tighten, fitting noticeably more conversations on screen — the difference between scanning and scrolling. RoomAvatarView was made size-configurable rather than hard-coded, so the compact size is a parameter, not a hack.

Drag-and-drop ordering that follows you

Rooms can be manually reordered within Channels and Contacts. The custom order is stored in Matrix account data, so it syncs across every device you sign in on — arrange your sidebar once, keep it everywhere.

A quieter space rail

The meta-space shortcuts (Home, "Other rooms") collapse behind a single toggle by default, with the state persisted locally. The left edge of the app stops competing for attention it doesn't need.

Active threads, surfaced in the room list

In upstream Element, threads live behind a panel you have to remember to open — easy to miss, easy to lose. Capacitor lifts a room's recently active threads directly into the room list, as clickable rows nested under the room itself.

  • Recency-filtered. Only threads active within the last N days appear — configurable in Preferences (activeThreadDays, default 3), so the list shows what's alive, not an archive.
  • Chevrons for cascading threads. Rooms with threads get an expand/collapse chevron, keeping the cascade tidy when you don't need it.
  • Unread state at a glance. Each thread row carries its own notification decoration, and unread threads render emphasized.
  • Bounded by design. Five threads show by default with an accessible "Show n more / Show fewer" toggle — a busy room can't flood the sidebar.
  • One click to the conversation. Each row opens its thread directly — no room-first, panel-second dance.

The feature ships with 170+ lines of new viewmodel tests and Storybook stories for every state.

A GIF picker, exactly where you expect it

A GIF button sits next to the emoji button in the message composer. It opens a searchable picker — trending GIFs by default, live search as you type — and a click sends the GIF as an animated image into the room.

  • Giphy-powered, provider-configurable. The integration is built behind a provider abstraction with a content-rating setting.
  • Zero secrets in git. The API key is read at runtime from config.json and is never committed. Without a key, the button still appears and the picker explains how to configure one.
{
    "gif_provider": {
        "provider": "giphy",
        "api_key": "YOUR_GIPHY_API_KEY",
        "rating": "pg-13"
    }
}

Fewer papercuts, quieter chrome

Quieter encryption shields

Element decorates messages whose authenticity merely "can't be guaranteed" with a grey informational shield — noise that trains users to ignore all shields. Capacitor hides the purely-informational grey icon while keeping every genuine red security warning intact. Signal stays, noise goes.

Presence dot, fixed

On the new compact rows the online/offline indicator drifted off the avatar; it now sits correctly on the avatar corner.

Rebranded end to end

App name, window icons, manifest, and in-app logos (including a redrawn SVG logo) are updated from Element to Capacitor — so the fork reads as its own product, not a mislabeled Element install.

Built the way upstream builds

Every feature follows Element's own architecture, so the fork stays mergeable and the code reads like it belongs.

MVVM New UI follows Element's viewmodel pattern — thread visibility, section headers, and item state live in RoomListItemViewModel and friends, with views kept thin in the shared-components package.
tested New behavior ships with unit tests (170+ lines for the threads viewmodel alone) and Storybook stories covering default, unread, expanded, and overflow states.
i18n Every new string goes through the translation pipeline, including proper pluralization ("Show 1 more thread" / "Show 5 more threads").
a11y Thread rows are real buttons with descriptive aria-labels; unread state is summarized for screen readers rather than conveyed by color alone.
synced User preferences live at the right layer: room order in Matrix account data (cross-device), rail collapse state locally (per device), thread recency in account settings.
configurable The GIF provider is runtime configuration, not a compile-time constant — deploys choose their own key, provider, and content rating.