import { useCallback, useEffect, useMemo, useState } from "react"; import type { UserComposerAttachment } from "@/attachments/types"; import type { DraftAgentControlsProps } from "@/composer/agent-controls"; import type { DraftCommandConfig } from "@/hooks/use-agent-commands-query"; import { useAgentFormState, type CreateAgentInitialValues, type UseAgentFormStateResult, } from "@/hooks/use-agent-form-state"; import { useDraftAgentFeatures } from "@/hooks/use-draft-agent-features"; import { buildDraftAgentControls, hasDraftContent, resolveDraftKey, type DraftKeyInput, } from "@/composer/draft/input-draft-core"; import { buildDraftCommandConfig, resolveEffectiveComposerModelId, resolveEffectiveComposerThinkingOptionId, type ProviderSelectionState, } from "@/provider-selection/provider-selection"; import { useDraftStore } from "@/stores/draft-store"; import { toDraftInputIfReady } from "@/stores/draft-store/state"; type AttachmentUpdater = | UserComposerAttachment[] | ((prev: UserComposerAttachment[]) => UserComposerAttachment[]); interface AgentInputDraftComposerOptions { initialServerId: string | null; initialValues?: CreateAgentInitialValues; initialFeatureValues?: Record; isVisible?: boolean; onlineServerIds?: string[]; lockedWorkingDir?: string; } interface UseAgentInputDraftInput { draftKey: DraftKeyInput; composer?: AgentInputDraftComposerOptions; } type DraftComposerState = UseAgentFormStateResult & { workingDir: string; effectiveModelId: string; effectiveThinkingOptionId: string; featureValues: Record | undefined; agentControls: DraftAgentControlsProps; commandDraftConfig: DraftCommandConfig | undefined; }; export interface AgentInputDraft { text: string; setText: (text: string) => void; attachments: UserComposerAttachment[]; setAttachments: (updater: AttachmentUpdater) => void; clear: (lifecycle: "sent" | "abandoned") => void; isHydrated: boolean; attachmentFocusRequestId: number; composerState: DraftComposerState | null; } export function useAgentInputDraft(input: UseAgentInputDraftInput): AgentInputDraft { const composerOptions = input.composer ?? null; const formState = useAgentFormState({ initialServerId: composerOptions?.initialServerId ?? null, initialValues: composerOptions?.initialValues, isVisible: composerOptions?.isVisible ?? false, isCreateFlow: true, onlineServerIds: composerOptions?.onlineServerIds ?? [], }); const draftKey = useMemo( () => resolveDraftKey({ draftKey: input.draftKey, selectedServerId: formState.selectedServerId, }), [formState.selectedServerId, input.draftKey], ); const draftRecord = useDraftStore((state) => state.drafts[draftKey]); const draft = useMemo(() => toDraftInputIfReady(draftRecord), [draftRecord]); const attachmentFocusRequestId = useDraftStore( (state) => state.attachmentFocusRequestByDraftKey[draftKey] ?? 0, ); const [hydratedDraftKey, setHydratedDraftKey] = useState(null); const text = draft?.text ?? ""; const attachments = draft?.attachments ?? []; const isHydrated = hydratedDraftKey === draftKey; const saveDraft = useCallback( ( update: (draft: { text: string; attachments: UserComposerAttachment[] }) => { text: string; attachments: UserComposerAttachment[]; }, ) => { const store = useDraftStore.getState(); const current = store.getDraftInput(draftKey) ?? { text: "", attachments: [] }; const next = update(current); if (!hasDraftContent(next)) { store.clearDraftInput({ draftKey, lifecycle: "abandoned" }); return; } store.saveDraftInput({ draftKey, draft: next }); }, [draftKey], ); const setText = useCallback( (nextText: string) => { saveDraft((current) => ({ ...current, text: nextText })); }, [saveDraft], ); const setAttachments = useCallback( (updater: AttachmentUpdater) => { saveDraft((current) => ({ ...current, attachments: typeof updater === "function" ? updater(current.attachments) : updater, })); }, [saveDraft], ); const clear = useCallback( (lifecycle: "sent" | "abandoned") => { useDraftStore.getState().clearDraftInput({ draftKey, lifecycle }); }, [draftKey], ); useEffect(() => { let cancelled = false; void (async () => { await useDraftStore.getState().hydrateDraftInput({ draftKey }); if (!cancelled) { setHydratedDraftKey(draftKey); } })(); return () => { cancelled = true; }; }, [draftKey]); const lockedWorkingDir = composerOptions?.lockedWorkingDir?.trim() ?? ""; useEffect(() => { if (!composerOptions || !lockedWorkingDir) { return; } if (formState.workingDir.trim() === lockedWorkingDir) { return; } formState.setWorkingDir(lockedWorkingDir); }, [composerOptions, formState, lockedWorkingDir]); const providerSelection = useMemo( () => ({ provider: formState.selectedProvider, modelId: formState.selectedModel, modeId: formState.selectedMode, thinkingOptionId: formState.selectedThinkingOptionId, availableModels: formState.availableModels, modeOptions: formState.modeOptions, }), [ formState.availableModels, formState.modeOptions, formState.selectedMode, formState.selectedModel, formState.selectedProvider, formState.selectedThinkingOptionId, ], ); const effectiveModelId = useMemo( () => resolveEffectiveComposerModelId(providerSelection), [providerSelection], ); const effectiveThinkingOptionId = useMemo( () => resolveEffectiveComposerThinkingOptionId(providerSelection, effectiveModelId), [effectiveModelId, providerSelection], ); const workingDir = lockedWorkingDir || formState.workingDir; const { features: draftFeatures, featureValues: draftFeatureValues, setFeatureValue: setDraftFeatureValue, } = useDraftAgentFeatures({ serverId: formState.selectedServerId, provider: formState.selectedProvider, cwd: workingDir, modeId: formState.selectedMode, modelId: effectiveModelId, thinkingOptionId: effectiveThinkingOptionId, initialFeatureValues: composerOptions?.initialFeatureValues, }); const commandDraftConfig = useMemo( () => composerOptions ? buildDraftCommandConfig({ selection: providerSelection, cwd: workingDir, effectiveModelId, effectiveThinkingOptionId, featureValues: draftFeatureValues, }) : undefined, [ composerOptions, effectiveModelId, effectiveThinkingOptionId, draftFeatureValues, providerSelection, workingDir, ], ); const composerState = useMemo(() => { if (!composerOptions) { return null; } return { ...formState, workingDir, effectiveModelId, effectiveThinkingOptionId, featureValues: draftFeatureValues, agentControls: buildDraftAgentControls({ formState, features: draftFeatures, onSetFeature: setDraftFeatureValue, }), commandDraftConfig, }; }, [ commandDraftConfig, composerOptions, effectiveModelId, effectiveThinkingOptionId, draftFeatures, draftFeatureValues, formState, setDraftFeatureValue, workingDir, ]); return { text, setText, attachments, setAttachments, clear, isHydrated, attachmentFocusRequestId, composerState, }; } export const __private__ = { resolveDraftKey, resolveEffectiveComposerModelId, resolveEffectiveComposerThinkingOptionId, buildDraftCommandConfig, buildDraftComposerCommandConfig: buildDraftCommandConfig, buildDraftAgentControls, };