From 20ff23ed027317191196b6de9fa3fffba4342558 Mon Sep 17 00:00:00 2001 From: David Battersby Date: Tue, 29 Jul 2025 16:24:36 +0400 Subject: [PATCH 01/43] DEV: remove redundant translations for disabled new topic btn (#33929) Small cleanup from #33495 to remove translations that are no longer used. --- config/locales/client.en.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index cbae1ab35778d..2e736d1b89ffc 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -3566,8 +3566,6 @@ en: one: "%{count} post in topic" other: "%{count} posts in topic" create: "New Topic" - create_disabled_category: "You're not allowed to create topics in this category" - create_disabled_tag: "You're not allowed to create topics with this tag" create_long: "Create a new Topic" open_draft: "Open Draft" private_message: "Start a message" From 8eddd6d1a2c56081a6d877987249b07ca12e3ee4 Mon Sep 17 00:00:00 2001 From: Kris Date: Tue, 29 Jul 2025 09:03:24 -0400 Subject: [PATCH 02/43] UX: show navigate to post button on ignored quotes (#33904) Currently we don't display quote controls if you're ignoring the quoted user: image Previously in the non-glimmer post stream, we would show the controls. The expand button was useless and showed no content, but the navigate to post button could still be useful for finding the ignored post. image This PR restores the jump to post button in the glimmer post stream, so someone can use it if they choose to: image --- .../discourse/app/components/post/quoted-content.gjs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/app/assets/javascripts/discourse/app/components/post/quoted-content.gjs b/app/assets/javascripts/discourse/app/components/post/quoted-content.gjs index da649b0051efd..f83c9fccc187f 100644 --- a/app/assets/javascripts/discourse/app/components/post/quoted-content.gjs +++ b/app/assets/javascripts/discourse/app/components/post/quoted-content.gjs @@ -95,11 +95,7 @@ export default class PostQuotedContent extends Component { } get shouldDisplayNavigateToPostButton() { - return ( - !this.args.quotedPostNotFound && - this.quotedPostUrl && - !this.isQuotedPostIgnored - ); + return !this.args.quotedPostNotFound && this.quotedPostUrl; } get shouldDisplayQuoteControls() { From d8141d18aa975c67fa7263c207dfcaab20f75c2f Mon Sep 17 00:00:00 2001 From: Kris Date: Tue, 29 Jul 2025 09:04:07 -0400 Subject: [PATCH 03/43] UX: on plugin index, unlink preinstall, simplify install banner (#33910) The link we were using only applied to newly moved plugins, so wasn't the best fit for all of them. This unlinks it. I've also removed the plugin install banner and inlined it in the page description. The blue banner was kind of intense for a message that's always visible on the page. We need to retain the class there so it can be hidden for sites on our hosting. Before: image After: image --- .../components/admin-plugins-list-item.gjs | 9 ++------- .../admin/addon/templates/plugins-index.gjs | 20 +++++++++---------- app/assets/stylesheets/admin/plugins.scss | 9 +++++---- 3 files changed, 17 insertions(+), 21 deletions(-) diff --git a/app/assets/javascripts/admin/addon/components/admin-plugins-list-item.gjs b/app/assets/javascripts/admin/addon/components/admin-plugins-list-item.gjs index d394ad1a6253a..1c4cc3d10c024 100644 --- a/app/assets/javascripts/admin/addon/components/admin-plugins-list-item.gjs +++ b/app/assets/javascripts/admin/addon/components/admin-plugins-list-item.gjs @@ -121,14 +121,9 @@ export default class AdminPluginsListItem extends Component { > {{@plugin.version}}
{{#if this.isPreinstalled}} - + {{i18n "admin.plugins.preinstalled"}} - + {{else}} {{/if}} diff --git a/app/assets/javascripts/admin/addon/templates/plugins-index.gjs b/app/assets/javascripts/admin/addon/templates/plugins-index.gjs index b3862ce657451..7138a2a854ed1 100644 --- a/app/assets/javascripts/admin/addon/templates/plugins-index.gjs +++ b/app/assets/javascripts/admin/addon/templates/plugins-index.gjs @@ -1,9 +1,10 @@ +import { concat } from "@ember/helper"; +import { htmlSafe } from "@ember/template"; import RouteTemplate from "ember-route-template"; import DBreadcrumbsItem from "discourse/components/d-breadcrumbs-item"; import DPageHeader from "discourse/components/d-page-header"; import NavItem from "discourse/components/nav-item"; import PluginOutlet from "discourse/components/plugin-outlet"; -import icon from "discourse/helpers/d-icon"; import lazyHash from "discourse/helpers/lazy-hash"; import { i18n } from "discourse-i18n"; import AdminFilterControls from "admin/components/admin-filter-controls"; @@ -15,8 +16,14 @@ export default RouteTemplate( ' + (i18n "admin.plugins.howto") + "" + ) + }} > <:breadcrumbs> @@ -48,13 +55,6 @@ export default RouteTemplate( - - {{#if @controller.model.length}} Date: Tue, 29 Jul 2025 08:07:15 -0500 Subject: [PATCH 04/43] UX: fix-overflow (#33914) --- app/assets/stylesheets/common/base/menu-panel.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/app/assets/stylesheets/common/base/menu-panel.scss b/app/assets/stylesheets/common/base/menu-panel.scss index 74fb49425e71b..9035742ccb765 100644 --- a/app/assets/stylesheets/common/base/menu-panel.scss +++ b/app/assets/stylesheets/common/base/menu-panel.scss @@ -679,6 +679,7 @@ > div { display: flex; flex-direction: column; + overflow: hidden; } } From 9e65d5c008d468de5ee6f4d486346117a6bcbe6f Mon Sep 17 00:00:00 2001 From: Jordan Vidrine <30537603+jordanvidrine@users.noreply.github.com> Date: Tue, 29 Jul 2025 08:07:30 -0500 Subject: [PATCH 05/43] UX: Content border variables 2 (#33911) --- app/assets/stylesheets/common/base/user.scss | 18 +++++++++--------- .../common/components/user-stream-item.scss | 4 ++-- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/app/assets/stylesheets/common/base/user.scss b/app/assets/stylesheets/common/base/user.scss index 302c809701196..0ecf0f213afb0 100644 --- a/app/assets/stylesheets/common/base/user.scss +++ b/app/assets/stylesheets/common/base/user.scss @@ -17,7 +17,7 @@ grid-row-end: 2; .horizontal-overflow-nav { - border-block: 1px solid var(--primary-low); + border-block: 1px solid var(--content-border-color); } .group-dropdown { @@ -36,7 +36,7 @@ grid-column-end: 3; grid-row-start: 2; grid-row-end: 3; - border-bottom: 1px solid var(--primary-low); + border-bottom: 1px solid var(--content-border-color); font-size: var(--font-down-1); } @@ -120,7 +120,7 @@ .user-notifications-filter { display: block; width: 100%; - border-bottom: 0.5px solid var(--primary-low); + border-bottom: 0.5px solid var(--content-border-color); } } @@ -146,7 +146,7 @@ .secondary { display: inline-block; width: 100%; - border-top: 1px solid var(--primary-low); + border-top: 1px solid var(--content-border-color); .btn { padding: 4px 12px; @@ -198,7 +198,7 @@ .details { background: rgb(var(--secondary-rgb), 0.8); - border-bottom: 1px solid var(--primary-low); + border-bottom: 1px solid var(--content-border-color); .groups { display: inline; @@ -836,12 +836,12 @@ .pref-passkeys, .pref-auth-tokens { .row { - border-top: 1px solid var(--primary-low); + border-top: 1px solid var(--content-border-color); padding: 0.5em 0; margin: 0.5em 0; &:last-child { - border-bottom: 1px solid var(--primary-low); + border-bottom: 1px solid var(--content-border-color); } } } @@ -895,7 +895,7 @@ width: 100%; display: flex; justify-content: space-between; - border-top: 1px solid var(--primary-low); + border-top: 1px solid var(--content-border-color); margin: 0.25em 0; padding: 0.25em 0; align-items: center; @@ -928,7 +928,7 @@ .wrapper { display: flex; - border: 1px solid var(--primary-low); + border: 1px solid var(--content-border-color); width: 100%; } diff --git a/app/assets/stylesheets/common/components/user-stream-item.scss b/app/assets/stylesheets/common/components/user-stream-item.scss index 050653003a0e6..62e983f65dc79 100644 --- a/app/assets/stylesheets/common/components/user-stream-item.scss +++ b/app/assets/stylesheets/common/components/user-stream-item.scss @@ -8,7 +8,7 @@ .item, .user-stream-item { background: var(--d-content-background, var(--secondary)); - border-bottom: 1px solid var(--primary-low); + border-bottom: 1px solid var(--content-border-color); padding: 1em 0.53em; list-style: none; @@ -121,7 +121,7 @@ li.notification { padding: var(--space-3); - border-bottom: 1px solid var(--primary-low); + border-bottom: 1px solid var(--content-border-color); a { align-items: center; From fa742b8e248f14036e4aeeac6110b6bfe28aedef Mon Sep 17 00:00:00 2001 From: chapoi <101828855+chapoi@users.noreply.github.com> Date: Tue, 29 Jul 2025 15:21:33 +0200 Subject: [PATCH 06/43] UX: Horizon > chat-drawer z-index adjustments for popup content (#33932) Because Horizon has a different drawer-composer interaction as default (#33677) this means that certain floating elements such as the `fk-d-menu[data-identifier="usercard"]` and the `chat-message-actions-container` need an adjusted z-index to remain visible. Scoped to chat-drawer for safety. --- themes/horizon/scss/chat.scss | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/themes/horizon/scss/chat.scss b/themes/horizon/scss/chat.scss index 960e50dbbd320..c3c7f5a63eb36 100644 --- a/themes/horizon/scss/chat.scss +++ b/themes/horizon/scss/chat.scss @@ -45,6 +45,19 @@ body.has-full-page-chat { } } +// the below are elements whose z-index needs to be adjusted due to the above change +.chat-message-actions-container { + .chat-drawer.is-expanded & { + z-index: calc(z("composer", "dropdown") + 1); + } +} + +.fk-d-menu[data-identifier="usercard"] { + .has-drawer-chat & { + z-index: z("modal", "dialog"); + } +} + .chat-drawer { .peek-mode-active & { max-width: 90vw; From 3c714044d9f13f5a7d96a4b64e3f3007eb5e0f9d Mon Sep 17 00:00:00 2001 From: Kris Date: Tue, 29 Jul 2025 09:24:39 -0400 Subject: [PATCH 07/43] UX: clarify color palette activation button (#33909) Slightly longer, but clearer if you're new and don't know what the default theme's name is yet Before: image After: image --- config/locales/client.en.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index 2e736d1b89ffc..e7776838a32f2 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -7088,7 +7088,7 @@ en: dark_mode_warning: "You're currently using dark mode. When setting an active color palette the colors will be applied to the light mode of the theme. If you want to change the default dark mode colors, please edit the dark mode site setting." title: "Colors" edit: "Edit" - set_default: "Set as active on %{theme}" + set_default: "Activate on default theme (%{theme})" set_default_success: "%{schemeName} set as active palette for %{themeName}" saved_refreshing: "Saved! Refreshing colors..." from_theme: "From theme: %{name}" From a7bd3e21b4ddd535ebe3ca13e6fd45b482d7cbde Mon Sep 17 00:00:00 2001 From: Jordan Vidrine <30537603+jordanvidrine@users.noreply.github.com> Date: Tue, 29 Jul 2025 09:23:45 -0500 Subject: [PATCH 08/43] DEV: Standardize x & y spacing variables (#33934) --- .../stylesheets/common/base/discourse.scss | 24 +++++++++---------- .../stylesheets/common/base/header.scss | 2 +- .../stylesheets/desktop/topic-list.scss | 12 +++++----- app/assets/stylesheets/mobile/topic-post.scss | 4 ++-- 4 files changed, 20 insertions(+), 22 deletions(-) diff --git a/app/assets/stylesheets/common/base/discourse.scss b/app/assets/stylesheets/common/base/discourse.scss index 4f0fefad4ff53..04ac959b84815 100644 --- a/app/assets/stylesheets/common/base/discourse.scss +++ b/app/assets/stylesheets/common/base/discourse.scss @@ -12,8 +12,6 @@ --d-input-bg-color--disabled: var(--primary-very-low); --d-input-text-color--disabled: var(--primary-medium); --d-input-border--disabled: 1px solid var(--primary-low); - --d-input-padding-h: 0.65em; - --d-input-padding-v: 0.5em; --d-nav-color: var(--primary); --d-nav-bg-color: transparent; --d-nav-color--hover: var(--tertiary); @@ -32,15 +30,15 @@ --nav-horizontal-padding: 0em; --d-main-content-gap: var(--space-8); --main-outlet-padding-top: var(--space-6); - --main-outlet-padding-v: 0em; + --main-outlet-padding-x: 0em; --main-outlet-padding-bottom: 0em; --table-border-width: 1px; --d-topic-list-avatar-size: 24px; --d-topic-list-title-font-size: var(--font-up-1); --d-topic-list-metadata-top-space: var(--space-1); - --d-topic-list-data-padding-h: var(--space-3); - --d-topic-list-data-padding-v: var(--space-1); - --d-topic-list-header-data-padding-h: var(--space-3); + --d-topic-list-data-padding-y: var(--space-3); + --d-topic-list-data-padding-x: var(--space-1); + --d-topic-list-header-data-padding-y: var(--space-3); --d-topic-list-header-data-padding-v: var(--space-1); --d-topic-list-likes-views-posts-width: 4.3em; --d-topic-list-data-padding-inline-start: var(--space-3); @@ -49,8 +47,8 @@ --category-boxes-text-alignment: center; --d-topic-list-header-background-color: var(--secondary); --d-topic-list-header-text-color: var(--primary-medium); - --d-topic-list-margin-h: 0em; - --d-topic-list-margin-v: 0em; + --d-topic-list-margin-y: 0em; + --d-topic-list-margin-x: 0em; --d-topic-list-margin-bottom: 10px; --d-tag-horizontal-padding: 0em; --d-tag-font-weight: initial; @@ -63,14 +61,14 @@ --category-boxes-title-font-size: var(--font-up-2); --d-category-boxes-margin-top: var(--space-4); --d-categories-list-title-margin-bottom: 0em; - --d-header-padding-v: 0.67em; + --d-header-padding-x: 0.67em; --d-table-border-top-height: 3px; - --d-wrap-padding-h: 0.67em; + --d-wrap-padding-x: 0.67em; --topic-title-font-weight: 400; --topic-title-font-weight--visited: 400; --topic-list-item-background-color: var(--secondary); --topic-list-item-background-color--visited: var(--secondary); - --list-container-horizontal-padding: 0em; + --list-container-padding-x: 0em; --d-topic-list-header-font-size: initial; } @@ -517,7 +515,7 @@ textarea { max-width: var(--d-max-width); margin-right: auto; margin-left: auto; - padding: 0 var(--d-wrap-padding-h); + padding: 0 var(--d-wrap-padding-x); @include viewport.until(sm) { min-width: 0; @@ -799,7 +797,7 @@ form { } #main-outlet { - padding: var(--main-outlet-padding-top) var(--main-outlet-padding-v) + padding: var(--main-outlet-padding-top) var(--main-outlet-padding-x) var(--main-outlet-padding-bottom); .mobile-view & { diff --git a/app/assets/stylesheets/common/base/header.scss b/app/assets/stylesheets/common/base/header.scss index be1577443c062..82be5de2f67d5 100644 --- a/app/assets/stylesheets/common/base/header.scss +++ b/app/assets/stylesheets/common/base/header.scss @@ -24,7 +24,7 @@ box-sizing: border-box; width: 100%; height: 100%; - padding: 0 var(--d-header-padding-v); + padding: 0 var(--d-header-padding-x); .contents { display: flex; diff --git a/app/assets/stylesheets/desktop/topic-list.scss b/app/assets/stylesheets/desktop/topic-list.scss index 8b0d08d9861c3..9a15ed3d2bfe0 100644 --- a/app/assets/stylesheets/desktop/topic-list.scss +++ b/app/assets/stylesheets/desktop/topic-list.scss @@ -26,7 +26,7 @@ .topic-list { @extend .topic-list-icons; - margin: var(--d-topic-list-margin-h) var(--d-topic-list-margin-v) + margin: var(--d-topic-list-margin-y) var(--d-topic-list-margin-x) var(--d-topic-list-margin-bottom); .topic-list-header { @@ -34,14 +34,14 @@ } .topic-list-header .topic-list-data { - padding: var(--d-topic-list-header-data-padding-h) - var(--d-topic-list-header-data-padding-v); + padding: var(--d-topic-list-header-data-padding-y) + var(--d-topic-list-header-data-padding-x); color: var(--d-topic-list-header-text-color); } .topic-list-data { - padding: var(--d-topic-list-data-padding-h) - var(--d-topic-list-data-padding-v); + padding: var(--d-topic-list-data-padding-y) + var(--d-topic-list-data-padding-x); &:first-of-type { padding-inline-start: var(--d-topic-list-data-padding-inline-start); @@ -317,5 +317,5 @@ .container.list-container { position: relative; - padding: 0 var(--list-container-horizontal-padding); + padding: 0 var(--list-container-padding-x); } diff --git a/app/assets/stylesheets/mobile/topic-post.scss b/app/assets/stylesheets/mobile/topic-post.scss index 48771e21ed6ff..e56b29bef9627 100644 --- a/app/assets/stylesheets/mobile/topic-post.scss +++ b/app/assets/stylesheets/mobile/topic-post.scss @@ -408,8 +408,8 @@ span.highlighted { .read-state { // contained within the padding to prevent vertical overflow - max-width: var(--d-wrap-padding-h); - right: calc(var(--d-wrap-padding-h) * -1); + max-width: var(--d-wrap-padding-x); + right: calc(var(--d-wrap-padding-x) * -1); font-size: 6px; // static size to avoid overflow issues svg { From f9d2a68aa6cfaa9c64e57026823b506910ae175e Mon Sep 17 00:00:00 2001 From: Jordan Vidrine <30537603+jordanvidrine@users.noreply.github.com> Date: Tue, 29 Jul 2025 09:49:56 -0500 Subject: [PATCH 09/43] FIX: correct variable name (#33935) --- app/assets/stylesheets/common/base/discourse.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/stylesheets/common/base/discourse.scss b/app/assets/stylesheets/common/base/discourse.scss index 04ac959b84815..ab7340d058d4b 100644 --- a/app/assets/stylesheets/common/base/discourse.scss +++ b/app/assets/stylesheets/common/base/discourse.scss @@ -39,7 +39,7 @@ --d-topic-list-data-padding-y: var(--space-3); --d-topic-list-data-padding-x: var(--space-1); --d-topic-list-header-data-padding-y: var(--space-3); - --d-topic-list-header-data-padding-v: var(--space-1); + --d-topic-list-header-data-padding-x: var(--space-1); --d-topic-list-likes-views-posts-width: 4.3em; --d-topic-list-data-padding-inline-start: var(--space-3); --d-topic-list-data-padding-inline-end: var(--space-3); From e508ba7291c4541f7af8d2c18c4e65d156329eee Mon Sep 17 00:00:00 2001 From: David Taylor Date: Tue, 29 Jul 2025 16:24:37 +0100 Subject: [PATCH 10/43] UX: Modernize footnote implementation and enable in preview (#33928) This commit reimplements inline footnotes to use our standard FloatKit tooltip system, which also allows us to enable them in the markdown composer preview. --- .../api-initializers/inline-footnotes.gjs | 66 +++++++++ .../initializers/inline-footnotes.js | 139 ------------------ .../assets/stylesheets/footnotes.scss | 76 ++-------- .../javascripts/acceptance/footnote-test.js | 37 ++--- 4 files changed, 86 insertions(+), 232 deletions(-) create mode 100644 plugins/footnote/assets/javascripts/api-initializers/inline-footnotes.gjs delete mode 100644 plugins/footnote/assets/javascripts/initializers/inline-footnotes.js diff --git a/plugins/footnote/assets/javascripts/api-initializers/inline-footnotes.gjs b/plugins/footnote/assets/javascripts/api-initializers/inline-footnotes.gjs new file mode 100644 index 0000000000000..18aa97dff7d31 --- /dev/null +++ b/plugins/footnote/assets/javascripts/api-initializers/inline-footnotes.gjs @@ -0,0 +1,66 @@ +import { htmlSafe } from "@ember/template"; +import { apiInitializer } from "discourse/lib/api"; +import { iconHTML } from "discourse/lib/icon-library"; +import DTooltipInstance from "float-kit/lib/d-tooltip-instance"; + +const TooltipContentComponent = ; + +export default apiInitializer((api) => { + function onFootnoteClick(event) { + event.preventDefault(); + + const tooltipService = api.container.lookup("service:tooltip"); + + const instance = new DTooltipInstance(api.container, { + identifier: "inline-footnote", + interactive: true, + closeOnScroll: false, + closeOnClickOutside: true, + component: TooltipContentComponent, + data: { + contentHtml: event.target.dataset.footnoteContent, + }, + }); + instance.trigger = event.target; + instance.detachedTrigger = true; + + tooltipService.show(instance); + } + + api.decorateCookedElement((elem) => { + if ( + !api.container.lookup("service:site-settings").display_footnotes_inline + ) { + return; + } + + const footnoteRefs = elem.querySelectorAll("sup.footnote-ref"); + + footnoteRefs.forEach((footnoteRef) => { + const refLink = footnoteRef.querySelector("a"); + if (!refLink) { + return; + } + + const footnoteId = refLink.getAttribute("href"); + const footnoteContent = elem.querySelector(footnoteId)?.innerHTML; + + const expandableFootnote = document.createElement("a"); + expandableFootnote.classList.add("expand-footnote"); + expandableFootnote.innerHTML = iconHTML("ellipsis"); + expandableFootnote.href = ""; + expandableFootnote.role = "button"; + expandableFootnote.dataset.footnoteId = footnoteId; + expandableFootnote.dataset.footnoteContent = footnoteContent; + expandableFootnote.addEventListener("click", onFootnoteClick); + + footnoteRef.after(expandableFootnote); + }); + + if (footnoteRefs.length) { + elem.classList.add("inline-footnotes"); + } + }); +}); diff --git a/plugins/footnote/assets/javascripts/initializers/inline-footnotes.js b/plugins/footnote/assets/javascripts/initializers/inline-footnotes.js deleted file mode 100644 index 934ad5d581535..0000000000000 --- a/plugins/footnote/assets/javascripts/initializers/inline-footnotes.js +++ /dev/null @@ -1,139 +0,0 @@ -import { createPopper } from "@popperjs/core"; -import { iconHTML } from "discourse/lib/icon-library"; -import { withPluginApi } from "discourse/lib/plugin-api"; - -let inlineFootnotePopper; - -function applyInlineFootnotes(elem) { - const footnoteRefs = elem.querySelectorAll("sup.footnote-ref"); - - footnoteRefs.forEach((footnoteRef) => { - const refLink = footnoteRef.querySelector("a"); - if (!refLink) { - return; - } - - const expandableFootnote = document.createElement("a"); - expandableFootnote.classList.add("expand-footnote"); - expandableFootnote.innerHTML = iconHTML("ellipsis"); - expandableFootnote.href = ""; - expandableFootnote.role = "button"; - expandableFootnote.dataset.footnoteId = refLink.getAttribute("href"); - - footnoteRef.after(expandableFootnote); - }); - - if (footnoteRefs.length) { - elem.classList.add("inline-footnotes"); - } -} - -function buildTooltip() { - const template = document.createElement("template"); - template.innerHTML = ` - - `.trim(); - - return template.content.firstChild; -} - -function footnoteEventHandler(event) { - const tooltip = document.getElementById("footnote-tooltip"); - const displayedFootnoteId = tooltip?.dataset.footnoteId; - const expandableFootnote = event.target; - const footnoteId = expandableFootnote.dataset.footnoteId; - - inlineFootnotePopper?.destroy(); - tooltip?.removeAttribute("data-show"); - tooltip?.removeAttribute("data-footnote-id"); - - if (!event.target.classList.contains("expand-footnote")) { - // dismissing the tooltip by clicking outside - return; - } - - event.preventDefault(); - event.stopPropagation(); - - if (displayedFootnoteId === footnoteId) { - // dismissing the tooltip by clicking the footnote button - return; - } - - // append footnote to tooltip body - const footnoteContent = tooltip.querySelector(".footnote-tooltip-content"); - let cooked = expandableFootnote.closest(".cooked"); - if (cooked.dataset.refPostId != null) { - // For full screen tables, redirect - cooked = document.querySelector( - `article[data-post-id="${cooked.dataset.refPostId}"] .cooked` - ); - } - const newContent = cooked.querySelector(footnoteId); - footnoteContent.innerHTML = newContent.innerHTML; - - // display tooltip - tooltip.dataset.show = ""; - tooltip.dataset.footnoteId = footnoteId; - - // setup popper - inlineFootnotePopper?.destroy(); - inlineFootnotePopper = createPopper(expandableFootnote, tooltip, { - modifiers: [ - { - name: "arrow", - options: { element: tooltip.querySelector("#arrow") }, - }, - { - name: "preventOverflow", - options: { - altAxis: true, - padding: 5, - }, - }, - { - name: "offset", - options: { - offset: [0, 12], - }, - }, - ], - }); -} - -export default { - name: "inline-footnotes", - - initialize(container) { - if (!container.lookup("service:site-settings").display_footnotes_inline) { - return; - } - - document.body.append(buildTooltip()); - window.addEventListener("click", footnoteEventHandler, true); - - withPluginApi((api) => { - api.decorateCookedElement((elem) => applyInlineFootnotes(elem), { - onlyStream: true, - id: "inline-footnotes", - }); - - api.onPageChange(() => { - inlineFootnotePopper?.destroy(); - - const tooltip = document.getElementById("footnote-tooltip"); - tooltip?.removeAttribute("data-show"); - tooltip?.removeAttribute("data-footnote-id"); - }); - }); - }, - - teardown() { - inlineFootnotePopper?.destroy(); - window.removeEventListener("click", footnoteEventHandler); - document.getElementById("footnote-tooltip")?.remove(); - }, -}; diff --git a/plugins/footnote/assets/stylesheets/footnotes.scss b/plugins/footnote/assets/stylesheets/footnotes.scss index 3c07028e95287..76189c9965538 100644 --- a/plugins/footnote/assets/stylesheets/footnotes.scss +++ b/plugins/footnote/assets/stylesheets/footnotes.scss @@ -38,78 +38,22 @@ } } -#footnote-tooltip { - background-color: var(--primary-low); - color: var(--primary); - padding: 0.5em; - font-size: var(--font-down-1); - border-radius: 3px; - display: none; - z-index: z("modal", "tooltip"); - max-width: 400px; +.fk-d-tooltip__content[data-identifier="inline-footnote"] { overflow-wrap: break-word; - box-sizing: border-box; + font-size: var(--font-down-1); - .mobile-view & { - // tooltips are positioned 5px from the left - // - 10px accounts for this and gives 5px space on the right - max-width: calc(100dvw - 10px); + .footnote-backref { + display: none; } - .footnote-tooltip-content { - overflow: hidden; - - .footnote-backref { - display: none; - } - - img { - object-fit: cover; - max-width: 100%; - } - - p { - margin: 0; - } + img { + object-fit: cover; + max-width: 100%; } -} -#footnote-tooltip[data-show] { - display: block; -} - -#arrow, -#arrow::before { - position: absolute; - width: 10px; - height: 10px; - background: inherit; -} - -#arrow { - visibility: hidden; -} - -#arrow::before { - visibility: visible; - content: ""; - transform: rotate(45deg); -} - -#footnote-tooltip[data-popper-placement^="top"] > #arrow { - bottom: -4px; -} - -#footnote-tooltip[data-popper-placement^="bottom"] > #arrow { - top: -4px; -} - -#footnote-tooltip[data-popper-placement^="left"] > #arrow { - right: -4px; -} - -#footnote-tooltip[data-popper-placement^="right"] > #arrow { - left: -4px; + p { + margin: 0; + } } .ProseMirror { diff --git a/plugins/footnote/test/javascripts/acceptance/footnote-test.js b/plugins/footnote/test/javascripts/acceptance/footnote-test.js index 9af93cead27f6..8867b7083e674 100644 --- a/plugins/footnote/test/javascripts/acceptance/footnote-test.js +++ b/plugins/footnote/test/javascripts/acceptance/footnote-test.js @@ -1,9 +1,12 @@ -import { click, visit } from "@ember/test-helpers"; +import { click, triggerEvent, visit } from "@ember/test-helpers"; import { test } from "qunit"; import { cloneJSON } from "discourse/lib/object"; import topicFixtures from "discourse/tests/fixtures/topic"; import { acceptance } from "discourse/tests/helpers/qunit-helpers"; +const TOOLTIP_SELECTOR = + ".fk-d-tooltip__content[data-identifier='inline-footnote']"; + acceptance("Discourse Footnote Plugin", function (needs) { needs.settings({ display_footnotes_inline: true, @@ -29,44 +32,24 @@ acceptance("Discourse Footnote Plugin", function (needs) { test("displays the footnote on click", async function (assert) { await visit("/t/-/45"); - assert.dom("#footnote-tooltip", document.body).exists(); - // open await click(".expand-footnote"); - assert - .dom(".footnote-tooltip-content", document.body) - .hasText("consectetur adipiscing elit ↩︎"); - assert.dom("#footnote-tooltip", document.body).hasAttribute("data-show"); + + assert.dom(TOOLTIP_SELECTOR).hasText("consectetur adipiscing elit ↩︎"); // close by clicking outside - await click(document.body); - assert - .dom("#footnote-tooltip", document.body) - .doesNotHaveAttribute("data-show"); + await triggerEvent(".d-header", "pointerdown"); + assert.dom(TOOLTIP_SELECTOR).doesNotExist(); // open again await click(".expand-footnote"); - assert - .dom(".footnote-tooltip-content", document.body) - .hasText("consectetur adipiscing elit ↩︎"); - assert.dom("#footnote-tooltip", document.body).hasAttribute("data-show"); - - // close by clicking the button - await click(".expand-footnote"); - assert - .dom("#footnote-tooltip", document.body) - .doesNotHaveAttribute("data-show"); + assert.dom(TOOLTIP_SELECTOR).hasText("consectetur adipiscing elit ↩︎"); }); test("clicking a second footnote with same name works", async function (assert) { await visit("/t/-/45"); - assert.dom("#footnote-tooltip", document.body).exists(); - await click(".second .expand-footnote"); - assert - .dom(".footnote-tooltip-content", document.body) - .hasText("consectetur adipiscing elit ↩︎"); - assert.dom("#footnote-tooltip", document.body).hasAttribute("data-show"); + assert.dom(TOOLTIP_SELECTOR).hasText("consectetur adipiscing elit ↩︎"); }); }); From 393cb961ffdda309b4e2e7673750d0ae5e453a52 Mon Sep 17 00:00:00 2001 From: Arpit Jalan Date: Tue, 29 Jul 2025 21:09:26 +0530 Subject: [PATCH 11/43] UX: show 0% instead of em dash in percent-type report columns (#33931) When the y-axis value is 0 and the column type is percent, display 0% instead of replacing it with an em dash. Only show an em dash when the value is null or undefined. --- app/assets/javascripts/admin/addon/models/report.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/javascripts/admin/addon/models/report.js b/app/assets/javascripts/admin/addon/models/report.js index 3e79bfc7673f9..367b2fc7baa03 100644 --- a/app/assets/javascripts/admin/addon/models/report.js +++ b/app/assets/javascripts/admin/addon/models/report.js @@ -622,7 +622,7 @@ export default class Report extends EmberObject { _percentLabel(value) { return { value: toNumber(value), - formattedValue: value ? `${value}%` : "—", + formattedValue: value === null || value === undefined ? "—" : `${value}%`, }; } From 4a0875b6c45234e0ff122b63fc94c0935871bf96 Mon Sep 17 00:00:00 2001 From: Kris Date: Tue, 29 Jul 2025 11:41:45 -0400 Subject: [PATCH 12/43] UX: improve admin width restriction, fix theme setting width (#33938) This fixes a width blowout in theme settings (caused by https://github.com/discourse/discourse/commit/8fea4eaaa5aa6c473dfed800b6c51c752ee68120), and improves width restriction so it doesn't apply on narrower screens where it may not be needed. This behavior is unified behind a reusable `--admin-content-max-width` variable. Before: image After: image Before: image After: image --- app/assets/stylesheets/admin/admin_base.scss | 4 ++++ app/assets/stylesheets/admin/admin_config_area.scss | 7 ++----- app/assets/stylesheets/admin/customize.scss | 1 + 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/app/assets/stylesheets/admin/admin_base.scss b/app/assets/stylesheets/admin/admin_base.scss index 6158f9308dab5..ec1ed7ac07dc2 100644 --- a/app/assets/stylesheets/admin/admin_base.scss +++ b/app/assets/stylesheets/admin/admin_base.scss @@ -4,6 +4,10 @@ $mobile-breakpoint: 700px; +:root { + --admin-content-max-width: min(700px, 100%); +} + // Common admin styles .admin-main-nav { display: inline-flex; diff --git a/app/assets/stylesheets/admin/admin_config_area.scss b/app/assets/stylesheets/admin/admin_config_area.scss index 248ec7d479e4e..31fd014f81949 100644 --- a/app/assets/stylesheets/admin/admin_config_area.scss +++ b/app/assets/stylesheets/admin/admin_config_area.scss @@ -74,14 +74,11 @@ } &__primary-content { - flex: 0 1 70%; + flex: 0 1 100%; display: flex; flex-direction: column; gap: var(--space-4); - - @media (max-width: $mobile-breakpoint) { - flex: 0 1 auto; - } + max-width: var(--admin-content-max-width); } &__aside { diff --git a/app/assets/stylesheets/admin/customize.scss b/app/assets/stylesheets/admin/customize.scss index 2d5c295dd9238..3e6a5f802fabe 100644 --- a/app/assets/stylesheets/admin/customize.scss +++ b/app/assets/stylesheets/admin/customize.scss @@ -144,6 +144,7 @@ .show-current-style { display: inline-block; vertical-align: top; + max-width: var(--admin-content-max-width); .title { font-family: var(--heading-font-family); From 3220565a0ce1d11932edbcc7fcebcfb968582ee3 Mon Sep 17 00:00:00 2001 From: benj <25828824+brrusselburg@users.noreply.github.com> Date: Tue, 29 Jul 2025 10:54:52 -0500 Subject: [PATCH 13/43] FIX: Move custom digest text to correct spot (#33912) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Missed a couple things re: custom inserts upon splitting the `*.text/html.erb`s into partials. Before (custom text shows up _after_ popular topics section): Screenshot 2025-07-28 at 5 02 35 PM After (custom text shows up _before_ popular topics section): Screenshot 2025-07-28 at 5 02 00 PM Screenshot 2025-07-29 at 8 25 11 AM Screenshot 2025-07-29 at 8 25 15 AM --- app/views/user_notifications/digest.html.erb | 2 -- .../digest/_footer.text.erb | 2 -- .../digest/_popular_posts.text.erb | 11 +++++----- .../digest/_popular_topics.html.erb | 4 ++++ .../digest/_popular_topics.text.erb | 22 ++++++++++--------- 5 files changed, 21 insertions(+), 20 deletions(-) diff --git a/app/views/user_notifications/digest.html.erb b/app/views/user_notifications/digest.html.erb index edb2434333c6c..db3bf05ae5419 100644 --- a/app/views/user_notifications/digest.html.erb +++ b/app/views/user_notifications/digest.html.erb @@ -39,10 +39,8 @@ <%= render partial: "user_notifications/digest/popular_posts" %> - <%= digest_custom_html("above_popular_topics") %> <%= render partial: "user_notifications/digest/new_topics" %> - <%= digest_custom_html("below_popular_topics") %> <%= render partial: "user_notifications/digest/styles" %> diff --git a/app/views/user_notifications/digest/_footer.text.erb b/app/views/user_notifications/digest/_footer.text.erb index 2ed5b2cd364ab..92db8e32127d4 100644 --- a/app/views/user_notifications/digest/_footer.text.erb +++ b/app/views/user_notifications/digest/_footer.text.erb @@ -1,7 +1,5 @@ <%- site_link = raw(@markdown_linker.create(@site_name, '/')) %> -<%= digest_custom_text("below_popular_topics") %> - <%= raw(@markdown_linker.references) %> <%= digest_custom_text("above_footer") %> diff --git a/app/views/user_notifications/digest/_popular_posts.text.erb b/app/views/user_notifications/digest/_popular_posts.text.erb index 3340c37754ac6..7e98d91bf7112 100644 --- a/app/views/user_notifications/digest/_popular_posts.text.erb +++ b/app/views/user_notifications/digest/_popular_posts.text.erb @@ -1,10 +1,9 @@ <%- if @popular_posts.present? %> -### <%=t 'user_notifications.digest.popular_posts' %> + ### <%=t 'user_notifications.digest.popular_posts' %> -<%- @popular_posts.each_with_index do |post,i| %> -<%= post.user.username -%> - <%= raw(@markdown_linker.create(post.topic.title, post.topic.url)) %> + <%- @popular_posts.each_with_index do |post,i| %> + <%= post.user.username -%> - <%= raw(@markdown_linker.create(post.topic.title, post.topic.url)) %> - <%= raw(post.excerpt(1000, strip_links: true, text_entities: true, markdown_images: true)) %> - -<%- end %> + <%= raw(post.excerpt(1000, strip_links: true, text_entities: true, markdown_images: true)) %> + <%- end %> <%- end %> \ No newline at end of file diff --git a/app/views/user_notifications/digest/_popular_topics.html.erb b/app/views/user_notifications/digest/_popular_topics.html.erb index 8fa3608cc3b63..0bae53568f2c6 100644 --- a/app/views/user_notifications/digest/_popular_topics.html.erb +++ b/app/views/user_notifications/digest/_popular_topics.html.erb @@ -2,6 +2,8 @@ + <%= digest_custom_html("above_popular_topics") %> + <% @popular_topics.each_with_index do |t, i| %> <%= render partial: "user_notifications/digest/popular_topic", locals: { topic: t } %> @@ -9,6 +11,8 @@ <%= digest_custom_html("below_post_#{i+1}") %> <% end %> <% end %> + + <%= digest_custom_html("below_popular_topics") %> diff --git a/app/views/user_notifications/digest/_popular_topics.text.erb b/app/views/user_notifications/digest/_popular_topics.text.erb index 47888041504b4..73a44de82b913 100644 --- a/app/views/user_notifications/digest/_popular_topics.text.erb +++ b/app/views/user_notifications/digest/_popular_topics.text.erb @@ -1,14 +1,16 @@ <%- if @popular_topics.present? %> -### <%=t 'user_notifications.digest.popular_topics' %> + <%= digest_custom_text("above_popular_topics") %> + ### <%=t 'user_notifications.digest.popular_topics' %> -<%- @popular_topics.each_with_index do |t,i| %> -<%= raw(@markdown_linker.create(t.title, t.url)) %> + <%- @popular_topics.each_with_index do |t,i| %> + <%= raw(@markdown_linker.create(t.title, t.url)) %> -<%- if t.best_post.present? %> - <%= raw(t.best_post.excerpt(1000, strip_links: true, text_entities: true, markdown_images: true)) %> + <%- if t.best_post.present? %> + <%= raw(t.best_post.excerpt(1000, strip_links: true, text_entities: true, markdown_images: true)) %> + <%- end %> -<%- end %> -<%= digest_custom_text("below_post_#{i+1}") %> -<%- end %> -<%- end %> -<%= digest_custom_text("above_popular_topics") %> \ No newline at end of file + <%= digest_custom_text("below_post_#{i+1}") %> + <%- end %> + + <%= digest_custom_text("below_popular_topics") %> +<%- end %> \ No newline at end of file From 3739c8223382b91804a289bf91a25e499a137ac7 Mon Sep 17 00:00:00 2001 From: David Battersby Date: Tue, 29 Jul 2025 20:08:45 +0400 Subject: [PATCH 14/43] Revert: FEATURE: create new topic while viewing restricted category or tag (#33937) --- .../app/components/create-topic-button.gjs | 19 +++++++++ .../discourse/app/components/d-navigation.gjs | 39 ++++++++++++++++++- .../tests/acceptance/category-banner-test.js | 9 ++--- .../discourse/tests/acceptance/tags-test.js | 4 +- config/locales/client.en.yml | 2 + .../composer/default_to_subcategory_spec.rb | 32 ++++----------- spec/system/drafts_dropdown_spec.rb | 5 +-- .../components/sidebar-new-topic-button.gjs | 30 +++++++++++++- .../spec/system/sidebar_topic_button_spec.rb | 4 +- 9 files changed, 103 insertions(+), 41 deletions(-) diff --git a/app/assets/javascripts/discourse/app/components/create-topic-button.gjs b/app/assets/javascripts/discourse/app/components/create-topic-button.gjs index 0f1150ae9e4ca..f70364f98d163 100644 --- a/app/assets/javascripts/discourse/app/components/create-topic-button.gjs +++ b/app/assets/javascripts/discourse/app/components/create-topic-button.gjs @@ -2,13 +2,23 @@ import Component from "@ember/component"; import { tagName } from "@ember-decorators/component"; import DButton from "discourse/components/d-button"; import TopicDraftsDropdown from "discourse/components/topic-drafts-dropdown"; +import { i18n } from "discourse-i18n"; import DButtonTooltip from "float-kit/components/d-button-tooltip"; +import DTooltip from "float-kit/components/d-tooltip"; @tagName("") export default class CreateTopicButton extends Component { label = "topic.create"; btnClass = "btn-default"; + get disallowedReason() { + if (this.canCreateTopicOnTag === false) { + return "topic.create_disabled_tag"; + } else if (this.disabled) { + return "topic.create_disabled_category"; + } + } +