Commit Graph

58677 Commits

Author SHA1 Message Date
c61b19e244 DEV: Update final {{raw}} usages (#32082) 2025-04-01 18:39:42 +01:00
083082f328 DEV: Remove the legacy widget post menu code (#31211)
https://meta.discourse.org/t/341014
2025-04-01 16:03:58 +01:00
4fb2fae0ed Build(deps): Bump pry from 0.14.2 to 0.15.2 (#32070)
Bumps [pry](https://github.com/pry/pry) from 0.14.2 to 0.15.2.
- [Release notes](https://github.com/pry/pry/releases)
- [Changelog](https://github.com/pry/pry/blob/master/CHANGELOG.md)
- [Commits](https://github.com/pry/pry/compare/v0.14.2...v0.15.2)

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-01 15:07:50 +02:00
05f533ee0e DEV: Add more granularity to the core features specs shared example 2025-04-01 14:54:11 +02:00
1519f3dde9 FIX: prevent duplicate category icons (#32103)
Follow up fix to #31795 to prevent duplicate icons in category chooser.

This change prevents duplicate icons or icon and emoji combinations.
2025-04-01 13:45:14 +04:00
69e6f94aa7 Build(deps-dev): Bump lefthook from 1.11.5 to 1.11.6 (#32091)
Bumps [lefthook](https://github.com/evilmartians/lefthook) from 1.11.5
to 1.11.6.
- [Release notes](https://github.com/evilmartians/lefthook/releases)
-
[Changelog](https://github.com/evilmartians/lefthook/blob/master/CHANGELOG.md)
-
[Commits](https://github.com/evilmartians/lefthook/compare/v1.11.5...v1.11.6)

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-01 11:41:50 +02:00
c6d443b9e9 Build(deps-dev): Bump html-entities from 2.5.3 to 2.6.0 (#32066)
Bumps [html-entities](https://github.com/mdevils/html-entities) from
2.5.3 to 2.6.0.
- [Release notes](https://github.com/mdevils/html-entities/releases)
-
[Changelog](https://github.com/mdevils/html-entities/blob/main/CHANGELOG.md)
-
[Commits](https://github.com/mdevils/html-entities/compare/v2.5.3...v2.6.0)

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-01 11:40:42 +02:00
74e8d1075e Build(deps): Bump faraday-retry from 2.2.1 to 2.3.0 (#32072)
Bumps [faraday-retry](https://github.com/lostisland/faraday-retry) from
2.2.1 to 2.3.0.
- [Release notes](https://github.com/lostisland/faraday-retry/releases)
-
[Changelog](https://github.com/lostisland/faraday-retry/blob/main/CHANGELOG.md)
-
[Commits](https://github.com/lostisland/faraday-retry/compare/v2.2.1...v2.3.0)

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-01 11:39:12 +02:00
e4911eb95f Build(deps-dev): Bump byebug from 11.1.3 to 12.0.0 (#32071)
Bumps [byebug](https://github.com/deivid-rodriguez/byebug) from 11.1.3
to 12.0.0.
- [Release notes](https://github.com/deivid-rodriguez/byebug/releases)
-
[Changelog](https://github.com/deivid-rodriguez/byebug/blob/master/CHANGELOG.md)
-
[Commits](https://github.com/deivid-rodriguez/byebug/compare/v11.1.3...v12.0.0)

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-01 11:38:21 +02:00
9402946781 Build(deps): Bump rdoc from 6.13.0 to 6.13.1 (#32068)
Bumps [rdoc](https://github.com/ruby/rdoc) from 6.13.0 to 6.13.1.
- [Release notes](https://github.com/ruby/rdoc/releases)
- [Changelog](https://github.com/ruby/rdoc/blob/master/History.rdoc)
- [Commits](https://github.com/ruby/rdoc/compare/v6.13.0...v6.13.1)

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-01 11:37:28 +02:00
e7d97e78ab Build(deps): Bump nokogiri from 1.18.6 to 1.18.7 (#32087)
Bumps [nokogiri](https://github.com/sparklemotion/nokogiri) from 1.18.6
to 1.18.7.
- [Release notes](https://github.com/sparklemotion/nokogiri/releases)
-
[Changelog](https://github.com/sparklemotion/nokogiri/blob/main/CHANGELOG.md)
-
[Commits](https://github.com/sparklemotion/nokogiri/compare/v1.18.6...v1.18.7)

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-01 11:36:11 +02:00
6eb0890f2b Build(deps-dev): Bump parser from 3.3.7.3 to 3.3.7.4 (#32088)
Bumps [parser](https://github.com/whitequark/parser) from 3.3.7.3 to
3.3.7.4.
-
[Changelog](https://github.com/whitequark/parser/blob/master/CHANGELOG.md)
-
[Commits](https://github.com/whitequark/parser/compare/v3.3.7.3...v3.3.7.4)

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-01 11:35:52 +02:00
c58237c01e FIX: always allow edit category bg color (#32102)
Some themes make use of the category background color even when using
emojis or icons as the category style.

Therefore we should always allow the color selector to be visible when
editing the category.
2025-04-01 13:31:41 +04:00
2a26555a7a DEV: Allow specifying a condition when preloading topics for topic list (#32101)
Currently when using `register_topic_preloader_associations`, we are
not able to specify a condition that is evaluated at runtime.

This commit allows specifying a condition and also keeps backward
compatibility.

```
   register_topic_preloader_associations(:linked_topic) { true }
   register_topic_preloader_associations({
     first_post: [uploads]
   })
```
2025-04-01 16:50:16 +08:00
b37591632d FIX: Update tag topic counts when bulk removing tags (#32095)
When bulk removing tags, the tag topic count isn't updated. You have to wait for the daily "ensure consistency" job to run for it to update properly. This is causing downstream problems, like the inability to use the "remove unused tags" feature.

The counter updating is handled on destroy by the join table model TopicTag. But we are calling #delete_all to perform the bulk tag removal, which bypasses callbacks.

This changes from #delete_all to #destroy_all, which invokes callbacks and updates the counters.
2025-04-01 11:46:07 +08:00
Sam
18433c45b9 FEATURE: when rich editor is enabled markdown is in monospace (#32097)
- adds .d-editor-container--rich-editor-enabled to d-editor-container
- conditionally swaps textarea to monospace when
siteSettings.rich_editor is enabled

We are doing this intentionally on the old composer when the
new rich editor is enabled to further differentiate the two and
to make it clear that markdown is the "source code"
2025-04-01 13:41:33 +10:00
7f25e13222 DEV: Double default capybara max wait time for certain tests take 3 (#32096)
This is a follow up to 59edec46a3de105e720bd2c716ae34d636794044

Test is flaky and seems to require more time to complete on CI so we
double the max wait time for now to see if it reduces the test's
flakiness.
2025-04-01 11:35:28 +08:00
b8dcabc2ac DEV: Add env var to silence SASS deprecation warnings (#32074)
SASS deprecation warnings are quite noisy, especially when
running specs, here is an example:

```
Deprecation Warning [mixed-decls]: Sass's behavior for declarations that appear after nested
rules will be changing to match the behavior specified by CSS in an upcoming
version. To keep the existing behavior, move the declaration above the nested
rule. To opt into the new behavior, wrap the declaration in `& {}`.

More info: https://sass-lang.com/d/mixed-decls

    ╷
52  │ ┌     @media screen and (max-width: 1048px) {
53  │ │       right: 0;
54  │ │     }
    │ └─── nested rule
... │
56  │       background: var(--secondary);
    │       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ declaration
    ╵
    theme-entrypoint/desktop.scss 56:5         @import
    /home/mb/repos/discourse/desktop.scss 3:9  root stylesheet
Deprecation Warning [mixed-decls]: Sass's behavior for declarations that appear after nested
rules will be changing to match the behavior specified by CSS in an upcoming
version. To keep the existing behavior, move the declaration above the nested
rule. To opt into the new behavior, wrap the declaration in `& {}`.
```

This commit adds an env var (`QUIET_SASS_DEPRECATIONS`) to control
turning them off.
2025-04-01 13:11:33 +10:00
1c1d687283 DEV: show no icon if icon name is missing and log at error level (#31866)
On 1st April 2025, we start showing no icons if the icon name used is a
deprecated one and therefore no longer part of the svg set.

We'll continue showing the messages with the correct icon name to aid
correction of these names.

Console logging will now be done at an error level for such icons.

We retain the behaviour of raising an error for such icons in plugins
from svg_sprite.rb in test environments, but removed this from
icon-library.js as it's harder to test the actual expected behaviour of
returning the original icon now that it's not part of the deprecation
workflow. (sinon.stub doesn't work well here for `isTesting` - the
alternative would be to override the environment.js module with
`proxyquire`) In any case, once we remove the mapping logic, we won't be
raising errors in test environment either for this scenario.
2025-04-01 10:54:48 +08:00
377a6554b9 FIX: create UserHistory only when setting changed (#32075)
Do not create UserHistory when new value is same as the old one. It is
even more important now, when we have bulk save option.
2025-04-01 09:56:33 +08:00
c4502b31e4 FEATURE: Bulk save site settings (#32013)
This feature adds a bulk-save feature to site settings.
2025-04-01 09:39:19 +08:00
b21c3e86f7 Revert "DEV: Allow specifying a condition when preloading topics for … (#32092)
…topic list (#32079)"

This reverts commit 70887351d7962947184e8351fea5745ee2b3a974.

This was causing errors:

```
NoMethodError (undefined method `call' for nil)
app/models/topic_list.rb:144:in `block in load_topics'
app/models/topic_list.rb:141:in `each'
app/models/topic_list.rb:141:in `load_topics'
app/models/topic_list.rb:75:in `topics'
app/serializers/suggested_topics_mixin.rb:15:in `include_suggested_topics?'
```
2025-04-01 09:37:47 +11:00
4a92b0afd8 DEV: Automatically reload dev when symlinked plugins change (#32073)
In a development environment, the server auto-restarter can't watch symlinked paths, which means that any symlinked plugins won't reload if their non-autoloaded files change.

This change does a simple resolve of the symlink for any symlinked plugins, and adds the real plugin path to those that it will auto-restart for.
2025-04-01 07:06:00 +11:00
119295f687 DEV: Preserve ids/classes in emails (#32084)
These aren't technically needed in the output, since we inline all
styles. However, there's no harm in including them, and having them
visible will improve the developer experience when restyling emails.
2025-03-31 20:58:45 +01:00
8b212d95ed DEV: skip watch word when message is created by bots (#31959)
Especially with AI we have no exact control on the words and this could
create and endless loop of errors.
2025-03-31 20:09:04 +02:00
2134925d7a UX: convert emdash+hyphen to a horizontal rule on rich editor (#32085)
iOS is automatically converting a double-hyphen (--) to an em-dash (–).

It may get in the way when we're trying to create an horizontal rule
with a ---, so this PR adds the "em-dash + hyphen" case to our input
rule regex.
2025-03-31 14:18:21 -03:00
ebed9cd013 DEV: Add a spec helper to upload a theme or a component 2025-03-31 17:28:09 +02:00
51db964859 FIX: allows to resize textarea (#32076)
This commit also ensures meta will resize with the textarea to avoid
meta being badly positioned.

I couldn't easily write a test working on all platforms.
2025-03-31 16:52:29 +02:00
70887351d7 DEV: Allow specifying a condition when preloading topics for topic list (#32079)
Currently when using `register_topic_preloader_associations`, we are not
able to specify a condition that is evaluated at runtime.

This commit allows specifying a condition and also keeps backward
compatibility.

```
   register_topic_preloader_associations({
     association: :linked_topic, condition: ->(topic) { topic.custom_field.present? }
   })
```
2025-03-31 21:32:05 +08:00
d12e0eb65d FIX: prevents d-menu trigger/untrigger propagation (#32078)
Before this fix a click on a d-menu trigger would fire a click on the
parent element, which could have undesired consequences.

This also fixes two things:
- moves float-kit/form-kit css files higher in the chain, so it's easier
to override
- increases the z-index of the notifications-tracking menu content or it
would be behind the timeline control
2025-03-31 13:47:24 +02:00
6b37aaf414 DEV: Fix FA icon deprecation 'smile' -> 'face-smile' (#32065)
Over the last few weeks, I have occasionally seen a deprecation warning
on my smartphone. Unfortunately, there was no corresponding entry in
/logs, and I never had the browser console available at the time.

I have now found the steps to reproduce the issue: the warning is
triggered by the emoji selection button in the chat.


https://github.com/user-attachments/assets/fbda7ee2-6967-48af-aa00-d081466e6aea

The icon used in the button was 'smile' instead of 'face-smile'. With
this fix, the warning no longer appears.

Otherwise, the icon will be missing once
https://github.com/discourse/discourse/pull/31866 is merged.

![After_1_April](https://github.com/user-attachments/assets/eed141e4-abd8-42c3-a27c-6dad7c4467f9)
2025-03-31 09:42:19 +01:00
b5f5c5a1ee DEV: Add @tracked to RestModel __state (#32064)
This property is used for the `isNew` and `isCreated` computed
properties, which are then used in templates. To make that work
correctly, we need to use `.set` or `@tracked`.
2025-03-31 09:26:31 +01:00
15751c89f4 FEATURE: auto contrast text color for categories (#32015)
This change removes the foreground color category setting to simplify
the category creation and edit process for admins.

Instead we determine the highest contrasting color (either white or
black) based on the background color.

Contrast algorithm is based on:
https://www.w3.org/TR/AERT/#color-contrast

We also implement the value transformer as part of this change, which
allows overriding the category text color.
2025-03-31 11:02:20 +04:00
d3c2bd015d FEATURE: Implement chat transcripts in rich editor (#31819)
This commit allows the ProseMirror rich editor to display chat
transcripts copied from chat using the "Copy" button.

The BBCode usually looks something like this:

```
[chat quote="hunter;29856;2025-03-20T07:13:04Z" channel="design gems 🎉" channelId="95"]
haha **ok** _cool_
[/chat]
```

But there are several variations that must be accounted for:

* Single message from single user
* Multiple messages from a single and multiple users
* Messages inside chat threads

The rich transcript extension has to ignore many of the chat transcript
markdown
tokens because they simply aren't necessary -- none of the ProseMirror
nodes need
to be editable. So, we basically recreate the same HTML that the chat
transcript markdown
rule does in the `toDOM()` function. Maybe in future we want to make the
markdown rule
do less and have this HTML creation in one place, but for now we need to
mirror in both files.

---------

Co-authored-by: Renato Atilio <renato@discourse.org>
2025-03-31 14:04:22 +10:00
b4cdc39e51 FEATURE: Allow rejected user details to be scrubbed (#31987)
When a site has the `must_approve_users` setting enabled, new user data is stored on the Reviewable model, including username, email, and any other data that is entered during signup. If the user is rejected, that data is retained, without a clear path to deleting it.

In order to allow data that could be PII to be removed, without breaking Discourse's audit and logging trails, this change scrubs the PII from the relevant `ReviewableUser` and `UserHistory` objects, replacing that data with who scrubbed it, and why.
2025-03-31 12:40:35 +11:00
2ac3c731de FIX: “Input data should be a string” error in ProseMirror (#32056)
When passing our ProsemirrorEditor component an initial value,
we were sometimes passing `null`, which causes the error in
the title of this commit. The fix here is to handle `null`
gracefully as string.

We originally found this on the category edit page, since
we have a `<DEditor />` in the form template form for the
category.
2025-03-31 11:40:19 +10:00
3f353a726b FIX: Prioritize !important CSS in emails (#32061)
7d587937 introduced css property deduplication in emails to workaround
bugs in some email clients. However, this didn't account for
admin-supplied customizations which were overriding core rules using
`!important`. This commit ensures that the deduplicator will always
prioritise `!important` rules, just like a browser.
2025-03-28 16:24:03 +00:00
12dffc5f7d DEV: supports bigger emoji in rich composer (#31933)
The rules are:
- between 1 and 3 emojis: bigger emoji
- more than 3 or any text or node in the same paragraph: regular emoji

This is implemented through a prose mirror plugin, which try to be smart
and recompute only edited paragraphs. Full scan on first load.

---------

Co-authored-by: Renato Atilio <renato@discourse.org>
2025-03-28 13:10:00 -03:00
a4b2b57db7 DEV: Introduce new breakpoints (#31886)
Usage:

```scss
@use "lib/viewport";

@include viewport.from(sm) {
  ...
}

@include viewport.until(lg) {
  ...
}

@include viewport.between(sm, lg) {
  ...
}
```

Values are inclusive on the lower bound, and exclusive on the upper
bound.
2025-03-28 14:50:06 +00:00
54c38e6163 DEV: Add a helper to enable current plugin in specs 2025-03-28 14:18:03 +01:00
387dc8c255 DEV: Drop 'cache_onebox_response_body' feature (#32035)
This is a hidden site setting which has never been publicized, and is
not recommended for use. If we decide to add a feature like this in
future as a visible site setting, it would need many more safeguards to
prevent misuse.
2025-03-28 10:55:29 +00:00
59edec46a3 DEV: Double default capybara wait time for certain flaky tests take 2 (#32058)
The `using_wait_time` DSL did not work when used in the `around` context
as the Capybara's session has not been initialized.

Follow-up to afde7cc1727a751e4d87be2ceee17f2d82edf89c
2025-03-28 16:09:15 +08:00
afde7cc172 DEV: Double default capybara wait time for certain flaky tests (#32057)
I'm not sure what is causing the tests to be flaky so I need more
information to determine where the problem is. For now, I'm doubling the
wait time to rule out any server side problem.
2025-03-28 13:49:42 +08:00
016c6a6894 DEV: Fix a flaky chat system test (#32055)
We are not waiting for asynchronous operations to complete before
executing our assertions.

### Reviewer notes

Example test failure:
https://github.com/discourse/discourse/actions/runs/14100970402/job/39496976787

```
Failure/Error: measurement = Benchmark.measure { example.run }
  expected `Chat::Channel.count` to have changed by 1, but was changed by 0

[Screenshot Image]: /__w/discourse/discourse/tmp/capybara/failures_r_spec_example_groups_chat_new_message_from_params_with_multiple_users_creates_a_dm_channel_when_none_exists_626.png

~~~~~~~ JS LOGS ~~~~~~~
~~~~~ END JS LOGS ~~~~~

./plugins/chat/spec/system/chat_new_message_spec.rb:57:in `block (3 levels) in <main>'
./spec/rails_helper.rb:619:in `block (3 levels) in <top (required)>'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/benchmark-0.4.0/lib/benchmark.rb:304:in `measure'
./spec/rails_helper.rb:619:in `block (2 levels) in <top (required)>'
./spec/rails_helper.rb:580:in `block (3 levels) in <top (required)>'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/timeout-0.4.3/lib/timeout.rb:185:in `block in timeout'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/timeout-0.4.3/lib/timeout.rb:192:in `timeout'
./spec/rails_helper.rb:570:in `block (2 levels) in <top (required)>'
./spec/rails_helper.rb:527:in `block (2 levels) in <top (required)>'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/webmock-3.25.1/lib/webmock/rspec.rb:39:in `block (2 levels) in <top (required)>'
```
2025-03-28 12:54:27 +08:00
b40cbfcb76 DEV: Move backfill into SiteSetting::Update service (#32037)
Some site settings support backfilling if the user specified it. This works fine for singular site settings sent to the SiteSettingsController#update endpoint, but with bulk save we need to support this for a list of settings as well.

This change alters the params format for SiteSetting::Update.

It also moves the backfill logic into the service.
2025-03-28 12:01:56 +08:00
654e858df3 DEV: remove singleton mixin (#32054)
Follow up to https://github.com/discourse/discourse/pull/30498.

Removes the singleton mixin and its test file.
2025-03-28 11:53:10 +08:00
1d49da467b DEV: Attempt to fix flaky search system test (#32053)
A system test in `system/search_spec.rb` was failing with the following
error frequently on CI:

```
Failure/Error: expect(search_page).to have_heading_text("Search")
  expected `#<PageObjects::Pages::Search:0x00007fb9fcd3f028>.has_heading_text?("Search")` to be truthy, got false

[Screenshot Image]: /__w/discourse/discourse/tmp/capybara/failures_r_spec_example_groups_search_when_using_full_page_search_on_mobile_works_and_clears_search_page_state_912.png

~~~~~~~ JS LOGS ~~~~~~~
(no logs)
~~~~~ END JS LOGS ~~~~~

./spec/system/search_spec.rb:42:in `block (3 levels) in <main>'
./spec/rails_helper.rb:619:in `block (3 levels) in <top (required)>'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/benchmark-0.4.0/lib/benchmark.rb:304:in `measure'
./spec/rails_helper.rb:619:in `block (2 levels) in <top (required)>'
./spec/rails_helper.rb:580:in `block (3 levels) in <top (required)>'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/timeout-0.4.3/lib/timeout.rb:185:in `block in timeout'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/timeout-0.4.3/lib/timeout.rb:192:in `timeout'
./spec/rails_helper.rb:570:in `block (2 levels) in <top (required)>'
./spec/rails_helper.rb:527:in `block (2 levels) in <top (required)>'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/webmock-3.25.1/lib/webmock/rspec.rb:39:in `block (2 levels) in <top (required)>'
```

The failure screenshot shows that the "user" is on the homepage even
though we have already clicked the search icon and ensured that the user
can see the search container. I suspect there is some sort of race
condition here since Capybara executes clicks in quick sucession where
we clicked on both the homepage logo and the search icon. It may be
possible that Ember redirected the user to the search page first
before the browser was able to finish navigating the user to the `/`
href.

### Reviewer notes

Test flaked in
https://github.com/discourse/discourse/actions/runs/14085443789/job/39448197089
with the following failure screenshot:


![image](https://github.com/user-attachments/assets/55a2834f-9357-460d-adeb-dc9a2fa475e8)
2025-03-28 11:16:43 +08:00
f1a67ecb37 DEV: Improvements to plugin API docs (#31988)
Follow-up to af03873d37decf8dba4278fc38bfcde7747a79f4
2025-03-28 09:59:25 +08:00
9d786ad870 DEV: Dump all threads for some flaky system tests (#32050)
This is to provide diagnostic information for some flaky system tests
which we are currently investigating. The previous attempt in
6f92f42eb83fea61b2712aaaefcc124e4939365d only dumped threads that
contains the `puma` gem in the backtrace but that is not enough. Since I
am now printing all thread backtraces, I added the
`dump_threads_on_failure` metadata
which will be used to determine if all the thread backtraces should be
printed out in the spec's failure output.
2025-03-28 09:54:05 +08:00
44da7b7fbf UX: Admin Dashboard title (#32022)
This adds a title to the admin dashboard to match other admin pages.
Adds a description to the admin dashboard.
Adds a plugin outlet after the header

BEFORE:
![CleanShot 2025-03-26 at 11 16
13@2x](https://github.com/user-attachments/assets/ebac6005-61b3-44ab-a97f-581e58e89eaf)


AFTER:
![CleanShot 2025-03-27 at 08 22
13@2x](https://github.com/user-attachments/assets/e64b1dae-8403-4545-9d6d-ac898e59a8bc)

---------

Co-authored-by: Jordan Vidrine <jordan@jordanvidrine.com>
2025-03-27 13:57:56 -06:00