Composer redesign behind enable_new_composer_actions
This introduces a redesigned composer experience and ships it behind a new alpha upcoming-change. With the flag off (default), nothing visibly changes. Admins can opt in via the upcoming-changes admin page.
What changes when the flag is on
-
Unified actions dropdown. The legacy SelectKit
composer-actionsdropdown is replaced by a new Glimmer/DMenu component (composer-actions-new.gjs). It still surfaces all the same mode-switch rows (reply to topic, reply as new topic, create PM, etc.) with clearer labels and icons. -
Toggles live with the actions. Whisper / no-bump / unlist now render as
<DToggleSwitch>items at the bottom of that same dropdown — no separate combo-button menu. Toggle state survives mode switches (e.g. toggle whisper on → switch from reply-to-topic to reply-to-post → still whispering).
-
Whisper indicator pill. A small button to the right of the dropdown trigger shows current whisper state in two visual variants:
--public(tertiary color, eye icon, “Reply will be visible”) and--whispering(primary-medium / primary-very-low background, eye-slash icon, “Whispering”). Clicking it toggles whisper — same code path as the toggle inside the dropdown, so the two stay in sync. The trigger icon itself also swaps tofar-eye-slashwhen whispering. -
Editing UX. When editing a post the dropdown collapses to a static, caret-less label (pencil icon + “Edit post”). The “Add edit reason” button that used to live elsewhere in the composer chrome is folded into this static trigger as a clickable “Describe your edit” button; clicking it swaps the label for a text input bound to
composer.editReasonand focuses it. -
Composer chrome. The
.action-titletext/span is removed — the dropdown trigger itself shows the current mode. -
Post/Topic Links will now only appear when necessary. A topic link will render when navigating away from the topic, and the post link will render when scrolled away from the post / navigating away.
Plugin author notes
The new component is not a SelectKit subclass, so the old extension APIs do not reach it. To support both flag states in this alpha phase, plugins should register both APIs:
| Old API (works on old component only) | New API (works on new component only) |
|---|---|
api.modifySelectKit("composer-actions").appendContent(...) |
api.registerValueTransformer("composer-actions-content", ({ value, context }) => { value.push(...); return value; }) |
api.modifyClass("component:composer-actions", { fooSelected(options, model) { ... } }) |
api.registerBehaviorTransformer("composer-actions-on-select", ({ context, next }) => { if (context.actionId === "foo") { ...; return; } next(); }) |
Row shape ({ name, description, icon, id }) is identical in both. The behavior transformer receives { actionId, options, model } in its context.
Plugins that only register the old hooks will silently disappear from the composer the moment an admin enables the flag. Plugins that only register the new transformers will be invisible while the flag is off (the default for everyone on merge).
References:
-
discourse-post-voting/extend-composer-actions.js — worked example of dual registration (in core)
-
discourse-staff-alias — separate PR adds dual registration there
-
transformers.js — the two new transformer names
Rollout notes
-
Flag status is alpha — opt-in for internal testing.
-
Plugins that integrate with composer actions should add dual registration before this is promoted to
beta/stable. -
Tests cover both states: existing acceptance/system specs cover flag-off; sibling
*-new-test.jsfiles cover flag-on (plus the new-component DOM selectors for the unified dropdown, toggle items, whisper indicator, and split-label spans).



