feat(core): open external link in web search result (#13362)

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

* **New Features**
* Search results now include clickable links that open in a new tab when
available, improving navigation from AI-generated results.

* **Style**
* Enhanced visual feedback for linked search results, including updated
cursor and hover effects for better user experience.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Wu Yue <akumatus@gmail.com>
This commit is contained in:
Cats Juice
2025-07-31 16:09:11 +08:00
committed by GitHub
parent dfce0116b6
commit 75cc9b432b
2 changed files with 51 additions and 3 deletions

View File

@@ -6,11 +6,13 @@ import { ToggleDownIcon, ToolIcon } from '@blocksuite/icons/lit';
import { type Signal } from '@preact/signals-core';
import { css, html, nothing, type TemplateResult } from 'lit';
import { property, state } from 'lit/decorators.js';
import { ifDefined } from 'lit/directives/if-defined.js';
export interface ToolResult {
title: string | TemplateResult<1>;
icon?: string | TemplateResult<1>;
content?: string;
href?: string;
}
export class ToolResultCard extends SignalWatcher(
@@ -18,6 +20,7 @@ export class ToolResultCard extends SignalWatcher(
) {
static override styles = css`
.ai-tool-result-wrapper {
display: block;
padding: 12px;
margin: 8px 0;
border-radius: 8px;
@@ -88,6 +91,12 @@ export class ToolResultCard extends SignalWatcher(
.result-item {
margin-top: 16px;
display: block;
cursor: default;
}
.result-item[href] {
cursor: pointer;
}
.result-item:first-child {
@@ -144,6 +153,37 @@ export class ToolResultCard extends SignalWatcher(
}
}
.result-item[href]:hover .result-title,
.result-item[href]:hover .result-content {
color: ${unsafeCSSVarV2('text/primary')};
}
.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')};
}
}
.result-item[href]:hover .result-title,
.result-item[href]:hover .result-content {
color: ${unsafeCSSVarV2('text/primary')};
}
.footer-icons {
display: flex;
position: relative;
@@ -202,7 +242,14 @@ export class ToolResultCard extends SignalWatcher(
<div class="ai-tool-results-content">
${this.results.map(
result => html`
<div class="result-item">
<a
class="result-item"
href=${ifDefined(result.href)}
target=${ifDefined(result.href ? '_blank' : undefined)}
rel=${ifDefined(
result.href ? 'noopener noreferrer' : undefined
)}
>
<div class="result-header">
<div class="result-title">${result.title}</div>
<div class="result-icon">
@@ -214,7 +261,7 @@ export class ToolResultCard extends SignalWatcher(
${result.content}
</div>`
: nothing}
</div>
</a>
`
)}
</div>

View File

@@ -56,11 +56,12 @@ export class WebSearchTool extends WithDisposable(ShadowlessElement) {
const result = this.data.result;
if (result && Array.isArray(result)) {
const results = result.map(item => {
const { favicon, title, content } = item;
const { favicon, title, content, url } = item;
return {
title: title,
icon: favicon || WebIcon(),
content: content,
href: url,
};
});
const footerIcons = result.map(item => item.favicon).filter(Boolean);