fix(app): treat a black instance theme colour as unset (#3502)

This commit is contained in:
Hampus authored and GitHub committed 2026-10-15 23:04:40 +02:00
1 parent 41da6b5985
commit b9ea837c75
5 files changed
+34 -20

No files matched your search

@@ -270,6 +270,21 @@ describe('runtimeSnapshotFromDiscovery', () => {
).toThrow(/appPublic\.branding\.icon_url/u);
});
test('treats a black or malformed theme colour as unset and normalises the rest', () => {
const snapshot = runtimeSnapshotFromDiscovery(parsedDocument('t.test'));
const themeColorOf = (theme_color: unknown) =>
requireRuntimeConfigSnapshot({
...snapshot,
appPublic: {...snapshot.appPublic, branding: {...snapshot.appPublic.branding, theme_color}},
}).appPublic.branding.theme_color;
for (const unset of ['#000000', '#000', '000000', '', 'rgb(1, 2, 3)', 'rebeccapurple', 0, null]) {
expect(themeColorOf(unset)).toBeNull();
}
expect(themeColorOf('#FF0000')).toBe('#ff0000');
expect(themeColorOf(' f00 ')).toBe('#ff0000');
expect(themeColorOf('#000001')).toBe('#000001');
});
test('carries the advertised domain migration', () => {
const domainMigration = {
enabled: true,
@@ -14,6 +14,7 @@ import {
} from '@fluxer/instance_bootstrap/src/Discovery';
import {InstanceEndpointKind, normalizeInstanceEndpoint} from '@fluxer/instance_bootstrap/src/EndpointNormalization';
import {CanonicalNetworkProtocol} from '@fluxer/instance_bootstrap/src/NetworkOrigin';
import {normalizeThemeColor} from '@fluxer/instance_bootstrap/src/ThemeColor';
import {
AGE_POLICY_ACTIONS,
type InstanceAgePolicy,
@@ -413,7 +414,7 @@ function readAppPublic(value: unknown): InstanceAppPublic {
logo_url: readNullableAbsoluteHttpUrl(branding, 'logo_url', 'appPublic.branding.logo_url'),
wordmark_url: readNullableAbsoluteHttpUrl(branding, 'wordmark_url', 'appPublic.branding.wordmark_url'),
favicon_url: readNullableAbsoluteHttpUrl(branding, 'favicon_url', 'appPublic.branding.favicon_url'),
theme_color: readNullableNonEmptyString(branding, 'theme_color', 'appPublic.branding.theme_color'),
theme_color: normalizeThemeColor(branding.theme_color),
status_page_url: readOptionalText(branding, 'status_page_url'),
status_page_incident_history_url: readOptionalText(branding, 'status_page_incident_history_url'),
premium_product_name: readOptionalText(branding, 'premium_product_name') ?? DEFAULT_PREMIUM_PRODUCT_NAME,
@@ -10,19 +10,11 @@ export const INSTANCE_BRAND_VARIABLES = [
type InstanceBrandVariable = (typeof INSTANCE_BRAND_VARIABLES)[number];
const HEX_COLOR_PATTERN = /^#(?:[0-9a-f]{3}|[0-9a-f]{6})$/i;
const HEX_COLOR_PATTERN = /^#[0-9a-f]{6}$/;
function parseHexColor(value: string): [number, number, number] | null {
const trimmed = value.trim();
if (!HEX_COLOR_PATTERN.test(trimmed)) return null;
const digits =
trimmed.length === 4
? trimmed
.slice(1)
.split('')
.map((digit) => digit + digit)
.join('')
: trimmed.slice(1);
if (!HEX_COLOR_PATTERN.test(value)) return null;
const digits = value.slice(1);
return [
Number.parseInt(digits.slice(0, 2), 16),
Number.parseInt(digits.slice(2, 4), 16),