mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-17 21:15:32 +09:00
fix(app): isolate bidi usernames from message timestamps (#2560)
This commit is contained in:
1 parent
6c0ce9369b
commit
f38619d974
2 files changed
+62
No files matched your search
@@ -428,6 +428,7 @@
|
|||||||
max-inline-size: none;
|
max-inline-size: none;
|
||||||
overflow: visible;
|
overflow: visible;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
unicode-bidi: plaintext;
|
||||||
user-select: text;
|
user-select: text;
|
||||||
-webkit-user-select: text;
|
-webkit-user-select: text;
|
||||||
}
|
}
|
||||||
@@ -505,6 +506,7 @@
|
|||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
color: var(--message-timestamp-color);
|
color: var(--message-timestamp-color);
|
||||||
line-height: var(--message-line-height);
|
line-height: var(--message-line-height);
|
||||||
|
unicode-bidi: isolate;
|
||||||
}
|
}
|
||||||
|
|
||||||
.messageTimestamp {
|
.messageTimestamp {
|
||||||
@@ -557,6 +559,7 @@
|
|||||||
font-size: var(--message-timestamp-compact-font-size);
|
font-size: var(--message-timestamp-compact-font-size);
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
line-height: var(--message-line-height);
|
line-height: var(--message-line-height);
|
||||||
|
unicode-bidi: isolate;
|
||||||
}
|
}
|
||||||
|
|
||||||
.messageTimestampHover {
|
.messageTimestampHover {
|
||||||
@@ -948,6 +951,7 @@
|
|||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
max-width: 30%;
|
max-width: 30%;
|
||||||
vertical-align: baseline;
|
vertical-align: baseline;
|
||||||
|
unicode-bidi: plaintext;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
color: color-mix(
|
color: color-mix(
|
||||||
in srgb,
|
in srgb,
|
||||||
|
|||||||
@@ -0,0 +1,58 @@
|
|||||||
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
|
import {readFileSync} from 'node:fs';
|
||||||
|
import {fileURLToPath} from 'node:url';
|
||||||
|
import {describe, expect, it} from 'vitest';
|
||||||
|
|
||||||
|
const MESSAGE_CSS = readFileSync(fileURLToPath(new URL('./Message.module.css', import.meta.url)), 'utf8');
|
||||||
|
|
||||||
|
const ISOLATED_NAMES = ['.messageUsername', '.repliedUsername'];
|
||||||
|
const ISOLATED_TIMESTAMPS = [
|
||||||
|
'.messageTimestamp',
|
||||||
|
'.messageTimestampCompact',
|
||||||
|
'.messageTimestampHover',
|
||||||
|
'.messageTimestampCompactHover',
|
||||||
|
];
|
||||||
|
|
||||||
|
function rules(css: string): Array<{selector: string; declarations: string}> {
|
||||||
|
const source = css.replace(/\/\*[\s\S]*?\*\//g, '');
|
||||||
|
const out: Array<{selector: string; declarations: string}> = [];
|
||||||
|
const chain: Array<string> = [];
|
||||||
|
let buffer = '';
|
||||||
|
for (const char of source) {
|
||||||
|
if (char === '{') {
|
||||||
|
chain.push(buffer.split(/[;}]/).pop()?.trim().replace(/\s+/g, ' ') ?? '');
|
||||||
|
buffer = '';
|
||||||
|
} else if (char === '}') {
|
||||||
|
if (chain.length > 0) {
|
||||||
|
out.push({selector: chain.join(' '), declarations: buffer});
|
||||||
|
chain.pop();
|
||||||
|
}
|
||||||
|
buffer = '';
|
||||||
|
} else {
|
||||||
|
buffer += char;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
function declaredBidi(className: string): Array<string> {
|
||||||
|
const selectorPattern = new RegExp(`\\${className}(?![\\w-])`);
|
||||||
|
const out: Array<string> = [];
|
||||||
|
for (const rule of rules(MESSAGE_CSS)) {
|
||||||
|
if (!rule.selector.split(',').some((selector) => selectorPattern.test(selector))) continue;
|
||||||
|
const match = rule.declarations.match(/unicode-bidi\s*:\s*([^;]+)/);
|
||||||
|
if (match) out.push(match[1].trim());
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('message bidi isolation', () => {
|
||||||
|
it.each(ISOLATED_NAMES)('resolves %s in its own bidi paragraph', (className) => {
|
||||||
|
expect(declaredBidi(className)).toContain('plaintext');
|
||||||
|
});
|
||||||
|
|
||||||
|
it.each(ISOLATED_TIMESTAMPS)('keeps %s out of a neighbouring right-to-left run', (className) => {
|
||||||
|
expect(declaredBidi(className)).toContain('isolate');
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in new issue
Block a user