import { useState, useEffect, useRef } from 'react';
import { X } from 'lucide-react';
import { Dialog, DialogOverlay, DialogPortal } from '@/components/ui/dialog';
import * as DialogPrimitive from '@radix-ui/react-dialog';
import { cardStyle } from '@/components/Card';
import type { TaskInfo } from 'widgets/Chat';
import { useClaude } from '@/Screens/Dashboard/Chat/useClaude';
import { useOpenCode } from '@/Screens/Dashboard/Chat/useOpenCode';
import { EmbeddableChat } from '@/Screens/Dashboard/Chat/EmbeddableChat';
import { useVisibleClaudeModels, useVisibleOpenCodeModels } from '@/state/useModels';
import { useSettings } from '@/state/useSettings';
import type { TaskSummary } from 'widgets/FileBrowser';
const playDing = () => {
const ctx = new AudioContext();
const t = ctx.currentTime;
const play = (freq: number, start: number, dur: number) => {
const osc = ctx.createOscillator();
const gain = ctx.createGain();
osc.type = 'sine';
osc.frequency.setValueAtTime(freq, t + start);
gain.gain.setValueAtTime(0.8, t + start);
gain.gain.setValueAtTime(0.8, t + start + dur * 0.6);
gain.gain.exponentialRampToValueAtTime(0.001, t + start + dur);
osc.connect(gain).connect(ctx.destination);
osc.start(t + start);
osc.stop(t + start + dur);
};
play(880, 0, 0.4);
play(1100, 0.25, 0.4);
play(1320, 0.5, 0.6);
setTimeout(() => ctx.close(), 1500);
};
type InnerProps = {
defaultInput: string;
cwd: { root?: string; path: string };
initialModel: string | null;
onProviderChange: (p: 'claude' | 'opencode') => void;
};
const ClaudeInner = ({
defaultInput,
cwd,
initialModel,
onProviderChange,
taskInfo,
}: InnerProps & { taskInfo: TaskInfo }) => {
const chat = useClaude(undefined, initialModel, { replaceUrl: false, taskInfo });
const models = useVisibleClaudeModels();
const wasGenerating = useRef(false);
useEffect(() => {
if (wasGenerating.current && !chat.isGenerating) playDing();
wasGenerating.current = chat.isGenerating;
}, [chat.isGenerating]);
return (
);
};
const OpenCodeInner = ({
defaultInput,
cwd,
initialModel,
onProviderChange,
taskInfo,
}: InnerProps & { taskInfo: TaskInfo }) => {
const chat = useOpenCode(undefined, initialModel, { replaceUrl: false, taskInfo });
const models = useVisibleOpenCodeModels();
const wasGenerating = useRef(false);
useEffect(() => {
if (wasGenerating.current && !chat.isGenerating) playDing();
wasGenerating.current = chat.isGenerating;
}, [chat.isGenerating]);
return (
);
};
type TaskRunnerModalProps = {
open: boolean;
onOpenChange: (open: boolean) => void;
task: TaskSummary;
entryName: string;
entryType: 'file' | 'directory';
cwd: { root?: string; path: string };
};
export const TaskRunnerModal = ({ open, onOpenChange, task, entryName, entryType, cwd }: TaskRunnerModalProps) => {
const { settings } = useSettings();
const taskSettings = settings.tasks;
const [provider, setProvider] = useState<'claude' | 'opencode'>(taskSettings.defaultProvider);
const defaultInput = `Read the task instructions at ${task.filePath} and execute them on the ${entryType}: ${entryName}`;
const taskInfo: TaskInfo = { taskName: task.name, taskDirName: task.dirName, entryName, entryType };
return (
);
};