diff --git a/.changeset/fn-7331-remove-quick-add-refine.md b/.changeset/fn-7331-remove-quick-add-refine.md new file mode 100644 index 0000000000..4382ccd5ce --- /dev/null +++ b/.changeset/fn-7331-remove-quick-add-refine.md @@ -0,0 +1,7 @@ +--- +"@runfusion/fusion": patch +--- + +summary: Remove the AI Refine action from Quick Add task creation. +category: fix +dev: QuickEntryBox no longer renders or calls the refine-text path; TaskForm refine remains available. diff --git a/packages/dashboard/app/__tests__/quick-entry-expanded-height.test.tsx b/packages/dashboard/app/__tests__/quick-entry-expanded-height.test.tsx index e107d64b01..c6b8fcb444 100644 --- a/packages/dashboard/app/__tests__/quick-entry-expanded-height.test.tsx +++ b/packages/dashboard/app/__tests__/quick-entry-expanded-height.test.tsx @@ -40,9 +40,8 @@ vi.mock("../api", () => ({ groupOverlappingFiles: true, autoMerge: true, }), - refineText: vi.fn(), - getRefineErrorMessage: vi.fn((err: unknown) => (err as Error)?.message || "Failed"), fetchAgents: vi.fn().mockResolvedValue([]), + fetchWorkflowOptionalSteps: vi.fn().mockResolvedValue([]), uploadAttachment: vi.fn().mockResolvedValue({}), updateGlobalSettings: vi.fn().mockResolvedValue({}), })); diff --git a/packages/dashboard/app/components/QuickEntryBox.tsx b/packages/dashboard/app/components/QuickEntryBox.tsx index 7277b086a0..ad8745a4e5 100644 --- a/packages/dashboard/app/components/QuickEntryBox.tsx +++ b/packages/dashboard/app/components/QuickEntryBox.tsx @@ -5,8 +5,8 @@ import { createPortal } from "react-dom"; import type { ToastType } from "../hooks/useToast"; import { DEFAULT_TASK_PRIORITY, TASK_PRIORITIES, getErrorMessage } from "@fusion/core"; import type { Task, Settings, TaskPriority, ResolvedWorkflowOptionalStep } from "@fusion/core"; -import type { ModelInfo, RefinementType, Agent, CreateTaskInput, DuplicateMatch, BoardWorkflowDefinition, NodeInfo } from "../api"; -import { checkDuplicateTasks, fetchModels, fetchSettings, refineText, getRefineErrorMessage, updateGlobalSettings, fetchAgents, uploadAttachment, fetchWorkflowOptionalSteps } from "../api"; +import type { ModelInfo, Agent, CreateTaskInput, DuplicateMatch, BoardWorkflowDefinition, NodeInfo } from "../api"; +import { checkDuplicateTasks, fetchModels, fetchSettings, updateGlobalSettings, fetchAgents, uploadAttachment, fetchWorkflowOptionalSteps } from "../api"; import { DuplicateWarningModal } from "./DuplicateWarningModal"; import { Link, Paperclip, Brain, Lightbulb, ListTree, Sparkles, Save, ChevronDown, ChevronUp, ChevronRight, Bot, Server, Flag } from "lucide-react"; import { CustomModelDropdown } from "./CustomModelDropdown"; @@ -233,13 +233,6 @@ export function QuickEntryBox({ onCreate, addToast, tasks = [], availableModels, const shouldShowNodePicker = useMemo(() => hasMeaningfulNodeChoice(nodes), [nodes]); const selectedNode = shouldShowNodePicker && nodeId ? nodes.find((node) => node.id === nodeId) : undefined; const effectiveNodeId = shouldShowNodePicker && selectedNode ? selectedNode.id : undefined; - // AI Refinement state - const [isRefineMenuOpen, setIsRefineMenuOpen] = useState(false); - const [isRefining, setIsRefining] = useState(false); - const refineMenuRef = useRef(null); - const refineMenuPortalRef = useRef(null); - const [refineMenuPosition, setRefineMenuPosition] = useState<{ top: number; left: number } | null>(null); - useEffect(() => { if (shouldShowNodePicker && (!nodeId || selectedNode)) { return; @@ -508,24 +501,6 @@ export function QuickEntryBox({ onCreate, addToast, tasks = [], availableModels, if (!showDeps) setDepSearch(""); }, [showDeps]); - // Close refine menu when clicking outside - useEffect(() => { - if (!isRefineMenuOpen) return; - - const handleClickOutside = (e: MouseEvent) => { - const target = e.target as Node; - const clickedInsideTrigger = refineMenuRef.current?.contains(target); - const clickedInsidePortal = refineMenuPortalRef.current?.contains(target); - - if (!clickedInsideTrigger && !clickedInsidePortal) { - setIsRefineMenuOpen(false); - } - }; - - document.addEventListener("mousedown", handleClickOutside); - return () => document.removeEventListener("mousedown", handleClickOutside); - }, [isRefineMenuOpen]); - // Close model menu when clicking outside useEffect(() => { if (!isModelMenuOpen) return; @@ -642,8 +617,6 @@ export function QuickEntryBox({ onCreate, addToast, tasks = [], availableModels, setIsModelMenuOpen(false); setModelMenuPosition(null); setActiveModelSubmenu(null); - setIsRefineMenuOpen(false); - setIsRefining(false); justResetRef.current = true; if (textareaRef.current) { textareaRef.current.style.height = "auto"; @@ -919,10 +892,6 @@ export function QuickEntryBox({ onCreate, addToast, tasks = [], availableModels, setShowDeps(false); return; } - if (isRefineMenuOpen) { - setIsRefineMenuOpen(false); - return; - } if (showNodePicker) { setShowNodePicker(false); setNodePickerPosition(null); @@ -970,7 +939,6 @@ export function QuickEntryBox({ onCreate, addToast, tasks = [], availableModels, showNodePicker, isModelMenuOpen, activeModelSubmenu, - isRefineMenuOpen, showPriorityPicker, showWorkflowPicker, projectId, @@ -1081,44 +1049,6 @@ export function QuickEntryBox({ onCreate, addToast, tasks = [], availableModels, }); }, [getEffectiveViewport]); - const updateRefineMenuPosition = useCallback(() => { - const trigger = refineMenuRef.current?.querySelector(".refine-button") as HTMLElement | null; - if (!trigger) return; - - const rect = trigger.getBoundingClientRect(); - const { width: viewportWidth, height: viewportHeight, offsetTop, offsetLeft } = getEffectiveViewport(); - const horizontalPadding = 8; - const verticalPadding = 12; - const gap = 4; - const expectedMenuHeight = Math.min(200, Math.max(viewportHeight - verticalPadding * 2, 160)); - const menuWidth = Math.min(200, viewportWidth - horizontalPadding * 2); - - const triggerTop = rect.top - offsetTop; - const triggerBottom = rect.bottom - offsetTop; - const triggerLeft = rect.left - offsetLeft; - - const spaceBelow = viewportHeight - triggerBottom; - const spaceAbove = triggerTop; - const openUpward = spaceBelow < expectedMenuHeight && spaceAbove > spaceBelow; - - const left = Math.min( - Math.max(triggerLeft, horizontalPadding), - viewportWidth - horizontalPadding - menuWidth, - ) + offsetLeft; - - const top = openUpward - ? Math.max(verticalPadding + offsetTop, triggerTop - expectedMenuHeight - gap + offsetTop) - : Math.min( - triggerBottom + gap + offsetTop, - viewportHeight + offsetTop - verticalPadding - expectedMenuHeight, - ); - - setRefineMenuPosition({ - top, - left, - }); - }, [getEffectiveViewport]); - const updateWorkflowPickerPosition = useCallback(() => { const trigger = workflowTriggerRef.current; if (!trigger) return; @@ -1421,31 +1351,6 @@ export function QuickEntryBox({ onCreate, addToast, tasks = [], availableModels, }; }, [isModelMenuOpen, updateModelMenuPosition]); - // Keep refine menu portal anchored during scroll/resize - useEffect(() => { - if (!isRefineMenuOpen) return; - - const handleReposition = () => updateRefineMenuPosition(); - - window.addEventListener("resize", handleReposition); - window.addEventListener("scroll", handleReposition, true); - - const vv = window.visualViewport; - if (vv) { - vv.addEventListener("resize", handleReposition); - vv.addEventListener("scroll", handleReposition); - } - - return () => { - window.removeEventListener("resize", handleReposition); - window.removeEventListener("scroll", handleReposition, true); - if (vv) { - vv.removeEventListener("resize", handleReposition); - vv.removeEventListener("scroll", handleReposition); - } - }; - }, [isRefineMenuOpen, updateRefineMenuPosition]); - // Keep workflow picker portal anchored during scroll/resize useEffect(() => { if (!showWorkflowPicker) return; @@ -1659,29 +1564,6 @@ export function QuickEntryBox({ onCreate, addToast, tasks = [], availableModels, handleSubmit(); }, [handleSubmit]); - const handleRefine = useCallback(async (type: RefinementType) => { - const trimmed = description.trim(); - if (!trimmed || isRefining) return; - - setIsRefineMenuOpen(false); - setIsRefining(true); - try { - const refined = await refineText(trimmed, type, projectId); - setDescription(refined); - addToast(t("tasks.descriptionRefined", "Description refined with AI"), "success"); - // Auto-resize textarea after content update - if (textareaRef.current) { - textareaRef.current.style.height = "auto"; - textareaRef.current.style.height = `${Math.min(textareaRef.current.scrollHeight, 200)}px`; - } - } catch (err) { - const errorMessage = getRefineErrorMessage(err); - addToast(errorMessage, "error"); - } finally { - setIsRefining(false); - } - }, [description, isRefining, addToast, projectId]); - const truncate = (s: string, len: number) => s.length > len ? s.slice(0, len) + "…" : s; @@ -2080,78 +1962,10 @@ export function QuickEntryBox({ onCreate, addToast, tasks = [], availableModels, {t("tasks.subtask", "Subtask")} )} -
- - {isRefineMenuOpen && portalRoot && refineMenuPosition && createPortal( -
e.preventDefault()} - style={{ - position: "fixed", - top: `${refineMenuPosition.top}px`, - left: `${refineMenuPosition.left}px`, - }} - > -
handleRefine("clarify")} - data-testid="refine-clarify" - > -
{t("tasks.refineClarify", "Clarify")}
-
{t("tasks.refineClarifyDesc", "Make the description clearer and more specific")}
-
-
handleRefine("add-details")} - data-testid="refine-add-details" - > -
{t("tasks.refineAddDetails", "Add details")}
-
{t("tasks.refineAddDetailsDesc", "Add implementation details and context")}
-
-
handleRefine("expand")} - data-testid="refine-expand" - > -
{t("tasks.refineExpand", "Expand")}
-
{t("tasks.refineExpandDesc", "Expand into a more comprehensive description")}
-
-
handleRefine("simplify")} - data-testid="refine-simplify" - > -
{t("tasks.refineSimplify", "Simplify")}
-
{t("tasks.refineSimplifyDesc", "Simplify and make more concise")}
-
-
, - portalRoot, - )} -
- + {/* + FNXC:QuickAddRefine 2026-06-30-00:00: + Quick Add intentionally omits AI Refine so the compact create row has no refine button, menu, loading state, or /ai/refine-text path. New Task and TaskForm keep their dedicated refine affordance for richer task drafting. + */}