fix(app): backport rendering improvements (#1580)

This commit is contained in:
Hampus authored and GitHub committed 2026-08-13 19:52:00 +02:00
1 parent 0c291a01da
commit 42cdc1f877
513 files changed
+98743 -70397

No files matched your search

@@ -19,6 +19,7 @@ import {Logger} from '@app/features/platform/utils/AppLogger';
import * as PremiumModalCommands from '@app/features/premium/commands/PremiumModalCommands';
import {shouldShowPremiumFeatures} from '@app/features/premium/utils/PremiumUtils';
import styles from '@app/features/theme/components/modals/BackgroundImageGalleryModal.module.css';
import {remFromPx} from '@app/features/theme/layout/RemFromPx';
import * as BackgroundImageDB from '@app/features/theme/utils/BackgroundImageDB';
import {MenuItem} from '@app/features/ui/action_menu/MenuItem';
import {Button} from '@app/features/ui/button/Button';
@@ -297,7 +298,7 @@ const BackgroundItem: React.FC<BackgroundItemProps> = React.memo(
>
{Icon && (
<Icon
size={24}
size={remFromPx(24)}
weight={isSelected ? 'fill' : 'regular'}
className={styles.backgroundItemIcon}
data-flx="theme.background-image-gallery-modal.background-item.background-item-icon"
@@ -334,7 +335,7 @@ const BackgroundItem: React.FC<BackgroundItemProps> = React.memo(
data-flx="theme.background-image-gallery-modal.background-item.error-container"
>
<WarningCircleIcon
size={24}
size={remFromPx(24)}
weight="fill"
className={styles.errorIcon}
data-flx="theme.background-image-gallery-modal.background-item.error-icon"
@@ -395,7 +396,7 @@ const BackgroundItem: React.FC<BackgroundItemProps> = React.memo(
data-flx="theme.background-image-gallery-modal.background-item.delete-button"
>
<TrashIcon
size={16}
size={remFromPx(16)}
weight="bold"
className={styles.deleteButtonIcon}
data-flx="theme.background-image-gallery-modal.background-item.delete-button-icon"
@@ -412,7 +413,7 @@ const BackgroundItem: React.FC<BackgroundItemProps> = React.memo(
data-flx="theme.background-image-gallery-modal.background-item.selected-badge"
>
<CheckIcon
size={16}
size={remFromPx(16)}
weight="bold"
className={styles.selectedIcon}
data-flx="theme.background-image-gallery-modal.background-item.selected-icon"
@@ -757,7 +758,7 @@ const BackgroundImageGalleryModal: React.FC = observer(() => {
<div className={styles.dragOverlay} data-flx="theme.background-image-gallery-modal.drag-overlay">
<div className={styles.dragContent} data-flx="theme.background-image-gallery-modal.drag-content">
<PlusIcon
size={48}
size={remFromPx(48)}
weight="bold"
className={styles.dragIcon}
data-flx="theme.background-image-gallery-modal.drag-icon"
@@ -803,7 +804,7 @@ const BackgroundImageGalleryModal: React.FC = observer(() => {
data-flx="theme.background-image-gallery-modal.action-button.stop-propagation"
>
<ArrowsClockwiseIcon
size={16}
size={remFromPx(16)}
weight="bold"
className={styles.actionButtonIcon}
data-flx="theme.background-image-gallery-modal.action-button-icon"
@@ -827,7 +828,7 @@ const BackgroundImageGalleryModal: React.FC = observer(() => {
data-flx="theme.background-image-gallery-modal.action-button.pick-gif-click"
>
<GifIcon
size={16}
size={remFromPx(16)}
weight="bold"
className={styles.actionButtonIcon}
data-flx="theme.background-image-gallery-modal.action-button-icon--gif"
@@ -851,7 +852,7 @@ const BackgroundImageGalleryModal: React.FC = observer(() => {
data-flx="theme.background-image-gallery-modal.action-button.stop-propagation--2"
>
<TrashIcon
size={16}
size={remFromPx(16)}
weight="bold"
className={styles.actionButtonIcon}
data-flx="theme.background-image-gallery-modal.action-button-icon--2"
@@ -881,7 +882,7 @@ const BackgroundImageGalleryModal: React.FC = observer(() => {
data-flx="theme.background-image-gallery-modal.upload-placeholder-content"
>
<PlusIcon
size={48}
size={remFromPx(48)}
weight="regular"
className={styles.uploadIcon}
data-flx="theme.background-image-gallery-modal.upload-icon"
@@ -963,7 +964,7 @@ const BackgroundImageGalleryModal: React.FC = observer(() => {
<div className={styles.premiumHeader} data-flx="theme.background-image-gallery-modal.premium-header">
<CrownIcon
weight="fill"
size={18}
size={remFromPx(18)}
className={styles.premiumIcon}
data-flx="theme.background-image-gallery-modal.premium-icon"
/>
@@ -18,6 +18,7 @@ import {http} from '@app/features/platform/transport/RestTransport';
import {Logger} from '@app/features/platform/utils/AppLogger';
import {failureCode} from '@app/features/platform/utils/ResponseInspection';
import styles from '@app/features/theme/components/modals/ShareThemeModal.module.css';
import {remFromPx} from '@app/features/theme/layout/RemFromPx';
import * as ModalCommands from '@app/features/ui/commands/ModalCommands';
import {modal} from '@app/features/ui/commands/ModalCommands';
import {Input} from '@app/features/ui/components/form/FormInput';
@@ -175,7 +176,7 @@ export const ShareThemeModal = observer(({themeCss}: {themeCss: string}) => {
placeholder={i18n._(SEARCH_FRIENDS_DESCRIPTOR)}
leftIcon={
<MagnifyingGlassIcon
size={20}
size={remFromPx(20)}
weight="bold"
className={selectorStyles.searchIcon}
data-flx="theme.share-theme-modal.magnifying-glass-icon"
@@ -2,7 +2,32 @@
export const REM_BASE_PX = 16;
class NonFinitePixelValueError extends Error {
public constructor(value: number) {
super(`Pixel value must be finite: ${value}`);
this.name = 'NonFinitePixelValueError';
}
}
export function remFromPx(px: number): `${number}rem` {
if (!Number.isFinite(px)) {
throw new NonFinitePixelValueError(px);
}
const rounded = Math.round((px / REM_BASE_PX) * 1e5) / 1e5;
return `${rounded}rem`;
}
export function getRemScaleForDocument(ownerDocument: Document | null): number {
if (ownerDocument == null) {
return 1;
}
const ownerWindow = ownerDocument.defaultView;
if (ownerWindow == null) {
return 1;
}
const rootFontSize = Number.parseFloat(ownerWindow.getComputedStyle(ownerDocument.documentElement).fontSize);
if (!Number.isFinite(rootFontSize) || rootFontSize <= 0) {
return 1;
}
return rootFontSize / REM_BASE_PX;
}
@@ -4,6 +4,9 @@
--markup-spoiler-hidden-bg: color-mix(in srgb, currentColor 16%, transparent);
--markup-spoiler-hidden-bg-hover: color-mix(in srgb, currentColor 24%, transparent);
--markup-spoiler-hidden-border: color-mix(in srgb, currentColor 10%, transparent);
--markup-inline-icon-baseline-shift: -0.125em;
--markup-inline-emoji-size: var(--emoji-size-emoji);
--markup-inline-emoji-baseline-shift: -0.4em;
font-size: inherit;
line-height: 1.5;
color: inherit;
@@ -108,7 +111,7 @@
}
.markup .link {
color: var(--text-link);
color: var(--text-link) !important;
text-decoration: var(--link-decoration, none);
transition:
color var(--transition-fast),
@@ -121,7 +124,12 @@
:global(:where(html.window-focused, html.unfocused-fully-interactive):not(.window-focus-activation-guard))
.markup
.link:hover {
color: var(--text-link);
color: var(--text-link) !important;
text-decoration: underline;
}
.markup .link[data-context-menu-open='true'] {
color: var(--text-link) !important;
text-decoration: underline;
}
@@ -203,6 +211,19 @@
color: inherit;
}
.markup :global(.emoji:not(.jumboable)) {
width: var(--markup-inline-emoji-size);
height: var(--markup-inline-emoji-size);
min-height: 0;
margin-block: 0;
vertical-align: var(--markup-inline-emoji-baseline-shift);
}
.markup :global(.emoji:not(.jumboable):not(img)) {
font-size: var(--markup-inline-emoji-size);
line-height: 1;
}
.markup .codeContainer {
position: relative;
border-radius: var(--radius-md);
@@ -505,42 +526,45 @@
.markup .mention,
.inlineFormat .mention {
--mention-border-color: var(--markup-mention-border);
--mention-gap: 0.2em;
--mention-icon-size: 0.9em;
display: inline-flex;
align-items: center;
gap: var(--mention-gap);
max-width: 100%;
--mention-gap: 0.125em;
--mention-icon-size: 1em;
--mention-color: var(--text-link);
display: inline;
box-sizing: border-box;
border-radius: var(--radius-sm);
padding: 0 0.25em;
margin-bottom: var(--mention-offset-block-end, 0.125rem);
border-radius: 0.1875rem;
padding-block: 0;
padding-inline: 0.125rem;
margin: 0;
font-weight: 500;
unicode-bidi: plaintext;
color: var(--markup-mention-text);
color: var(--mention-color) !important;
background-color: var(--markup-mention-fill);
border: 0;
box-shadow: inset 0 0 0 0.0625rem var(--mention-border-color);
box-shadow: none;
transition:
background-color var(--transition-fast),
color var(--transition-fast),
box-shadow var(--transition-fast);
font-size: 90%;
color var(--transition-fast);
font-size: inherit;
line-height: inherit;
vertical-align: middle;
text-decoration: none;
vertical-align: baseline;
white-space: nowrap;
box-decoration-break: clone;
-webkit-box-decoration-break: clone;
}
.markup .mention svg,
.markup .mention img,
.inlineFormat .mention svg,
.inlineFormat .mention img {
display: block;
width: var(--mention-icon-size);
height: var(--mention-icon-size);
flex: 0 0 var(--mention-icon-size);
margin: 0;
display: inline-block;
inline-size: var(--mention-icon-size);
block-size: var(--mention-icon-size);
min-inline-size: var(--mention-icon-size);
min-block-size: var(--mention-icon-size);
max-inline-size: var(--mention-icon-size);
max-block-size: var(--mention-icon-size);
vertical-align: var(--markup-inline-icon-baseline-shift);
}
.markup .mention.interactive,
@@ -553,25 +577,16 @@
.mention.interactive:hover,
:global(:where(html.window-focused, html.unfocused-fully-interactive):not(.window-focus-activation-guard))
.inlineFormat
.mention.interactive:hover {
.mention.interactive:hover,
.markup .mention.interactive[aria-expanded='true'],
.inlineFormat .mention.interactive[aria-expanded='true'],
.markup .mention.interactive[data-context-menu-open='true'],
.inlineFormat .mention.interactive[data-context-menu-open='true'],
.markup .mention.interactive.mentionActive,
.inlineFormat .mention.interactive.mentionActive {
background-color: var(--markup-interactive-hover-fill);
color: var(--markup-interactive-hover-text);
}
@supports (background-color: color-mix(in srgb, #000 50%, transparent)) {
.markup .mention,
.inlineFormat .mention {
--mention-border-color: color-mix(in srgb, var(--markup-mention-border) 75%, transparent);
}
:global(:where(html.window-focused, html.unfocused-fully-interactive):not(.window-focus-activation-guard))
.markup
.mention.interactive:hover,
:global(:where(html.window-focused, html.unfocused-fully-interactive):not(.window-focus-activation-guard))
.inlineFormat
.mention.interactive:hover {
--mention-border-color: color-mix(in srgb, var(--markup-interactive-hover-fill) 60%, transparent);
}
color: var(--text-link) !important;
text-decoration: none;
}
.markup .timestamp {
@@ -795,14 +810,26 @@
.markup.inlineFormat,
.markup .inlineFormat {
--markup-inline-icon-baseline-shift: var(--markup-restricted-inline-icon-baseline-shift);
--markup-inline-emoji-size: var(--markup-restricted-inline-emoji-size);
--markup-inline-emoji-baseline-shift: var(--markup-restricted-inline-emoji-baseline-shift);
display: inline;
white-space: normal;
}
.markup.inlineFormat :global(.emoji),
.markup .inlineFormat :global(.emoji) {
font-size: 85%;
margin-bottom: 0.0625rem;
.markup.inlineFormat :global(.emoji:not(.jumboable)),
.markup .inlineFormat :global(.emoji:not(.jumboable)) {
width: var(--markup-inline-emoji-size);
height: var(--markup-inline-emoji-size);
min-height: 0;
margin-block: 0;
vertical-align: var(--markup-inline-emoji-baseline-shift);
}
.markup.inlineFormat :global(.emoji:not(.jumboable):not(img)),
.markup .inlineFormat :global(.emoji:not(.jumboable):not(img)) {
font-size: var(--markup-inline-emoji-size);
line-height: 1;
}
.markup.inlineFormat h1,
@@ -1,6 +1,12 @@
/* SPDX-License-Identifier: AGPL-3.0-or-later */
.label {
line-height: inherit;
vertical-align: baseline;
white-space: normal;
}
.channelIcon {
width: var(--mention-icon-size);
height: var(--mention-icon-size);
margin-inline-end: var(--mention-gap);
color: currentColor;
}
@@ -987,7 +987,9 @@
}
.repliedTextContent {
--mention-offset-block-end: 0px;
--markup-restricted-inline-icon-baseline-shift: -0.125em;
--markup-restricted-inline-emoji-size: var(--reply-height);
--markup-restricted-inline-emoji-baseline-shift: bottom;
display: inline;
overflow: hidden;
text-overflow: ellipsis;
@@ -1004,15 +1006,15 @@
}
.repliedTextContent :global(.emoji) {
width: var(--reply-height);
height: var(--reply-height);
width: var(--markup-restricted-inline-emoji-size);
height: var(--markup-restricted-inline-emoji-size);
min-height: 0;
margin-bottom: 0;
vertical-align: text-bottom;
margin-block: 0;
vertical-align: var(--markup-restricted-inline-emoji-baseline-shift);
}
.repliedTextContent :global(.emoji:not(img)) {
font-size: var(--reply-height);
font-size: var(--markup-restricted-inline-emoji-size);
line-height: 1;
}
@@ -1282,7 +1284,6 @@
grid-column: 1 / -1;
justify-self: flex-start;
margin-inline-start: 0;
transform: translateY(50%);
}
:global(.theme-light) .typingPill {
@@ -1324,10 +1325,37 @@
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-size: 0.625rem;
font-size: 0.75rem;
line-height: var(--typing-pill-height);
}
.typingClusterComposerStatus {
display: inline-flex;
grid-template-columns: none;
column-gap: 0;
width: auto;
max-width: 100%;
flex: 0 1 auto;
}
.typingPillComposerStatus {
height: var(--composer-status-pill-height);
min-height: var(--composer-status-pill-height);
max-width: 100%;
min-width: 0;
padding: 0 0.5rem;
border-radius: var(--radius-full);
}
.typingPillComposerStatus .typingIndicator,
.typingPillComposerStatus .typingAvatarContainer {
height: var(--composer-status-pill-height);
}
.typingPillComposerStatus .typingText {
line-height: var(--composer-status-pill-height);
}
.compactContentWrapper {
grid-area: content;
display: grid;
@@ -160,6 +160,7 @@ export const THEME_VARIABLES: ReadonlyArray<ThemeVariableDefinition> = [
{name: "--layout-user-area-height", kind: "dimension", groupId: "layout", groupLabel: "Layout", source: "globals"},
{name: "--layout-user-area-reserved-height", kind: "dimension", groupId: "layout", groupLabel: "Layout", source: "globals"},
{name: "--link-decoration", kind: "other", groupId: "other", groupLabel: "Other", source: "runtime-accessibility"},
{name: "--macos-traffic-light-inset", kind: "dimension", groupId: "other", groupLabel: "Other", source: "globals"},
{name: "--markup-everyone-border", kind: "color", groupId: "markup", groupLabel: "Markup & mentions", source: "color-system"},
{name: "--markup-everyone-fill", kind: "color", groupId: "markup", groupLabel: "Markup & mentions", source: "color-system"},
{name: "--markup-everyone-text", kind: "color", groupId: "markup", groupLabel: "Markup & mentions", source: "color-system"},
@@ -174,6 +175,9 @@ export const THEME_VARIABLES: ReadonlyArray<ThemeVariableDefinition> = [
{name: "--markup-mention-border", kind: "color", groupId: "markup", groupLabel: "Markup & mentions", source: "color-system"},
{name: "--markup-mention-fill", kind: "color", groupId: "markup", groupLabel: "Markup & mentions", source: "color-system"},
{name: "--markup-mention-text", kind: "color", groupId: "markup", groupLabel: "Markup & mentions", source: "color-system"},
{name: "--markup-restricted-inline-emoji-baseline-shift", kind: "dimension", groupId: "emoji", groupLabel: "Emoji", source: "globals"},
{name: "--markup-restricted-inline-emoji-size", kind: "dimension", groupId: "emoji", groupLabel: "Emoji", source: "globals"},
{name: "--markup-restricted-inline-icon-baseline-shift", kind: "dimension", groupId: "markup", groupLabel: "Markup & mentions", source: "globals"},
{name: "--markup-strikethrough-color", kind: "color", groupId: "markup", groupLabel: "Markup & mentions", source: "runtime-accessibility"},
{name: "--media-border-radius", kind: "dimension", groupId: "borders", groupLabel: "Borders & focus", source: "globals"},
{name: "--message-action-bar-offset", kind: "dimension", groupId: "messages", groupLabel: "Messages", source: "message-layout"},
@@ -496,6 +500,7 @@ export const THEME_VARIABLE_NAMES: ReadonlyArray<string> = [
"--layout-user-area-height",
"--layout-user-area-reserved-height",
"--link-decoration",
"--macos-traffic-light-inset",
"--markup-everyone-border",
"--markup-everyone-fill",
"--markup-everyone-text",
@@ -510,6 +515,9 @@ export const THEME_VARIABLE_NAMES: ReadonlyArray<string> = [
"--markup-mention-border",
"--markup-mention-fill",
"--markup-mention-text",
"--markup-restricted-inline-emoji-baseline-shift",
"--markup-restricted-inline-emoji-size",
"--markup-restricted-inline-icon-baseline-shift",
"--markup-strikethrough-color",
"--media-border-radius",
"--message-action-bar-offset",
@@ -1008,12 +1016,13 @@ export const THEME_STUDIO_DARK_DEFAULT_VARIABLE_VALUES: Readonly<Record<string,
"--layout-gap-sm": "0.5rem",
"--layout-guild-list-width": "4.5rem",
"--layout-header-height": "3.5rem",
"--layout-header-popout-width": "calc(16.875rem - (1rem * 2))",
"--layout-header-popout-width": "calc(20rem - (1rem * 2))",
"--layout-mobile-bottom-nav-reserved-height": "0px",
"--layout-sidebar-width": "16.875rem",
"--layout-sidebar-width": "20rem",
"--layout-user-area-height": "4.5rem",
"--layout-user-area-reserved-height": "0px",
"--link-decoration": "none",
"--macos-traffic-light-inset": "78px",
"--markup-everyone-border": "hsla(250, calc(80% * var(--saturation-factor)), 75%, 0.3)",
"--markup-everyone-fill": "color-mix(in srgb, hsl(250, calc(80% * var(--saturation-factor)), 75%) 18%, transparent)",
"--markup-everyone-text": "hsl(250, calc(80% * var(--saturation-factor)), 75%)",
@@ -1028,6 +1037,9 @@ export const THEME_STUDIO_DARK_DEFAULT_VARIABLE_VALUES: Readonly<Record<string,
"--markup-mention-border": "hsla(198, calc(92% * var(--saturation-factor)), 70%, 0.3)",
"--markup-mention-fill": "color-mix(in srgb, hsl(198, calc(92% * var(--saturation-factor)), 70%) 20%, transparent)",
"--markup-mention-text": "hsl(198, calc(92% * var(--saturation-factor)), 70%)",
"--markup-restricted-inline-emoji-baseline-shift": "-0.18em",
"--markup-restricted-inline-emoji-size": "1.25em",
"--markup-restricted-inline-icon-baseline-shift": "-0.125em",
"--markup-strikethrough-color": "currentColor",
"--media-border-radius": "0.25rem",
"--message-action-bar-offset": "3rem",
@@ -1344,12 +1356,13 @@ export const THEME_STUDIO_LIGHT_DEFAULT_VARIABLE_VALUES: Readonly<Record<string,
"--layout-gap-sm": "0.5rem",
"--layout-guild-list-width": "4.5rem",
"--layout-header-height": "3.5rem",
"--layout-header-popout-width": "calc(16.875rem - (1rem * 2))",
"--layout-header-popout-width": "calc(20rem - (1rem * 2))",
"--layout-mobile-bottom-nav-reserved-height": "0px",
"--layout-sidebar-width": "16.875rem",
"--layout-sidebar-width": "20rem",
"--layout-user-area-height": "4.5rem",
"--layout-user-area-reserved-height": "0px",
"--link-decoration": "none",
"--macos-traffic-light-inset": "78px",
"--markup-everyone-border": "hsla(250, calc(70% * var(--saturation-factor)), 45%, 0.4)",
"--markup-everyone-fill": "color-mix(in srgb, hsl(250, calc(70% * var(--saturation-factor)), 45%) 12%, transparent)",
"--markup-everyone-text": "hsl(250, calc(70% * var(--saturation-factor)), 45%)",
@@ -1364,6 +1377,9 @@ export const THEME_STUDIO_LIGHT_DEFAULT_VARIABLE_VALUES: Readonly<Record<string,
"--markup-mention-border": "hsla(198, calc(92% * var(--saturation-factor)), 45%, 0.4)",
"--markup-mention-fill": "color-mix(in srgb, hsl(198, calc(92% * var(--saturation-factor)), 45%) 20%, transparent)",
"--markup-mention-text": "hsl(198, calc(92% * var(--saturation-factor)), 45%)",
"--markup-restricted-inline-emoji-baseline-shift": "-0.18em",
"--markup-restricted-inline-emoji-size": "1.25em",
"--markup-restricted-inline-icon-baseline-shift": "-0.125em",
"--markup-strikethrough-color": "currentColor",
"--media-border-radius": "0.25rem",
"--message-action-bar-offset": "3rem",