优化帖子详情与列表展示,并新增问答帖类型与已解决状态。

统一标题锚点定位、编辑底栏固定与操作栏分层,列表徽章前置并区分置顶/问答配色。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-08-04 00:19:32 +08:00
parent 3aca5c42c5
commit b24f6c23ea
16 changed files with 696 additions and 239 deletions

View File

@@ -132,10 +132,22 @@ export function renderPostContentHtml(
enhanceHeadingAnchors(doc.body);
enhanceCodeBlocks(doc.body);
wrapContentTables(doc.body);
return doc.body.innerHTML;
}
/** 表格外包一层,避免 display:block 时边框撑满而单元格背景偏窄 */
function wrapContentTables(root: ParentNode): void {
root.querySelectorAll('table').forEach(table => {
if (table.parentElement?.classList.contains('md-table-wrap')) return;
const wrap = table.ownerDocument.createElement('div');
wrap.className = 'md-table-wrap';
table.replaceWith(wrap);
wrap.appendChild(table);
});
}
function isFloatDisplayImage(el: Element): boolean {
if (el.tagName !== 'IMG') return false;
const display = el.getAttribute('data-display') || '';

View File

@@ -5,13 +5,21 @@ export interface PostHeading {
text: string;
}
/** 去掉标题内的锚点复制链,避免目录文案带上 # */
function headingPlainText(el: Element): string {
const clone = el.cloneNode(true) as HTMLElement;
clone.querySelectorAll('.post-heading-anchor-link').forEach(n => n.remove());
return (clone.textContent || '').replace(/\s+/g, ' ').trim();
}
/** 为标题补全锚点 id并返回目录树数据 */
export function enhanceHeadingAnchors(root: ParentNode): PostHeading[] {
const headings: PostHeading[] = [];
const used = new Map<string, number>();
const doc = root.ownerDocument ?? (typeof document !== 'undefined' ? document : null);
root.querySelectorAll('h1, h2, h3, h4, h5, h6').forEach((el, index) => {
const text = (el.textContent || '').replace(/\s+/g, ' ').trim();
const text = headingPlainText(el);
if (!text) return;
const level = Number(el.tagName.slice(1)) || 2;
@@ -25,6 +33,18 @@ export function enhanceHeadingAnchors(root: ParentNode): PostHeading[] {
el.setAttribute('id', id);
el.classList.add('post-heading-anchor');
// hover 显示 #,点击复制带 hash 的链接
if (doc && !el.querySelector('.post-heading-anchor-link')) {
const link = doc.createElement('a');
link.className = 'post-heading-anchor-link';
link.href = `#${id}`;
link.setAttribute('aria-label', '复制本节链接');
link.setAttribute('data-heading-copy', id);
link.setAttribute('tabindex', '-1');
link.textContent = '#';
el.appendChild(link);
}
headings.push({ id, level, text });
});
@@ -38,7 +58,7 @@ export function extractHeadingsFromHtml(html: string): PostHeading[] {
const headings: PostHeading[] = [];
doc.querySelectorAll('h1, h2, h3, h4, h5, h6').forEach(el => {
const id = el.getAttribute('id')?.trim();
const text = (el.textContent || '').replace(/\s+/g, ' ').trim();
const text = headingPlainText(el);
if (!id || !text) return;
headings.push({
id,