fix: reorder skill tags while dragging
This commit is contained in:
parent
ec1bdb7396
commit
4f5a986162
@ -436,6 +436,17 @@ function reorderCanvasSkillTags(
|
||||
return nextTags;
|
||||
}
|
||||
|
||||
function applyCanvasSkillTagOrder(tags: CanvasSkillTag[], order: string[]) {
|
||||
if (tags.length < 2 || order.length === 0) return tags;
|
||||
|
||||
const tagById = new Map(tags.map((tag) => [tag.id, tag]));
|
||||
const orderedTags = order
|
||||
.map((id) => tagById.get(id))
|
||||
.filter((tag): tag is CanvasSkillTag => Boolean(tag));
|
||||
const orderedIds = new Set(orderedTags.map((tag) => tag.id));
|
||||
return [...orderedTags, ...tags.filter((tag) => !orderedIds.has(tag.id))];
|
||||
}
|
||||
|
||||
type ClawdComposerSubmissionSnapshot = {
|
||||
chatId: string;
|
||||
canvasId?: string;
|
||||
@ -2185,8 +2196,13 @@ export function CanvasChatPanel({
|
||||
const [selectedSkillTagId, setSelectedSkillTagId] = useState<string | null>(null);
|
||||
const [areCanvasSkillTagsExpanded, setAreCanvasSkillTagsExpanded] = useState(false);
|
||||
const [draggingSkillTagId, setDraggingSkillTagId] = useState<string | null>(null);
|
||||
const [canvasSkillTagOrderOverride, setCanvasSkillTagOrderOverride] = useState<string[] | null>(
|
||||
null
|
||||
);
|
||||
const pendingSelectedClawdSkillTagRef = useRef<CanvasSkillTag | null>(null);
|
||||
const draggingSkillTagIdRef = useRef<string | null>(null);
|
||||
const latestCanvasSkillTagsRef = useRef<CanvasSkillTag[]>([]);
|
||||
const pendingCanvasSkillTagOrderSaveRef = useRef<string[] | null>(null);
|
||||
const suppressSkillTagClickRef = useRef(false);
|
||||
const [messageViewportKey, setMessageViewportKey] = useState(0);
|
||||
const messagesEndRef = useRef<HTMLDivElement>(null);
|
||||
@ -4164,16 +4180,34 @@ export function CanvasChatPanel({
|
||||
const canvasSkillTagsEnabled = canvasSkillTagsData?.enabled !== false;
|
||||
const canvasSkillTagsTitle =
|
||||
String(canvasSkillTagsData?.title || "").trim() || DEFAULT_CANVAS_SKILL_TAGS_TITLE;
|
||||
const canvasSkillTags = useMemo(
|
||||
const baseCanvasSkillTags = useMemo(
|
||||
() => (canvasSkillTagsEnabled ? normalizeCanvasSkillTags(canvasSkillTagsData?.tags) : []),
|
||||
[canvasSkillTagsData?.tags, canvasSkillTagsEnabled]
|
||||
);
|
||||
const handleReorderCanvasSkillTag = useCallback(
|
||||
async (draggedId: string, targetId: string) => {
|
||||
const nextTags = reorderCanvasSkillTags(canvasSkillTags, draggedId, targetId);
|
||||
if (nextTags === canvasSkillTags) return;
|
||||
const canvasSkillTags = useMemo(
|
||||
() =>
|
||||
canvasSkillTagOrderOverride
|
||||
? applyCanvasSkillTagOrder(baseCanvasSkillTags, canvasSkillTagOrderOverride)
|
||||
: baseCanvasSkillTags,
|
||||
[baseCanvasSkillTags, canvasSkillTagOrderOverride]
|
||||
);
|
||||
useEffect(() => {
|
||||
latestCanvasSkillTagsRef.current = canvasSkillTags;
|
||||
}, [canvasSkillTags]);
|
||||
|
||||
const userOrder = nextTags.map((tag) => tag.id);
|
||||
const persistCanvasSkillTagOrder = useCallback(
|
||||
async (nextOrder: string[]) => {
|
||||
const knownIds = new Set(baseCanvasSkillTags.map((tag) => tag.id));
|
||||
const userOrder = Array.from(
|
||||
new Set(
|
||||
nextOrder
|
||||
.map((id) => String(id || "").trim())
|
||||
.filter((id) => id && knownIds.has(id))
|
||||
)
|
||||
);
|
||||
if (userOrder.length < 2) return;
|
||||
|
||||
const nextTags = applyCanvasSkillTagOrder(baseCanvasSkillTags, userOrder);
|
||||
const optimisticData = {
|
||||
...(canvasSkillTagsData || {}),
|
||||
enabled: canvasSkillTagsData?.enabled,
|
||||
@ -4198,11 +4232,12 @@ export function CanvasChatPanel({
|
||||
toast.success("快捷标签顺序已保存");
|
||||
} catch (error) {
|
||||
console.error("[CanvasChat] 保存快捷标签顺序失败:", error);
|
||||
setCanvasSkillTagOrderOverride(null);
|
||||
toast.error("快捷标签顺序保存失败");
|
||||
void mutate("/api/canvas-skill-tags");
|
||||
}
|
||||
},
|
||||
[canvasSkillTags, canvasSkillTagsData, mutate]
|
||||
[baseCanvasSkillTags, canvasSkillTagsData, mutate]
|
||||
);
|
||||
const selectedSkillTag = useMemo(
|
||||
() => canvasSkillTags.find((tag) => tag.id === selectedSkillTagId) || null,
|
||||
@ -5429,6 +5464,7 @@ export function CanvasChatPanel({
|
||||
const handleCanvasSkillTagDragStart = useCallback(
|
||||
(event: ReactDragEvent<HTMLButtonElement>, tagId: string) => {
|
||||
draggingSkillTagIdRef.current = tagId;
|
||||
pendingCanvasSkillTagOrderSaveRef.current = null;
|
||||
setDraggingSkillTagId(tagId);
|
||||
event.dataTransfer.effectAllowed = "move";
|
||||
event.dataTransfer.setData("text/plain", tagId);
|
||||
@ -5436,6 +5472,24 @@ export function CanvasChatPanel({
|
||||
[]
|
||||
);
|
||||
|
||||
const handleCanvasSkillTagDragEnter = useCallback(
|
||||
(event: ReactDragEvent<HTMLButtonElement>, targetId: string) => {
|
||||
const draggedId = draggingSkillTagIdRef.current;
|
||||
if (!draggedId || draggedId === targetId) return;
|
||||
|
||||
event.preventDefault();
|
||||
const currentTags = latestCanvasSkillTagsRef.current;
|
||||
const nextTags = reorderCanvasSkillTags(currentTags, draggedId, targetId);
|
||||
if (nextTags === currentTags) return;
|
||||
|
||||
const nextOrder = nextTags.map((tag) => tag.id);
|
||||
pendingCanvasSkillTagOrderSaveRef.current = nextOrder;
|
||||
latestCanvasSkillTagsRef.current = nextTags;
|
||||
setCanvasSkillTagOrderOverride(nextOrder);
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
const handleCanvasSkillTagDragOver = useCallback(
|
||||
(event: ReactDragEvent<HTMLButtonElement>, targetId: string) => {
|
||||
const draggedId = draggingSkillTagIdRef.current;
|
||||
@ -5451,23 +5505,41 @@ export function CanvasChatPanel({
|
||||
event.preventDefault();
|
||||
const draggedId =
|
||||
event.dataTransfer.getData("text/plain") || draggingSkillTagIdRef.current;
|
||||
draggingSkillTagIdRef.current = null;
|
||||
setDraggingSkillTagId(null);
|
||||
if (!draggedId || draggedId === targetId) return;
|
||||
if (draggedId && draggedId !== targetId && !pendingCanvasSkillTagOrderSaveRef.current) {
|
||||
const nextTags = reorderCanvasSkillTags(
|
||||
latestCanvasSkillTagsRef.current,
|
||||
draggedId,
|
||||
targetId
|
||||
);
|
||||
if (nextTags !== latestCanvasSkillTagsRef.current) {
|
||||
const nextOrder = nextTags.map((tag) => tag.id);
|
||||
pendingCanvasSkillTagOrderSaveRef.current = nextOrder;
|
||||
latestCanvasSkillTagsRef.current = nextTags;
|
||||
setCanvasSkillTagOrderOverride(nextOrder);
|
||||
}
|
||||
}
|
||||
|
||||
suppressSkillTagClickRef.current = true;
|
||||
window.setTimeout(() => {
|
||||
suppressSkillTagClickRef.current = false;
|
||||
}, 0);
|
||||
void handleReorderCanvasSkillTag(draggedId, targetId);
|
||||
},
|
||||
[handleReorderCanvasSkillTag]
|
||||
[]
|
||||
);
|
||||
|
||||
const handleCanvasSkillTagDragEnd = useCallback(() => {
|
||||
const pendingOrder = pendingCanvasSkillTagOrderSaveRef.current;
|
||||
pendingCanvasSkillTagOrderSaveRef.current = null;
|
||||
draggingSkillTagIdRef.current = null;
|
||||
setDraggingSkillTagId(null);
|
||||
}, []);
|
||||
if (pendingOrder?.length) {
|
||||
suppressSkillTagClickRef.current = true;
|
||||
window.setTimeout(() => {
|
||||
suppressSkillTagClickRef.current = false;
|
||||
}, 0);
|
||||
void persistCanvasSkillTagOrder(pendingOrder);
|
||||
}
|
||||
}, [persistCanvasSkillTagOrder]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!effectiveAgentEnabled || typeof window === "undefined") return;
|
||||
@ -12760,6 +12832,9 @@ export function CanvasChatPanel({
|
||||
onDragStart={(event) =>
|
||||
handleCanvasSkillTagDragStart(event, tag.id)
|
||||
}
|
||||
onDragEnter={(event) =>
|
||||
handleCanvasSkillTagDragEnter(event, tag.id)
|
||||
}
|
||||
onDragOver={(event) =>
|
||||
handleCanvasSkillTagDragOver(event, tag.id)
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user