perf(ui): avoid parsing markdown control icons

This commit is contained in:
c_w_xiaohei
2026-08-26 00:42:36 +08:00
parent ed37d8e249
commit cd179bd118
2 changed files with 42 additions and 24 deletions
@@ -43,28 +43,30 @@ export type DecorateContext = {
onPreviewLoopback?: (url: string) => void;
};
// Reference the app's icon sprite (injected into <body> by the shared Icon
// component) so DOM-built controls use the same themed icons as the rest of
// the app. Sprite symbols are registered under `#oc-<name>`.
const spriteIcon = (name: IconName): string =>
`<svg class="remixicon size-3.5" viewBox="0 0 24 24" aria-hidden="true"><use href="#oc-${name}"></use></svg>`;
const ICONS = {
copy: spriteIcon('file-copy'),
check: spriteIcon('check'),
download: spriteIcon('download'),
zoomIn: spriteIcon('add'),
zoomOut: spriteIcon('subtract'),
fit: spriteIcon('refresh'),
textWrap: spriteIcon('text-wrap'),
image: spriteIcon('file-image'),
} as const;
copy: 'file-copy',
check: 'check',
download: 'download',
zoomIn: 'add',
zoomOut: 'subtract',
fit: 'refresh',
textWrap: 'text-wrap',
image: 'file-image',
} as const satisfies Record<string, IconName>;
const ICON_BTN_CLASS =
'p-1 rounded hover:bg-interactive-hover/60 text-muted-foreground hover:text-foreground transition-colors focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[var(--interactive-focus-ring)]';
const setIconHtml = (el: Element, html: string): void => {
el.innerHTML = html;
const setIcon = (el: Element, icon: keyof typeof ICONS): void => {
const iconName = ICONS[icon];
const svg = el.ownerDocument.createElementNS('http://www.w3.org/2000/svg', 'svg');
svg.setAttribute('class', 'remixicon size-3.5');
svg.setAttribute('viewBox', '0 0 24 24');
svg.setAttribute('aria-hidden', 'true');
const use = el.ownerDocument.createElementNS('http://www.w3.org/2000/svg', 'use');
use.setAttribute('href', `#oc-${iconName}`);
svg.appendChild(use);
el.replaceChildren(svg);
};
const decorateImageLabels = (root: HTMLElement): void => {
@@ -74,7 +76,7 @@ const decorateImageLabels = (root: HTMLElement): void => {
icon.className = 'inline-flex shrink-0';
icon.setAttribute('aria-hidden', 'true');
icon.setAttribute('data-openchamber-markdown-image-label-icon', 'true');
setIconHtml(icon, ICONS.image);
setIcon(icon, 'image');
label.prepend(icon);
}
};
@@ -86,7 +88,7 @@ const makeIconButton = (icon: keyof typeof ICONS, title: string, slot: string):
button.setAttribute('data-md-action', slot);
button.setAttribute('title', title);
button.setAttribute('aria-label', title);
setIconHtml(button, ICONS[icon]);
setIcon(button, icon);
return button;
};
@@ -196,11 +198,11 @@ export const applyMarkdownCodeBlockWrapState = (root: HTMLElement, enabled: bool
};
const flashCopied = (button: HTMLButtonElement, copiedTitle: string, restore: keyof typeof ICONS, restoreTitle: string): void => {
setIconHtml(button, ICONS.check);
setIcon(button, 'check');
button.setAttribute('title', copiedTitle);
button.setAttribute('aria-label', copiedTitle);
window.setTimeout(() => {
setIconHtml(button, ICONS[restore]);
setIcon(button, restore);
button.setAttribute('title', restoreTitle);
button.setAttribute('aria-label', restoreTitle);
}, 2000);
@@ -492,7 +494,7 @@ const decorateLinks = (root: HTMLElement, ctx: DecorateContext): void => {
preview.setAttribute('data-md-url', href);
preview.setAttribute('title', ctx.labels.previewTitle);
preview.setAttribute('aria-label', ctx.labels.previewLabel);
setIconHtml(preview, ICONS.download);
setIcon(preview, 'download');
anchor.parentNode?.insertBefore(preview, anchor.nextSibling);
}
}