feat(ui): add refresh button for showhtml cards
This commit is contained in:
parent
591a5e64cc
commit
3cd8bdabef
@ -380,6 +380,45 @@ function escapeHtml(input: string) {
|
|||||||
.replace(/'/g, ''');
|
.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) {
|
function renderShowImages(root: ParentNode | null = document) {
|
||||||
if (!root) return;
|
if (!root) return;
|
||||||
const debugMode = getShowImageDebugMode();
|
const debugMode = getShowImageDebugMode();
|
||||||
@ -648,6 +687,24 @@ function renderShowImages(root: ParentNode | null = document) {
|
|||||||
};
|
};
|
||||||
showHtmlJsIframeRenderByPath.set(pathKey, persistent);
|
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.wrapper.style.width = `${widthPx}px`;
|
||||||
persistent.iframe.style.height = `${heightPx}px`;
|
persistent.iframe.style.height = `${heightPx}px`;
|
||||||
const needsUpdate =
|
const needsUpdate =
|
||||||
@ -784,6 +841,7 @@ function renderShowImages(root: ParentNode | null = document) {
|
|||||||
wrapper.appendChild(host);
|
wrapper.appendChild(host);
|
||||||
persistent = {
|
persistent = {
|
||||||
encoded: '',
|
encoded: '',
|
||||||
|
safeHtml: '',
|
||||||
ratioKey: '1:1',
|
ratioKey: '1:1',
|
||||||
widthPx: 0,
|
widthPx: 0,
|
||||||
heightPx: 0,
|
heightPx: 0,
|
||||||
@ -793,6 +851,22 @@ function renderShowImages(root: ParentNode | null = document) {
|
|||||||
};
|
};
|
||||||
showHtmlPersistentRenderByPath.set(pathKey, persistent);
|
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.wrapper.style.width = `${widthPx}px`;
|
||||||
persistent.host.style.height = `${heightPx}px`;
|
persistent.host.style.height = `${heightPx}px`;
|
||||||
@ -804,6 +878,7 @@ function renderShowImages(root: ParentNode | null = document) {
|
|||||||
if (needsUpdate) {
|
if (needsUpdate) {
|
||||||
persistent.root.innerHTML = safeHtml;
|
persistent.root.innerHTML = safeHtml;
|
||||||
persistent.encoded = effectiveEncoded;
|
persistent.encoded = effectiveEncoded;
|
||||||
|
persistent.safeHtml = safeHtml;
|
||||||
persistent.widthPx = widthPx;
|
persistent.widthPx = widthPx;
|
||||||
persistent.heightPx = heightPx;
|
persistent.heightPx = heightPx;
|
||||||
persistent.ratioKey = ratioKey;
|
persistent.ratioKey = ratioKey;
|
||||||
@ -888,6 +963,7 @@ const showHtmlPersistentRenderByPath = new Map<
|
|||||||
string,
|
string,
|
||||||
{
|
{
|
||||||
encoded: string,
|
encoded: string,
|
||||||
|
safeHtml: string,
|
||||||
ratioKey: string,
|
ratioKey: string,
|
||||||
widthPx: number,
|
widthPx: number,
|
||||||
heightPx: number,
|
heightPx: number,
|
||||||
|
|||||||
@ -965,6 +965,54 @@ body[data-theme='dark'] .more-icon {
|
|||||||
|
|
||||||
.chat-inline-card--html {
|
.chat-inline-card--html {
|
||||||
display: block;
|
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 {
|
.chat-inline-html__host--pending {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user