processTree() in dts-generator (used to bundle each vendored package's
.d.ts into @discourse/types) replaces certain nodes, like the `declare`
keyword, with an empty string. TypeScript scans a node's `pos` to
include its leading trivia (blank lines, comments), but skip() jumps
straight to `node.end` on replacement, so that trivia got silently
deleted along with the token being replaced.
This surfaced concretely in @floating-ui/dom's type declarations, where
`export { Axis }` was immediately followed by a blank line and `declare
type BivariantCallback = ...`. Removing `declare` also removed the blank
line, merging the two statements onto a single line and producing
invalid TypeScript in the generated
external-types/floating-ui__dom/index.d.ts (confirmed by PR #43280,
which bumped to a build containing this and failed `pnpm lint:types`
with `TS1005: ';' expected`).
The fix emits the node's leading trivia before applying the replacement,
so only the token itself is swallowed.
**Previously**, a signup flow could supply answers for the required
signup user fields (#43145) but had nowhere to ask for them: the only
extension point on the email step is the heading outlet, which sits
outside the form, so a field added there could not be `required` — the
Continue button belongs to core's form and never validated it.
**In this update**, a `code-login-email-step-fields` outlet inside that
form yields the form-kit `form`, so a connector can add its own fields
and have them validated and submitted along with the email. Core reads
only `email` off the submitted data, so extra keys go no further.
Previously, `site_contact_group_name` was written as a group name by the
About config page but as a group id by the all-settings page, which grew
a group picker in #40981 — and `GroupSettingValidator` was relaxed in
the
same commit to accept either, so the mismatched value saved cleanly and
the row still displayed the right group. `SystemMessage` looks the value
up by name, so an admin who set the contact group from All site settings
silently stopped it being invited to automated personal messages, and
the
About page then showed no group selected at all.
This change makes the id the stored format everywhere, because a group
name can be renamed or localized while an id cannot. `TypeSupervisor`
converts a name to its id on write, so console, API and plugin callers
that pass a name keep working; the validator now accepts ids only; and a
post-deploy migration converts the values sites have already stored,
post-deploy because the old code can only resolve a name. The About page
writes and reads the id, and `SystemMessage` resolves it through a new
`Discourse.site_contact_group`, which still accepts a name for sites
that
configure this through a `DISCOURSE_SITE_CONTACT_GROUP_NAME` global
override, since no migration can reach those. Both conversions go
through
`Group.find_by_id_or_name`.
The lookups guard on a digits-only match rather than casting, because
Rails turns `Group.find_by(id: "0support")` into `WHERE id = 0` — the
everyone group.
Two behaviour changes are worth calling out. Renaming the contact group
no longer breaks the setting; a spec asserted the opposite, which was
the
bug written down as intent. And the migration matches names
case-insensitively, so a site that stored `Staff` for a group named
`staff` — which `Group.exists?(name:)` never matched — starts inviting
that group again.
`site_settings.errors.invalid_group` now says "There's no such group",
since it fires for an unknown name as well as an unknown id and is
shared
with `AtLeastOneGroupValidator`, which has only ever validated ids.
Finally, the setting keeps its name. Renaming it would change a key that
self-hosters set through the environment and that third parties read,
and
the deprecated-settings alias does not cover global overrides, so that
belongs in its own change rather than in a fix.
**Previously**, code signup always collected required signup user fields
in a step of its own, even when the flow around it had already asked for
them.
**In this update**, a `code-login-user-field-values` value transformer
can supply those answers alongside the code, so the step is skipped.
They are withheld unless every required field ends up answered, since
the server reads any `user_fields` it receives as the complete set and
would fail the signup rather than prompt for the rest.
Previously, the create-topic button's click always opened the composer,
so a plugin that needs its own creation flow (for example, an event form
in event categories) had to hide the core button with a CSS hack and
render a second button next to it, losing the drafts menu along the way.
This change wraps the click in a `create-topic-button-click` behavior
transformer with the current category and tag as context, so a plugin
can take over the click (or call `next()` to keep the default) while
core's button, its label and icon transformers, and the drafts menu stay
in place.
Previously, the admin plugins page only exposed an outlet below the
list, so a plugin had nowhere to put a notice admins see before
scrolling through every installed plugin.
This change adds a matching `admin-above-plugins-index` outlet between
the page header and the filter controls, with the same `model` outlet
argument.
Adds a WarpDrive store (`service:warp-store`, LegacyMode + JSON:API
cache) alongside the existing `service:store`, routing requests through
Discourse's `ajax()` helper.
`RestCompatModel` bridges legacy `RestModel` callsites
(`get`/`set`/`setProperties`, `store.createRecord`, `save`,
`destroyRecord`) onto it.
Converts **badge, user-badge, topic-details, bookmark, tag, tag-group,
tag-info, tag-notification, tag-settings and archetype**, with per-model
schemas, request builders and payload normalizers.
Attributes outside a schema (plugin `add_to_serializer` fields, ad-hoc
`create` keys) are retained separately so nothing the server sends is
dropped.
The browse-more message was not consuming the tracking state’s
invalidation
token, so its new and unread counts remained stale after reading a
topic.
Recompute the message whenever the topic tracking state changes.
`DTextField` took `@placeholder` through a computed setter, with a
getter that fell back to `@placeholderKey`. Ember caches whatever a
computed setter returns at assignment time and never re-links the
computed's dependent keys, so the rendered placeholder depended on which
of the two arguments the caller passed first. Passing `@placeholder`
before `@placeholderKey` produced an empty placeholder; the composer
title only worked because its invocation happened to list the key first.
The attribute now binds to a plain computed over both arguments, and the
base `TextField` no longer binds `placeholder` itself so the two
bindings do not compete. Behaviour for existing callers is unchanged: an
explicit placeholder still wins over the key.
Rendering tests cover both argument orders and an explicit placeholder
overriding the key.
Extracted from the Ember 7 PR (#40407). Vite injects the
`development`/`production` export condition automatically, but our
rolldown config did not.
This is a no-op on `main` today, but becomes relevant since Ember 7
makes use of these flags.
Extracted from the Ember 7 PR (#40407). Ember 7's <LinkTo> no longer
considers `@models` when `@current-when` is a string.
Sidebar category/tag links pass a static multi-route `current-when`
string plus `@models` to stay highlighted across filter routes while
narrowing to a single category/tag. Under Ember 7 it causes every link
sharing the route list to light up at once.
This commit implements the `currentWhen` logic we want in JS. On Ember 6
this reproduces what <LinkTo> already does, so behaviour is unchanged.
`Application#injectTestHelpers` sets up Ember's legacy global test
helpers (`find`, `click`, `visit`, `pauseTest`, etc.). We've been
linting against these globals for a long time, and the system will be
finally removed in Ember 7.
We maintain the `pauseTest` global via a custom shim.
Extracted from the Ember 7 PR (#40407).
The Site Traffic Explorer rounded average session duration to an integer
before formatting it, so positive averages below half a second appeared
as 0s.
This commit preserves the fractional average and lets this metric
display up to two decimal places below one second. Durations of at least
one second keep the existing whole-second and minute formatting.
The following fixes are made here to make the transition to
`granular_anonymous_and_logged_in_groups_permissions` being
permanently enabled easier for people.
- **DEV: Show logged_in_users and anonymous_users in setting list**
- Previously, these groups would only show if
`granular_anonymous_and_logged_in_groups_permissions` is enabled,
but they are usable now everywhere in core, and this will help ease the
transition for themes + settings.
- **DEV: Add everyone -> logged_in_users map for theme settings**
- Theme settings now copy the same pattern as site settings, where
previously selected `everyone`
is dynamically changed to `logged_in_users` when
`granular_anonymous_and_logged_in_groups_permissions`
is enabled.
- **DEV: Show everyone group as everyone (legacy) in site setting group
list pickers**
- Makes it clearer this group will soon not be supported
- **DEV: Allow anonymous_users in AnonymousUser#in_any_groups?**
- Anon users should return true for `in_any_groups?` for
`anonymous_users` regardless of whether
`granular_anonymous_and_logged_in_groups_permissions` is on
Previously, local dates could only be inserted — once a chip was in the
rich editor there was no way to change it, so correcting a date meant
switching back to markdown and editing the bbcode by hand.
This change adds an edit button to each chip that reopens the date modal
pre-filled and replaces the node in place, reachable by keyboard and
named for the date it edits. It also fixes `DCalendarDateTimeInput`
never seeding pikaday with its initial `@date`, which opened the
calendar on the current month instead of the date being edited.
Previously, there was no reliable way to close or leave a chat channel
on a tablet (touch + desktop view): the leave button required hover, the
long-press menu only worked in mobile view, and the sidebar channel menu
was hidden on all touch devices.
On touch devices that can't hover (`@media (hover: none)`) in desktop
view, the leave button and the sidebar channel menu button are now
always visible instead of hover-revealed, and long-press opens the
channel menu. Swipe stays limited to the mobile layout, and narrow
mobile view is untouched. The sidebar button had been hidden on touch
(#41368, earlier #36969) so rows could be tapped to navigate without the
hover reveal in the way. As a plainly visible button, row taps still
navigate and the menu opens only from the button.
Reported in https://meta.discourse.org/t/close-a-chat/297444