feat(skills): flag unmanaged skills with a green dot on the Import button

- Add an optional `enabled` flag to useScanUnmanagedSkills; the Skills panel
  scans once on mount (enabled:true), with 30s staleTime + keepPreviousData to
  dedupe disk IO across navigations.
- App subscribes to the shared query (enabled:false) and renders a green dot +
  tooltip on the top-bar Import button when unmanaged skills are available.
This commit is contained in:
Jason
2026-06-24 23:47:04 +08:00
parent a4eb5f3778
commit f1328d89fc
3 changed files with 28 additions and 4 deletions
+17 -1
View File
@@ -46,6 +46,7 @@ import { useAutoCompact } from "@/hooks/useAutoCompact";
import { useUsageCacheBridge } from "@/hooks/useUsageCacheBridge";
import { useTauriEvent } from "@/hooks/useTauriEvent";
import { useLastValidValue } from "@/hooks/useLastValidValue";
import { useScanUnmanagedSkills } from "@/hooks/useSkills";
import { extractErrorMessage } from "@/utils/errorUtils";
import { isTextEditableTarget } from "@/utils/domUtils";
import { deepClone } from "@/utils/deepClone";
@@ -250,6 +251,10 @@ function App() {
const mcpPanelRef = useRef<any>(null);
const skillsPageRef = useRef<any>(null);
const unifiedSkillsPanelRef = useRef<any>(null);
// 订阅未管理 Skill 的共享缓存(实际扫描由 UnifiedSkillsPanel 进入页面时触发)。
// 这里 enabled 默认 false,仅用于「导入」按钮的绿点提示,不主动发起扫描。
const { data: unmanagedSkills } = useScanUnmanagedSkills();
const hasUnmanagedSkills = (unmanagedSkills?.length ?? 0) > 0;
const addActionButtonClass =
"bg-orange-500 hover:bg-orange-600 dark:bg-orange-500 dark:hover:bg-orange-600 text-white shadow-lg shadow-orange-500/30 dark:shadow-orange-500/40 rounded-full w-8 h-8";
@@ -1315,10 +1320,21 @@ function App() {
onClick={() =>
unifiedSkillsPanelRef.current?.openImport()
}
className="hover:bg-black/5 dark:hover:bg-white/5"
className="relative hover:bg-black/5 dark:hover:bg-white/5"
title={
hasUnmanagedSkills
? t("skills.unmanagedAvailable")
: undefined
}
>
<Download className="w-4 h-4 mr-2" />
{t("skills.import")}
{hasUnmanagedSkills && (
<span
className="absolute top-1 right-1 h-2 w-2 rounded-full bg-green-500"
aria-hidden="true"
/>
)}
</Button>
<Button
variant="ghost"