Merge branch 'codex/fix/catalog-literal-symbol-preview'

This commit is contained in:
Tommaso Casaburi
2026-04-29 15:25:06 +07:00
5 changed files with 52 additions and 15 deletions
@@ -480,6 +480,22 @@ describe('CatalogRow', () => {
expect(container.textContent).toContain('Text title: Plain thread body');
});
it('preserves literal catalog teaser markers without applying body markdown styles', async () => {
const post: TestComment = {
cid: 'literal-markers',
content: '>we got 5chan before Half Life 3\n*poisons u*',
replyCount: 2,
communityAddress: 'music-posting.eth',
};
await renderWithRouter(createElement(CatalogRow, { row: [post] }), '/mu/catalog');
expect(container.textContent).toContain('>we got 5chan before Half Life 3');
expect(container.textContent).toContain('*poisons u*');
expect(container.querySelector('.greentext')).toBeNull();
expect(container.querySelector('.spoilertext')).toBeNull();
});
it('applies the estimated row height to the virtualization wrapper', async () => {
const post: TestComment = {
cid: 'estimated-post',
+2 -2
View File
@@ -18,7 +18,7 @@ import useCountLinksInReplies from '../../hooks/use-count-links-in-replies';
import useFetchGifFirstFrame from '../../hooks/use-fetch-gif-first-frame';
import useHide from '../../hooks/use-hide';
import { isCommentArchived } from '../../lib/utils/comment-moderation-utils';
import { removeMarkdown } from '../../lib/utils/post-utils';
import { CATALOG_PREVIEW_MARKDOWN_OPTIONS, removeMarkdown } from '../../lib/utils/post-utils';
import PostMenuDesktop from '../post-desktop/post-menu-desktop';
import styles from './catalog-row.module.css';
import capitalize from 'lodash/capitalize';
@@ -215,7 +215,7 @@ const CatalogPost = memo(
{content ? ': ' : ''}
</span>
)}
{content && removeMarkdown(content)}
{content && removeMarkdown(content, CATALOG_PREVIEW_MARKDOWN_OPTIONS)}
</>
)}
</div>
@@ -105,6 +105,9 @@ describe('misc utils', () => {
expect(removeMarkdown('[spoiler]secret[/spoiler]\n>greentext\n**bold** [label](https://example.com) `code` ```block``` &nbsp;')).toBe(
'secret\ngreentext\nbold label code block',
);
expect(removeMarkdown('>greentext\n*poisons u* _and underlines_', { preserveEmphasisMarkers: true, preserveGreentextMarkers: true })).toBe(
'>greentext\n*poisons u* _and underlines_',
);
});
it('preloads theme asset URLs through Image instances', () => {
+23 -5
View File
@@ -21,13 +21,31 @@ export function getTextColorForBackground(rgb: string): string {
return brightness > 125 ? 'black' : 'white';
}
export function removeMarkdown(md: string): string {
return md
export interface RemoveMarkdownOptions {
preserveEmphasisMarkers?: boolean;
preserveGreentextMarkers?: boolean;
}
export const CATALOG_PREVIEW_MARKDOWN_OPTIONS: RemoveMarkdownOptions = {
preserveEmphasisMarkers: true,
preserveGreentextMarkers: true,
};
export function removeMarkdown(md: string, options: RemoveMarkdownOptions = {}): string {
let withoutMarkdown = md
.replace(/\[spoiler\](.*?)\[\/spoiler\]/gis, '$1') // spoiler tags - keep inner text
.replace(/\[([^\]]*?)\]\([^)]*\)/g, '$1') // [text](url) -> text
.replace(/&nbsp;/g, ' ') // &nbsp; -> space
.replace(/^>\s*/gm, '') // greentext at line start
.replace(/[*_]/g, '') // bold/italic markers
.replace(/&nbsp;/g, ' '); // &nbsp; -> space
if (!options.preserveGreentextMarkers) {
withoutMarkdown = withoutMarkdown.replace(/^>\s*/gm, ''); // greentext at line start
}
if (!options.preserveEmphasisMarkers) {
withoutMarkdown = withoutMarkdown.replace(/[*_]/g, ''); // bold/italic markers
}
return withoutMarkdown
.replace(/```[\s\S]*?```/g, (m) => m.slice(3, -3)) // code blocks - keep content
.replace(/`([^`]*)`/g, '$1') // inline code - keep content
.trim();
+8 -8
View File
@@ -2,7 +2,7 @@ import type { Comment } from '@bitsocial/bitsocial-react-hooks';
import { layout, layoutNextLine, prepare, prepareWithSegments } from '@chenglou/pretext';
import { getCommentMediaInfo, getHasThumbnail } from './media-utils';
import { EXPANDED_MEDIA_DATA_ATTRIBUTE } from './measurement-attributes';
import { removeMarkdown } from './post-utils';
import { CATALOG_PREVIEW_MARKDOWN_OPTIONS, removeMarkdown, type RemoveMarkdownOptions } from './post-utils';
import { getRenderableMobileBacklinks } from './reply-backlink-utils';
export type ReplyVirtualizationMode = 'off' | 'estimates' | 'item-size';
@@ -393,8 +393,8 @@ const getCatalogPostMediaHeight = (post: Comment | undefined, imageSize: Catalog
return mediaBox ? mediaBox.height + CATALOG_CARD_MEDIA_GAP_HEIGHT : 0;
};
const normalizeRenderedCommentText = (rawContent: string): string =>
removeMarkdown(rawContent)
const normalizeRenderedCommentText = (rawContent: string, markdownOptions?: RemoveMarkdownOptions): string =>
removeMarkdown(rawContent, markdownOptions)
.replace(/\n&nbsp;\n/g, '\n\n')
.replace(/\n{3,}/g, '\n\n')
.split('\n')
@@ -402,7 +402,7 @@ const normalizeRenderedCommentText = (rawContent: string): string =>
.join('\n')
.trim();
const getVisibleCommentBodyText = (comment: Comment | undefined, maxContentChars: number): string => {
const getVisibleCommentBodyText = (comment: Comment | undefined, maxContentChars: number, markdownOptions?: RemoveMarkdownOptions): string => {
if (!comment) {
return '';
}
@@ -412,7 +412,7 @@ const getVisibleCommentBodyText = (comment: Comment | undefined, maxContentChars
const removed = comment.removed;
const reason = comment.reason?.trim();
const rawContent = comment.content || '';
const content = normalizeRenderedCommentText(rawContent.slice(0, maxContentChars));
const content = normalizeRenderedCommentText(rawContent.slice(0, maxContentChars), markdownOptions);
if (purged) {
return 'This post was purged';
@@ -433,13 +433,13 @@ const getVisibleCommentBodyText = (comment: Comment | undefined, maxContentChars
return content;
};
const getVisibleCommentText = (comment: Comment | undefined, maxContentChars: number): string => {
const getVisibleCommentText = (comment: Comment | undefined, maxContentChars: number, markdownOptions?: RemoveMarkdownOptions): string => {
if (!comment) {
return '';
}
const title = comment.title?.trim();
const content = getVisibleCommentBodyText(comment, maxContentChars);
const content = getVisibleCommentBodyText(comment, maxContentChars, markdownOptions);
if (title && content) {
return `${title}: ${content}`;
@@ -455,7 +455,7 @@ const getVisibleCatalogText = (post: Comment | undefined, showOPComment: boolean
return '';
}
return getVisibleCommentText(post, 1600);
return getVisibleCommentText(post, 1600, CATALOG_PREVIEW_MARKDOWN_OPTIONS);
};
const getMobileBacklinkCount = (reply: Comment, backlinkMaps: ReplyBacklinkMaps): number => {