fix(core): fallback to default icon if image icon load error (#13349)

Close [AI-286](https://linear.app/affine-design/issue/AI-286)

<img width="586" height="208" alt="截屏2025-07-29 18 23 52"
src="https://github.com/user-attachments/assets/15eadb38-8cb9-4418-8f13-de7b1a3a3beb"
/>


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **New Features**
* Enhanced image icon handling with a fallback display if an icon image
fails to load.

* **Style**
* Unified and improved styling for icons to ensure a consistent
appearance across result and footer sections.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
Wu Yue
2025-07-30 10:24:28 +08:00
committed by GitHub
parent f7a094053e
commit 69e23e6a42

View File

@@ -111,30 +111,6 @@ export class ToolResultCard extends SignalWatcher(
flex: 1;
}
.result-icon {
width: 18px;
height: 18px;
&:has(img) {
background-color: ${unsafeCSSVarV2('layer/background/primary')};
border-radius: 100%;
border: 0.5px solid ${unsafeCSSVarV2('layer/insideBorder/border')};
}
img {
width: inherit;
height: inherit;
border-radius: 100%;
border: 1px solid ${unsafeCSSVarV2('layer/insideBorder/border')};
}
svg {
width: inherit;
height: inherit;
color: ${unsafeCSSVarV2('icon/primary')};
}
}
.result-content {
font-size: 12px;
line-height: 20px;
@@ -147,6 +123,27 @@ export class ToolResultCard extends SignalWatcher(
text-overflow: ellipsis;
}
.result-icon,
.footer-icon {
width: 18px;
height: 18px;
border-radius: 100%;
background-color: ${unsafeCSSVarV2('layer/background/primary')};
img {
width: 18px;
height: 18px;
border-radius: 100%;
border: 0.5px solid ${unsafeCSSVarV2('layer/insideBorder/border')};
}
svg {
width: 18px;
height: 18px;
color: ${unsafeCSSVarV2('icon/primary')};
}
}
.footer-icons {
display: flex;
position: relative;
@@ -157,26 +154,6 @@ export class ToolResultCard extends SignalWatcher(
user-select: none;
}
.footer-icon {
width: 18px;
height: 18px;
background-color: ${unsafeCSSVarV2('layer/background/primary')};
border-radius: 100%;
border: 0.5px solid ${unsafeCSSVarV2('layer/insideBorder/border')};
img {
width: 18px;
height: 18px;
border-radius: 100%;
}
svg {
width: 18px;
height: 18px;
color: ${unsafeCSSVarV2('icon/primary')};
}
}
.footer-icon:not(:first-child) {
margin-left: -8px;
}
@@ -194,7 +171,7 @@ export class ToolResultCard extends SignalWatcher(
accessor name: string = 'Tool result';
@property({ attribute: false })
accessor icon: TemplateResult<1> | string = ToolIcon();
accessor icon: TemplateResult<1> = ToolIcon();
@property({ attribute: false })
accessor footerIcons: TemplateResult<1>[] | string[] = [];
@@ -214,7 +191,7 @@ export class ToolResultCard extends SignalWatcher(
return html`
<div class="ai-tool-result-wrapper">
<div class="ai-tool-header" @click=${this.toggleCard}>
<div class="ai-icon">${this.renderIcon(this.icon)}</div>
<div class="ai-icon">${this.icon}</div>
<div class="ai-tool-name">${this.name}</div>
${this.isCollapsed
? this.renderFooterIcons()
@@ -284,7 +261,18 @@ export class ToolResultCard extends SignalWatcher(
}
if (typeof icon === 'string') {
return html`<img src=${this.buildUrl(icon)} />`;
return html`<div class="image-icon">
<img
src=${this.buildUrl(icon)}
@error=${(e: Event) => {
const img = e.target as HTMLImageElement;
img.style.display = 'none';
const iconElement = img.nextElementSibling as HTMLDivElement;
iconElement.style.display = 'block';
}}
/>
<div style="display: none;">${this.icon}</div>
</div>`;
}
return html`${icon}`;
}