feat(ui): add refresh button for showhtml cards

This commit is contained in:
JOJO 2026-04-27 01:42:24 +08:00
parent 591a5e64cc
commit 3cd8bdabef
2 changed files with 124 additions and 0 deletions

View File

@ -380,6 +380,45 @@ function escapeHtml(input: string) {
.replace(/'/g, ''');
}
function bindShowHtmlRefreshControl(wrapper: HTMLElement, onRefresh: () => void) {
let toolbar = wrapper.querySelector(':scope > .chat-inline-card__toolbar') as HTMLElement | null;
if (!toolbar) {
toolbar = document.createElement('div');
toolbar.className = 'chat-inline-card__toolbar';
wrapper.appendChild(toolbar);
}
let refreshBtn = toolbar.querySelector(
':scope > .chat-inline-card__refresh-btn'
) as HTMLButtonElement | null;
if (!refreshBtn) {
refreshBtn = document.createElement('button');
refreshBtn.type = 'button';
refreshBtn.className = 'chat-inline-card__refresh-btn';
refreshBtn.title = '刷新卡片';
refreshBtn.setAttribute('aria-label', '刷新卡片');
refreshBtn.textContent = '↻';
toolbar.appendChild(refreshBtn);
}
refreshBtn.onclick = (event) => {
event.preventDefault();
event.stopPropagation();
markShowTagDrawingActive(1200);
refreshBtn!.classList.remove('is-refreshing');
// 触发重放动画
void refreshBtn!.offsetWidth;
refreshBtn!.classList.add('is-refreshing');
try {
onRefresh();
} catch (error) {
debugShowHtmlLog('refresh:error', {
message: error instanceof Error ? error.message : String(error || '')
});
} finally {
window.setTimeout(() => refreshBtn?.classList.remove('is-refreshing'), 720);
}
};
}
function renderShowImages(root: ParentNode | null = document) {
if (!root) return;
const debugMode = getShowImageDebugMode();
@ -648,6 +687,24 @@ function renderShowImages(root: ParentNode | null = document) {
};
showHtmlJsIframeRenderByPath.set(pathKey, persistent);
}
bindShowHtmlRefreshControl(persistent.wrapper, () => {
// 强制重新挂载同一份 srcdoc触发 iframe 内页面重载
const keep = persistent?.srcdoc || srcdoc;
persistent.iframe.srcdoc =
'<!doctype html><html><head><meta charset="utf-8"></head><body></body></html>';
requestAnimationFrame(() => {
if (persistent?.iframe) {
persistent.iframe.srcdoc = keep;
}
});
debugShowHtmlLog('refresh:iframe', {
pathKey,
ratioKey: persistent?.ratioKey || ratioKey,
widthPx: persistent?.widthPx || widthPx,
heightPx: persistent?.heightPx || heightPx,
srcdocLength: keep.length
});
});
persistent.wrapper.style.width = `${widthPx}px`;
persistent.iframe.style.height = `${heightPx}px`;
const needsUpdate =
@ -784,6 +841,7 @@ function renderShowImages(root: ParentNode | null = document) {
wrapper.appendChild(host);
persistent = {
encoded: '',
safeHtml: '',
ratioKey: '1:1',
widthPx: 0,
heightPx: 0,
@ -793,6 +851,22 @@ function renderShowImages(root: ParentNode | null = document) {
};
showHtmlPersistentRenderByPath.set(pathKey, persistent);
}
bindShowHtmlRefreshControl(persistent.wrapper, () => {
const keep = persistent?.safeHtml || '';
persistent.root.innerHTML = '';
requestAnimationFrame(() => {
if (persistent?.root) {
persistent.root.innerHTML = keep;
}
});
debugShowHtmlLog('refresh:shadow-root', {
pathKey,
ratioKey: persistent?.ratioKey || ratioKey,
widthPx: persistent?.widthPx || widthPx,
heightPx: persistent?.heightPx || heightPx,
safeLength: keep.length
});
});
persistent.wrapper.style.width = `${widthPx}px`;
persistent.host.style.height = `${heightPx}px`;
@ -804,6 +878,7 @@ function renderShowImages(root: ParentNode | null = document) {
if (needsUpdate) {
persistent.root.innerHTML = safeHtml;
persistent.encoded = effectiveEncoded;
persistent.safeHtml = safeHtml;
persistent.widthPx = widthPx;
persistent.heightPx = heightPx;
persistent.ratioKey = ratioKey;
@ -888,6 +963,7 @@ const showHtmlPersistentRenderByPath = new Map<
string,
{
encoded: string,
safeHtml: string,
ratioKey: string,
widthPx: number,
heightPx: number,

View File

@ -965,6 +965,54 @@ body[data-theme='dark'] .more-icon {
.chat-inline-card--html {
display: block;
position: relative;
}
.chat-inline-card__toolbar {
position: absolute;
top: 8px;
right: 8px;
z-index: 4;
display: flex;
align-items: center;
gap: 6px;
pointer-events: none;
}
.chat-inline-card__refresh-btn {
pointer-events: auto;
width: 26px;
height: 26px;
border-radius: 999px;
border: 1px solid var(--claude-border);
background: color-mix(in srgb, var(--claude-card) 78%, transparent);
color: var(--claude-text-secondary);
display: inline-flex;
align-items: center;
justify-content: center;
font-size: 15px;
line-height: 1;
cursor: pointer;
transition: color 0.18s ease, border-color 0.18s ease, background 0.18s ease;
}
.chat-inline-card__refresh-btn:hover {
color: var(--claude-text);
border-color: var(--claude-accent);
background: color-mix(in srgb, var(--claude-highlight) 72%, transparent);
}
.chat-inline-card__refresh-btn.is-refreshing {
animation: chat-inline-refresh-spin 0.6s linear 1;
}
@keyframes chat-inline-refresh-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
.chat-inline-html__host--pending {