Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1c62fb4722 |
@@ -69,8 +69,15 @@ export const Reactions = as<'div', ReactionsProps>(
|
||||
const measure = useCallback(() => {
|
||||
const container = containerRef.current;
|
||||
if (!container) return;
|
||||
const rowWidth = container.getBoundingClientRect().width;
|
||||
const gap = parseFloat(getComputedStyle(container).columnGap) || 8;
|
||||
// [Gitea #305] The width the chips actually get (content box). The border
|
||||
// box grows by 4 px while collapsed (margin -2 / padding 2), and measuring
|
||||
// it made a row within 4 px of fitting flip collapsed↔expanded every frame.
|
||||
const cs = getComputedStyle(container);
|
||||
const rowWidth =
|
||||
container.clientWidth -
|
||||
(parseFloat(cs.paddingLeft) || 0) -
|
||||
(parseFloat(cs.paddingRight) || 0);
|
||||
const gap = parseFloat(cs.columnGap) || 8;
|
||||
const chips = keys ? keys.split('\u0000').map((k) => chipRefs.current.get(k)) : [];
|
||||
const widths = chips.map((el) => el?.getBoundingClientRect().width ?? 0);
|
||||
const moreWidth = moreRef.current?.getBoundingClientRect().width ?? 48;
|
||||
@@ -110,8 +117,21 @@ export const Reactions = as<'div', ReactionsProps>(
|
||||
style={{
|
||||
...props.style,
|
||||
...(collapsed && rowHeight
|
||||
? // 2px breathing room so row-1 focus outlines are not clipped.
|
||||
{ maxHeight: rowHeight + 4, overflow: 'hidden', padding: 2, margin: -2 }
|
||||
? // 2px breathing room so row-1 focus outlines are not clipped. Longhand
|
||||
// margins so the timeline's marginTop survives (Gitea #305: the
|
||||
// `margin` shorthand overrode it and, once removed, cleared it).
|
||||
{
|
||||
maxHeight: rowHeight + 4,
|
||||
overflow: 'hidden',
|
||||
padding: 2,
|
||||
marginLeft: -2,
|
||||
marginRight: -2,
|
||||
marginBottom: -2,
|
||||
marginTop:
|
||||
props.style?.marginTop !== undefined
|
||||
? `calc(${typeof props.style.marginTop === 'number' ? `${props.style.marginTop}px` : props.style.marginTop} - 2px)`
|
||||
: -2,
|
||||
}
|
||||
: {}),
|
||||
}}
|
||||
ref={(el) => {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { test } from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import {
|
||||
plain,
|
||||
DEFAULT_GROUPS,
|
||||
kumaBeatTime,
|
||||
kumaUrls,
|
||||
@@ -261,27 +260,3 @@ test('a dismissed calls-down banner lets the next one through, never hides serve
|
||||
'server-down',
|
||||
);
|
||||
});
|
||||
|
||||
test('plain: strips Markdown syntax only, keeps ordinary #, _, > and * (#308)', () => {
|
||||
assert.equal(
|
||||
plain('join #lotus-general or #announcements:lotusguild.org'),
|
||||
'join #lotus-general or #announcements:lotusguild.org',
|
||||
);
|
||||
assert.equal(
|
||||
plain('ping @some_user about snake_case and C#'),
|
||||
'ping @some_user about snake_case and C#',
|
||||
);
|
||||
assert.equal(
|
||||
plain('calls -> fixed, > 5 people, 5 * 3 = 15'),
|
||||
'calls -> fixed, > 5 people, 5 * 3 = 15',
|
||||
);
|
||||
assert.equal(
|
||||
plain('# Heading\n* item one\n- item two\n> quoted'),
|
||||
'Heading\nitem one\nitem two\nquoted',
|
||||
);
|
||||
assert.equal(plain('**bold**, *em*, _em_, `/sfu/get`'), 'bold, em, em, /sfu/get');
|
||||
assert.equal(
|
||||
plain('Read the <b>guide</b> <img src=x onerror=alert(1)> [docs](https://x.y)'),
|
||||
'Read the guide docs',
|
||||
);
|
||||
});
|
||||
|
||||
@@ -240,25 +240,12 @@ const incidentTone = (style: string): BannerTone => {
|
||||
};
|
||||
|
||||
/** Markdown-ish incident text as plain text for the Details line. */
|
||||
/**
|
||||
* [Gitea #308] Markdown → plain text for the Details line. Strips only the
|
||||
* syntax: removing every `*_\`#>` also broke "#lotus-general", "snake_case",
|
||||
* "->" and "5 * 3". HTML tags are dropped (React escapes the text anyway).
|
||||
*/
|
||||
export const plain = (s: string): string =>
|
||||
const plain = (s: string): string =>
|
||||
s
|
||||
.replace(/!\[[^\]]*\]\([^)]*\)/g, '')
|
||||
.replace(/\[([^\]]+)\]\([^)]*\)/g, '$1')
|
||||
.replace(/<\/?[a-z][^>]*>/gi, '')
|
||||
.replace(/^[ \t]{0,3}#{1,6}[ \t]+/gm, '')
|
||||
.replace(/^[ \t]*>[ \t]?/gm, '')
|
||||
.replace(/^[ \t]*[*+-][ \t]+/gm, '')
|
||||
.replace(/`([^`\n]+)`/g, '$1')
|
||||
.replace(/\*\*(?=\S)([^*\n]*?\S)\*\*/g, '$1')
|
||||
.replace(/__(?=\S)([^_\n]*?\S)__/g, '$1')
|
||||
.replace(/(^|[^\w*])\*(?=\S)([^*\n]*?\S)\*(?![\w*])/g, '$1$2')
|
||||
.replace(/(^|[^\w])_(?=\S)([^_\n]*?\S)_(?!\w)/g, '$1$2')
|
||||
.replace(/[ \t]+\n/g, '\n')
|
||||
.replace(/[*_`#>]/g, '')
|
||||
.replace(/\s+\n/g, '\n')
|
||||
.trim();
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user