RFC: desktop channel-feature registry to replace the per-feature tab/settings/sidebar wiring (behavior-preserving; complements #3275)
- Dominant language
- Rust
- Stars
- 32.7k
- Forks
- 4.3k
- Avg merge
- 1d 13h
- Merged PRs (30d)
- 253
Description
**Motivation**
Adding a channel-scoped feature to the desktop client today means editing a spread of centrally-owned files in lockstep: the `ChannelScreen` dispatch if/else, the two near-identical `*ChannelContent` tab shells, the settings section wiring (a union type + a values array + a render `switch` with a `never` exhaustiveness gate + a nav-groups map), the scattered binding parsers plus the glyph mapper, and a hand-rolled read/write hook per feature. A correct new feature must touch all of them consistently — the client-side equivalent of the relay's per-kind "fork tax" (companion proposal: #3167).
This isn't hypothetical. #3275 ("host MCP Apps as channel tabs") adds a genuinely new tab type today by extending those shared shells directly. Sequence/board/docs-style surfaces do the same. Every such addition edits the same central files and grows the `switch`/if-else chains.
**Proposed solution**
A `ChannelFeaturePlugin` registry for the desktop client — modeled on the existing `shared/features/` flag manifest ("typed definition list + resolver hook + gate"), but for channel features. A plugin declares, in one object: how it classifies a channel (`parseBinding` off the kind-39000 `about`), and the surfaces it contributes — `tabs`, `settingsPanel`, `sidebar` group/create-actions, `headerAction`, and `glyph`.
- One generic `ChannelFeatureShell` renders a plugin's `tabs` (lazy) — collapsing the two hand-rolled `*ChannelContent` shells into one. The `ChannelScreen` dispatch if/else becomes a classify + shell render.
- The settings section list becomes a single descriptor array (`render` per entry), retiring the parallel union / values-array / `switch` / nav-map.
- Sidebar grouping, create-actions, and glyphs are driven by the plugins.
- A shared `useReplaceableEvent` / `usePublishReplaceable` hook pair removes the duplicated "fetch → subscribe → newest-wins" read logic and the "sign → publish" write logic that each feature currently re-implements.
Built-in surfaces (chat/forum, the settings sections) register through the same path — dogfooded, so it's a behavior-preserving refactor, not a new privileged plugin lane. Because every icon/affordance (tab, glyph, sidebar group, create button) is a declared field, a feature like #3275's MCP-App tabs plugs in without editing any shared file.
**Alternatives considered**
- Keep hand-wiring each feature into the shared shells (status quo) — the fork-tax remains, the central `switch`/if-else chains keep growing, and #3275-style additions keep editing central files.
- A heavier dynamic plugin/loader system — unnecessary. This is compile-time registration, exactly like the existing `shared/features/` manifest; no runtime plugin machinery.
**Additional context**
We have a complete working implementation, verified behavior-preserving against the existing desktop Playwright specs (channel tabs, sidebar hierarchy, settings), with downstream-specific features split into an opt-in pack that a vanilla build omits. It's the client-side companion to the relay-side registry proposed in #3167.
It also directly complements #3275 (host MCP Apps as channel tabs): a channel-feature registry would give those App tabs — and Sequence/board/docs — a first-class extension point rather than each tab type extending the shared shells. Happy to align on the shape (registry surface, how MCP-App tabs would register) before writing the PR, and to split it into review-sized commits.
Searched open issues and PRs for duplicates (channel feature registry / tab registry / feature plugin): none found.
Contributor guide
Research direction
Start by reading the existing shared/features/ manifest, ChannelScreen dispatch, the two *ChannelContent shells, settings-section wiring, binding parsers, glyph mapper, and feature-specific read/write hooks described in the issue. Run the existing desktop Playwright specs for channel tabs, sidebar hierarchy, and settings; done means those behaviors remain unchanged while built-in surfaces use the registry and downstream-specific features stay opt-in.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- playwright, rust
- Domain
- desktop
- Issue type
- Refactor
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100