c5aba094bd
- AI Tools moves from a Settings section into a modal behind a button at the Settings tab bar's right edge (AiToolsModal, `actions` on PageTabs, /settings#ai-tools); rows get an enable switch, Default badge, and an Update button for every tool whose catalog names an updater (no version lookup on open); not-installed tools fold behind a disclosure, a finished job's log folds to one line; shared AiToolIcon - codex updates through its own `codex update`, openclaw/hermes gain `update --yes`; update-all runs every installed updater and skips an npm one only when npm does not manage the binary - Add-lane tool picker is one button showing the chosen tool that opens the enabled tools as buttons - Claude Code / Codex AgentHub sub-tab folded into Overview (home editor, hooks, pricing); the path list drops CLAUDE_HOME/CODEX_HOME; HomeDirSetting shows the saved value only while the field differs - Preview/Raw toggle for markdown files in the config FileViewer and the Changes panel's whole-file view (MarkdownPreview: react-markdown + remark-gfm, raw HTML never run, frontmatter as a code block) - docs and the CLAUDE.md marker updated
43 lines
1.2 KiB
TypeScript
43 lines
1.2 KiB
TypeScript
/**
|
|
* @file AiToolIcon.tsx
|
|
* @description An AI tool's icon: the server's cached favicon
|
|
* (GET /api/ai-tools/:id/icon), or the label's first letter when there is
|
|
* none. Shared by the AI Tools modal and the Add-lane tool picker.
|
|
* @author Nguyễn Ngọc Trí Vĩ <vinnt@smartgift.vn>
|
|
*/
|
|
|
|
import { useState } from "react";
|
|
import { api, type AiTool } from "../lib/api";
|
|
|
|
/**
|
|
* @param props.tool - The catalog entry.
|
|
* @param props.className - Size classes (default `h-8 w-8`).
|
|
*/
|
|
export function AiToolIcon({
|
|
tool,
|
|
className = "h-8 w-8",
|
|
}: {
|
|
tool: Pick<AiTool, "id" | "label">;
|
|
className?: string;
|
|
}) {
|
|
// Keyed by id: one instance showing another tool must retry its icon.
|
|
const [failedId, setFailedId] = useState<string | null>(null);
|
|
if (failedId !== tool.id) {
|
|
return (
|
|
<img
|
|
src={api.aiTools.iconUrl(tool.id)}
|
|
alt=""
|
|
onError={() => setFailedId(tool.id)}
|
|
className={`${className} shrink-0 rounded-md bg-surface-4 object-contain p-1`}
|
|
/>
|
|
);
|
|
}
|
|
return (
|
|
<span
|
|
className={`${className} flex shrink-0 items-center justify-center rounded-md bg-surface-4 text-xs font-semibold text-fg-secondary`}
|
|
>
|
|
{tool.label.charAt(0).toUpperCase()}
|
|
</span>
|
|
);
|
|
}
|