When a topic is pinned as a site banner, we include automatically
generated anchor links from the post in the banner's content... but do
not include the anchor functionality from the topic (topic seen below)
<img width="246" height="48" alt="image"
src="https://github.com/user-attachments/assets/6f145875-97a3-401c-bef6-ffd5cd6a22d1"
/>
This means we end up with a 0 dimension link in the banner content,
which is still reachable via keyboard and other forms of navigation. You
can see the outlined link below:
<img width="1097" height="174" alt="image"
src="https://github.com/user-attachments/assets/d5d50892-72f3-4ab8-8a33-2f2eb73fdc3d"
/>
This can be confusing if you're using a screenreader, and the anchor
links don't serve much purpose when included in these banners. Hiding
them with CSS avoids the issue.
This adds some new icon aliases so the lock icon can be overridden in
specific scenarios without applying globally to all locks:
* "topic.closed"
* "topic.opened"
* "category.restricted"
This enables customizations like:

```
import { apiInitializer } from "discourse/lib/api";
export default apiInitializer((api) => {
api.replaceIcon("topic.closed", "xmark");
api.replaceIcon("category.restricted", "shield-halved");
});
```
More documentation on this feature in
https://meta.discourse.org/t/change-icons-globally/87751
The `form-kit` container subtitle doesn't play well with the category
description. The issue experienced appears to be due to the flex
direction.
Since we can only update the category description from the topic and not
within the edit category page, I have moved the description to a
read-only span which follows a similar pattern that we use on other
forms.
followup for #33545
The `z-index: calc(z("mobile-composer") + 1);` (1101) was lower dan the
modal overlay on mobile (1300), which made the menu inaccessible.
it's now scoped to the desktop version of dmenu only, which should
suffice for desktop/tablet usecases.
Addressing some z-index issues with the first use of Dmenu within the
composer:
* z-index need an adjustment on mobile/tablets
* z-index need an adjustment when in fullscreen on desktop
Also limiting the max height on desktop to 30vh to avoid it potentially
scrolling of the page without a way to fix it by resizing composer
height.
Meta report:
https://meta.discourse.org/t/composer-more-menu-partly-hidden-behind-header-and-composer/372859
We want to add the ability to enable/disable users selecting Gravatar for their avatar.
This change adds a site setting to enable/disable the option (default to enabled.) This will prevent users from configuring Gravatars from that point on. It does not affect already configured avatars.
We're also taking this chance to hide some of the advanced settings from the UI.
The reasons for these changes is https://meta.discourse.org/t/-/89605
broke and admins were not able to log back in if they had previously
enabled the "read only" mode.
Thus ensued a deep dive into how all the "read only" modes worked, which
was made difficult due to the lack of tests.
The "cornerstone" of this PR is the `read_only_mixin.rb` file which was
improved to be able to differentiate between the "readonly" mode and the
"staff writes only" mode.
I then made use of the `allow_in_readonly_mode` and
`allow_in_staff_writes_only_mode` method to **explicitely** list all the
actions that should work in those modes.
I also added the "readonly" mixin to the `WebhooksController` since it
doesn't inherit from the `ApplicationController`.
I improved the security of the `/u/admin-login` endpoint by always
sending the same message no matter if we found or not an admin account
with the provided email address.
I added two system specs:
1. for ensuring that admins can log in via /u/admin-lgoin and then
clicking the link in the email they received while the site is in
readonly mode.
2. for ensuring the "staff writes only mode" is _actually_ tested by
ensuring a moderator can log in and create a topic while the site is in
that mode.
Plenty of specs were updated to ensure 100% converage of the various
"read only" modes.
JS assets defined under `admin/` in plugins are compiled into a separate
bundle that should be loaded for staff users only, but we currently load
it for all users including normal users and anons. This is wasteful and
unexpected since it could cause errors if an asset in the admin bundle
of a plugin imports a module from the admin bundle in core (which
doesn't get loaded for non-staff users).
1. Ensure `Creating optimized images...` message is only available
during optimization
2. Guard upload lookup for optimization
3. Replace undefined `stacktrace` method invocation with `backtrace`
Working on adding a lot more variables to core. Doing so enables easy
styling & changes to the main bulk of the interface.
**Example of what can be done**
Using these variables in core allows for the following stylistic changes
to be made with less than 100 lines of code. Most of which are changing
the value assigned to variables in this PR.

## 🔍 Overview
This update handles how editing post translations should work. It
ensures that when you are viewing a translated post, and try to edit a
post, you are shown a dialog to either edit the original or the
translation.
## 📸 Screenshots
<img width="843" alt="Screenshot 2025-06-24 at 14 49 53"
src="https://github.com/user-attachments/assets/fce82b2f-8c48-4f3b-ae0a-46a6afbf742e"
/>
When a FormKit textarea is focussed, there's currently no way to submit the form using the keyboard without tabbing out of the textarea.
This change lets the form be submitted using the Ctrl/Cmd+Enter key combination, bringing consistency with the post and chat composers.
Hardcoding the default limit and using a setting for the max value could
result in an error if the setting is changed to anything lower than the
hardcoded value for the default limit. It's better to use the setting
for the default value for the param and use the same max limit for the
setting as the max limit for the param.
Internal topic: t/156246.
This commit fixes a problem in the new reviewable item template that
uses the `reviewable-refresh/topic-link` component where a topic is
always shown as deleted even if the topic is present.
This is a general clean-up of the reviewable note form component,
including these changes:
- Hands off the note validation to FormKit validation checks (thanks
@jjaffeux and @lis2 for pointing these out).
- Removes the keyboard shortcut for form submission, in favour of
putting it into FormKit (#33527)
- Removes the check that disables the submit button when no note content
is entered.
This commit enhances the accuracy of post height calculations during cloaking and uncloaking in the Discourse post stream. The main improvements include:
- Capturing and restoring the full set of post styles (height, margin, etc.) instead of only the height, reducing rounding errors and visual glitches when posts are cloaked or uncloaked.
- Introducing a hysteresis mechanism to the uncloaking logic. This means posts now require a more significant and sustained intersection with the viewport before being uncloaked, preventing rapid toggling or flickering when a post is near the edge of the viewport.
- Refactoring the style application and visibility tracking logic to support these improvements and ensure smoother post rendering and scrolling performance.
Adds a `` input rule that uses the markdown parser to
create an image node.
Supporting this format surfaced an issue with dimension-less images, so
now the image node view uses the max width/height site settings in
addition to the loaded image natural dimensions to determine what a 100%
looks like – it's used for display, and also when a zoom-out button
action is taken, so we calculate the next step % of the calculated
dimensions.
This commit expands the extensibility of the post and post avatar components:
- Adds `post-article-class` and `post-avatar-class` value transformers, allowing plugins and themes to inject custom classes into post articles and avatars.
- Introduces value/behavior transformers for custom event listeners (`post-event-listener`) and avatar templates (`post-avatar-template`).
- Refactors error handling in post-stream to use a new post-stream-error-loading behavior transformer, enabling customization of error display logic.
- Deprecates the use of `api.modifyClass` for `component:scrolling-post-stream` with new deprecation warnings in the plugin API.
- Updates the deprecation documentation URL for widget overrides.
- Updates and expands integration tests to cover custom class injection on both post articles and avatars.
This commit addresses an issue where the removal of users/groups was targeting the wrong user.
---------
Co-authored-by: Sérgio Saquetim <saquetim@discourse.org>
At the moment emptying a input-text is making it `undefined` instead of
`null`. It's generally not a big deal but can be harder to work with.
There's also the very specific case of `JSON.stringify(data)` which will
remove keys if the value is undefined.