u
This commit is contained in:
@@ -29,6 +29,38 @@ import {
|
|||||||
SURFACE_RAISED,
|
SURFACE_RAISED,
|
||||||
} from '../../colors';
|
} from '../../colors';
|
||||||
|
|
||||||
|
class GoogleFaviconIcon extends PureComponent {
|
||||||
|
state = { hasError: false };
|
||||||
|
|
||||||
|
handleError = () => {
|
||||||
|
this.setState({ hasError: true });
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { sx } = this.props;
|
||||||
|
if (this.state.hasError) {
|
||||||
|
return <GoogleIcon sx={sx} />;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<Box
|
||||||
|
component="img"
|
||||||
|
src="https://www.google.com/favicon.ico"
|
||||||
|
alt="Google"
|
||||||
|
onError={this.handleError}
|
||||||
|
sx={{
|
||||||
|
width: 18,
|
||||||
|
height: 18,
|
||||||
|
borderRadius: '2px',
|
||||||
|
objectFit: 'contain',
|
||||||
|
verticalAlign: 'middle',
|
||||||
|
display: 'inline-block',
|
||||||
|
...sx,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const TOOL_ICON_MAP = {
|
const TOOL_ICON_MAP = {
|
||||||
find_files: SearchRoundedIcon,
|
find_files: SearchRoundedIcon,
|
||||||
javascript_execute: ComputerRoundedIcon,
|
javascript_execute: ComputerRoundedIcon,
|
||||||
@@ -41,7 +73,7 @@ const TOOL_ICON_MAP = {
|
|||||||
read_file: DescriptionIcon,
|
read_file: DescriptionIcon,
|
||||||
reset_vfs: DeleteForeverIcon,
|
reset_vfs: DeleteForeverIcon,
|
||||||
rm: DeleteIcon,
|
rm: DeleteIcon,
|
||||||
web_search: GoogleIcon,
|
web_search: GoogleFaviconIcon,
|
||||||
url_get: WebIcon,
|
url_get: WebIcon,
|
||||||
sub_agent: EscalatorWarningIcon,
|
sub_agent: EscalatorWarningIcon,
|
||||||
pdf_read: PictureAsPdfIcon,
|
pdf_read: PictureAsPdfIcon,
|
||||||
@@ -318,12 +350,114 @@ function BrainIconView({ reasoning }) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function extractUrlFromTool(tool) {
|
||||||
|
if (!tool) return null;
|
||||||
|
const candidates = [tool.parameters, tool.arguments, tool.content, tool.result];
|
||||||
|
for (const item of candidates) {
|
||||||
|
if (!item) continue;
|
||||||
|
if (typeof item === 'object') {
|
||||||
|
const u = item.url || item.Url || item.uri || item.URI || item.link;
|
||||||
|
if (typeof u === 'string' && u.trim()) return u.trim();
|
||||||
|
}
|
||||||
|
const str = typeof item === 'string' ? item : JSON.stringify(item);
|
||||||
|
const match = str.match(/https?:\/\/[^\s"'`<>{}|\\]+/);
|
||||||
|
if (match) {
|
||||||
|
return match[0].replace(/[.,;:)]+$/, '');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
class UrlFaviconIcon extends PureComponent {
|
||||||
|
state = {
|
||||||
|
srcIndex: 0,
|
||||||
|
hasError: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
sources = [];
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.sources = this.buildSources(props.url);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidUpdate(prevProps) {
|
||||||
|
if (this.props.url !== prevProps.url) {
|
||||||
|
this.sources = this.buildSources(this.props.url);
|
||||||
|
this.setState({ srcIndex: 0, hasError: false });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buildSources(urlStr) {
|
||||||
|
if (!urlStr || typeof urlStr !== 'string') return [];
|
||||||
|
try {
|
||||||
|
const parsed = new URL(
|
||||||
|
urlStr.startsWith('http://') || urlStr.startsWith('https://')
|
||||||
|
? urlStr
|
||||||
|
: `https://${urlStr}`
|
||||||
|
);
|
||||||
|
const hostname = parsed.hostname;
|
||||||
|
const origin = parsed.origin;
|
||||||
|
if (!hostname) return [];
|
||||||
|
|
||||||
|
const list = [];
|
||||||
|
if (hostname === 'localhost' || hostname === '127.0.0.1' || hostname.endsWith('.local')) {
|
||||||
|
list.push(`${origin}/favicon.ico`);
|
||||||
|
} else {
|
||||||
|
list.push(`https://icons.duckduckgo.com/ip3/${hostname}.ico`);
|
||||||
|
list.push(`${origin}/favicon.ico`);
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
} catch {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleError = () => {
|
||||||
|
if (this.state.srcIndex + 1 < this.sources.length) {
|
||||||
|
this.setState((prev) => ({ srcIndex: prev.srcIndex + 1 }));
|
||||||
|
} else {
|
||||||
|
this.setState({ hasError: true });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { sx } = this.props;
|
||||||
|
const { srcIndex, hasError } = this.state;
|
||||||
|
const currentSrc = this.sources[srcIndex];
|
||||||
|
|
||||||
|
if (!currentSrc || hasError) {
|
||||||
|
return <WebIcon sx={sx} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box
|
||||||
|
component="img"
|
||||||
|
src={currentSrc}
|
||||||
|
alt=""
|
||||||
|
onError={this.handleError}
|
||||||
|
sx={{
|
||||||
|
width: 18,
|
||||||
|
height: 18,
|
||||||
|
borderRadius: '2px',
|
||||||
|
objectFit: 'contain',
|
||||||
|
verticalAlign: 'middle',
|
||||||
|
display: 'inline-block',
|
||||||
|
...sx,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function WorkIconView({ tool }) {
|
function WorkIconView({ tool }) {
|
||||||
if (!tool || !tool.name) return null;
|
if (!tool || !tool.name) return null;
|
||||||
const isGreen = tool?.state === 'green';
|
const isGreen = tool?.state === 'green';
|
||||||
|
const isUrlGet = tool?.name === 'url_get';
|
||||||
const ToolNameIcon = tool?.name ? TOOL_ICON_MAP[tool.name] : null;
|
const ToolNameIcon = tool?.name ? TOOL_ICON_MAP[tool.name] : null;
|
||||||
const prefix = ToolNameIcon ? tool?.name : null;
|
const prefix = ToolNameIcon ? tool?.name : null;
|
||||||
const tooltipText = formatToolTooltip(tool?.content, tool?.result, tool?.parameters || tool?.arguments, prefix);
|
const tooltipText = formatToolTooltip(tool?.content, tool?.result, tool?.parameters || tool?.arguments, prefix);
|
||||||
|
const targetUrl = isUrlGet ? extractUrlFromTool(tool) : null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ClickTooltip
|
<ClickTooltip
|
||||||
@@ -358,7 +492,9 @@ function WorkIconView({ tool }) {
|
|||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{ToolNameIcon ? (
|
{isUrlGet ? (
|
||||||
|
<UrlFaviconIcon url={targetUrl} sx={{ fontSize: 18, color: 'inherit' }} />
|
||||||
|
) : ToolNameIcon ? (
|
||||||
<ToolNameIcon sx={{ fontSize: 18, color: 'inherit' }} />
|
<ToolNameIcon sx={{ fontSize: 18, color: 'inherit' }} />
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
|
|||||||
Reference in New Issue
Block a user