block / block/buzz

RFC: desktop channel-feature registry to replace the per-feature tab/settings/sidebar wiring (behavior-preserving; complements #3275)

Open
#3,280 1 comment 1 reaction 0 assignees View on GitHub
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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.