2026-03-25 09:27:12 +08:00
|
|
|
import { CATEGORIES, TOOLS } from "@stirling-image/shared";
|
2026-03-25 09:42:48 +08:00
|
|
|
import { useEffect, useMemo, useState } from "react";
|
2026-04-05 00:23:21 +08:00
|
|
|
import { useSettingsStore } from "@/stores/settings-store";
|
2026-03-22 03:01:23 +08:00
|
|
|
import { SearchBar } from "../common/search-bar";
|
|
|
|
|
import { ToolCard } from "../common/tool-card";
|
|
|
|
|
|
|
|
|
|
export function ToolPanel() {
|
|
|
|
|
const [search, setSearch] = useState("");
|
2026-04-10 17:38:54 +08:00
|
|
|
const { disabledTools, experimentalEnabled, loaded, fetch } = useSettingsStore();
|
2026-03-25 09:42:48 +08:00
|
|
|
|
|
|
|
|
useEffect(() => {
|
2026-04-05 00:23:21 +08:00
|
|
|
fetch();
|
|
|
|
|
}, [fetch]);
|
|
|
|
|
|
2026-03-25 09:42:48 +08:00
|
|
|
const visibleTools = useMemo(() => {
|
2026-04-05 00:23:21 +08:00
|
|
|
if (!loaded) return [];
|
2026-03-25 09:42:48 +08:00
|
|
|
return TOOLS.filter((t) => {
|
|
|
|
|
if (disabledTools.includes(t.id)) return false;
|
|
|
|
|
if (t.experimental && !experimentalEnabled) return false;
|
|
|
|
|
return true;
|
|
|
|
|
});
|
2026-04-05 00:23:21 +08:00
|
|
|
}, [disabledTools, experimentalEnabled, loaded]);
|
2026-03-22 03:01:23 +08:00
|
|
|
|
|
|
|
|
const filteredTools = useMemo(() => {
|
2026-03-25 09:42:48 +08:00
|
|
|
if (!search) return visibleTools;
|
2026-03-22 03:01:23 +08:00
|
|
|
const q = search.toLowerCase();
|
2026-03-25 09:42:48 +08:00
|
|
|
return visibleTools.filter(
|
2026-03-25 09:27:12 +08:00
|
|
|
(t) => t.name.toLowerCase().includes(q) || t.description.toLowerCase().includes(q),
|
2026-03-22 03:01:23 +08:00
|
|
|
);
|
2026-03-25 09:42:48 +08:00
|
|
|
}, [search, visibleTools]);
|
2026-03-22 03:01:23 +08:00
|
|
|
|
|
|
|
|
const groupedTools = useMemo(() => {
|
|
|
|
|
const groups = new Map<string, typeof TOOLS>();
|
|
|
|
|
for (const tool of filteredTools) {
|
|
|
|
|
const list = groups.get(tool.category) || [];
|
|
|
|
|
list.push(tool);
|
|
|
|
|
groups.set(tool.category, list);
|
|
|
|
|
}
|
|
|
|
|
return groups;
|
|
|
|
|
}, [filteredTools]);
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div className="w-72 border-r border-border bg-background overflow-y-auto flex flex-col shrink-0">
|
|
|
|
|
<div className="p-3 sticky top-0 bg-background z-10">
|
|
|
|
|
<SearchBar value={search} onChange={setSearch} />
|
|
|
|
|
</div>
|
|
|
|
|
<div className="px-3 pb-4 flex-1">
|
2026-03-25 09:27:12 +08:00
|
|
|
{CATEGORIES.filter((cat) => groupedTools.has(cat.id)).map((category) => (
|
|
|
|
|
<div key={category.id} className="mb-4">
|
|
|
|
|
<h3 className="text-xs font-semibold uppercase text-muted-foreground tracking-wider mb-2">
|
|
|
|
|
{category.name}
|
|
|
|
|
</h3>
|
|
|
|
|
<div className="space-y-0.5">
|
|
|
|
|
{groupedTools.get(category.id)?.map((tool) => (
|
2026-04-10 17:38:54 +08:00
|
|
|
<ToolCard key={tool.id} tool={tool} />
|
2026-03-25 09:27:12 +08:00
|
|
|
))}
|
2026-03-22 03:01:23 +08:00
|
|
|
</div>
|
2026-03-25 09:27:12 +08:00
|
|
|
</div>
|
|
|
|
|
))}
|
2026-03-22 03:01:23 +08:00
|
|
|
{filteredTools.length === 0 && (
|
2026-03-25 09:27:12 +08:00
|
|
|
<p className="text-sm text-muted-foreground text-center py-8">No tools found</p>
|
2026-03-22 03:01:23 +08:00
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|