feat: add Claw skill search
This commit is contained in:
parent
d467694aa7
commit
233c7b3308
@ -25,7 +25,7 @@ import {
|
||||
type UiSurfaceSnapshot,
|
||||
} from "@/lib/ui-surface-protocol";
|
||||
import useSWR, { useSWRConfig } from "swr";
|
||||
import { Loader2, ImagePlus, X, Plus, History, LayoutGrid, ArrowRightToLine, RefreshCw, Globe, ArrowUp, Video, Sparkles, Check, ChevronDown, ChevronUp, AlertCircle, CheckCircle2, Brain, ThumbsUp, ThumbsDown, Eye, Trash2, Download, MessageCircle, Clapperboard, Bot, FileSpreadsheet, Paperclip, Copy, Square, GripVertical } from "lucide-react";
|
||||
import { Loader2, ImagePlus, X, Plus, History, LayoutGrid, ArrowRightToLine, RefreshCw, Globe, ArrowUp, Video, Sparkles, Check, ChevronDown, ChevronUp, AlertCircle, CheckCircle2, Brain, ThumbsUp, ThumbsDown, Eye, Trash2, Download, MessageCircle, Clapperboard, Bot, FileSpreadsheet, Paperclip, Copy, Square, Search, GripVertical } from "lucide-react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
Collapsible,
|
||||
@ -2656,6 +2656,7 @@ export function CanvasChatPanel({
|
||||
const [isComposerModeMenuOpen, setIsComposerModeMenuOpen] = useState(false);
|
||||
const [selectedSkillTagId, setSelectedSkillTagId] = useState<string | null>(null);
|
||||
const [areCanvasSkillTagsExpanded, setAreCanvasSkillTagsExpanded] = useState(false);
|
||||
const [canvasSkillTagSearchQuery, setCanvasSkillTagSearchQuery] = useState("");
|
||||
const [draggingSkillTagId, setDraggingSkillTagId] = useState<string | null>(null);
|
||||
const [canvasSkillTagOrderOverride, setCanvasSkillTagOrderOverride] = useState<string[] | null>(
|
||||
null
|
||||
@ -4817,6 +4818,25 @@ export function CanvasChatPanel({
|
||||
: baseCanvasSkillTags,
|
||||
[baseCanvasSkillTags, canvasSkillTagOrderOverride]
|
||||
);
|
||||
const normalizedCanvasSkillTagSearchQuery = canvasSkillTagSearchQuery
|
||||
.trim()
|
||||
.toLocaleLowerCase();
|
||||
const filteredCanvasSkillTags = useMemo(() => {
|
||||
if (!normalizedCanvasSkillTagSearchQuery) {
|
||||
return canvasSkillTags;
|
||||
}
|
||||
|
||||
const keywords = normalizedCanvasSkillTagSearchQuery.split(/\s+/).filter(Boolean);
|
||||
return canvasSkillTags.filter((tag) => {
|
||||
const searchableText = [tag.name, tag.prompt, tag.placeholder]
|
||||
.map((value) => String(value || "").trim())
|
||||
.filter(Boolean)
|
||||
.join("\n")
|
||||
.toLocaleLowerCase();
|
||||
return keywords.every((keyword) => searchableText.includes(keyword));
|
||||
});
|
||||
}, [canvasSkillTags, normalizedCanvasSkillTagSearchQuery]);
|
||||
const isCanvasSkillTagSearching = normalizedCanvasSkillTagSearchQuery.length > 0;
|
||||
useEffect(() => {
|
||||
latestCanvasSkillTagsRef.current = canvasSkillTags;
|
||||
}, [canvasSkillTags]);
|
||||
@ -4869,7 +4889,8 @@ export function CanvasChatPanel({
|
||||
() => canvasSkillTags.find((tag) => tag.id === selectedSkillTagId) || null,
|
||||
[canvasSkillTags, selectedSkillTagId]
|
||||
);
|
||||
const shouldShowCanvasSkillTagsToggle = canvasSkillTags.length > 4;
|
||||
const shouldShowCanvasSkillTagsToggle =
|
||||
!isCanvasSkillTagSearching && canvasSkillTags.length > 4;
|
||||
const activeChatSession = useMemo(
|
||||
() => chatSessions.find((session) => session.id === activeChatId) || null,
|
||||
[activeChatId, chatSessions]
|
||||
@ -6169,6 +6190,7 @@ export function CanvasChatPanel({
|
||||
promptApplied: false,
|
||||
});
|
||||
pendingSelectedClawdSkillTagRef.current = tag;
|
||||
setCanvasSkillTagSearchQuery("");
|
||||
window.setTimeout(() => {
|
||||
setSelectedSkillTagId(tag.id);
|
||||
const editor = textareaRef.current as HTMLDivElement | null;
|
||||
@ -14382,15 +14404,49 @@ export function CanvasChatPanel({
|
||||
<h3 className="text-[14px] font-semibold leading-none text-[#1f2328] dark:text-zinc-100">
|
||||
{canvasSkillTagsTitle}
|
||||
</h3>
|
||||
{canvasSkillTags.length > 4 ? (
|
||||
<div className="mx-auto mt-5 flex h-9 w-full max-w-[360px] items-center gap-2 rounded-lg border border-[#e5e7eb] bg-[#fafbfc] px-3 text-left transition-colors focus-within:border-[#aeb6c5] focus-within:bg-white dark:border-white/10 dark:bg-white/5 dark:focus-within:border-white/25 dark:focus-within:bg-white/[0.07]">
|
||||
<Search className="h-4 w-4 shrink-0 text-[#9299a6] dark:text-zinc-500" />
|
||||
<input
|
||||
type="text"
|
||||
value={canvasSkillTagSearchQuery}
|
||||
onChange={(event) =>
|
||||
setCanvasSkillTagSearchQuery(event.target.value)
|
||||
}
|
||||
onKeyDown={(event) => {
|
||||
if (event.key === "Escape") {
|
||||
setCanvasSkillTagSearchQuery("");
|
||||
}
|
||||
}}
|
||||
placeholder="搜索快捷技能"
|
||||
aria-label="搜索快捷技能"
|
||||
autoComplete="off"
|
||||
enterKeyHint="search"
|
||||
spellCheck={false}
|
||||
className="h-full min-w-0 flex-1 bg-transparent text-[13px] text-[#1f2328] outline-none placeholder:text-[#9aa1ad] dark:text-zinc-100 dark:placeholder:text-zinc-500"
|
||||
/>
|
||||
{canvasSkillTagSearchQuery ? (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setCanvasSkillTagSearchQuery("")}
|
||||
className="inline-flex h-6 w-6 shrink-0 items-center justify-center text-[#9299a6] transition-colors hover:text-[#1f2328] dark:text-zinc-500 dark:hover:text-zinc-100"
|
||||
title="清空搜索"
|
||||
aria-label="清空快捷技能搜索"
|
||||
>
|
||||
<X className="h-3.5 w-3.5" />
|
||||
</button>
|
||||
) : null}
|
||||
</div>
|
||||
) : null}
|
||||
<div
|
||||
className={cn(
|
||||
"mt-4 flex flex-wrap justify-center gap-1.5 transition-[max-height] duration-150 ease-out",
|
||||
areCanvasSkillTagsExpanded
|
||||
isCanvasSkillTagSearching || areCanvasSkillTagsExpanded
|
||||
? "max-h-none overflow-visible"
|
||||
: "max-h-[76px] overflow-hidden"
|
||||
)}
|
||||
>
|
||||
{canvasSkillTags.map((tag) => {
|
||||
{filteredCanvasSkillTags.map((tag) => {
|
||||
const isActive = selectedSkillTag?.id === tag.id;
|
||||
const isDragging = draggingSkillTagId === tag.id;
|
||||
const TagIcon = getCanvasSkillIconComponent(tag.iconName);
|
||||
@ -14398,7 +14454,7 @@ export function CanvasChatPanel({
|
||||
<button
|
||||
key={tag.id}
|
||||
type="button"
|
||||
draggable={canvasSkillTags.length > 1}
|
||||
draggable={!isCanvasSkillTagSearching && canvasSkillTags.length > 1}
|
||||
onDragStart={(event) =>
|
||||
handleCanvasSkillTagDragStart(event, tag.id)
|
||||
}
|
||||
@ -14412,13 +14468,20 @@ export function CanvasChatPanel({
|
||||
onDragEnd={handleCanvasSkillTagDragEnd}
|
||||
onClick={() => handleSelectCanvasSkillTag(tag)}
|
||||
className={cn(
|
||||
"inline-flex h-8 max-w-full cursor-grab items-center justify-center gap-1.5 rounded-full border px-3 text-[12px] font-medium leading-none transition-colors duration-150 active:cursor-grabbing",
|
||||
"inline-flex h-8 max-w-full items-center justify-center gap-1.5 rounded-full border px-3 text-[12px] font-medium leading-none transition-colors duration-150",
|
||||
isCanvasSkillTagSearching
|
||||
? "cursor-pointer"
|
||||
: "cursor-grab active:cursor-grabbing",
|
||||
isActive
|
||||
? "border-[#d8dce4] bg-[#f7f8fa] text-[#1f2328] dark:border-blue-400/40 dark:bg-blue-500/15 dark:text-blue-200"
|
||||
: "border-[#e5e7eb] bg-white text-[#1f2328] hover:border-[#d6dae3] hover:bg-[#fafbfc] dark:border-white/10 dark:bg-white/5 dark:text-zinc-200 dark:hover:border-white/20 dark:hover:bg-white/10",
|
||||
isDragging && "scale-95 opacity-45"
|
||||
)}
|
||||
title="点击使用,拖动调整顺序"
|
||||
title={
|
||||
isCanvasSkillTagSearching
|
||||
? "点击使用快捷技能"
|
||||
: "点击使用,拖动调整顺序"
|
||||
}
|
||||
>
|
||||
{tag.iconUrl ? (
|
||||
<img
|
||||
@ -14440,6 +14503,11 @@ export function CanvasChatPanel({
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
{isCanvasSkillTagSearching && filteredCanvasSkillTags.length === 0 ? (
|
||||
<p className="w-full py-2 text-center text-[13px] text-[#9299a6] dark:text-zinc-500">
|
||||
未找到匹配的快捷技能
|
||||
</p>
|
||||
) : null}
|
||||
</div>
|
||||
{shouldShowCanvasSkillTagsToggle ? (
|
||||
<button
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user