This update adds randomized SVG placeholder designs for themes without
screenshots. Each placeholder pulls from the theme’s color palette to
give a better visual hint of what the theme might feel like.
There are a few different styles:
1. Envelope-style gradient – uses `tertiary`, `quaternary`, and
`highlight`
2. Soft blur gradient – uses `tertiary`, `quaternary`, and `highlight`
3. Mesh – uses `tertiary` and `secondary`
4. Layered waves – uses `tertiary`, `quaternary`, `highlight`, `danger`,
and `success`
The style is randomly picked per theme, so the grid looks more varied by
default.
<img width="1109" alt="image"
src="https://github.com/user-attachments/assets/5ede0ecf-a1e4-46c0-9fd2-bee4b6c672c2"
/>
Internal: /t/149935
---------
Co-authored-by: Jarek Radosz <jradosz@gmail.com>
Don't require password to create users with previous authentication in
the session, regardless of email/email verification during user creation
Before my changes we were calling `user.password_required!` in
`UserAuthenticator.authenticated?` based on whether authentication
session data contained a matching email address and email verified
externally. I believe `authenticated?` is intended for our own email
validation during the activation process, but shouldn't be a gate for
password requirement.
Even when we were setting `user.password_required`, then during user
creation we were creating random passwords to get around the blank
password restriction when creating accounts with external auth.
After my change we can remove the random password creation because we no
longer require a password when the session has previous authentication
info at all.
Looks like this extra password requirement may have been introduced as a
side effect during a previous refactor of `UserController` here:
51eff92170
Before that change, password requirement was simply based on whether
session[:authentication] existed, but after that change it was based on
the email/email_valid fields as well.
Building the Discourse JS app is very resource-intensive. This commit
introduces an `assemble_ember_build` script which will check the
existing content of the `dist/` directory and re-use the core build if
possible. Plugins will always be rebuilt.
For now, this functionality is only useful for multi-stage (i.e.
non-standard) Discourse deployments. But in future, this script may be
extended to pull the contents of the `dist/` directory from a remote
location.
This commits changes the language for components that aren't used on any themes to be "used/unused" instead of "active/inactive" throughout the new components listing page.
When an installed component doesn't have a description in its locale
files, we should fallback to the description that core has for the
component if it's one of the popular components that core features.
On sites with many components, serializing and rendering all components
in one-go on a page can take quite a bit of time. The new components
listing page that was introduced in
https://github.com/discourse/discourse/pull/32164 currently loads all
components in one-go, so this commit implements infinite-scrolling
pagination for the page to address this performance issue for sites with
many components.
Theme/component description is fetched via the `locale_fields` and
`theme_translation_overrides` associations on the `Theme` model. We're
currently not preloading these associations when serializing
themes/components, which causes an N+1 performance issue when accessing
the themes/components listing pages.
The `DMultiSelect` component provides a customizable multi-select
dropdown with support for both mouse and keyboard interactions.

### Parameters
#### `@loadFn` (required)
An async function that returns the data to populate the dropdown
options.
```javascript
const loadFn = async () => {
return [
{ id: 1, name: "Option 1" },
{ id: 2, name: "Option 2" },
];
};
```
#### `@compareFn`
A function used to determine equality between items. This is
particularly useful when working with complex objects. By default, `id`
will be used.
```javascript
const compareFn = (a, b) => {
return a.name === b.name;
};
```
#### `@selection`
An array of pre-selected items that will be displayed as selected when
the component renders.
```javascript
const selection = [
{ id: 1, name: "Option 1" },
{ id: 2, name: "Option 2" },
];
```
#### `@label`
Text label displayed in the trigger element when no items are selected.
```javascript
@label="Select options"
```
### Named Blocks
#### :selection
Block for customizing how selected items appear in the trigger.
```javascript
<:selection as |result|>{{result.name}}</:selection>
```
#### :result
Block for customizing how items appear in the dropdown list.
```javascript
<:result as |result|>{{result.name}}</:result>
```
#### :result
Block for customizing how errors appear in the component.
```javascript
<:error as |error|>{{error}}</:error>
```
### Example Usage
```javascript
<DMultiSelect
@loadFn={{this.loadOptions}}
@selection={{this.selectedItems}}
@compareFn={{this.compareItems}}
@label="Select options">
<:selection as |result|>{{result.name}}</:selection>
<:result as |result|>{{result.name}}</:result>
<:error as |error|>{{error}}</:error>
</DMultiSelect>
```
Co-Authored-By: Jordan Vidrine
<30537603+jordanvidrine@users.noreply.github.com>
---------
Co-authored-by: Jordan Vidrine <30537603+jordanvidrine@users.noreply.github.com>
Adds a plugin outlet below the login buttons in the login-required
splash screen.
Use case: adding a custom footer to the splash screen with links to tos
and privacy policy.
This makes two tweaks to the setup wizard:
1. Move the branding (site logo) step to after styling (look & feel).
2. Remove the corporate (your organization) step.
This commit removes the Ctrl+F search shortcut, which hijacks the
browser search shortcut in Discourse. We are doing this because there
has been many years of complaints around this behaviour, and now that
we have the new header search it has become even more annoying.
We originally added this because we lazy load posts in a topic, and it
might not have been immediately clear to users that they are not
searching
all the posts in the topic when pressing Ctrl+F. However, that was 10+
years
ago, most people are very familiar with lazy loading now, it doesn't
make sense
to keep this additional hijack for this one topic use case.
Search is still accessible by pressing the `/` shortcut.
When we do replaceText in the ProseMirror TextManipulation lib,
we are creating a regex for the provided markdown. However this markdown
can have things like * (which is valid for a markdown list), which also
doubles as a regex symbol. We can escape the markdown provided to
the regex first to fix the issue.
It takes a moment to sync site settings. Therefore, it is better to pass
new values to `refreshPage` function.
Also, it was not working for some fonts like `JetBrains Mono`.
SiteSetting key is `jet_brains_mono` but font family value should be
`JetBrains Mono`.
`PreviewBase#drawPills` expects a `homepageStyle` option, but on the logo wizard page we weren't passing any. In this change we pass one of the possible values: `hot`.
New lines on `alt`/`title` embedded media are not allowed, causing an
invalid serialized Markdown which is not supposed to contain new lines
for these attributes.
This PR removes them during HTML parsing – we are not creating this new
line scenario otherwise.
Previously, there was only those helpers in `crawler.html.erb` as we
added it in https://github.com/discourse/discourse/pull/32259
However, to keep it consistent, we need to add it in
`application.html.erb` as well.
Our `insertBlock` implementation was only taking the `firstChild` into
consideration.
We didn't notice this before because there's only one use of this in
core, quoting from a post, which has a single child.
This fix is necessary to support the `insert-block` call that the
https://github.com/discourse/discourse-templates plugin makes when
inserting a template.
Continues the work done on
https://github.com/discourse/discourse/pull/30815.
Adds a `footnote` node, parser, `^[inline]` input rule, toolbar button
item, and serializer.
Also adds a NodeView with an internal ProseMirror editor to edit the
footnote content.
When using a html block within the rich editor (currently only possible
after parsing it from Markdown), it would get serialized with the
default escape=true processing.
Additionally, html blocks are a continuous block, if a `\n\n` is seen
during cooking the block is closed, so we make sure at most one
continuous `\n` is seen in the html block node on the ProseMirror doc.
EDIT: It turns out, it's not so simple. I'm merging this as is, but it's
important to link the actual behavior to close a paragraph from
markdown-it:
0fe7ccb4b7/lib/rules_block/html_block.mjs (L6-L17)
On some cases during serialization (like with headings), the previous
node is still "open" when it's the "turn" of the node we're serializing.
In this case, checking the boundaries before writing was getting the
wrong state, because the `write` call uses `flushClose()`, which makes
sure we have the newlines from closing the previous node.
This would create scenarios like (notice the space before each node
below the heading)
```markdown
# Heading
🎉
# Heading
@mention
# Heading
#hashtag
```
This PR makes sure we call flushClose() before checking boundaries, and
adds tests for that.
Repro steps:
1. Go to `/admin/customize/site_texts`
2. Click edit on any translation
3. Go back by clicking on "Back to search"
4. Click edit on another translation
5. Change the text field in any way
Expected results:
The disabled state on the "Save changes" button is removed and you're
able to click it
Actual results:
The "Save changes" button remains disabled
This happens because the computed property for the button's disabled
state doesn't get re-evaluated when navigating between translation
strings because it doesn't include on the `siteText` property in its
dependent properties, so changing the site text doesn't invalidate the
old value for the disabled state and it always stays the same.
Meta topic:
https://meta.discourse.org/t/i-cant-save-edit-on-site-texts/360990?u=osama
Followup 198dc813750d7a0de98cc94372dea1222b97743b
Indicate the shortcut in the tooltip for the rich/markdown
editor toggle, and also add the control translation for mac
in `translateModKey`
## Requirements
Initially defined in
https://meta.discourse.org/t/software-engineer-frontend-ember-js-yuriy-kurant/353612/14?u=yaran.
1. On mobile devices and tablets, users can open the search input field
by tapping the search icon
2. When opened, the search input takes over the entire header area:
- Users can tap the sliders icon to navigate to the advanced search page
(/search)
- Users can tap Cancel to close search input
3. After submitting a search, results area will take over the entire
screen:
- Users can tap the X icon to clear the search term from the input field
- Users can tap Cancel to close search results area and return to their
previous page (i.e. search area overlays content)
## Implementation
1. When opened, the search input takes over the entire header area:
- changed panel width from `90vw` to `100vw`
- on initial render (when search input is empty), search panel hovers over the header section only (doesn't cover main content below)
- quick tip and recent searches lists are not displayed on mobile view
2. Tap on the sliders icon navigates to the advanced search page
(`/search`)
3. Tap on the **Cancel** button:
- closes search menu
- if the search term is present, it is cleared
4. Tap on the left-hand side **Search** icon triggers a topics search
## Dev notes
1. Added `// TODO` questioning `search` service `noResults` default value of `false`
2. Added animation on toggling header search panel (created `delayed-destroy` custom modifier)
3. Extracted in-context search filters into a standalone component `search-menu/active-filters`:
- mobile: active filters below search input
- desktop: active filters inside search input
3. Removed unnecessary top padding when search results are empty
4. Added `data-test-` attrs for Ember tests. Benefits:
- semantically `data-test-` attrs indicate that these parts of the layout are covered with tests
- decouples selector dependency on `id/class` names for testing purposes - eliminates risk of broken tests due to `id/class` name changes